aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocumentTypes.ts3
-rw-r--r--src/client/documents/Documents.ts26
-rw-r--r--src/client/util/CurrentUserUtils.ts60
-rw-r--r--src/client/util/Scripting.ts2
-rw-r--r--src/client/util/SearchUtil.ts3
-rw-r--r--src/client/views/DocComponent.tsx27
-rw-r--r--src/client/views/EditableView.tsx41
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/MainView.tsx108
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx66
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx79
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx18
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss15
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx36
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx13
-rw-r--r--src/client/views/collections/CollectionSubView.tsx10
-rw-r--r--src/client/views/collections/CollectionView.tsx9
-rw-r--r--src/client/views/collections/SchemaTable.tsx33
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx5
-rw-r--r--src/client/views/nodes/FieldView.tsx7
-rw-r--r--src/client/views/nodes/LabelBox.tsx19
-rw-r--r--src/client/views/nodes/QueryBox.tsx71
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx48
-rw-r--r--src/client/views/pdf/PDFViewer.tsx12
-rw-r--r--src/client/views/search/SearchBox.scss72
-rw-r--r--src/client/views/search/SearchBox.tsx713
-rw-r--r--src/client/views/search/SearchItem.scss33
-rw-r--r--src/client/views/search/SearchItem.tsx330
-rw-r--r--src/fields/Doc.ts2
-rw-r--r--src/server/ApiManagers/SearchManager.ts2
-rw-r--r--src/server/ApiManagers/UploadManager.ts36
-rw-r--r--src/server/websocket.ts19
32 files changed, 1508 insertions, 412 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 985fcce11..71d6c2ccc 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -13,7 +13,7 @@ export enum DocumentType {
INK = "ink", // ink stroke
SCREENSHOT = "screenshot", // view of a desktop application
FONTICON = "fonticonbox", // font icon
- QUERY = "query", // search query
+ SEARCH = "search", // search query
LABEL = "label", // simple text label
BUTTON = "button", // onClick button
WEBCAM = "webcam", // webcam
@@ -31,6 +31,7 @@ export enum DocumentType {
COLOR = "color", // color picker (view of a color picker for a color string)
YOUTUBE = "youtube", // youtube directory (view of you tube search results)
DOCHOLDER = "docholder", // nested document (view of a document)
+ SEARCHITEM= "searchitem",
COMPARISON = "comparison", // before/after view with slider (view of 2 images)
GROUP = "group", // group of users
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 40eae0528..a71450319 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -21,6 +21,9 @@ import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
import { LinkManager } from "../util/LinkManager";
import { Scripting } from "../util/Scripting";
import { UndoManager } from "../util/UndoManager";
+import { DocumentType } from "./DocumentTypes";
+import { SearchItem } from "../views/search/SearchItem";
+import { SearchBox, filterData } from "../views/search/SearchBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
import { ContextMenu } from "../views/ContextMenu";
@@ -38,7 +41,6 @@ import { LabelBox } from "../views/nodes/LabelBox";
import { LinkBox } from "../views/nodes/LinkBox";
import { PDFBox } from "../views/nodes/PDFBox";
import { PresBox } from "../views/nodes/PresBox";
-import { QueryBox } from "../views/nodes/QueryBox";
import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
import { ScriptingBox } from "../views/nodes/ScriptingBox";
import { SliderBox } from "../views/nodes/SliderBox";
@@ -186,10 +188,11 @@ export interface DocumentOptions {
flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse";
selectedIndex?: number;
syntaxColor?: string; // can be applied to text for syntax highlighting all matches in the text
- searchText?: string; //for searchbox
- searchQuery?: string; // for queryBox
- filterQuery?: string;
+ searchQuery?: string, // for quersyBox
+ filterQuery?: filterData,
linearViewIsExpanded?: boolean; // is linear view expanded
+ border?: string; //for searchbox
+ hovercolor?: string;
}
class EmptyBox {
@@ -219,8 +222,8 @@ export namespace Docs {
layout: { view: FormattedTextBox, dataField: "text" },
options: { _height: 150, _xMargin: 10, _yMargin: 10 }
}],
- [DocumentType.QUERY, {
- layout: { view: QueryBox, dataField: defaultDataKey },
+ [DocumentType.SEARCH, {
+ layout: { view: SearchBox, dataField: defaultDataKey },
options: { _width: 400 }
}],
[DocumentType.COLOR, {
@@ -306,6 +309,9 @@ export namespace Docs {
[DocumentType.PRESELEMENT, {
layout: { view: PresElementBox, dataField: defaultDataKey }
}],
+ [DocumentType.SEARCHITEM, {
+ layout: { view: SearchItem, dataField: defaultDataKey }
+ }],
[DocumentType.INK, {
layout: { view: InkingStroke, dataField: defaultDataKey },
options: { backgroundColor: "transparent" }
@@ -623,8 +629,8 @@ export namespace Docs {
return instance;
}
- export function QueryDocument(options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.QUERY), "", options);
+ export function SearchDocument(options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.SEARCH), new List<Doc>([]), options);
}
export function ColorDocument(options: DocumentOptions = {}) {
@@ -790,6 +796,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
}
+ export function SearchItemBoxDocument(options?: DocumentOptions) {
+ return InstanceFromProto(Prototypes.get(DocumentType.SEARCHITEM), undefined, { ...(options || {}) });
+ }
+
export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
Doc.GetProto(inst).data = new List<Doc>(documents);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6d752832a..2d95d081e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -43,7 +43,7 @@ export class CurrentUserUtils {
if (doc["template-button-query"] === undefined) {
const queryTemplate = Docs.Create.MulticolumnDocument(
[
- Docs.Create.QueryDocument({ title: "query", _height: 200 }),
+ Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, lockedPosition: true, title: "query", _height: 200 }),
Docs.Create.FreeformDocument([], { title: "data", _height: 100 })
],
{ _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true }
@@ -411,13 +411,10 @@ export class CurrentUserUtils {
if (doc.emptyButton === undefined) {
doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button" });
}
- if (doc.emptySearch === undefined) {
- doc.emptySearch = Docs.Create.QueryDocument({ _width: 200, title: "empty search" });
- }
if (doc.emptyDocHolder === undefined) {
- doc.emptyDocHolder = Docs.Create.DocumentDocument(
- ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
- { _width: 250, _height: 250, title: "container" });
+ // doc.emptyDocHolder = Docs.Create.DocumentDocument(
+ // ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
+ // { _width: 250, _height: 250, title: "container" });
}
if (doc.emptyWebpage === undefined) {
doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 600, UseCors: true });
@@ -426,21 +423,20 @@ export class CurrentUserUtils {
this.setupActiveMobileMenu(doc);
}
return [
- { toolTip: "Drag a collection", title: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
- { toolTip: "Drag a web page", title: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc },
- { toolTip: "Drag a cat image", title: "Image", icon: "cat", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyImage as Doc },
- { toolTip: "Drag a comparison box", title: "Comp", icon: "columns", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyComparison as Doc },
- { toolTip: "Drag a screengrabber", title: "Grab", icon: "photo-video", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScreenshot as Doc },
- // { title: "Drag a webcam", title: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
- { toolTip: "Drag a audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyAudio as Doc },
- { toolTip: "Drag a button", title: "Button", icon: "bolt", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyButton as Doc },
-
- { toolTip: "Drag a presentation view", title: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true)`, dragFactory: doc.emptyPresentation as Doc },
- { toolTip: "Drag a search box", title: "Query", icon: "search", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptySearch as Doc },
- { toolTip: "Drag a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScript as Doc },
- // { title: "Drag an import folder", title: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
- { toolTip: "Drag a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
- // { title: "Drag an instance of the device collection", title: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
+ { title: "Drag a comparison box", toolTip: "columns", icon: "columns", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyComparison as Doc },
+ { title: "Drag a collection", toolTip: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc },
+ { title: "Drag a web page", toolTip: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc },
+ { title: "Drag a cat image", toolTip: "Img", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth:250, title: "an image of a cat" })' },
+ { title: "Drag a screenshot", toolTip: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' },
+ { title: "Drag a webcam", toolTip: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
+ { title: "Drag a audio recorder", toolTip: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
+ { title: "Drag a clickable button", toolTip: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding:10, _yPadding: 10, title: "Button" })' },
+ { title: "Drag a presentation view", toolTip: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc },
+ { title: "Drag a search box", toolTip: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.SearchDocument({ _width: 200, title: "an image of a cat" })' },
+ { title: "Drag a scripting box", toolTip: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' },
+ { title: "Drag an import folder", toolTip: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
+ { title: "Drag a mobile view", toolTip: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
+ { title: "Drag an instance of the device collection", toolTip: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
// { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
// { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
// { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
@@ -705,7 +701,7 @@ export class CurrentUserUtils {
doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({
_width: 50, _height: 25, title: "Search", _fontSize: "10pt",
letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
- sourcePanel: new PrefetchProxy(Docs.Create.QueryDocument({ title: "search stack", })) as any as Doc,
+ sourcePanel: new PrefetchProxy(Docs.Create.SearchDocument({ ignoreClick: true, childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", })) as any as Doc,
searchFileTypes: new List<string>([DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.VID, DocumentType.WEB, DocumentType.SCRIPTING]),
targetContainer: new PrefetchProxy(sidebarContainer) as any as Doc,
lockedPosition: true,
@@ -731,7 +727,9 @@ export class CurrentUserUtils {
const toolsBtn = await CurrentUserUtils.setupToolsBtnPanel(doc, sidebarContainer);
const libraryBtn = CurrentUserUtils.setupLibraryPanel(doc, sidebarContainer);
const searchBtn = CurrentUserUtils.setupSearchBtnPanel(doc, sidebarContainer);
-
+ if (doc["search-panel"] === undefined) {
+ doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({ _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", })) as any as Doc;
+ }
// Finally, setup the list of buttons to display in the sidebar
if (doc["tabs-buttons"] === undefined) {
doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], {
@@ -740,6 +738,15 @@ export class CurrentUserUtils {
}));
(toolsBtn.onClick as ScriptField).script.run({ this: toolsBtn });
}
+
+
+
+ // new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], {
+ // _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode",
+ // title: "sidebar btn row stack", backgroundColor: "dimGray",
+ // }));
+
+
}
static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
@@ -773,6 +780,11 @@ export class CurrentUserUtils {
// the initial presentation Doc to use
static setupDefaultPresentation(doc: Doc) {
+ if (doc["template-presentation"] === undefined) {
+ doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
+ title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
+ }));
+ }
if (doc.activePresentation === undefined) {
doc.activePresentation = Docs.Create.PresDocument(new List<Doc>(), {
title: "Presentation", _viewType: CollectionViewType.Stacking, targetDropAction: "alias",
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index f1e6155d2..cb0a4bea0 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -134,7 +134,7 @@ export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
Scripting.addGlobal(constructor);
}
-const _scriptingGlobals: { [name: string]: any } = {};
+export const _scriptingGlobals: { [name: string]: any } = {};
let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
const _scriptingDescriptions: { [name: string]: any } = {};
const _scriptingParams: { [name: string]: any } = {};
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 0a01d8ac7..911340ab1 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -4,6 +4,7 @@ import { Doc } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
import { Utils } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
+import { StringMap } from 'libxmljs';
export namespace SearchUtil {
export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
@@ -29,6 +30,8 @@ export namespace SearchUtil {
rows?: number;
fq?: string;
allowAliases?: boolean;
+ "facet"?:string;
+ "facet.field"?: string;
}
export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 4c82149e2..177c1b5ee 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -152,25 +152,14 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
if (added.length) {
if (effectiveAcl === AclPrivate || (effectiveAcl === AclReadonly && !getPlaygroundMode())) {
return false;
- }
- else {
- if (this.props.Document[AclSym]) {
- added.forEach(d => {
- const dataDoc = d[DataSym];
- dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym];
- for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
- dataDoc[key] = d[key] = this.AclMap.get(value);
- }
- });
- }
- if (effectiveAcl === AclAddonly) {
- added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
- }
- else {
- added.map(doc => doc.context = this.props.Document);
- targetDataDoc[this.annotationKey] = new List<Doc>([...docList, ...added]);
- targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
- }
+ } else if (this.dataDoc[AclSym] === AclAddonly) {
+ added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
+ } else {
+ added.map(doc => doc.context = this.props.Document);
+ targetDataDoc[this.annotationKey] = new List<Doc>([...docList, ...added]);
+ targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ targetDataDoc["lastModified"] = new DateField(new Date(Date.now()));
+
}
}
return true;
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ad61d3f91..0435e70c4 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -6,6 +6,8 @@ import { ObjectField } from '../../fields/ObjectField';
import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import "./EditableView.scss";
import { DragManager } from '../util/DragManager';
+import { ComputedField } from '../../fields/ScriptField';
+import { FieldValue } from '../../fields/Types';
export interface EditableProps {
/**
@@ -52,6 +54,10 @@ export interface EditableProps {
color?: string | undefined;
onDrop?: any;
placeholder?: string;
+ highlight?: boolean;
+ positions?: number[];
+ search?: string;
+ bing?: () => string | undefined;
}
/**
@@ -179,6 +185,34 @@ export class EditableView extends React.Component<EditableProps> {
placeholder={this.props.placeholder}
/>;
}
+
+ returnHighlights() {
+ let results = [];
+ let contents = this.props.bing!();
+
+ if (contents !== undefined) {
+ if (this.props.positions !== undefined) {
+ let positions = this.props.positions;
+ let length = this.props.search!.length;
+
+ // contents = String(this.props.contents.valueOf());
+
+ results.push(<span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(0, this.props.positions![0]) : this.props.placeholder?.valueOf()}</span>);
+ positions.forEach((num, cur) => {
+ results.push(<span style={{ backgroundColor: "#FFFF00", fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(num, num + length) : this.props.placeholder?.valueOf()}</span>);
+ let end = 0;
+ cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1];
+ results.push(<span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{contents ? contents.slice(num + length, end) : this.props.placeholder?.valueOf()}</span>);
+ }
+ )
+ }
+ return results;
+ }
+ else {
+ return <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>;
+ }
+ }
+
render() {
if (this._editing && this.props.GetValue() !== undefined) {
return this.props.sizeToContent ?
@@ -193,11 +227,8 @@ export class EditableView extends React.Component<EditableProps> {
ref={this._ref}
style={{ display: this.props.display, minHeight: "20px", height: `${this.props.height ? this.props.height : "auto"}`, maxHeight: `${this.props.maxHeight}` }}
onClick={this.onClick} placeholder={this.props.placeholder}>
- <span style={{
- fontStyle: this.props.fontStyle, fontSize: this.props.fontSize,
- color: this.props.contents ? this.props.color ? this.props.color : "black" : "grey"
- }}>
- {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
+ {this.props.highlight === undefined || this.props.positions === undefined || this.props.bing === undefined ? <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize, color: this.props.contents ? "black" : "grey" }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span>
+ : this.returnHighlights()}
</div>
);
}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c9f95a538..49ddc7374 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -320,6 +320,8 @@ export default class KeyManager {
undoBatch(() => {
targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ targetDataDoc["lastModified"] = new DateField(new Date(Date.now()));
+
})();
}
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index bb73df75c..8995aa78b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -52,6 +52,7 @@ import { DocumentManager } from '../util/DocumentManager';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { LinkMenu } from './linking/LinkMenu';
import { LinkDocPreview } from './nodes/LinkDocPreview';
+import { SearchBox } from './search/SearchBox';
import { TaskCompletionBox } from './nodes/TaskCompletedBox';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
@@ -77,6 +78,8 @@ export class MainView extends React.Component {
@computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
@computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
@computed public get sidebarButtonsDoc() { return Cast(this.userDoc["tabs-buttons"], Doc) as Doc; }
+ @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; }
+
public isPointerDown = false;
@@ -159,10 +162,24 @@ export class MainView extends React.Component {
const targets = document.elementsFromPoint(e.x, e.y);
if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) {
ContextMenu.Instance.closeMenu();
+ //SearchBox.Instance.closeSearch();
}
if (targets && (targets.length && targets[0].className.toString() !== "timeline-menu-desc" && targets[0].className.toString() !== "timeline-menu-item" && targets[0].className.toString() !== "timeline-menu-input")) {
TimelineMenu.Instance.closeMenu();
}
+ if (targets && targets.length && SearchBox.Instance._searchbarOpen) {
+ let check = false;
+ targets.forEach((thing) => {
+ if (thing.className.toString() === "collectionSchemaView-table" || thing.className.toString() === "beta" || thing.className.toString() === "collectionSchemaView-menuOptions-wrapper") {
+ check = true;
+ }
+ });
+ if (check === false) {
+ SearchBox.Instance.closeSearch();
+ }
+ }
+
+
});
globalPointerUp = () => this.isPointerDown = false;
@@ -306,6 +323,39 @@ export class MainView extends React.Component {
}
}
}
+
+ @computed get search() {
+ return <DocumentView Document={this.searchDoc!}
+ DataDoc={undefined}
+ LibraryPath={emptyPath}
+ addDocument={undefined}
+ addDocTab={this.addDocTabFunc}
+ pinToPres={emptyFunction}
+ rootSelected={returnTrue}
+ onClick={undefined}
+ backgroundColor={this.defaultBackgroundColors}
+ removeDocument={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ ContentScaling={returnOne}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
+ PanelWidth={this.getPWidth}
+ PanelHeight={this.getPHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ parentActive={returnTrue}
+ whenActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={returnEmptyFilter}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />;
+ }
+
+
+
+
+
@computed get mainDocView() {
return <DocumentView Document={this.mainContainer!}
DataDoc={undefined}
@@ -333,6 +383,7 @@ export class MainView extends React.Component {
ContainingCollectionDoc={undefined}
/>;
}
+
@computed get dockingContent() {
TraceMobx();
const mainContainer = this.mainContainer;
@@ -466,34 +517,39 @@ export class MainView extends React.Component {
const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0);
const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`;
return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
- <div className="mainView-mainContent" style={{
- color: this.darkScheme ? "rgb(205,205,205)" : "black",
- //change to times 2 for both pinned
- height,
- width: (FormatShapePane.Instance?.Pinned) ? `calc(100% - 200px)` : "100%"
- }} >
- <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
- <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
- <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown}
- style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
- <span title="library View Dragger" style={{
- width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
- //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
- position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
- top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
- }} />
- </div>
- <div className="mainView-libraryFlyout" style={{
- //transformOrigin: this._flyoutTranslate ? "" : "left center",
- transition: this._flyoutTranslate ? "" : "width .5s",
- //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
- boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
- }}>
- {this.flyout}
- {this.expandButton}
+ <div>
+ <div style={{ height: "32px", width: "100%", backgroundColor: "black" }}>
+ {this.search}
+ </div>
+ <div className="mainView-mainContent" style={{
+ color: this.darkScheme ? "rgb(205,205,205)" : "black",
+ //change to times 2 for both pinned
+ height,
+ width: (FormatShapePane.Instance?.Pinned) ? `calc(100% - 200px)` : "100%"
+ }} >
+ <div style={{ display: "contents", flexDirection: "row", position: "relative" }}>
+ <div className="mainView-flyoutContainer" onPointerLeave={this.pointerLeaveDragger} style={{ width: this.flyoutWidth }}>
+ <div className="mainView-libraryHandle" onPointerDown={this.onPointerDown}
+ style={{ backgroundColor: this.defaultBackgroundColors(sidebar) }}>
+ <span title="library View Dragger" style={{
+ width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw",
+ //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh",
+ position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed",
+ top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
+ }} />
+ </div>
+ <div className="mainView-libraryFlyout" style={{
+ //transformOrigin: this._flyoutTranslate ? "" : "left center",
+ transition: this._flyoutTranslate ? "" : "width .5s",
+ //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+ boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
+ }}>
+ {this.flyout}
+ {this.expandButton}
+ </div>
</div>
+ {this.dockingContent}
</div>
- {this.dockingContent}
</div>
</div>);
}
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index eecaf7672..bf826857e 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -32,6 +32,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { DateField } from "../../../fields/DateField";
+import { indexOf } from "lodash";
const path = require('path');
library.add(faExpand);
@@ -193,7 +194,8 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc);
const onItemDown = (e: React.PointerEvent) => {
- fieldIsDoc && SetupDrag(this._focusRef,
+ //fieldIsDoc &&
+ SetupDrag(this._focusRef,
() => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document,
this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument,
this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e);
@@ -241,24 +243,75 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// </div>
// );
trace();
-
-
-
+ let positions = [];
+ if (StrCast(this.props.Document._searchString) !== "") {
+ 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 search = StrCast(this.props.Document._searchString)
+ let start = term.indexOf(search) as number;
+ let tally = 0;
+ if (start!==-1){
+ positions.push(start);
+ }
+ while (start < contents.length && start !== -1) {
+ term = term.slice(start + search.length + 1);
+ tally += start + search.length + 1;
+ start = term.indexOf(search);
+ positions.push(tally + start);
+ }
+ if (positions.length > 1) {
+ positions.pop();
+ }
+ }
return (
<div className="collectionSchemaView-cellContainer" style={{ cursor: fieldIsDoc ? "grab" : "auto" }} ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
<div className={className} ref={this._focusRef} onPointerDown={onItemDown} tabIndex={-1}>
<div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}>
-
-
<EditableView
+ positions={positions.length > 0 ? positions : undefined}
+ search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined}
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
display={"inline"}
contents={contents ? contents : type === "number" ? "0" : "undefined"}
+ highlight={positions.length > 0 ? true : undefined}
//contents={StrCast(contents)}
height={"auto"}
maxHeight={Number(MAX_ROW_HEIGHT)}
placeholder={"enter value"}
+ bing={() => {
+ // if (type === "number" && (contents === 0 || contents === "0")) {
+ // return "0";
+ // } else {
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
+ if (cfield!==undefined){
+ if (cfield.Text!==undefined){
+ return(cfield.Text);
+ }
+ else if (StrCast(cfield)){
+ return StrCast(cfield);
+ }
+ else {
+ return String(NumCast(cfield));
+ }
+ }
+ // console.log(cfield.Text);
+ // console.log(StrCast(cfield));
+ // return StrCast(cfield);
+ // }
+
+ }}
GetValue={() => {
if (type === "number" && (contents === 0 || contents === "0")) {
return "0";
@@ -272,6 +325,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
return val;
+
}
}}
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index efff4db98..0ee225407 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -9,6 +9,8 @@ import { ColumnType } from "./CollectionSchemaView";
import { faFile } from "@fortawesome/free-regular-svg-icons";
import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
+import { Doc } from "../../../fields/Doc";
+import { StrCast } from "../../../fields/Types";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -288,9 +290,10 @@ export interface KeysDropdownProps {
existingKeys: string[];
canAddNew: boolean;
addNew: boolean;
- onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
+ onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void;
setIsEditing: (isEditing: boolean) => void;
width?: string;
+ docs?: Doc[];
}
@observer
export class KeysDropdown extends React.Component<KeysDropdownProps> {
@@ -306,14 +309,29 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@action
onSelect = (key: string): void => {
- this.props.onSelect(this._key, key, this.props.addNew);
- this.setKey(key);
+ if (key.slice(0, this._key.length) === this._key && this._key !== key) {
+ let filter = key.slice(this._key.length - key.length);
+ this.props.onSelect(this._key, this._key, this.props.addNew, filter);
+ }
+ else {
+ this.props.onSelect(this._key, key, this.props.addNew);
+ this.setKey(key);
+ this._isOpen = false;
+ this.props.setIsEditing(false);
+ }
+ }
+
+ @action
+ onSelect2 = (key: string): void => {
+ this._searchTerm = this._searchTerm.slice(0, this._key.length) + key;
this._isOpen = false;
- this.props.setIsEditing(false);
+
}
@undoBatch
onKeyDown = (e: React.KeyboardEvent): void => {
+ //if (this._key !==)
+
if (e.key === "Enter") {
const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
if (keyOptions.length) {
@@ -371,22 +389,56 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
});
// if search term does not already exist as a group type, give option to create new group type
- if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
- options.push(<div key={""} className="key-option" style={{
+ if (this._key !== this._searchTerm.slice(0, this._key.length)) {
+ if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) {
+ options.push(<div key={""} className="key-option" style={{
+ border: "1px solid lightgray",
+ width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
+ }}
+ onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
+ Create "{this._searchTerm}" key</div>);
+ }
+ }
+
+ return options;
+ }
+
+ renderFilterOptions = (): JSX.Element[] | JSX.Element => {
+ if (!this._isOpen) return <></>;
+
+ const keyOptions: string[] = [];
+ console.log(this._searchTerm.slice(this._key.length))
+ let temp = this._searchTerm.slice(this._key.length);
+ this.props.docs?.forEach((doc) => {
+ let key = StrCast(doc[this._key]);
+ if (keyOptions.includes(key) === false && key.includes(temp)) {
+ keyOptions.push(key);
+ }
+ });
+
+
+ const options = keyOptions.map(key => {
+ return <div key={key} className="key-option" style={{
border: "1px solid lightgray",
width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
}}
- onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
- Create "{this._searchTerm}" key</div>);
- }
+ onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect2(key); }}>{key}</div>;
+ });
return options;
}
+
render() {
+ console.log(this.props.docs);
return (
- <div className="keys-dropdown" style={{ width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" }}>
- <input className="keys-search" //style={{ width: this.props.width, maxWidth: "1000" }}
+ <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}>
+ {this._key === this._searchTerm.slice(0, this._key.length) ?
+ <div style={{ position: "absolute", marginLeft: "4px", marginTop: "3", color: "grey", pointerEvents: "none", lineHeight: 1.15 }}>
+ {this._key}
+ </div>
+ : undefined}
+ <input className="keys-search" style={{ width: "100%" }}
ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown}
onChange={e => this.onChange(e.target.value)}
onClick={(e) => {
@@ -395,10 +447,11 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}} onFocus={this.onFocus} onBlur={this.onBlur}></input>
<div className="keys-options-wrapper" style={{
backgroundColor: "white",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden"
+ width: this.props.width, maxWidth: this.props.width,
}}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}>
- {this.renderOptions()}
+ {this._key === this._searchTerm.slice(0, this._key.length) ?
+ this.renderFilterOptions() : this.renderOptions()}
</div>
</div >
);
diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
index b77173b25..dade4f2f2 100644
--- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
+++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx
@@ -209,6 +209,14 @@ export class MovableRow extends React.Component<MovableRowProps> {
return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
}
+ @action
+ onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ console.log("yes");
+ if (e.key === "Backspace" || e.key === "Delete") {
+ undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
+ }
+ }
+
render() {
const { children = null, rowInfo } = this.props;
if (!rowInfo) {
@@ -227,14 +235,14 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (this.props.rowWrapped) className += " row-wrapped";
return (
- <div className={className} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
- <div className="collectionSchema-row-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- <ReactTableDefaults.TrComponent>
- <div className="row-dragger">
+ <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
+ <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
+ {/* <div className="row-dragger">
<div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
<div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
<div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "onRight")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
- </div>
+ </div> */}
{children}
</ReactTableDefaults.TrComponent>
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index 5226a60f1..93878d799 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -6,7 +6,7 @@
border-style: solid;
border-radius: $border-radius;
box-sizing: border-box;
- position: absolute;
+ position: relative;
top: 0;
width: 100%;
height: 100%;
@@ -25,7 +25,6 @@
.collectionSchemaView-tableContainer {
width: 100%;
height: 100%;
- overflow: scroll;
}
.collectionSchemaView-dividerDragger {
@@ -59,9 +58,7 @@
}
.rt-thead {
- width: calc(100% - 52px);
- margin-left: 50px;
-
+ width: 100%;
z-index: 100;
overflow-y: visible;
@@ -165,7 +162,7 @@
.collectionSchema-col {
height: 100%;
- .collectionSchema-col-wrapper {
+ .collectionSchema-apper {
&.col-before {
border-left: 2px solid red;
}
@@ -297,7 +294,6 @@ button.add-column {
background-color: white;
border: 1px solid lightgray;
padding: 2px 3px;
- overflow-x: hidden;
&:not(:first-child) {
border-top: 0;
@@ -525,14 +521,13 @@ button.add-column {
.collectionSchemaView-table {
width: 100%;
height: 100%;
- overflow: visible;
}
.reactTable-sub {
padding: 10px 30px;
background-color: rgb(252, 252, 252);
- width: calc(100% - 50px);
- margin-left: 50px;
+ width: 100%;
+
.row-dragger {
background-color: rgb(252, 252, 252);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 5553bbbb7..11382b722 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -6,7 +6,7 @@ import { action, computed, observable, untracked } from "mobx";
import { observer } from "mobx-react";
import { Resize } from "react-table";
import "react-table/react-table.css";
-import { Doc } from "../../../fields/Doc";
+import { Doc, DocCastAsync } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField";
@@ -170,6 +170,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
const columns = this.columns;
+ columns.forEach(col => {
+ col.setDesc(undefined);
+ })
+
const index = columns.findIndex(c => c.heading === columnField.heading);
const column = columns[index];
column.setDesc(descending);
@@ -310,7 +314,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@undoBatch
@action
- changeColumns = (oldKey: string, newKey: string, addNew: boolean) => {
+ changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
const columns = this.columns;
if (columns === undefined) {
this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
@@ -325,6 +329,22 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
column.setHeading(newKey);
columns[index] = column;
this.columns = columns;
+ if (filter) {
+ console.log(newKey);
+ console.log(filter);
+ Doc.setDocFilter(this.props.Document, newKey, filter, "match");
+ if (this.props.Document.selectedDoc !== undefined) {
+ let doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
+ Doc.setDocFilter(doc, newKey, filter, "match");
+ }
+ }
+ else {
+ this.props.Document._docFilters = undefined;
+ if (this.props.Document.selectedDoc !== undefined) {
+ let doc = Cast(this.props.Document.selectedDoc, Doc) as Doc;
+ doc._docFilters = undefined;
+ }
+ }
}
}
}
@@ -442,6 +462,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
this.props.select(false);
}
}
+ console.log("yeeeet");
}
@computed
@@ -591,6 +612,10 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
}
}
+
+
+ onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
+ }
render() {
TraceMobx();
const menuContent = this.renderMenuContent;
@@ -608,14 +633,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
{({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
</Measure>
</div>;
-
return <div className="collectionSchemaView-container"
style={{
+ overflow: this.props.overflow === true ? "auto" : undefined,
pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%"
+ width: this.props.PanelWidth() || "100%", height: this.props.PanelPosition === "absolute" ? this.props.PanelHeight() : this.props.PanelHeight() || "100%", top: this.props.PanelPosition === "absolute" ? 52 : 0, position: this.props.PanelPosition || "relative",
}} >
<div className="collectionSchemaView-tableContainer"
- style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
+ style={{ backgroundColor: "white", width: `calc(100% - ${this.previewWidth()}px)` }}
+ onKeyPress={this.onKeyPress}
onPointerDown={this.onPointerDown}
onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.onExternalDrop(e, {})}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0332b4bf2..1c5cf4290 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -46,10 +46,12 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
@computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField)); }
@computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); }
- @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, NumCast(this.layoutDoc._yMargin, 0)); } // 2 * this.gridGap)); }
- @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
- @computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); }
+ @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
+ @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); }
+ @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); }
+ @computed get searchDoc() { return BoolCast(this.props.Document._searchDoc, false); }
+
+ @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); }
@computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
@computed get showAddAGroup() { return (this.pivotField && (this.layoutDoc._chromeStatus !== 'view-mode' && this.layoutDoc._chromeStatus !== 'disabled')); }
@computed get columnWidth() {
@@ -76,7 +78,8 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
const dxf = () => this.getDocTransform(d, dref.current!);
this._docXfs.push({ dxf, width, height });
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+
+ const style = this.isStackingView ? { width: width(), marginTop: i || this.searchDoc ? this.gridGap : 0, marginBottom: this.searchDoc ? 10 : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} ref={dref} style={style} >
{this.getDisplayDoc(d, (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc, dxf, width)}
</div>;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9f78c15eb..dacb06e5b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,7 +1,7 @@
import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
import { basename } from 'path';
import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field } from "../../../fields/Doc";
+import { Doc, Opt, Field, DocListCast } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
@@ -126,8 +126,14 @@ 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 searchDocs = DocListCast(this.props.Document._searchDocs);
+ if (searchDocs !== undefined && searchDocs.length > 0) {
+ childDocs = searchDocs;
+ }
const docFilters = this.docFilters();
- const viewSpecScript = ScriptCast(this.props.Document.viewSpecScript);
const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
return this.props.Document.dontRegisterView ? docs : DocUtils.FilterDocs(docs, docFilters, docRangeFilters, viewSpecScript);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 42d320308..d41248a77 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -74,7 +74,7 @@ export enum CollectionViewType {
Pile = "pileup"
}
export interface CollectionViewCustomProps {
- filterAddDocument: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+ filterAddDocument?: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
childLayoutTemplate?: () => Opt<Doc>; // specify a layout Doc template to use for children of the collection
childLayoutString?: string; // specify a layout string to use for children of the collection
childOpacity?: () => number;
@@ -87,6 +87,7 @@ export interface CollectionRenderProps {
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
PanelWidth: () => number;
+ PanelHeight: () => number;
ChildLayoutTemplate?: () => Doc;
ChildLayoutString?: string;
}
@@ -181,6 +182,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ targetDataDoc["lastModified"] = new DateField(new Date(Date.now()));
+
}
}
}
@@ -518,6 +521,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
</div>
<div className="collectionTimeView-tree" key="tree">
<CollectionTreeView
+ PanelPosition={""}
Document={facetCollection}
DataDoc={facetCollection}
fieldKey={`${this.props.fieldKey}-filter`}
@@ -568,6 +572,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
active: this.active,
whenActiveChanged: this.whenActiveChanged,
PanelWidth: this.bodyPanelWidth,
+ PanelHeight: this.props.PanelHeight,
ChildLayoutTemplate: this.childLayoutTemplate,
ChildLayoutString: this.childLayoutString,
};
@@ -576,7 +581,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
style={{ pointerEvents: this.props.Document.isBackground ? "none" : undefined, boxShadow }}>
{this.showIsTagged()}
- <div className="collectionView-facetCont" style={{ width: `calc(100% - ${this.facetWidth()}px)` }}>
+ <div className="collectionView-facetCont" style={{ display: this.props.PanelPosition === "absolute" ? "flex" : "", justifyContent: this.props.PanelPosition === "absolute" ? "center" : "", width: `calc(100% - ${this.facetWidth()}px)` }}>
{this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)}
</div>
{this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d =>
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index cde795098..829db9c6a 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -128,7 +128,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@computed get sorted(): SortingRule[] {
return this.props.columns.reduce((sorted, shf) => {
- shf.desc && sorted.push({ id: shf.heading, desc: shf.desc });
+ shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc });
return sorted;
}, [] as SortingRule[]);
}
@@ -188,7 +188,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
addNew={false}
onSelect={this.props.changeColumns}
setIsEditing={this.props.setHeaderIsEditing}
-
+ docs={this.props.childDocs}
// try commenting this out
width={"100%"}
/>;
@@ -208,7 +208,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}}>
{col.heading}</div>;
- const sortIcon = col.desc === undefined ? "circle" : col.desc === true ? "caret-down" : "caret-up";
+ const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
const header =
<div //className="collectionSchemaView-header"
@@ -216,21 +216,21 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
className="collectionSchemaView-menuOptions-wrapper"
style={{
background: col.color, padding: "2px",
- display: "flex"
+ display: "flex", cursor: "default", height: "100%",
}}>
- <FontAwesomeIcon icon={icon} size="lg" style={{ display: "inline", paddingLeft: "7px" }} />
- <div className="keys-dropdown"
- style={{ display: "inline", zIndex: 1000 }}>
- {keysDropdown}
- </div>
+ <FontAwesomeIcon icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px" }} />
+ {/* <div className="keys-dropdown"
+ style={{ display: "inline", zIndex: 1000 }}> */}
+ {keysDropdown}
+ {/* </div> */}
<div onClick={e => this.changeSorting(col)}
- style={{ paddingRight: "6px", display: "inline" }}>
- <FontAwesomeIcon icon={sortIcon} size="sm" />
+ style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit" }}>
+ <FontAwesomeIcon icon={sortIcon} size="lg" />
</div>
- <div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)}
- style={{ float: "right", paddingRight: "6px" }}>
+ {/* <div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)}
+ style={{ float: "right", paddingRight: "6px", zIndex: 1, background: "inherit" }}>
<FontAwesomeIcon icon={"compass"} size="sm" />
- </div>
+ </div> */}
</div>;
return {
@@ -450,7 +450,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
//@ts-ignore
expandedRowsList.forEach(row => expanded[row] = true);
const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
-
return <ReactTable
style={{ position: "relative" }}
data={children}
@@ -465,6 +464,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
sorted={this.sorted}
expanded={expanded}
resized={this.resized}
+ NoDataComponent={() => null}
onResizedChange={this.props.onResizedChange}
SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
<div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
@@ -574,7 +574,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return <div className="collectionSchemaView-table" onPointerDown={this.props.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
- <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ {StrCast(this.props.Document.type) !== "search" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
+ : undefined}
{!this._showDoc ? (null) :
<div className="collectionSchemaView-documentPreview" //onClick={() => { this.onOpenClick(); }}
style={{
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e8173d103..c724ba50b 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,8 @@ import { ImageBox } from "./ImageBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
import { PresBox } from "./PresBox";
-import { QueryBox } from "./QueryBox";
+import { SearchBox } from "../search/SearchBox";
+import { SearchItem } from "../search/SearchItem"
import { ColorBox } from "./ColorBox";
import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
import { LinkAnchorBox } from "./LinkAnchorBox";
@@ -191,7 +192,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
components={{
FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
- PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
+ PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, SearchItem,
ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
ScreenshotBox, HTMLtag, ComparisonBox
}}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 48e1f6ce3..c10ff22fe 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -48,11 +48,18 @@ export interface FieldViewProps {
ignoreAutoHeight?: boolean;
PanelWidth: () => number;
PanelHeight: () => number;
+ PanelPosition: string;
+ overflow?: boolean;
NativeHeight: () => number;
NativeWidth: () => number;
setVideoBox?: (player: VideoBox) => void;
ContentScaling: () => number;
+
ChromeHeight?: () => number;
+ childLayoutTemplate?: () => Opt<Doc>;
+ 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/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 0dfbdc5cf..209a3cc6b 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,4 +1,6 @@
-import { action } from 'mobx';
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faEdit } from '@fortawesome/free-regular-svg-icons';
+import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
@@ -56,18 +58,27 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps, LabelDocument
e.stopPropagation();
}
}
+
+
+
+ @observable backColor = "unset";
+
+ @observable clicked = false;
// (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
render() {
const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
const missingParams = params?.filter(p => !this.paramsDoc[p]);
params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ...
+ console.log(this.backColor);
return (
- <div className="labelBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
+ <div className="labelBox-outerDiv" onClick={() => runInAction(() => { this.clicked = !this.clicked; this.clicked ? this.backColor = StrCast(this.layoutDoc.hovercolor) : this.backColor = "unset" })} onMouseLeave={() => runInAction(() => { !this.clicked ? this.backColor = "unset" : null })}
+ onMouseOver={() => runInAction(() => { this.backColor = StrCast(this.layoutDoc.hovercolor); })} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
style={{ boxShadow: this.layoutDoc.opacity ? StrCast(this.layoutDoc.boxShadow) : "" }}>
<div className="labelBox-mainButton" style={{
background: StrCast(this.layoutDoc.backgroundColor),
- color: StrCast(this.layoutDoc.color, "inherit"),
- fontSize: StrCast(this.layoutDoc._fontSize) || "inherit",
+ color: StrCast(this.layoutDoc.color),
+ backgroundColor: this.backColor,
+ fontSize: NumCast(this.layoutDoc.fontSize) || "inherit",
fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit",
letterSpacing: StrCast(this.layoutDoc.letterSpacing),
textTransform: StrCast(this.layoutDoc.textTransform) as any,
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
index 0fff0b57f..1b6056be6 100644
--- a/src/client/views/nodes/QueryBox.tsx
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -1,41 +1,38 @@
-import React = require("react");
-import { IReactionDisposer } from "mobx";
-import { observer } from "mobx-react";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from '../../../fields/FieldSymbols';
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { StrCast, Cast } from "../../../fields/Types";
-import { ViewBoxAnnotatableComponent } from '../DocComponent';
-import { SearchBox } from "../search/SearchBox";
-import { FieldView, FieldViewProps } from './FieldView';
-import "./QueryBox.scss";
-import { List } from "../../../fields/List";
-import { SnappingManager } from "../../util/SnappingManager";
+// import React = require("react");
+// import { IReactionDisposer } from "mobx";
+// import { observer } from "mobx-react";
+// import { documentSchema } from "../../../new_fields/documentSchemas";
+// import { Id } from '../../../new_fields/FieldSymbols';
+// import { makeInterface, listSpec } from "../../../new_fields/Schema";
+// import { StrCast, Cast } from "../../../new_fields/Types";
+// import { ViewBoxAnnotatableComponent } from '../DocComponent';
+// import { SearchBox } from "../search/SearchBox";
+// import { FieldView, FieldViewProps } from './FieldView';
+// import "./QueryBox.scss";
+// import { List } from "../../../new_fields/List";
+// import { SnappingManager } from "../../util/SnappingManager";
-type QueryDocument = makeInterface<[typeof documentSchema]>;
-const QueryDocument = makeInterface(documentSchema);
+// type QueryDocument = makeInterface<[typeof documentSchema]>;
+// const QueryDocument = makeInterface(documentSchema);
-@observer
-export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
- _docListChangedReaction: IReactionDisposer | undefined;
- componentDidMount() {
- }
+// @observer
+// export class QueryBox extends ViewBoxAnnotatableComponent<FieldViewProps, QueryDocument>(QueryDocument) {
+// public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
+// _docListChangedReaction: IReactionDisposer | undefined;
+// componentDidMount() {
+// }
- componentWillUnmount() {
- this._docListChangedReaction?.();
- }
+// componentWillUnmount() {
+// this._docListChangedReaction?.();
+// }
- render() {
- const dragging = !SnappingManager.GetIsDragging() ? "" : "-dragging";
- return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
- <SearchBox
- id={this.props.Document[Id]}
- setSearchQuery={q => this.dataDoc.searchQuery = q}
- searchQuery={StrCast(this.dataDoc.searchQuery)}
- setSearchFileTypes={q => this.dataDoc.searchFileTypes = new List<string>(q)}
- searchFileTypes={Cast(this.dataDoc.searchFileTypes, listSpec("string"), [])}
- filterQquery={StrCast(this.dataDoc.filterQuery)} />
- </div >;
- }
-} \ No newline at end of file
+// render() {
+// const dragging = !SnappingManager.GetIsDragging() ? "" : "-dragging";
+// return <div className={`queryBox${dragging}`} onWheel={(e) => e.stopPropagation()} >
+
+// <SearchBox Document={this.props.Document} />
+// </div >;
+// }
+// }
+
+// //<SearchBox id={this.props.Document[Id]} sideBar={side} Document={this.props.Document} searchQuery={StrCast(this.dataDoc.searchQuery)} filterQuery={this.dataDoc.filterQuery} />
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 627c6e363..4dbe59e60 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -236,7 +236,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
this._applyingChange = true;
- (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
+ (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))) && (this.dataDoc["lastModified"] = new DateField(new Date(Date.now())));
if ((!curTemp && !curProto) || curText || curLayout?.Data.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
if (json.replace(/"selection":.*/, "") !== curLayout?.Data.replace(/"selection":.*/, "")) {
!curText && tx.storedMarks?.map(m => m.type.name === "pFontSize" && (Doc.UserDoc().fontSize = this.layoutDoc._fontSize = m.attrs.fontSize));
@@ -294,19 +294,60 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
public highlightSearchTerms = (terms: string[]) => {
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));
+ let length = res[0].length;
let tr = this._editorView.state.tr;
const flattened: TextSelection[] = [];
res.map(r => r.map(h => flattened.push(h)));
+
+
const lastSel = Math.min(flattened.length - 1, this._searchIndex);
flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
+
+ console.log(this._searchIndex, length);
+ 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;
+ }
+ let index = this._searchIndex;
+
+ Doc.GetProto(this.dataDoc).searchIndex = index;
+ Doc.GetProto(this.dataDoc).length = length;
}
}
+ public highlightSearchTerms2 = (terms: string[]) => {
+ 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));
+ let length = res[0].length;
+ let tr = this._editorView.state.tr;
+ const flattened: TextSelection[] = [];
+ res.map(r => r.map(h => flattened.push(h)));
+ const lastSel = Math.min(flattened.length - 1, this._searchIndex);
+ flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark));
+ this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
+ this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView());
+ let index = this._searchIndex;
+
+ Doc.GetProto(this.dataDoc).searchIndex = index;
+ Doc.GetProto(this.dataDoc).length = length;
+ }
+ }
+
+
public unhighlightSearchTerms = () => {
if (window.screen.width < 600) null;
else if (this._editorView && (this._editorView as any).docView) {
@@ -738,7 +779,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.setupEditor(this.config, this.props.fieldKey);
- this._disposers.search = reaction(() => this.rootDoc.searchMatch,
+ this._disposers.search = reaction(() => this.rootDoc.searchMatch2,
+ search => search ? this.highlightSearchTerms2([Doc.SearchQuery()]) : this.unhighlightSearchTerms(),
+ { fireImmediately: true });
+ this._disposers.search2 = reaction(() => this.rootDoc.searchMatch,
search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(),
{ fireImmediately: this.rootDoc.searchMatch ? true : false });
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c792df882..94a052824 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -106,6 +106,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
private _scrollTopReactionDisposer?: IReactionDisposer;
private _filterReactionDisposer?: IReactionDisposer;
private _searchReactionDisposer?: IReactionDisposer;
+ private _searchReactionDisposer2?: IReactionDisposer;
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _selectionText: string = "";
@@ -328,7 +329,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
@action
prevAnnotation = () => {
+ console.log(this.Index);
this.Index = Math.max(this.Index - 1, 0);
+ console.log(this.Index);
+ console.log(this.allAnnotations);
this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
}
@@ -336,6 +340,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
nextAnnotation = () => {
this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1);
this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
+ this.Document.searchIndex = this.Index;
+ this.Document.length = this.allAnnotations.length;
+
}
@action
@@ -403,6 +410,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
phraseSearch: true,
query: searchString
});
+ this.Document.searchIndex = this.Index;
+ this.Document.length = this.allAnnotations.length;
}
else if (this._mainCont.current) {
const executeFind = () => {
@@ -416,7 +425,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
};
this._mainCont.current.addEventListener("pagesloaded", executeFind);
this._mainCont.current.addEventListener("pagerendered", executeFind);
+ this.Document.searchIndex = this.Index;
+ this.Document.length = this.allAnnotations.length;
}
+
}
@action
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index bb62113a1..8cd2f00b4 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -17,10 +17,9 @@
.searchBox-bar {
height: 32px;
display: flex;
- justify-content: flex-end;
+ justify-content: center;
align-items: center;
- padding-left: 2px;
-
+ background-color: black;
.searchBox-barChild {
&.searchBox-collection {
@@ -30,24 +29,29 @@
}
&.searchBox-input {
+ margin:5px;
+ border-radius:20px;
+ border:black;
display: block;
width: 130px;
-webkit-transition: width 0.4s;
transition: width 0.4s;
align-self: stretch;
-
+ outline:none;
}
.searchBox-input:focus {
width: 500px;
- outline: 3px solid lightblue;
+ outline:none;
}
&.searchBox-filter {
align-self: stretch;
+ button{
+ transform:none;
+ }
button:hover{
- transform:scale(1.0);
- background:"#121721";
+ transform:none;
}
}
@@ -81,8 +85,6 @@
.no-result {
width: 500px;
background: $light-color-secondary;
- border-color: $intermediate-color;
- border-bottom-style: solid;
padding: 10px;
height: 50px;
text-transform: uppercase;
@@ -96,20 +98,20 @@
background: #121721;
flex-direction: column;
transform-origin: top;
- transition: height 0.3s ease, display 0.6s ease;
+ transition: height 0.3s ease, display 0.6s ease, overflow 0.6s ease;
height:0px;
overflow:hidden;
.filter-header {
- display: flex;
+ //display: flex;
position: relative;
- flex-wrap:wrap;
+ //flex-wrap:wrap;
right: 1px;
color: grey;
- flex-direction: row-reverse;
+ //flex-direction: row-reverse;
transform-origin: top;
- justify-content: space-evenly;
+ //justify-content: space-evenly;
margin-bottom: 5px;
overflow:hidden;
transition:height 0.3s ease-out;
@@ -130,9 +132,7 @@
color: grey;
transform-origin: top;
border-top: 0px;
- //padding-top: 5px;
- margin-left: 10px;
- margin-right: 10px;
+
overflow:hidden;
transition:height 0.3s ease-out;
height:0px;
@@ -144,30 +144,28 @@
color: grey;
transform-origin: top;
border-top: 0px;
- //padding-top: 5px;
- margin-left: 10px;
- margin-right: 10px;
overflow:hidden;
transition:height 0.3s ease-out;
height:0px;
- .filter-keybar {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-evenly;
- height: auto;
- width: 100%;
- flex-direction: row-reverse;
- margin-top:5px;
-
- .filter-item {
- position: relative;
- border:1px solid grey;
- border-radius: 16px;
-
- }
- }
-
+ .labelBox-mainButton:hover{
+ color:"White";
+ }
+ // .filter-keybar {
+ // display: flex;
+ // flex-wrap: wrap;
+ // justify-content: space-evenly;
+ // height: auto;
+ // width: 100%;
+ // flex-direction: row-reverse;
+ // margin-top:5px;
+
+ // .filter-item {
+ // position: relative;
+ // border:1px solid grey;
+ // border-radius: 16px;
+ // }
+ // }
}
}
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 99fa6da21..72cb3a04e 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -5,32 +5,51 @@ import { action, computed, observable, runInAction, IReactionDisposer, reaction
import { observer } from 'mobx-react';
import * as React from 'react';
import * as rp from 'request-promise';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { Utils } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
-import { SetupDrag } from '../../util/DragManager';
+import { Utils, returnTrue, emptyFunction, returnFalse, emptyPath, returnOne, returnEmptyString, returnEmptyFilter } from '../../../Utils';
+import { Docs, DocumentOptions } from '../../documents/Documents';
+import { SetupDrag, DragManager } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
import "./SearchBox.scss";
import { SearchItem } from './SearchItem';
import { IconBar } from './IconBar';
-import { FieldView } from '../nodes/FieldView';
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { DocumentType } from "../../documents/DocumentTypes";
import { DocumentView } from '../nodes/DocumentView';
import { SelectionManager } from '../../util/SelectionManager';
+import { FilterQuery } from 'mongodb';
+import { CollectionLinearView } from '../collections/CollectionLinearView';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { ScriptField, ComputedField } from '../../../fields/ScriptField';
+import { PrefetchProxy } from '../../../fields/Proxy';
+import { List } from '../../../fields/List';
+import { faSearch, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia, faBan, faVideo, faCaretDown } from '@fortawesome/free-solid-svg-icons';
+import { Transform } from '../../util/Transform';
+import { MainView } from "../MainView";
+import { Scripting, _scriptingGlobals } from '../../util/Scripting';
+import { CollectionView, CollectionViewType } from '../collections/CollectionView';
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface, createSchema } from '../../../fields/Schema';
import { listSpec } from '../../../fields/Schema';
+import * as _ from "lodash";
+import { checkIfStateModificationsAreAllowed } from 'mobx/lib/internal';
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { indexOf } from 'lodash';
+import { protocol } from 'socket.io-client';
+
library.add(faTimes);
-export interface SearchProps {
- id: string;
- searchQuery: string;
- filterQquery?: string;
- setSearchQuery: (q: string) => {};
- searchFileTypes: string[];
- setSearchFileTypes: (types: string[]) => {};
-}
+export const searchSchema = createSchema({
+ id: "string",
+ Document: Doc,
+ searchQuery: "string",
+});
export enum Keys {
TITLE = "title",
@@ -38,22 +57,37 @@ export enum Keys {
DATA = "data"
}
+export interface filterData {
+ deletedDocsStatus: boolean;
+ authorFieldStatus: boolean;
+ titleFieldStatus: boolean;
+ basicWordStatus: boolean;
+ icons: string[];
+}
+
+type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
+const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
+
+//React.Component<SearchProps>
@observer
-export class SearchBox extends React.Component<SearchProps> {
+export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
- private get _searchString() { return this.props.searchQuery; }
- private set _searchString(value) { this.props.setSearchQuery(value); }
+ @computed get _searchString() { return this.layoutDoc.searchQuery; }
+ @computed set _searchString(value) { this.layoutDoc.searchQuery = (value); }
@observable private _resultsOpen: boolean = false;
- @observable private _searchbarOpen: 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[] = [];
private _resultsSet = new Map<Doc, number>();
private _resultsRef = React.createRef<HTMLDivElement>();
public inputRef = React.createRef<HTMLInputElement>();
private _isSearch: ("search" | "placeholder" | undefined)[] = [];
+ private _isSorted: ("sorted" | "placeholder" | undefined)[] = [];
+
private _numTotalResults = -1;
private _endIndex = -1;
@@ -63,39 +97,112 @@ export class SearchBox extends React.Component<SearchProps> {
private _curRequest?: Promise<any> = undefined;
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
-
+ 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;
+ @observable private newAssign: boolean = true;
constructor(props: any) {
+
super(props);
SearchBox.Instance = this;
+ if (!_scriptingGlobals.hasOwnProperty("handleNodeChange")) {
+ Scripting.addGlobal(this.handleNodeChange);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("handleKeyChange")) {
+ Scripting.addGlobal(this.handleKeyChange);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("handleWordQueryChange")) {
+ Scripting.addGlobal(this.handleWordQueryChange);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("updateIcon")) {
+ Scripting.addGlobal(this.updateIcon);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("updateTitleStatus")) {
+ Scripting.addGlobal(this.updateTitleStatus);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("updateAuthorStatus")) {
+ Scripting.addGlobal(this.updateAuthorStatus);
+ }
+ if (!_scriptingGlobals.hasOwnProperty("updateDeletedStatus")) {
+ Scripting.addGlobal(this.updateDeletedStatus);
+ }
+
+
this.resultsScrolled = this.resultsScrolled.bind(this);
+
+ // new PrefetchProxy(Docs.Create.SearchItemBoxDocument({
+ // title: "search item template",
+ // backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data"
+ // }));
+
+
+ // if (!this.searchItemTemplate) { // create exactly one presElmentBox template to use by any and all presentations.
+ // Doc.UserDoc().searchItemTemplate = new PrefetchProxy(Docs.Create.SearchItemBoxDocument({ title: "search item template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
+ // // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
+ // // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
+ // // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data
+ // // stored on each pres element.
+ // (this.searchItemTemplate as Doc).lookupField = ScriptField.MakeFunction("lookupSearchBoxField(container, field, data)",
+ // { field: "string", data: Doc.name, container: Doc.name });
+ // }
}
+ @observable setupButtons = false;
+ componentDidMount = () => {
+ if (this.setupButtons == false) {
- componentDidMount = action(() => {
+ runInAction(() => this.setupButtons == true);
+ }
if (this.inputRef.current) {
this.inputRef.current.focus();
- this._searchbarOpen = true;
+ runInAction(() => { this._searchbarOpen = true });
}
- if (this.props.searchQuery) { // bcz: why was this here? } && this.props.filterQquery) {
- this._searchString = this.props.searchQuery;
- this.submitSearch();
+ 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();
+ });
}
- });
+ };
@action
getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc)
+
+ @observable newsearchstring: string = "";
@action.bound
onChange(e: React.ChangeEvent<HTMLInputElement>) {
- this._searchString = e.target.value;
+ this.layoutDoc._searchString = e.target.value;
+ console.log(e.target.value);
+ this.newsearchstring = e.target.value;
+
+
+ if (e.target.value === "") {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([]);
+ if (this.currentSelectedCollection !== undefined) {
+ this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
+ this.currentSelectedCollection = undefined;
+ this.props.Document.selectedDoc = undefined;
+ }
+ console.log("CLOSE");
+ runInAction(() => { this.open = false });
+ }
this._openNoResults = false;
this._results = [];
this._resultsSet.clear();
@@ -108,10 +215,28 @@ export class SearchBox extends React.Component<SearchProps> {
enter = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
+ this.layoutDoc._searchString = this.newsearchstring;
+ // if (this._icons !== this._allIcons) {
+ // runInAction(() => { this.expandedBucket = false });
+ // }
+ if (StrCast(this.layoutDoc._searchString) !== "") {
+ console.log("OPEN");
+ runInAction(() => { this.open = true });
+ }
+ else {
+ console.log("CLOSE");
+ runInAction(() => { this.open = false });
+
+ }
this.submitSearch();
+
+
}
}
+ @observable open: boolean = false;
+
+
public static async convertDataUri(imageUri: string, returnedFilename: string) {
try {
const posting = Utils.prepend("/uploadURI");
@@ -134,10 +259,11 @@ export class SearchBox extends React.Component<SearchProps> {
//this also serves as an indicator if the word status filter is applied
@observable private _filterOpen: boolean = false;
//if icons = all icons, then no icon filter is applied
- get _icons() { return this.props.searchFileTypes; }
- set _icons(value) {
- this.props.setSearchFileTypes(value);
- }
+ // get _icons() { return this.props.searchFileTypes; }
+ // set _icons(value) {
+ // this.props.setSearchFileTypes(value);
+ // }
+ @observable _icons: string[] = this._allIcons;
//if all of these are true, no key filter is applied
@observable private _titleFieldStatus: boolean = true;
@observable private _authorFieldStatus: boolean = true;
@@ -162,10 +288,12 @@ export class SearchBox extends React.Component<SearchProps> {
query = query.replace(/\s+/g, ' ').trim();
}
- //if should be searched in a specific collection
+ // if should be searched in a specific collection
if (this._collectionStatus) {
query = this.addCollectionFilter(query);
query = query.replace(/\s+/g, ' ').trim();
+ console.log(query)
+
}
return query;
}
@@ -176,14 +304,14 @@ export class SearchBox extends React.Component<SearchProps> {
@action
filterDocsByType(docs: Doc[]) {
- if (this._icons.length === this._allIcons.length) {
- return docs;
- }
const finalDocs: Doc[] = [];
+ const blockedTypes: string[] = ["preselement", "docholder", "collection", "search", "searchitem", "script", "fonticonbox", "button", "label"];
docs.forEach(doc => {
const layoutresult = Cast(doc.type, "string");
- if (layoutresult && this._icons.includes(layoutresult)) {
- finalDocs.push(doc);
+ if (layoutresult && !blockedTypes.includes(layoutresult)) {
+ if (layoutresult && this._icons.includes(layoutresult)) {
+ finalDocs.push(doc);
+ }
}
});
return finalDocs;
@@ -191,6 +319,8 @@ export class SearchBox extends React.Component<SearchProps> {
addCollectionFilter(query: string): string {
const collections: Doc[] = this.getCurCollections();
+
+ console.log(collections);
const oldWords = query.split(" ");
const collectionString: string[] = [];
@@ -216,7 +346,6 @@ export class SearchBox extends React.Component<SearchProps> {
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
@@ -236,6 +365,84 @@ export class SearchBox extends React.Component<SearchProps> {
}
+ currentSelectedCollection: DocumentView | undefined = undefined;
+ docsforfilter: Doc[] = [];
+
+ searchCollection(query: string) {
+
+ const selectedCollection: DocumentView = SelectionManager.SelectedDocuments()[0];
+
+ if (selectedCollection !== undefined) {
+ this.currentSelectedCollection = selectedCollection;
+ if (this.filter === true) {
+ this.props.Document.selectedDoc = selectedCollection.props.Document;
+ }
+ let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
+ let found: [Doc, string[], string[]][] = [];
+ let docsforFilter: Doc[] = []
+ let newarray: Doc[] = [];
+
+ while (docs.length > 0) {
+ console.log("iteration");
+ newarray = [];
+ docs.forEach((d) => {
+ console.log(d);
+ if (d.data != undefined) {
+ let newdocs = DocListCast(d.data);
+ newdocs.forEach((newdoc) => {
+ console.log(newdoc);
+ newarray.push(newdoc);
+
+ });
+ }
+
+
+ let hlights: string[] = [];
+
+ const protos = Doc.GetAllPrototypes(d);
+ let proto = protos[protos.length - 1];
+ protos.forEach(proto => {
+ Object.keys(proto).forEach(key => {
+ // console.log(key, d[key]);
+ if (StrCast(d[key]).includes(query) && !hlights.includes(key)) {
+ hlights.push(key);
+ }
+ })
+ });
+ if (hlights.length > 0) {
+ found.push([d, hlights, []]);
+ docsforFilter.push(d);
+ };
+ });
+ docs = newarray;
+ }
+ this._results = found;
+ this.docsforfilter = docsforFilter;
+ if (this.filter === true) {
+ selectedCollection.props.Document._searchDocs = new List<Doc>(docsforFilter);
+ }
+ this._numTotalResults = found.length;
+ }
+ else {
+ 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.
+ // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
+ // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
+ // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
+ // is displayed (unlikely) it won't show up until something else changes.
+ //TODO Types
+ Doc.GetAllPrototypes(doc).map
+ (proto => Object.keys(proto).forEach(key => keys[key] = false));
+ return Array.from(Object.keys(keys));
+ }
+
applyBasicFieldFilters(query: string) {
let finalQuery = "";
@@ -276,30 +483,59 @@ export class SearchBox extends React.Component<SearchProps> {
get fieldFiltersApplied() { return !(this._authorFieldStatus && this._titleFieldStatus); }
-
@action
- submitSearch = async () => {
- const query = this._searchString;
+ submitSearch = async (reset?: boolean) => {
+ this.checkIcons();
+ if (reset) {
+ this.layoutDoc._searchString = "";
+ }
+ this.props.Document._docFilters = undefined;
+ this.noresults = "";
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ this.headercount = 0;
+ this.children = 0;
+ this.buckets = [];
+ this.new_buckets = {};
+ const query = StrCast(this.layoutDoc._searchString);
this.getFinalQuery(query);
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._endIndex = 12;
this._maxSearchIndex = 0;
this._numTotalResults = -1;
- await this.getResults(query);
-
+ this.scale === true ? await this.getResults(query) : this.searchCollection(query);
runInAction(() => {
this._resultsOpen = true;
this._searchbarOpen = true;
this._openNoResults = true;
this.resultsScrolled();
+
});
}
}
+ @observable scale = true;
+
+ @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 });
}
@@ -309,7 +545,7 @@ export class SearchBox extends React.Component<SearchProps> {
const baseExpr = "NOT baseProto_b:true";
const includeDeleted = this.getDataStatus() ? "" : " NOT deleted_b:true";
const includeIcons = this.getDataStatus() ? "" : " NOT type_t:fonticonbox";
- // const typeExpr = !types ? "" : ` (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})`; // this line was causing issues for me, check solr logging -syip2
+ // const typeExpr = !types ? "" : ` (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})`;
// fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello
const query = [baseExpr, includeDeleted, includeIcons].join(" AND ").replace(/AND $/, "");
return query;
@@ -317,21 +553,20 @@ export class SearchBox extends React.Component<SearchProps> {
getDataStatus() { return this._deletedDocsStatus; }
-
private NumResults = 25;
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: this.NumResults, 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;
}
-
const highlighting = res.highlighting || {};
const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
const lines = new Map<string, string[]>();
@@ -340,19 +575,34 @@ export class SearchBox extends React.Component<SearchProps> {
const highlights: typeof res.highlighting = {};
docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
const filteredDocs = this.filterDocsByType(docs);
+
runInAction(() => {
- //this._results.push(...filteredDocs);
- filteredDocs.forEach(doc => {
+ 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)) : [];
- 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);
+ console.log(hlights);
+ doc ? console.log(Cast(doc.context, Doc)) : null;
+ if (this.findCommonElements(hlights)) {
+ }
+ else {
+ const layoutresult = Cast(doc.type, "string");
+ if (layoutresult) {
+ if (this.new_buckets[layoutresult] === undefined) {
+ this.new_buckets[layoutresult] = 1;
+ }
+ else {
+ this.new_buckets[layoutresult] = this.new_buckets[layoutresult] + 1;
+ }
+ }
+ 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);
+ }
}
});
});
@@ -363,15 +613,16 @@ export class SearchBox extends React.Component<SearchProps> {
await this._curRequest;
}
+
this.resultsScrolled();
res();
});
return this.lockPromise;
}
-
+ @observable noresults = "";
collectionRef = React.createRef<HTMLSpanElement>();
startDragCollection = async () => {
- const res = await this.getAllResults(this.getFinalQuery(this._searchString));
+ 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);
@@ -404,7 +655,14 @@ export class SearchBox extends React.Component<SearchProps> {
y += 300;
}
}
- return Docs.Create.QueryDocument({ _autoHeight: true, title: this._searchString, filterQuery: this.filterQuery, searchQuery: this._searchString });
+ const filter: filterData = {
+ deletedDocsStatus: this._deletedDocsStatus,
+ authorFieldStatus: this._authorFieldStatus,
+ titleFieldStatus: this._titleFieldStatus,
+ basicWordStatus: this._basicWordStatus,
+ icons: this._icons,
+ }
+ return Docs.Create.SearchDocument({ _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString), searchQuery: StrCast(this.layoutDoc._searchString) });
}
@action.bound
@@ -427,23 +685,30 @@ export class SearchBox extends React.Component<SearchProps> {
this._results = [];
this._resultsSet.clear();
this._visibleElements = [];
+ this._visibleDocuments = [];
this._numTotalResults = -1;
this._endIndex = -1;
this._curRequest = undefined;
}
+ @observable children: number = 0;
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
if (!this._resultsRef.current) return;
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([]);
+
const scrollY = e ? e.currentTarget.scrollTop : this._resultsRef.current ? this._resultsRef.current.scrollTop : 0;
const itemHght = 53;
const startIndex = Math.floor(Math.max(0, scrollY / itemHght));
- const endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this._resultsRef.current.getBoundingClientRect().height / itemHght)));
-
+ //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;
+ let headers = new Set<string>(["title", "author", "lastModified"]);
if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) {
- this._visibleElements = [<div className="no-result">No Search Results</div>];
+ if (this.noresults === "") {
+ this.noresults = "No search results :(";
+ }
return;
}
@@ -456,16 +721,19 @@ export class SearchBox extends React.Component<SearchProps> {
else if (this._visibleElements.length !== this._numTotalResults) {
// undefined until a searchitem is put in there
this._visibleElements = Array<JSX.Element>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- // indicates if things are placeholders
+ this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
+ // indicates if things are placeholders
this._isSearch = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- }
+ this._isSorted = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
+ }
for (let i = 0; i < this._numTotalResults; 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 (i < startIndex || i > endIndex) {
if (this._isSearch[i] !== "placeholder") {
this._isSearch[i] = "placeholder";
+ this._isSorted[i] = "placeholder";
this._visibleElements[i] = <div className="searchBox-placeholder" key={`searchBox-placeholder-${i}`}>Loading...</div>;
}
}
@@ -473,30 +741,60 @@ export class SearchBox extends React.Component<SearchProps> {
if (this._isSearch[i] !== "search") {
let result: [Doc, string[], string[]] | undefined = undefined;
if (i >= this._results.length) {
- this.getResults(this._searchString);
+ this.getResults(StrCast(this.layoutDoc._searchString));
if (i < this._results.length) result = this._results[i];
if (result) {
const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
+ let lines = new List<string>(result[2]);
+ result[0]._height = 46;
+ result[0].lines = lines;
+ result[0].highlighting = highlights.join(", ");
+ highlights.forEach((item) => headers.add(item));
+ this._visibleDocuments[i] = result[0];
this._isSearch[i] = "search";
+ console.log(result[0]);
+ Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
+ this.children++;
}
}
else {
result = this._results[i];
if (result) {
const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- this._visibleElements[i] = <SearchItem doc={result[0]} query={this._searchString} key={result[0][Id]} lines={result[2]} highlighting={highlights} />;
- this._isSearch[i] = "search";
+ let lines = new List<string>(result[2]);
+ highlights.forEach((item) => headers.add(item));
+ result[0]._height = 46;
+ result[0].lines = lines;
+ result[0].highlighting = highlights.join(", ");
+ if (i < this._visibleDocuments.length) {
+ this._visibleDocuments[i] = result[0];
+ this._isSearch[i] = "search";
+ Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
+ this.children++;
+ }
}
}
}
}
}
+ let schemaheaders: SchemaHeaderField[] = [];
+ this.headerscale = headers.size;
+ headers.forEach((item) => schemaheaders.push(new SchemaHeaderField(item, "#f1efeb")))
+ this.headercount = schemaheaders.length;
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>(schemaheaders);
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[]) {
+ let arr1 = ["layout", "data"];
+ return arr1.some(item => arr2.includes(item))
+ }
@computed
get resFull() { return this._numTotalResults <= 8; }
@@ -506,29 +804,48 @@ export class SearchBox extends React.Component<SearchProps> {
//if true, any keywords can be used. if false, all keywords are required.
@action.bound
- handleWordQueryChange = () => {
- this._basicWordStatus = !this._basicWordStatus;
+ handleWordQueryChange = async () => {
+ this._collectionStatus = !this._collectionStatus;
+ if (this._collectionStatus) {
+ let doc = await Cast(this.props.Document.keywords, Doc)
+ doc!.backgroundColor = "grey";
+
+ }
+ else {
+ let doc = await Cast(this.props.Document.keywords, Doc)
+ doc!.backgroundColor = "black";
+ }
}
@action.bound
- handleNodeChange = () => {
+ handleNodeChange = async () => {
this._nodeStatus = !this._nodeStatus;
+
if (this._nodeStatus) {
- this.expandSection(`node${this.props.id}`);
+ this.expandSection(`node${this.props.Document[Id]}`);
+ let doc = await Cast(this.props.Document.nodes, Doc)
+ doc!.backgroundColor = "grey";
+
}
else {
- this.collapseSection(`node${this.props.id}`);
+ this.collapseSection(`node${this.props.Document[Id]}`);
+ let doc = await Cast(this.props.Document.nodes, Doc)
+ doc!.backgroundColor = "black";
}
}
@action.bound
- handleKeyChange = () => {
+ handleKeyChange = async () => {
this._keyStatus = !this._keyStatus;
if (this._keyStatus) {
- this.expandSection(`key${this.props.id}`);
+ this.expandSection(`key${this.props.Document[Id]}`);
+ let doc = await Cast(this.props.Document.keys, Doc)
+ doc!.backgroundColor = "grey";
}
else {
- this.collapseSection(`key${this.props.id}`);
+ this.collapseSection(`key${this.props.Document[Id]}`);
+ let doc = await Cast(this.props.Document.keys, Doc)
+ doc!.backgroundColor = "black";
}
}
@@ -536,11 +853,12 @@ export class SearchBox extends React.Component<SearchProps> {
handleFilterChange = () => {
this._filterOpen = !this._filterOpen;
if (this._filterOpen) {
- this.expandSection(`filterhead${this.props.id}`);
- document.getElementById(`filterhead${this.props.id}`)!.style.padding = "5";
+ this.expandSection(`filterhead${this.props.Document[Id]}`);
+ document.getElementById(`filterhead${this.props.Document[Id]}`)!.style.padding = "5";
+ console.log(this.props.Document[Id])
}
else {
- this.collapseSection(`filterhead${this.props.id}`);
+ this.collapseSection(`filterhead${this.props.Document[Id]}`);
}
@@ -553,7 +871,7 @@ export class SearchBox extends React.Component<SearchProps> {
collapseSection(thing: string) {
- const id = this.props.id;
+ const id = this.props.Document[Id];
const element = document.getElementById(thing)!;
// get the height of the element's inner content, regardless of its actual size
const sectionHeight = element.scrollHeight;
@@ -614,55 +932,230 @@ export class SearchBox extends React.Component<SearchProps> {
}
@action.bound
- updateTitleStatus() { this._titleFieldStatus = !this._titleFieldStatus; }
+ updateTitleStatus = async () => {
+ this._titleFieldStatus = !this._titleFieldStatus;
+ if (this._titleFieldStatus) {
+ let doc = await Cast(this.props.Document.title, Doc)
+ doc!.backgroundColor = "grey";
+ }
+ else {
+ let doc = await Cast(this.props.Document.title, Doc)
+ doc!.backgroundColor = "black";
+ }
+ }
@action.bound
- updateAuthorStatus() { this._authorFieldStatus = !this._authorFieldStatus; }
+ updateAuthorStatus = async () => {
+ this._authorFieldStatus = !this._authorFieldStatus;
+ if (this._authorFieldStatus) {
+ let doc = await Cast(this.props.Document.author, Doc)
+ doc!.backgroundColor = "grey";
+ }
+ else {
+ let doc = await Cast(this.props.Document.author, Doc)
+ doc!.backgroundColor = "black";
+ }
+ }
@action.bound
- updateDataStatus() { this._deletedDocsStatus = !this._deletedDocsStatus; }
+ updateDeletedStatus = async () => {
+ this._deletedDocsStatus = !this._deletedDocsStatus;
+ if (this._deletedDocsStatus) {
+ let doc = await Cast(this.props.Document.deleted, Doc)
+ doc!.backgroundColor = "grey";
+ }
+ else {
+ let doc = await Cast(this.props.Document.deleted, Doc)
+ doc!.backgroundColor = "black";
+ }
+ }
+
+ 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);
+
+ @action.bound
+ updateIcon = async (icon: string) => {
+ if (this._icons.includes(icon)) {
+ _.pull(this._icons, icon);
+ let cap = icon.charAt(0).toUpperCase() + icon.slice(1)
+ console.log(cap);
+ let doc = await Cast(this.props.Document[cap], Doc)
+ doc!.backgroundColor = "black";
+ }
+ else {
+ this._icons.push(icon);
+ let cap = icon.charAt(0).toUpperCase() + icon.slice(1)
+ let doc = await Cast(this.props.Document[cap], Doc)
+ doc!.backgroundColor = "grey";
+ }
+ }
+
+ @action.bound
+ checkIcons = async () => {
+ for (let i = 0; i < this._allIcons.length; i++) {
+
+ let cap = this._allIcons[i].charAt(0).toUpperCase() + this._allIcons[i].slice(1)
+ let doc = await Cast(this.props.Document[cap], Doc)
+ if (this._icons.includes(this._allIcons[i])) {
+ doc!.backgroundColor = "grey";
+ }
+ else {
+ doc!.backgroundColor = "black";
+ }
+ }
+ }
+
+ @computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); }
+
+ 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;
- render() {
+ //Make id layour document
+ render() {
+ this.props.Document._chromeStatus === "disabled";
+ this.props.Document._searchDoc = true;
+ let cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length;
+ let length = 0;
+ cols > 5 ? length = 1076 : length = cols * 205 + 51;
+ let height = 0;
+ let rows = this.children;
+ rows > 8 ? height = 31 + 31 * 8 : height = 31 * rows + 31;
return (
- <div className="searchBox-container">
+ <div style={{ pointerEvents: "all" }} className="searchBox-container">
<div className="searchBox-bar">
- <span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, () => this._searchString ? this.startDragCollection() : undefined)} ref={this.collectionRef} title="Drag Results as Collection">
+ {/* <span className="searchBox-barChild searchBox-collection" onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} ref={this.collectionRef} title="Drag Results as Collection">
<FontAwesomeIcon icon="object-group" size="lg" />
- </span>
- <input value={this._searchString} onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
+ </span> */}
+ <div style={{ position: "absolute", left: 15 }}>{Doc.CurrentUserEmail}</div>
+ <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg" style={{ position: "relative", left: 24, padding: 1 }} />
+ <input value={this.newsearchstring} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this.inputRef}
className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch}
- style={{ width: this._searchbarOpen ? "500px" : "100px" }} />
- <button className="searchBox-barChild searchBox-filter" title="Advanced Filtering Options" onClick={() => this.handleFilterChange()}><FontAwesomeIcon icon="ellipsis-v" color="white" /></button>
+ style={{ paddingLeft: 23, width: this._searchbarOpen ? "200px" : "200px" }} />
</div>
-
- <div id={`filterhead${this.props.id}`} className="filter-form" >
- <div id={`filterhead2${this.props.id}`} className="filter-header" style={this._filterOpen ? {} : {}}>
- <button className="filter-item" style={this._basicWordStatus ? { background: "#aaaaa3", } : {}} onClick={this.handleWordQueryChange}>Keywords</button>
- <button className="filter-item" style={this._keyStatus ? { background: "#aaaaa3" } : {}} onClick={this.handleKeyChange}>Keys</button>
- <button className="filter-item" style={this._nodeStatus ? { background: "#aaaaa3" } : {}} onClick={this.handleNodeChange}>Nodes</button>
- </div>
- <div id={`node${this.props.id}`} className="filter-body" style={this._nodeStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}>
- <IconBar setIcons={(icons: string[]) => {
- this._icons = icons;
- }} />
- </div>
- <div className="filter-key" id={`key${this.props.id}`} style={this._keyStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}>
- <div className="filter-keybar">
- <button className="filter-item" style={this._titleFieldStatus ? { background: "#aaaaa3", } : {}} onClick={this.updateTitleStatus}>Title</button>
- <button className="filter-item" style={this._deletedDocsStatus ? { background: "#aaaaa3", } : {}} onClick={this.updateDataStatus}>Deleted Docs</button>
- <button className="filter-item" style={this._authorFieldStatus ? { background: "#aaaaa3", } : {}} onClick={this.updateAuthorStatus}>Author</button>
- </div>
- </div>
+ <div style={{ zIndex: 2000 }}>
+ {this._searchbarOpen === true ?
+ <div style={{ display: "flex", justifyContent: "center", }}>
+ <div style={{
+ width: cols > 0 ? length : 253,
+ height: 25,
+ borderColor: "#9c9396",
+ border: "1px solid",
+ borderRadius: "0.3em",
+ borderBottom: this.open === false ? "1px solid" : "none",
+ position: "absolute",
+ background: "rgb(241, 239, 235)",
+ top: 29
+ }}>
+ <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
+ <div className="checkbox" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }}>
+ <input style={{ marginLeft: -16, marginTop: -1, color: this.scale == false ? "black" : "grey" }} checked={this.filter === true} onChange={() => {
+ runInAction(() => {
+ if (this.scale === false) {
+ this.filter = !this.filter;
+ if (this.filter === true && this.currentSelectedCollection !== undefined) {
+ this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter);
+ this.currentSelectedCollection.props.Document._docFilters = new List<string>(Cast(this.props.Document._docFilters, listSpec("string"), []));
+ this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document;
+ }
+ else if (this.filter === false && this.currentSelectedCollection !== undefined) {
+ this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]);
+ this.currentSelectedCollection.props.Document._docFilters = undefined;
+ this.props.Document.selectedDoc = undefined;
+ }
+ }
+ })
+ }} type="checkbox"></input>
+ Filter
+ </label>
+ </div>
+ <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.scale === false} onChange={() => {
+ runInAction(() => {
+ this.scale = !this.scale;
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ if (this.currentSelectedCollection !== undefined) {
+ this.currentSelectedCollection.props.Document._docFilters = undefined;
+ this.currentSelectedCollection.props.Document._searchDocs = undefined;
+ this.currentSelectedCollection = undefined;
+ }
+ this.submitSearch();
+ })
+ }} />
+ Current collection
+ </label>
+ </div>
+ <div className="radio" style={{ margin: 0 }}>
+ <label style={{ fontSize: 12, marginTop: 6 }} >
+ <input style={{ marginLeft: -16, marginTop: -1 }} type="radio" checked={this.scale === true} onChange={() => {
+ runInAction(() => {
+ this.scale = !this.scale;
+ this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ this.filter = false;
+ if (this.currentSelectedCollection !== undefined) {
+ this.currentSelectedCollection.props.Document._docFilters = undefined;
+ this.currentSelectedCollection.props.Document._searchDocs = undefined;
+ this.currentSelectedCollection = undefined;
+ }
+ this.submitSearch();
+ })
+ }} />
+ Workspace
+ </label>
+ </div>
+ </div>
+ </form>
+ </div>
+ {this.noresults === "" ? <div style={{ display: this.open === true ? "contents" : "none" }}> <CollectionView {...this.props}
+ Document={this.props.Document}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ PanelHeight={this.open === true ? () => height : () => 0}
+ PanelWidth={this.open === true ? () => length : () => 0}
+ PanelPosition={"absolute"}
+ overflow={cols > 5 || rows > 8 ? true : false}
+ focus={this.selectElement}
+ ScreenToLocalTransform={Transform.Identity}
+ /></div> : <div style={{ display: "flex", justifyContent: "center" }}><div style={{ height: 200, top: 54, width: 250, position: "absolute", backgroundColor: "rgb(241, 239, 235)", display: "flex", justifyContent: "center", alignItems: "center", border: "black 1px solid", }}>
+ <div>{this.noresults}</div>
+ </div></div>}
+ </div> : undefined}
</div>
+
<div className="searchBox-results" onScroll={this.resultsScrolled} style={{
display: this._resultsOpen ? "flex" : "none",
height: this.resFull ? "auto" : this.resultHeight,
overflow: "visibile" // this.resFull ? "auto" : "visible"
}} ref={this._resultsRef}>
- {this._visibleElements}
</div>
- </div>
+ </div >
);
}
-} \ No newline at end of file
+}
+
+Scripting.addGlobal(function lookupSearchBoxField(container: Doc, field: string, data: Doc) {
+ // if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
+ // if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Schema ? 50 : 46;
+ // if (field === 'presStatus') return container.presStatus;
+ // if (field === '_itemIndex') return container._itemIndex;
+ if (field == "query") return container._searchString;
+ return undefined;
+});
+
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 469f062b2..5ce022d41 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -11,15 +11,15 @@
.searchItem-overview .searchItem {
width: 100%;
background: $light-color-secondary;
- border-color: $intermediate-color;
- border-bottom-style: solid;
- padding: 10px;
- min-height: 50px;
+ padding: 8px;
+ min-height: 46px;
+ height:46px;
max-height: 150px;
height: auto;
z-index: 0;
display: flex;
overflow: visible;
+ box-shadow: rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw;
.searchItem-body {
display: flex;
@@ -146,7 +146,7 @@
}
.searchBox-placeholder {
- min-height: 50px;
+ min-height: 46px;
margin-left: 150px;
width: calc(100% - 150px);
text-transform: uppercase;
@@ -160,4 +160,27 @@
.collection-item {
width: 35px;
+}
+
+.bucket-title{
+ width:auto;
+ padding: 5px;
+ height: auto;
+ top: -18;
+ z-index: 55;
+ position: absolute;
+}
+
+.bucket-expand{
+ bottom: 0;
+ position: absolute;
+ width: 100%;
+ height: 15;
+ transform:none;
+ .bucket-expand:hover{
+ transform:none;
+ }
+ button:hover{
+ transform:none;
+ }
} \ No newline at end of file
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 2436bf418..6b06289c4 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -2,9 +2,9 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlobeAsia, faImage, faLink, faMusic, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable, runInAction, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
+import { Doc, DocCastAsync } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero, returnEmptyString, returnEmptyFilter } from "../../../Utils";
@@ -15,13 +15,21 @@ import { SearchUtil } from "../../util/SearchUtil";
import { Transform } from "../../util/Transform";
import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionViewType } from "../collections/CollectionView";
+import { CollectionViewType, CollectionView } from "../collections/CollectionView";
import { ParentDocSelector } from "../collections/ParentDocumentSelector";
import { ContextMenu } from "../ContextMenu";
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
import { SearchBox } from "./SearchBox";
import "./SearchItem.scss";
import "./SelectorContextMenu.scss";
+import { FieldViewProps, FieldView } from "../nodes/FieldView";
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { makeInterface, createSchema, listSpec } from "../../../fields/Schema";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { PrefetchProxy } from "../../../fields/Proxy";
+import { Docs } from "../../documents/Documents";
+import { ScriptField } from "../../../fields/ScriptField";
+import { CollectionStackingView } from "../collections/CollectionStackingView";
export interface SearchItemProps {
doc: Doc;
@@ -122,30 +130,113 @@ export class LinkContextMenu extends React.Component<LinkMenuProps> {
}
+
+type SearchSchema = makeInterface<[typeof documentSchema]>;
+
+export const SearchSchema = createSchema({
+ targetDoc: Doc,
+});
+
+const SearchDocument = makeInterface(documentSchema);
+
+
+
@observer
-export class SearchItem extends React.Component<SearchItemProps> {
+export class SearchItem extends ViewBoxBaseComponent<FieldViewProps, SearchSchema>(SearchDocument) {
+
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchItem, fieldKey); }
+
+ constructor(props:any){
+ super(props);
+ //this.rootDoc._viewType= CollectionViewType.Stacking;
+ this.props.Document._height=46;
+ if (!this.searchItemTemplate) { // create exactly one presElmentBox template to use by any and all presentations.
+ Doc.UserDoc().searchItemTemplate = new PrefetchProxy(Docs.Create.SearchItemBoxDocument({ title: "search item template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" }));
+ // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
+ // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
+ // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data
+ // stored on each pres element.
+ (this.searchItemTemplate as Doc).lookupField = ScriptField.MakeFunction("lookupSearchBoxField(container, field, data)",
+ { field: "string", data: Doc.name, container: Doc.name });
+ }
+
+ }
@observable _selected: boolean = false;
onClick = () => {
- // I dont think this is the best functionality because clicking the name of the collection does that. Change it back if you'd like
- DocumentManager.Instance.jumpToDocument(this.props.doc, false);
+ DocumentManager.Instance.jumpToDocument(this.rootDoc, false);
}
@observable _useIcons = true;
@observable _displayDim = 50;
+ @computed get query() { return StrCast(this.lookupField("query")); }
+
+ private _oldHeight: number = 46;
+
componentDidMount() {
- Doc.SetSearchQuery(this.props.query);
- this.props.doc.searchMatch = true;
+ let parent: Doc |undefined = undefined;
+ let height = 0;
+ if (this.rootDoc.parent){
+ parent = Cast(this.rootDoc.parent, Doc, null);
+ if (parent!== undefined){
+ height=(NumCast(parent._height));
+ }
+ }
+
+ this._reactionDisposer2 = reaction(
+ () => this._useIcons,
+ el=> {
+ if (this.rootDoc.parent){
+ parent = Cast(this.rootDoc.parent, Doc, null) as Doc;
+ height=(NumCast(parent._height));
+ };
+ console.log(height);
+ console.log(this._oldHeight);
+ setTimeout(() =>{this._mainRef.current?.getBoundingClientRect()? this.props.Document._height= this._mainRef.current?.getBoundingClientRect().height : null;
+ parent!==undefined? this._mainRef.current?.getBoundingClientRect()? parent._height= -this._oldHeight + height +this._mainRef.current?.getBoundingClientRect().height : null: null;
+ this._mainRef.current?.getBoundingClientRect()? this._oldHeight= this._mainRef.current?.getBoundingClientRect().height : null;
+ // this._oldHeight 55? this._oldHeight =55:null;
+ }, 1);
+ }
+ );
+
+ this._reactionDisposer3 = reaction(
+ () => this._displayLines,
+ el=> {
+ if (this.rootDoc.parent){
+ parent = Cast(this.rootDoc.parent, Doc, null) as Doc;
+ height=(NumCast(parent._height));
+ };
+ setTimeout(() =>{this._mainRef.current?.getBoundingClientRect()? this.props.Document._height= this._mainRef.current?.getBoundingClientRect().height : null;
+ parent!==undefined? this._mainRef.current?.getBoundingClientRect()? parent._height= -this._oldHeight + height +this._mainRef.current?.getBoundingClientRect().height : null: null;
+ this._mainRef.current?.getBoundingClientRect()? this._oldHeight= this._mainRef.current?.getBoundingClientRect().height : null;
+ }, 1);
+ }
+ );
+
+ Doc.SetSearchQuery(this.query);
+ this.rootDoc.searchMatch = true;
}
componentWillUnmount() {
- this.props.doc.searchMatch = undefined;
+ this.rootDoc.searchMatch = undefined;
+ this._reactionDisposer2 && this._reactionDisposer2();
+ this._reactionDisposer3 && this._reactionDisposer3();
+
}
- //@computed
+
+
+ private _reactionDisposer2?: IReactionDisposer;
+ private _reactionDisposer3?: IReactionDisposer;
+
+
+
+ @computed get highlightPos(){return NumCast(this.rootDoc.searchIndex)}
+
@action
public DocumentIcon() {
- const layoutresult = StrCast(this.props.doc.type);
+ const layoutresult = StrCast(this.rootDoc.type);
if (!this._useIcons) {
const returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
const returnYDimension = () => this._displayDim;
@@ -156,10 +247,10 @@ export class SearchItem extends React.Component<SearchItemProps> {
})}
onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} >
<ContentFittingDocumentView
- Document={this.props.doc}
+ Document={this.rootDoc}
LibraryPath={emptyPath}
rootSelected={returnFalse}
- fitToBox={StrCast(this.props.doc.type).indexOf(DocumentType.COL) !== -1}
+ fitToBox={StrCast(this.rootDoc.type).indexOf(DocumentType.COL) !== -1}
addDocument={returnFalse}
removeDocument={returnFalse}
addDocTab={returnFalse}
@@ -192,9 +283,12 @@ export class SearchItem extends React.Component<SearchItemProps> {
layoutresult.indexOf(DocumentType.LINK) !== -1 ? faLink :
layoutresult.indexOf(DocumentType.WEB) !== -1 ? faGlobeAsia :
faCaretUp;
- return <div onClick={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
+ return <div><div onClick={action(() => { this._useIcons = false; this._displayDim = Number(SEARCH_THUMBNAIL_SIZE); })} >
<FontAwesomeIcon icon={button} size="2x" />
- </div>;
+ </div>
+ <div className="searchItem-label">{this.rootDoc.type ? this.rootDoc.type : "Other"}</div>
+ </div>
+ ;
}
collectionRef = React.createRef<HTMLDivElement>();
@@ -202,38 +296,60 @@ export class SearchItem extends React.Component<SearchItemProps> {
@action
pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
- nextHighlight = (e: React.PointerEvent) => {
+ @action
+ nextHighlight = (e: React.MouseEvent) => {
e.preventDefault();
- e.button === 0 && SearchBox.Instance.openSearch(e);
- this.props.doc.searchMatch = false;
- setTimeout(() => this.props.doc.searchMatch = true, 0);
+ e.stopPropagation();
+ //e.button === 0 && SearchBox.Instance.openSearch(e);
+
+ this.rootDoc!.searchMatch = false;
+ setTimeout(() => this.rootDoc!.searchMatch = true, 0);
+ this.rootDoc.searchIndex=NumCast(this.rootDoc.searchIndex);
+ this.length=NumCast(this.rootDoc!.length);
}
+
+ @action
+ nextHighlight2 = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ //e.button === 0 && SearchBox.Instance.openSearch(e);
+
+ this.rootDoc!.searchMatch2 = false;
+ setTimeout(() => this.rootDoc!.searchMatch2 = true, 0);
+ this.rootDoc.searchIndex=NumCast(this.rootDoc.searchIndex);
+
+ this.length=NumCast(this.rootDoc!.length);
+ }
+
+ @observable length:number|undefined = 0;
+
highlightDoc = (e: React.PointerEvent) => {
- if (this.props.doc.type === DocumentType.LINK) {
- if (this.props.doc.anchor1 && this.props.doc.anchor2) {
+ if (this.rootDoc!.type === DocumentType.LINK) {
+ if (this.rootDoc!.anchor1 && this.rootDoc!.anchor2) {
- const doc1 = Cast(this.props.doc.anchor1, Doc, null);
- const doc2 = Cast(this.props.doc.anchor2, Doc, null);
+ const doc1 = Cast(this.rootDoc!.anchor1, Doc, null);
+ const doc2 = Cast(this.rootDoc!.anchor2, Doc, null);
Doc.BrushDoc(doc1);
Doc.BrushDoc(doc2);
}
} else {
- Doc.BrushDoc(this.props.doc);
+ Doc.BrushDoc(this.rootDoc!);
}
e.stopPropagation();
}
unHighlightDoc = (e: React.PointerEvent) => {
- if (this.props.doc.type === DocumentType.LINK) {
- if (this.props.doc.anchor1 && this.props.doc.anchor2) {
+ if (this.rootDoc!.type === DocumentType.LINK) {
+ if (this.rootDoc!.anchor1 && this.rootDoc!.anchor2) {
- const doc1 = Cast(this.props.doc.anchor1, Doc, null);
- const doc2 = Cast(this.props.doc.anchor2, Doc, null);
+ const doc1 = Cast(this.rootDoc!.anchor1, Doc, null);
+ const doc2 = Cast(this.rootDoc!.anchor2, Doc, null);
Doc.UnBrushDoc(doc1);
Doc.UnBrushDoc(doc2);
}
} else {
- Doc.UnBrushDoc(this.props.doc);
+ Doc.UnBrushDoc(this.rootDoc!);
}
}
@@ -243,7 +359,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
ContextMenu.Instance.clearItems();
ContextMenu.Instance.addItem({
description: "Copy ID", event: () => {
- Utils.CopyText(this.props.doc[Id]);
+ Utils.CopyText(StrCast(this.rootDoc[Id]));
},
icon: "fingerprint"
});
@@ -268,7 +384,7 @@ export class SearchItem extends React.Component<SearchItemProps> {
Math.abs(e.clientY - this._downY) > Utils.DRAG_THRESHOLD) {
document.removeEventListener("pointermove", this.onPointerMoved);
document.removeEventListener("pointerup", this.onPointerUp);
- const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc;
+ const doc = Doc.IsPrototype(this.rootDoc) ? Doc.MakeDelegate(this.rootDoc) : this.rootDoc;
DragManager.StartDocumentDrag([this._target], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
}
}
@@ -279,32 +395,160 @@ export class SearchItem extends React.Component<SearchItemProps> {
@computed
get contextButton() {
- return <ParentDocSelector Document={this.props.doc} addDocTab={(doc, where) => CollectionDockingView.AddRightSplit(doc)} />;
+ return <ParentDocSelector Document={this.rootDoc} addDocTab={(doc, where) => CollectionDockingView.AddRightSplit(doc)} />;
+ }
+
+ @computed get searchElementDoc() { return this.rootDoc; }
+ // @computed get targetDoc() { return this.searchElementDoc?.targetDoc as Doc; }
+
+ @computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); }
+ childLayoutTemplate = () => this.layoutDoc._viewType === CollectionViewType.Stacking ? this.searchItemTemplate: undefined;
+ 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), NumCast(this.layoutDoc._itemIndex));
+ }
+
+ newsearch(){
+ runInAction(()=>{
+ if (StrCast(this.rootDoc.bucketfield)!=="results"){
+ SearchBox.Instance._icons=[StrCast(this.rootDoc.bucketfield)];
+ SearchBox.Instance._icons=SearchBox.Instance._icons;
+ }
+ else{
+ SearchBox.Instance._icons=SearchBox.Instance._allIcons;
+ }
+ SearchBox.Instance.expandedBucket= true;
+ SearchBox.Instance.submitSearch();
+ })
+ }
+
+ @action
+ returnLines(){
+ if ((Cast(this.rootDoc.lines, listSpec("string")))!.length>1){
+ if (!this._displayLines) {
+ console.log(Cast(this.rootDoc.lines, listSpec("string")));
+ return <div style={{width: 10}}
+ onPointerDown={action(() => {
+ this._displayLines = !this._displayLines;
+ //this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE);
+ })}
+ //onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))}
+ >
+ {Cast(this.rootDoc.lines, listSpec("string"))!.filter((m, i) => i).map((l, i) => <div style={{overflow:"visible"}}id={i.toString()} className="searchItem-highlighting">{l}</div>)}
+ </div>;;
+ }
+ }
+ }
+
+ //this._displayDim = Number(SEARCH_THUMBNAIL_SIZE);
+
+ @observable _displayLines: boolean = true;
+
+ returnButtons(){
+ return <div>
+ <div onClick={action(() => { this.rootDoc!.type === DocumentType.PDF? this._displayLines = !this._displayLines : null;
+ })}>
+ {this.rootDoc!.type === DocumentType.PDF?"Expand Lines": null}
+ {NumCast(this.rootDoc!.length)>1?`Instance ${NumCast(this.rootDoc.searchIndex)===0? NumCast(this.rootDoc.length):NumCast(this.rootDoc.searchIndex) } of ${NumCast(this.rootDoc.length)}`: null}
+ <button onClick={this.nextHighlight} style={{padding:2, position:"absolute", left:77}}>
+ <FontAwesomeIcon icon="arrow-up" size="sm" />
+ </button>
+ <button onClick={this.nextHighlight2} style={{padding:2, position:"absolute", left:87}}>
+ <FontAwesomeIcon icon="arrow-down" size="sm" />
+ </button>
+ </div>
+ <div>
+ <div style={{background: "lightgrey"}}>
+ {this.returnLines()}
+ </div>
+ </div>
+ </div>
}
+ private _mainRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+
render() {
- const doc1 = Cast(this.props.doc.anchor1, Doc);
- const doc2 = Cast(this.props.doc.anchor2, Doc);
+ const doc1 = Cast(this.rootDoc!.anchor1, Doc);
+ const doc2 = Cast(this.rootDoc!.anchor2, Doc);
+ if (StrCast(this.rootDoc.bucketfield)==="webs"){
+ this.props.Document._viewType=CollectionViewType.Stacking;
+ this.props.Document._chromeStatus='disabled';
+ this.props.Document._height=this.rootDoc._height;
+ return <div>
+ <CollectionView {...this.props}
+ Document={this.props.Document}
+ PanelHeight={this.panelHeight}
+ whenActiveChanged={emptyFunction}
+ onClick={undefined}
+ moveDocument={returnFalse}
+ childLayoutTemplate={undefined}
+ addDocument={undefined}
+ removeDocument={returnFalse}
+ focus={this.selectElement}
+ ScreenToLocalTransform={this.getTransform} />
+ </div>
+ }
+ if (this.rootDoc.isBucket === true){
+ this.props.Document._viewType=CollectionViewType.Stacking;
+ this.props.Document._chromeStatus='disabled';
+ this.props.Document._height=this.rootDoc._height;
+
+ return <div>
+ <CollectionView {...this.props}
+ Document={this.props.Document}
+ PanelHeight={this.panelHeight}
+ whenActiveChanged={emptyFunction}
+ onClick={undefined}
+ moveDocument={returnFalse}
+ childLayoutTemplate={this.childLayoutTemplate}
+ addDocument={undefined}
+ removeDocument={returnFalse}
+ focus={this.selectElement}
+ ScreenToLocalTransform={this.getTransform} />
+ <button onClick={()=>this.newsearch()}className="bucket-expand" style={{transform:"none", fontSize:"100%",textTransform:"none", background: "lightgray",color: "black", bottom: 8, marginBottom:-2, paddingTop:2,fontFamily:"Arial, sans-serif"}}>See all {StrCast(this.rootDoc.bucketfield)}...
+ </button>
+ </div>
+ }
+ else if (this.rootDoc.isBucket === false){
+ this.props.Document._chromeStatus='disabled';
+ return <div className="searchItem">
+ <div className="searchItem-body" >
+ <div className="searchItem-title-container">
+ <div className="searchItem-title" style={{height:"10px", overflow:"hidden", textOverflow:"ellipsis"}}>No Search Results</div>
+ </div>
+ </div>
+ </div>
+ }
+ else {
return <div className="searchItem-overview" onPointerDown={this.pointerDown} onContextMenu={this.onContextMenu}>
- <div className="searchItem" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc}>
+ <div ref={this._mainRef} className="searchItem" onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc}>
<div className="searchItem-body" onClick={this.onClick}>
<div className="searchItem-title-container">
- <div className="searchItem-title">{StrCast(this.props.doc.title)}</div>
- <div className="searchItem-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
- {this.props.lines.filter((m, i) => i).map((l, i) => <div id={i.toString()} className="searchItem-highlighting">`${l}`</div>)}
- </div>
+ <div className="searchItem-title" style={{height:"10px", overflow:"hidden", textOverflow:"ellipsis"}}>{StrCast(this.rootDoc.title)}</div>
+ <div className="searchItem-highlighting">
+ {this.rootDoc.highlighting? StrCast(this.rootDoc.highlighting).length ? "Matched fields:" + StrCast(this.rootDoc.highlighting) : Cast(this.rootDoc.lines, listSpec("string"))!.length ? Cast(this.rootDoc.lines, listSpec("string"))![0] : "":null}</div>
+ <div className={`icon-${this._displayLines ? "q" : "a"}`}>
+ {NumCast(this.rootDoc.length) > 1 || this.rootDoc!.type === DocumentType.PDF?this.returnButtons(): null}
+ </div>
+ </div>
</div>
<div className="searchItem-info" style={{ width: this._useIcons ? "30px" : "100%" }}>
<div className={`icon-${this._useIcons ? "icons" : "live"}`}>
<div className="searchItem-type" title="Click to Preview" onPointerDown={this.onPointerDown}>{this.DocumentIcon()}</div>
- <div className="searchItem-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
</div>
</div>
- <div className="searchItem-context" title="Drag as document">
- {(doc1 instanceof Doc && doc2 instanceof Doc) && this.props.doc.type === DocumentType.LINK ? <LinkContextMenu doc1={doc1} doc2={doc2} /> :
+ {/* <div className="searchItem-context" title="Drag as document">
+ {(doc1 instanceof Doc && doc2 instanceof Doc) && this.rootDoc!.type === DocumentType.LINK ? <LinkContextMenu doc1={doc1} doc2={doc2} /> :
this.contextButton}
- </div>
+ </div> */}
</div>
</div>;
+ }
}
} \ No newline at end of file
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 267defa4b..b78d5611c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1262,4 +1262,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo
return docs.length ? new List(docs) : prevValue;
});
Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file
+Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); });
diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts
index 753c31fcf..7251e07a1 100644
--- a/src/server/ApiManagers/SearchManager.ts
+++ b/src/server/ApiManagers/SearchManager.ts
@@ -176,7 +176,7 @@ export namespace SolrManager {
"audio": ["_t", "url"],
"web": ["_t", "url"],
"date": ["_d", value => new Date(value.date).toISOString()],
- "proxy": ["_i", "fieldId"],
+ // "proxy": ["_i", "fieldId"],
"list": ["_l", list => {
const results = [];
for (const value of list.fields) {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 4455d11eb..515fbe4ff 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -14,7 +14,6 @@ import { normalize } from "path";
import RouteSubscriber from "../RouteSubscriber";
const imageDataUri = require('image-data-uri');
import { isWebUri } from "valid-url";
-import { launch } from "puppeteer";
import { Opt } from "../../fields/Doc";
import { SolrManager } from "./SearchManager";
@@ -281,25 +280,26 @@ function delay(ms: number) {
*
* On failure, returns undefined.
*/
-async function captureYoutubeScreenshot(targetUrl: string): Promise<Opt<Buffer>> {
- const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
- const page = await browser.newPage();
- await page.setViewport({ width: 1920, height: 1080 });
+async function captureYoutubeScreenshot(targetUrl: string){
+ // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
+ // const page = await browser.newPage();
+ // // await page.setViewport({ width: 1920, height: 1080 });
- await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
+ // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
- const videoPlayer = await page.$('.html5-video-player');
- videoPlayer && await page.focus("video");
- await delay(7000);
- const ad = await page.$('.ytp-ad-skip-button-text');
- await ad?.click();
- await videoPlayer?.click();
- await delay(1000);
- // hide youtube player controls.
- await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
+ // const videoPlayer = await page.$('.html5-video-player');
+ // videoPlayer && await page.focus("video");
+ // await delay(7000);
+ // const ad = await page.$('.ytp-ad-skip-button-text');
+ // await ad?.click();
+ // await videoPlayer?.click();
+ // await delay(1000);
+ // // hide youtube player controls.
+ // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
- const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
- await browser.close();
+ // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
+ // await browser.close();
- return buffer;
+ // return buffer;
+ return null;
} \ No newline at end of file
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index f63a35e43..c80056579 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -228,7 +228,8 @@ export namespace WebSocket {
"script": ["_t", value => value.script.originalScript],
"RichTextField": ["_t", value => value.Text],
"date": ["_d", value => new Date(value.date).toISOString()],
- "proxy": ["_i", "fieldId"],
+ // "proxy": ["_i", "fieldId"],
+ // "proxy": ["", "fieldId"],
"list": ["_l", list => {
const results = [];
for (const value of list.fields) {
@@ -242,25 +243,27 @@ export namespace WebSocket {
};
function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
+
if (val === null || val === undefined) {
return;
}
const type = val.__type || typeof val;
+
let suffix = suffixMap[type];
if (!suffix) {
return;
}
-
if (Array.isArray(suffix)) {
const accessor = suffix[1];
if (typeof accessor === "function") {
val = accessor(val);
} else {
val = val[accessor];
+
}
suffix = suffix[0];
- }
+ }
return { suffix, value: val };
}
@@ -282,10 +285,18 @@ export namespace WebSocket {
dynfield = true;
const val = docfield[key];
key = key.substring(7);
- Object.values(suffixMap).forEach(suf => update[key + getSuffix(suf)] = { set: null });
+ if (key==="_height"){
+ Object.values(suffixMap).forEach(suf => {update[key] = { set: null };});
+ }
+ else {
+ Object.values(suffixMap).forEach(suf => {update[key + getSuffix(suf)] = { set: null };});
+ }
const term = ToSearchTerm(val);
if (term !== undefined) {
const { suffix, value } = term;
+ if (key==="_height"){
+ update[key] = { set: value };
+ }
update[key + suffix] = { set: value };
}
}