aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/CurrentUserUtils.ts52
-rw-r--r--src/client/views/GlobalKeyHandler.ts8
-rw-r--r--src/client/views/MainView.tsx35
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx3
-rw-r--r--src/client/views/search/SearchBox.scss182
-rw-r--r--src/client/views/search/SearchBox.tsx729
7 files changed, 383 insertions, 628 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e1cbaf125..9a634017a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx";
import * as rp from 'request-promise';
import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
+import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types";
+import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
import { nullAudio } from "../../fields/URLField";
import { SharingPermissions } from "../../fields/util";
import { Utils } from "../../Utils";
@@ -19,6 +20,7 @@ import { Networking } from "../Network";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { Colors } from "../views/global/globalEnums";
import { MainView } from "../views/MainView";
import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { LabelBox } from "../views/nodes/LabelBox";
@@ -31,13 +33,10 @@ import { LinkManager } from "./LinkManager";
import { Scripting } from "./Scripting";
import { SearchUtil } from "./SearchUtil";
import { SelectionManager } from "./SelectionManager";
-import { UndoManager } from "./UndoManager";
-import { SnappingManager } from "./SnappingManager";
-import { InkTool } from "../../fields/InkField";
-import { SharingManager } from "./SharingManager";
-import { computedFn } from "mobx-utils";
import { ColorScheme } from "./SettingsManager";
-import { Colors } from "../views/global/globalEnums";
+import { SharingManager } from "./SharingManager";
+import { SnappingManager } from "./SnappingManager";
+import { UndoManager } from "./UndoManager";
export let resolvedPorts: { server: number, socket: number };
@@ -47,6 +46,7 @@ export class CurrentUserUtils {
//TODO tfs: these should be temporary...
private static mainDocId: string | undefined;
+ public static searchBtn: Doc;
public static get id() { return this.curr_id; }
public static get MainDocId() { return this.mainDocId; }
public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
@@ -56,6 +56,7 @@ export class CurrentUserUtils {
@observable public static GuestDashboard: Doc | undefined;
@observable public static GuestMobile: Doc | undefined;
@observable public static propertiesWidth: number = 0;
+ @observable public static searchPanelWidth: number = 0;
// sets up the default User Templates - slideView, headerView
static setupUserTemplateButtons(doc: Doc) {
@@ -530,6 +531,7 @@ export class CurrentUserUtils {
static async menuBtnDescriptions(doc: Doc) {
return [
{ title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+ { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
{ title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' },
{ title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
{ title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
@@ -542,14 +544,6 @@ export class CurrentUserUtils {
];
}
- static setupSearchPanel(doc: Doc) {
- if (doc.mySearchPanelDoc === undefined) {
- doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({
- _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
- childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true
- })) as any as Doc;
- }
- }
static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
if (doc.menuStack === undefined) {
await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing
@@ -569,10 +563,23 @@ export class CurrentUserUtils {
_height: 60,
watchedDocuments,
onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
- }));
+ })
+ );
+
+ menuBtns.forEach(menuBtn => {
+ if (menuBtn.title == "Search") {
+ this.searchBtn = menuBtn;
+ }
+ });
// hack -- last button is assumed to be the userDoc
menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()");
+ menuBtns.forEach(menuBtn => {
+ if (menuBtn.title == "Search") {
+ doc.searchBtn = menuBtn
+ }
+ })
+
doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
title: "menuItemPanel",
childDropAction: "alias",
@@ -819,6 +826,7 @@ export class CurrentUserUtils {
(doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
}
}
+
static setupFilterDocs(doc: Doc) {
// setup Filter item
if (doc.currentFilter === undefined) {
@@ -953,6 +961,16 @@ export class CurrentUserUtils {
}
}
+ // Search sidebar is where searches within the document are performed
+ static setupSearchSidebar(doc: Doc) {
+ if (doc.mySearchPanel === undefined) {
+ doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
+ backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
+ childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Sidebar", system: true
+ })) as any as Doc;
+ }
+ }
+
static setupClickEditorTemplates(doc: Doc) {
if (doc["clickFuncs-child"] === undefined) {
// to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target
@@ -1041,8 +1059,8 @@ export class CurrentUserUtils {
this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupImportSidebar(doc);
+ this.setupSearchSidebar(doc); // sets up the search sidebar
this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
- this.setupSearchPanel(doc);
this.setupOverlays(doc); // documents in overlay layer
this.setupDockedButtons(doc); // the bottom bar of font icons
await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 76eb4c142..0127d3080 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -27,7 +27,6 @@ import { LightboxView } from "./LightboxView";
import { MainView } from "./MainView";
import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
import { AnchorMenu } from "./pdf/AnchorMenu";
-import { SearchBox } from "./search/SearchBox";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { SettingsManager } from "../util/SettingsManager";
@@ -223,8 +222,11 @@ export class KeyManager {
PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
break;
case "f":
- SearchBox.Instance._searchFullDB = "My Stuff";
- SearchBox.Instance.enter(undefined);
+ const searchBtn = Doc.UserDoc().searchBtn as Doc;
+
+ if (searchBtn) {
+ MainView.Instance.selectMenu(searchBtn);
+ }
break;
case "o":
const target = SelectionManager.Views()[0];
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 005e46836..6ff8825ed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { BoolCast, PromiseValue, StrCast } from '../../fields/Types';
import { TraceMobx } from '../../fields/util';
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { Docs, DocUtils } from '../documents/Documents';
@@ -29,28 +29,27 @@ import { SettingsManager } from '../util/SettingsManager';
import { SharingManager } from '../util/SharingManager';
import { SnappingManager } from '../util/SnappingManager';
import { Transform } from '../util/Transform';
-import { undoBatch, UndoManager } from '../util/UndoManager';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
import { CollectionLinearView } from './collections/CollectionLinearView';
import { CollectionMenu } from './collections/CollectionMenu';
import { CollectionViewType } from './collections/CollectionView';
+import "./collections/TreeView.scss";
import { ContextMenu } from './ContextMenu';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import { GestureOverlay } from './GestureOverlay';
import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './global/globalCssVariables.scss';
+import { Colors } from './global/globalEnums';
import { KeyManager } from './GlobalKeyHandler';
import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import "./MainView.scss";
-import "./collections/TreeView.scss";
import { AudioBox } from './nodes/AudioBox';
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewProps, DocAfterFocusFunc } from './nodes/DocumentView';
-import { FieldViewProps } from './nodes/FieldView';
+import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview } from './nodes/LinkDocPreview';
@@ -61,10 +60,8 @@ import { OverlayView } from './OverlayView';
import { AnchorMenu } from './pdf/AnchorMenu';
import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
-import { SearchBox } from './search/SearchBox';
-import { DefaultStyleProvider, DashboardStyleProvider, StyleProp } from './StyleProvider';
+import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
import { TopBar } from './topbar/TopBar';
-import { Colors } from './global/globalEnums';
const _global = (window /* browser */ || global /* node */) as any;
@observer
@@ -182,12 +179,12 @@ export class MainView extends React.Component {
const targets = document.elementsFromPoint(e.x, e.y);
if (targets.length) {
const targClass = targets[0].className.toString();
- // if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) {
- // const check = targets.some((thing) =>
- // (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
- // thing.className === "collectionSchema-header-menuOptions"));
- // !check && SearchBox.Instance.resetSearch(true);
- // }
+ /*if (SearchBox.Instance._resultsOpen) {
+ const check = targets.some((thing) =>
+ (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" ||
+ thing.className === "collectionSchema-header-menuOptions"));
+ !check && SearchBox.Instance.resetSearch(true);
+ }*/
!targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
!["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
}
@@ -394,10 +391,6 @@ export class MainView extends React.Component {
case "Settings":
SettingsManager.Instance.open();
break;
- case "Catalog":
- SearchBox.Instance._searchFullDB = "My Stuff";
- SearchBox.Instance.enter(undefined);
- break;
case "Help":
break;
default:
@@ -537,7 +530,7 @@ export class MainView extends React.Component {
</svg>;
}
- @computed get search() {
+ @computed get topbar() {
TraceMobx();
return <div className="mainView-topbar">
<TopBar />
@@ -591,7 +584,7 @@ export class MainView extends React.Component {
<GroupManager />
<GoogleAuthenticationManager />
<DocumentDecorations boundsLeft={this.leftOffset} boundsTop={this.topOffset} />
- {this.search}
+ {this.topbar}
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
{LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
@@ -609,7 +602,7 @@ export class MainView extends React.Component {
{this.snapLines}
<div className="mainView-webRef" ref={this.makeWebRef} />
<LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
- </div>);
+ </div >);
}
makeWebRef = (ele: HTMLDivElement) => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 380f82813..1efea96be 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -556,7 +556,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
style={{
overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index de08c327a..abe549072 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -197,6 +197,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
<div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
<FontAwesomeIcon icon={sortIcon} size="lg" />
</div>
+ {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
</div>;
return {
@@ -561,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
- {this.props.Document._chromeHidden ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
+ {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
{!this._showDoc ? (null) :
<div className="collectionSchemaView-documentPreview" ref="overlay"
style={{
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 6a2fe6f19..3d173f115 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -2,141 +2,95 @@
@import "./NaviconButton.scss";
.searchBox-container {
- display: flex;
- flex-direction: column;
width: 100%;
height: 100%;
- position: relative;
font-size: 10px;
line-height: 1;
- overflow-y: auto;
- overflow-x: visible;
- background: lightgrey;
- overflow: visible;
+ background: none;
z-index: 1000;
+ padding: 0px;
+ cursor: default;
.searchBox-bar {
- height: $searchpanel-height;
+ width: 100%;
+ height: 35px;
display: flex;
justify-content: center;
align-items: center;
- background-color: $dark-gray;
+ background-color: none;
+ padding: 5px;
- .searchBox-lozenges {
- position: absolute;
- left: 15;
- display: flex;
-
- .searchBox-lozenge-user,
- .searchBox-lozenge-dashboard,
- .searchBox-lozenge {
- height: 18px;
- padding: 4px;
- margin-right: 5px;
- display: flex;
- align-items: center;
- border: grey 1px solid;
- .searchBox-logoff,
- .searchBox-dashboards {
- border-radius: 3px;
- background: olivedrab;
- color: white;
- display: none;
- margin-left: 5px;
- padding: 1px 2px 1px 2px;
- cursor: pointer;
- }
- .searchBox-logoff {
- background: red;
- }
-
- .searchBox-dashSelect{
- border: none;
- background-color: transparent;
+ .searchBox-type {
+ display: block;
+ width: 55px;
+ outline: none;
+ padding: 1px 5px 1px 5px;
+ color: black;
+ height: 25px;
+ border: 1px solid black;
+ border-right: 0px;
+ }
- &:hover {
- cursor: pointer;
- }
- }
- }
- .searchBox-lozenge-user:hover {
- .searchBox-logoff {
- display:inline-block;
- }
- }
- .searchBox-lozenge-dashboard:hover {
- .searchBox-dashboards {
- display:inline-block;
- }
- }
+ .searchBox-input {
+ display: block;
+ width: calc(100% - 55px);
+ outline: none;
+ padding: 1px 5px 1px 5px;
+ color: black;
+ height: 25px;
+ border: 1px solid black;
}
- .searchBox-query {
- position: relative;
+ }
+
+ .searchBox-results-container {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ justify-content: "center";
+
+ .searchBox-results-count {
display: flex;
- width: 450;
+ color: gray;
+ margin-left: 5px;
}
- .searchBox-barChild {
+
+ .searchBox-results-scroll-view {
+ margin-top: 10px;
+ display: inline-block;
+ width: 100%;
+ height: calc(100% - 55px);
+ overflow-y: scroll;
- &.searchBox-collection {
- flex: 0 1 auto;
- margin-left: 2px;
- margin-right: 2px
- }
+ .searchBox-results-scroll-view-result {
+ display: inline-block;
+ vertical-align: middle;
+ width: 100%;
+ height: 40px;
+ cursor: pointer;
+ font-size: 15px;
+ padding: 10px;
- &.searchBox-input {
- margin:5px;
- border-radius:20px;
- border:$dark-gray;
- display: block;
- width: 130px;
- -webkit-transition: width 0.4s;
- transition: width 0.4s;
- align-self: stretch;
- outline:none;
- &:focus {
- width: 500px;
- outline:none;
+ &.searchBox-results-scroll-view-result-selected {
+ background: gray;
}
- }
- &.searchBox-filter {
- align-self: stretch;
- button{
- transform:none;
- &:hover {
- transform: none;
- }
- }
- }
- &.searchBox-submit {
- margin-left: 2px;
- margin-right: 2px
- }
+ .searchBox-result-title {
+ display: relative;
+ float: left;
+ width: calc(100% - 60px);
+ text-align: left;
+ }
- &.searchBox-close {
- color: $white;
- max-height: $searchpanel-height;
+ .searchBox-result-type {
+ font-size: 12px;
+ display: relative;
+ float: right;
+ width: 60px;
+ text-align: right;
+ color: #333;
+ }
}
}
- }
-}
-
-.searchBox-results {
- display: flex;
- flex-direction: column;
- top: 300px;
- display: flex;
- flex-direction: column;
- height: 100%;
- overflow: visible;
-
- .no-result {
- width: 500px;
- background: $light-gray;
- padding: 10px;
- height: 50px;
- text-transform: uppercase;
- text-align: left;
- font-weight: bold;
}
} \ No newline at end of file
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index c72b25040..51b1319d4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,205 +1,73 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from '@material-ui/core';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, Field, Opt, DocListCastAsync } from '../../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../../fields/Doc';
import { documentSchema } from "../../../fields/documentSchemas";
-import { Copy, Id } from '../../../fields/FieldSymbols';
-import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
-import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
-import { Docs } from '../../documents/Documents';
+import { Id } from '../../../fields/FieldSymbols';
+import { createSchema, makeInterface } from '../../../fields/Schema';
+import { StrCast } from '../../../fields/Types';
import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SetupDrag } from '../../util/DragManager';
-import { SearchUtil } from '../../util/SearchUtil';
-import { Transform } from '../../util/Transform';
+import { DocumentManager } from "../../util/DocumentManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionSchemaView, ColumnType } from "../collections/collectionSchema/CollectionSchemaView";
-import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
-import { undoBatch } from "../../util/UndoManager";
-import { DocServer } from "../../DocServer";
-import { MainView } from "../MainView";
export const searchSchema = createSchema({ Document: Doc });
type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
+const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"]
+
@observer
export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
public static Instance: SearchBox;
- private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB];
- private _numResultsPerPage = 500;
- private _numTotalResults = -1;
- private _endIndex = -1;
- private _lockPromise?: Promise<void>;
private _resultsSet = new Map<Doc, number>();
private _inputRef = React.createRef<HTMLInputElement>();
- private _maxSearchIndex: number = 0;
- private _curRequest?: Promise<any> = undefined;
- private _disposers: { [name: string]: IReactionDisposer } = {};
- private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
- private docsforfilter: Doc[] | undefined = [];
- private realTotalResults: number = 0;
- private newsearchstring = "";
- private collectionRef = React.createRef<HTMLDivElement>();
-
-
- @observable _undoBackground: string | undefined = "";
- @observable _icons: string[] = this._allIcons;
+ @observable _searchString = "";
+ @observable _docTypeString = "all";
@observable _results: [Doc, string[], string[]][] = [];
- @observable _visibleElements: JSX.Element[] = [];
- @observable _visibleDocuments: Doc[] = [];
+ @observable _selectedResult: Doc | undefined = undefined;
@observable _deletedDocsStatus: boolean = false;
@observable _onlyAliases: boolean = true;
- @observable _searchbarOpen = false;
- @observable _searchFullDB = "DB"; // "DB" means searh the entire database. "My Stuff" adds a flag that selects only documents that the current user has authored
- @observable _noResults = "";
- @observable _pageStart = 0;
- @observable open = false;
- @observable children = 0;
- @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
constructor(props: any) {
super(props);
SearchBox.Instance = this;
+ this._searchString = "reset_search";
+ this.submitSearch();
+ this._searchString = "";
+ this.submitSearch();
}
componentDidMount = action(() => {
if (this._inputRef.current) {
this._inputRef.current.focus();
}
- this._disposers.filters = reaction(() => this.props.Document._docFilters,
- (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter));
});
componentWillUnmount() {
- Object.values(this._disposers).forEach(disposer => disposer?.());
+ this.resetSearch;
}
- @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
-
- onChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
- this.newsearchstring = e.target.value;
- if (e.target.value === "") {
- console.log("Reset start");
- this.docsforfilter = undefined;
- this.setSearchFilter(this.currentSelectedCollection, undefined);
- this.resetSearch(false);
-
- this.open = false;
- this._results = [];
- this._resultsSet.clear();
- this._visibleElements = [];
- this._numTotalResults = -1;
- this._endIndex = -1;
- this._curRequest = undefined;
- this._maxSearchIndex = 0;
- }
+ onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this._searchString = e.target.value;
+ this.submitSearch();
});
- enter = action((e: React.KeyboardEvent | undefined) => {
- if (!e || e.key === "Enter") {
- this.layoutDoc._searchString = this.newsearchstring;
- this._pageStart = 0;
- this.open = StrCast(this.layoutDoc._searchString) !== "" || this._searchFullDB !== "DB";
- this.submitSearch();
- }
+ onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
+ this._docTypeString = e.target.value;
});
- getFinalQuery(query: string): string {
- //alters the query so it looks in the correct fields
- //if this is true, then not all of the field boxes are checked
- //TODO: data
- const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
-
- const filters: string[] = [];
-
- for (const initFilter of initialfilters) {
- const fields = initFilter.split(":");
- if (fields[2] !== undefined) {
- filters.push(fields[0]);
- filters.push(fields[1]);
- filters.push(fields[2]);
- }
- }
-
- const finalfilters: { [key: string]: string[] } = {};
-
- for (let i = 0; i < filters.length; i = i++) {
- const fields = filters[i].split(":");
- if (finalfilters[fields[0]] !== undefined) {
- finalfilters[fields[0]].push(fields[1]);
- }
- else {
- finalfilters[fields[0]] = [fields[1]];
- }
- }
-
- for (const key in finalfilters) {
- const values = finalfilters[key];
- if (values.length === 1) {
- const mod = "_t:";
- const newWords: string[] = [];
- const oldWords = values[0].split(" ");
- oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word));
- query = `(${query}) AND (${newWords.join(" ")})`;
- }
- else {
- for (let i = 0; i < values.length; i++) {
- const mod = "_t:";
- const newWords: string[] = [];
- const oldWords = values[i].split(" ");
- oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word));
- const v = "(" + newWords.join(" ") + ")";
- if (i === 0) {
- query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : "");
- }
- else query = query + " OR " + v + (i === values.length - 1 ? ")" : "");
- }
- }
- }
-
- return query.replace(/-\s+/g, '');
- }
-
- @action
- filterDocsByType(docs: Doc[]) {
- const finalDocs: Doc[] = [];
- docs.forEach(doc => {
- const layoutresult = StrCast(doc.type, "string") as DocumentType;
- if (layoutresult && !this._blockedTypes.includes(layoutresult) && this._icons.includes(layoutresult)) {
- finalDocs.push(doc);
- }
- });
- return finalDocs;
- }
+ onResultClick = action((doc: Doc) => {
+ this.selectElement(doc);
+ this._selectedResult = doc;
+ });
- static async foreachRecursiveDocAsync(docs: Doc[], func: (doc: Doc) => void) {
- let newarray: Doc[] = [];
- while (docs.length > 0) {
- newarray = [];
- await Promise.all(docs.filter(d => d).map(async d => {
- const fieldKey = Doc.LayoutFieldKey(d);
- const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
- const data = d[annos ? fieldKey + "-annotations" : fieldKey];
- const docs = await DocListCastAsync(data);
- docs && newarray.push(...docs);
- func(d);
- }));
- docs = newarray;
- }
- }
static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
let newarray: Doc[] = [];
while (docs.length > 0) {
@@ -215,31 +83,39 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
+ static formatType(type: String): String {
+ if (type == "pdf") {
+ return "PDF";
+ }
+ else if (type == "image") {
+ return "Img";
+ }
+
+ return type.charAt(0).toUpperCase() + type.substring(1, 3);
+ }
+
@action
searchCollection(query: string) {
- const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
+ const selectedCollection = CollectionDockingView.Instance;
+ const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
query = query.toLowerCase();
+ this._results = []
+
if (selectedCollection !== undefined) {
- // this._currentSelectedCollection = selectedCollection;
const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
- const found: [Doc, string[], string[]][] = [];
SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
- const hlights = new Set<string>();
- SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
- Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
+ const dtype = StrCast(doc.type, "string") as DocumentType;
+ if (dtype && !blockedTypes.includes(dtype)) {
+ const hlights = new Set<string>();
+ SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+ Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys()), []]);
+ }
});
-
- this._results = found;
- this.docsforfilter = this._results.map(r => r[0]);
- this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
- this._numTotalResults = found.length;
- this.realTotalResults = found.length;
- }
- else {
- this._noResults = "No collection selected :(";
}
+ this._results = Array.from(new Set(this._results))
+ this._selectedResult = undefined
}
static documentKeys(doc: Doc) {
@@ -256,347 +132,258 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@action
submitSearch = async () => {
- this.resetSearch(false);
+ this.resetSearch();
- //this.props.Document._docFilters = new List();
- this._noResults = "";
-
- this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.children = 0;
- let query = StrCast(this.layoutDoc._searchString);
+ //this.dataDoc[this.fieldKey] = new List<Doc>([]);
+ let query = StrCast(this._searchString);
Doc.SetSearchQuery(query);
- this._searchFullDB && (query = this.getFinalQuery(query));
this._results = [];
this._resultsSet.clear();
- this._visibleElements = [];
- this._visibleDocuments = [];
-
- if (query || this._searchFullDB === "My Stuff") {
- this._endIndex = 12;
- this._maxSearchIndex = 0;
- this._numTotalResults = -1;
- this._searchFullDB ? await this.searchDatabase(query) : this.searchCollection(query);
- runInAction(() => {
- this.open = this._searchbarOpen = true;
- this.resultsScrolled();
- });
+
+ if (query) {
+ this.searchCollection(query);
}
}
- getAllResults = async (query: string) => {
- return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 });
- }
+ resetSearch = action(() => {
+ this._results.forEach(result => {
+ Doc.UnBrushDoc(result[0]);
+ Doc.UnHighlightDoc(result[0]);
+ Doc.ClearSearchMatches();
+ });
+ });
- private get filterQuery() {
- const baseExpr = "NOT system_b:true";
- const authorExpr = this._searchFullDB === "My Stuff" ? ` author_t:${Doc.CurrentUserEmail}` : undefined;
- const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true";
- const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`;
- // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello
- return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
+ selectElement = async (doc: Doc) => {
+ await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right
}
- @computed get primarySort() {
- const suffixMap = (type: ColumnType) => {
- switch (type) {
- case ColumnType.Date: return "_d";
- case ColumnType.String: return "_t";
- case ColumnType.Boolean: return "_b";
- case ColumnType.Number: return "_n";
+ render() {
+ var validResults = 0;
+
+ const results = this._results.map(result => {
+ var className = "searchBox-results-scroll-view-result";
+
+ if (this._selectedResult == result[0]) {
+ className += " searchBox-results-scroll-view-result-selected"
}
- };
- const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
- return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined);
- }
- searchDatabase = async (query: string) => {
- this._lockPromise && (await this._lockPromise);
- this._lockPromise = new Promise(async res => {
- while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: "on", "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
- // happens at the beginning
- this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound;
- if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
- this._numTotalResults = res.numFound;
- }
- const highlighting = res.highlighting || {};
- const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
- const lines = new Map<string, string[]>();
- res.docs.map((doc, i) => lines.set(doc[Id], res.lines[i]));
- const docs = res.docs;
- const highlights: typeof res.highlighting = {};
- docs.forEach((doc, index) => highlights[doc[Id]] = highlightList[index]);
- const filteredDocs = this.filterDocsByType(docs);
-
- runInAction(() => filteredDocs.forEach((doc, i) => {
- const index = this._resultsSet.get(doc);
- const highlight = highlights[doc[Id]];
- const line = lines.get(doc[Id]) || [];
- const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : [];
- // if (this.findCommonElements(hlights)) {
- // }
- if (index === undefined) {
- this._resultsSet.set(doc, this._results.length);
- this._results.push([doc, hlights, line]);
- } else {
- this._results[index][1].push(...hlights);
- this._results[index][2].push(...line);
- }
-
- }));
-
- this._curRequest = undefined;
- }));
- this._maxSearchIndex += this._numResultsPerPage;
-
- await this._curRequest;
+ if (this._docTypeString == "all" || this._docTypeString == result[0].type) {
+ validResults++;
+ return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="searchBox-result-title">{result[0].title}</div><div className="searchBox-result-type">{SearchBox.formatType(StrCast(result[0].type))}</div></div>)
}
- this.resultsScrolled();
+ return null;
+ })
- const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
- this.docsforfilter = this._results.map(r => r[0]);
- this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined);
- res();
- });
- return this._lockPromise;
- }
+ results.filter(result => result);
- startDragCollection = async () => {
- const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString)));
- const filtered = this.filterDocsByType(res.docs);
- const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc));
- let x = 0;
- let y = 0;
- for (const doc of docs.map(d => Doc.Layout(d))) {
- doc.x = x;
- doc.y = y;
- const size = 200;
- const aspect = Doc.NativeHeight(doc) / (Doc.NativeWidth(doc) || 1);
- if (aspect > 1) {
- doc._height = size;
- doc._width = size / aspect;
- } else if (aspect > 0) {
- doc._width = size;
- doc._height = size * aspect;
- } else {
- doc._width = size;
- doc._height = size;
- }
- x += 250;
- if (x > 1000) {
- x = 0;
- y += 300;
- }
- }
- const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; });
- return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
+ const selectOptions = selectValues.map(value => {
+ return <option key={value} value={value}>{SearchBox.formatType(value)}</option>
+ })
+
+ return (
+ <div style={{ pointerEvents: "all" }} className="searchBox-container">
+ <div className="searchBox-bar">
+ <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+ {selectOptions}
+ </select>
+ <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" ref={this._inputRef} />
+ </div >
+ <div className="searchBox-results-container">
+ <div className="searchBox-results-count">
+ {`${validResults}` + " result" + (validResults == 1 ? "" : "s")}
+ </div>
+ <div className="searchBox-results-scroll-view">
+ {results}
+ </div>
+ </div>
+ </div >
+ );
}
+}
+
+
+
+/*
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc';
+import { documentSchema } from "../../../fields/documentSchemas";
+import { Copy, Id, ToString } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from "../../documents/DocumentTypes";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { SetupDrag } from '../../util/DragManager';
+import { SearchUtil } from '../../util/SearchUtil';
+import { Transform } from '../../util/Transform';
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
+import { CollectionViewType } from '../collections/CollectionView';
+import { ViewBoxBaseComponent } from "../DocComponent";
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import "./SearchBox.scss";
+import { undoBatch } from "../../util/UndoManager";
+import { DocServer } from "../../DocServer";
+import { MainView } from "../MainView";
+import { SelectionManager } from "../../util/SelectionManager";
+import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells";
+import { transpileModule } from "typescript";
+import { DocumentManager } from "../../util/DocumentManager";
+
+export const searchSchema = createSchema({ Document: Doc });
- @action.bound
- openSearch(e: React.SyntheticEvent) {
- e.stopPropagation();
- this._results.forEach(result => Doc.BrushDoc(result[0]));
+type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
+const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
+
+@observer
+export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDocument>(SearchBoxDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+ public static Instance: SearchBox;
+
+ @observable _searchString = "";
+ @observable _docTypeString = "all";
+ @observable _results: [Doc, string[], string[]][] = [];
+ @observable _selectedResult: Doc | undefined = undefined;
+ @observable _deletedDocsStatus: boolean = false;
+ @observable _onlyAliases: boolean = true;
+
+ constructor(props: any) {
+ super(props);
+ SearchBox.Instance = this;
}
- resetSearch = action((close: boolean) => {
- this._results.forEach(result => {
- Doc.UnBrushDoc(result[0]);
- Doc.ClearSearchMatches();
- });
- close && (this.open = this._searchbarOpen = false);
+ onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
+ this._searchString = e.target.value;
+ this.submitSearch();
});
- @action.bound
- closeResults() {
- this._results = [];
- this._resultsSet.clear();
- this._visibleElements = [];
- this._visibleDocuments = [];
- this._numTotalResults = -1;
- this._endIndex = -1;
- this._curRequest = undefined;
- }
+ onSelectChange = action((e: React.ChangeEvent<HTMLSelectElement>) => {
+ this._docTypeString = e.target.value;
+ this.submitSearch();
+ });
- @action
- resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
- this._endIndex = 30;
- const headers = new Set<string>(["title", "author", "text", "type", "data", "*lastModified", "context"]);
+ onResultClick = action((doc: Doc) => {
+ this.selectElement(doc);
+ this._selectedResult = doc;
+ });
- if (this._numTotalResults <= this._maxSearchIndex) {
- this._numTotalResults = this._results.length;
+ static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) {
+ const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
+ let newarray: Doc[] = [];
+ while (docs.length > 0) {
+ newarray = [];
+ docs.filter(d => d).forEach(d => {
+ const dtype = StrCast(d.type, "string") as DocumentType;
+ if (dtype && !blockedTypes.includes(dtype)) {
+ const fieldKey = Doc.LayoutFieldKey(d);
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+ const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ data && newarray.push(...DocListCast(data));
+ func(d);
+ }
+ });
+ docs = newarray;
}
+ }
- // only hit right at the beginning
- // visibleElements is all of the elements (even the ones you can't see)
- 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);
- this._visibleDocuments = Array<Doc>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
- }
- let max = this._numResultsPerPage;
- max > this._results.length ? max = this._results.length : console.log("");
- for (let i = this._pageStart; i < max; i++) {
- //if the index is out of the window then put a placeholder in
- //should ones that have already been found get set to placeholders?
-
- let result: [Doc, string[], string[]] | undefined = undefined;
-
- result = this._results[i];
- if (result) {
- const highlights = Array.from([...Array.from(new Set(result[1]).values())]);
- const lines = new List<string>(result[2]);
- highlights.forEach((item) => headers.add(item));
- Doc.SetSearchMatch(result[0], { searchMatch: 1 });
- if (i < this._visibleDocuments.length) {
- this._visibleDocuments[i] = result[0];
- Doc.BrushDoc(result[0]);
- Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]);
- this.children++;
+ @action
+ searchCollection(query: string) {
+ const selectedCollection = CollectionDockingView.Instance;
+ query = query.toLowerCase();
+
+ if (selectedCollection !== undefined) {
+ console.log("hello111")
+ // this._currentSelectedCollection = selectedCollection;
+ const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
+ const found: [Doc, string[], string[]][] = [];
+ SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => {
+ console.log("HELLO")
+ if (this._docTypeString == "all" || this._docTypeString == doc.type) {
+ const hlights = new Set<string>();
+ SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+ Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]);
}
- }
- }
- if (this.props.Document._schemaHeaders === undefined) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
- }
- if (this._maxSearchIndex >= this._numTotalResults) {
- this._visibleElements.length = this._results.length;
- this._visibleDocuments.length = this._results.length;
+ });
+
+ this._results = found;
+ //this.setSearchFilter(selectedCollection, this.filter && found.length ? this._docsforfilter : undefined);
}
}
- getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
- panelHeight = () => this.props.PanelHeight();
- selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ };
- returnHeight = () => NumCast(this.layoutDoc._height);
- returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
+ static 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));
+ }
@action
- changeSearchScope = (scope: string) => {
- this.docsforfilter = undefined;
- this.setSearchFilter(this.currentSelectedCollection, undefined);
- this._searchFullDB = scope;
+ submitSearch = async () => {
+ Doc.ClearSearchMatches();
+ this._results = [];
+
this.dataDoc[this.fieldKey] = new List<Doc>([]);
- this.submitSearch();
- }
+ let query = StrCast(this._searchString);
+ Doc.SetSearchQuery(query);
+ this._results = [];
- @computed get scopeButtons() {
- return <div style={{ height: 25, paddingLeft: "4px", paddingRight: "4px" }}>
- <form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
- <div style={{ display: "contents" }}>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this._searchFullDB} onChange={() => this.changeSearchScope("")} />
- Dashboard
- </label>
- </div>
- <div className="radio" style={{ margin: 0 }}>
- <label style={{ fontSize: 12, marginTop: 6 }} >
- <input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={this._searchFullDB?.length ? true : false} onChange={() => this.changeSearchScope("DB")} />
- DB
- <span onClick={action(() => this._searchFullDB = this._searchFullDB === "My Stuff" ? "DB" : "My Stuff")}>
- {this._searchFullDB === "My Stuff" ? "(me)" : "(full)"}
- </span>
- </label>
- </div>
- </div>
- </form>
- </div>;
+ if (query) {
+ this.searchCollection(query);
+ }
}
- setSearchFilter = action((collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => {
- if (collectionView) {
- const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
- collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
- collectionView.props.Document._docFilters = docsForFilter?.length && docFilters?.length ? new List<string>(docFilters) : undefined;
- }
- });
+ selectElement = async (doc: Doc) => {
+ await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right
+ }
render() {
- const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+ const results = this._results.map(result => {
+ var className = "searchBox-results-scroll-view-result";
+
+ if (this._selectedResult == result[0]) {
+ className += " searchBox-results-scroll-view-result-selected"
+ }
+
+ return (<div key={result[0][Id]} onClick={() => this.onResultClick(result[0])} className={className}><div className="titletitle">{result[0].title}</div></div>)
+ })
+
return (
<div style={{ pointerEvents: "all" }} className="searchBox-container">
- <div className="searchBox-bar" style={{ background: SearchBox.Instance._undoBackground }}>
- <div className="searchBox-lozenges" >
- <div className="searchBox-lozenge-user">
- {`${Doc.CurrentUserEmail}`}
- <div className="searchBox-logoff" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
- Logoff
- </div>
- </div>
- <div className="searchBox-lozenge" onClick={() => DocServer.UPDATE_SERVER_CACHE()}>
- {`UI project`}
- </div>
- <div className="searchBox-lozenge-dashboard" >
- <select className="searchBox-dashSelect" onChange={e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)])}
- value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}>
- {myDashboards.map((dash, i) => <option key={dash[Id]} value={i} style={{ backgroundColor: "black" }}> {StrCast(dash.title)} </option>)}
- </select>
- <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
- New
- </div>
- <div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}>
- Snapshot
- </div>
- </div>
- </div>
- <div className="searchBox-query" >
- <input defaultValue={""} autoComplete="off" onChange={this.onChange} type="text" placeholder="Search..." id="search-input" ref={this._inputRef}
- className="searchBox-barChild searchBox-input" onKeyPress={this.enter}
- style={{ padding: 1, paddingLeft: 20, paddingRight: 60, color: "black", height: 20, width: 250 }} />
- <div style={{ display: "flex", alignItems: "center" }}>
- <div style={{ position: "absolute", left: 10 }}>
- <Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>}>
- <div ref={this.collectionRef}><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
- style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
- </Tooltip>
- </div>
- <div style={{ position: "absolute", left: Doc.UserDoc().noviceMode ? 220 : 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}>
- {`${this._results.length}` + " of " + `${this.realTotalResults}`}
- </div>
- {Doc.UserDoc().noviceMode ? (null) : <div style={{ cursor: "default", left: 235, position: "absolute", }}>
- <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} >
- <div>
- <FontAwesomeIcon icon={"filter"} size="lg"
- style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
- onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
- onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
- </div>
- </Tooltip>
- </div>}
- {this.scopeButtons}
- </div>
- </div >
+ <div className="searchBox-bar">
+ <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+ <option value="all">All</option>
+ <option value="rtf">Text</option>
+ <option value="img">Img</option>
+ <option value="pdf">PDF</option>
+ <option value="video">Vid</option>
+ <option value="audio">Aud</option>
+ <option value="inks">Ink</option>
+ <option value="col">Col</option>
+ </select>
+ <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} type="text" placeholder="Search..." id="search-input" className="searchBox-input" />
</div >
- {!this._searchbarOpen ? (null) :
- <div style={{ zIndex: 20000, color: "black" }} ref={(r) => r?.focus()}>
- <div style={{ display: "flex", justifyContent: "center", }}>
- <div style={{ display: this.open ? "flex" : "none", overflow: "auto", position: "absolute" }}>
- <CollectionSchemaView {...this.props}
- CollectionView={undefined}
- addDocument={returnFalse}
- Document={this.props.Document}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelHeight={this.open ? this.returnHeight : returnZero}
- PanelWidth={this.open ? this.returnLength : returnZero}
- scrollOverflow={length > window.innerWidth || this.children > 6 ? true : false}
- focus={this.selectElement}
- ScreenToLocalTransform={Transform.Identity}
- />
- <div style={{ position: "absolute", right: 5, bottom: 7, width: 15, height: 15, }}
- onPointerDown={e => setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => {
- this.props.Document._height = NumCast(this.props.Document._height) + delta[1];
- return false;
- }, returnFalse, emptyFunction)}
- >
- <FontAwesomeIcon icon="grip-lines" size="lg" />
- </div>
- </div>
- </div>
+ <div className="searchBox-results-container">
+ <div className="searchBox-results-count">
+ {`${this._results.length}` + " result" + (this._results.length == 1 ? "" : "s")}
</div>
- }
+ <div className="searchBox-results-scroll-view">
+ {results}
+ </div>
+ </div>
</div >
);
}
-} \ No newline at end of file
+}*/ \ No newline at end of file