diff options
Diffstat (limited to 'src')
22 files changed, 1221 insertions, 256 deletions
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 7ba21b2f6..454fb2ad2 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) LINKDB = "linkdb", // database of links ??? why do we have this diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dac3a1aaa..c32d187a0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -19,6 +19,11 @@ import { LinkManager } from "../util/LinkManager"; import { Scripting } from "../util/Scripting"; import { UndoManager } from "../util/UndoManager"; import { DocumentType } from "./DocumentTypes"; +import { filterData} from "../views/search/SearchBox"; +//import { PresBox } from "../views/nodes/PresBox"; +//import { PresField } from "../../new_fields/PresField"; +import { SearchItem } from "../views/search/SearchItem"; +import { SearchBox } from "../views/search/SearchBox"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { ContextMenu } from "../views/ContextMenu"; @@ -176,10 +181,12 @@ 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; + searchText?: string, //for searchbox + searchQuery?: string, // for quersyBox + filterQuery?: filterData, linearViewIsExpanded?: boolean; // is linear view expanded + border?: string; //for searchbox + hovercolor?:string; } class EmptyBox { @@ -209,8 +216,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, { @@ -300,6 +307,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" } @@ -596,8 +606,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 = {}) { @@ -760,6 +770,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 fc73dbf58..a7cb29815 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -42,7 +42,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.Stacking, title: "query", _height: 200 }), Docs.Create.FreeformDocument([], { title: "data", _height: 100, _LODdisable: true }) ], { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true } @@ -386,7 +386,7 @@ export class CurrentUserUtils { { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding:10, _yPadding: 10, title: "Button" })' }, { title: "Drag a presentation view", label: "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", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, + { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.SearchDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, { title: "Drag an import folder", label: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, { title: "Drag a mobile view", label: "Phone", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, @@ -628,7 +628,7 @@ export class CurrentUserUtils { doc["tabs-button-search"] = new PrefetchProxy(Docs.Create.ButtonDocument({ _width: 50, _height: 25, title: "Search", _fontSize: 10, 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({_viewType: CollectionViewType.Stacking, 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, @@ -704,6 +704,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 e6cf50de3..09e2aa56b 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -136,7 +136,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 5679c0a14..e4c4f5fb7 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>; @@ -128,7 +131,6 @@ export namespace SearchUtil { }); const result: IdSearchResult = JSON.parse(response); const { ids, numFound, highlighting } = result; - //console.log(ids.length); const docMap = await DocServer.GetRefFields(ids); const docs: Doc[] = []; for (const id of ids) { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4aab43b08..9f6643ee0 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -49,6 +49,8 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) @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.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); } @@ -77,7 +79,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/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 508b9e5e7..ca8b0c14e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -73,7 +73,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; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index f1438fd54..37718e028 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"; @@ -193,7 +194,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, RecommendationsBox, ScreenshotBox, HTMLtag, ComparisonBox }} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 71cb18e08..8709e76f4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1185,7 +1185,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu pointerEvents: this.ignorePointerEvents ? "none" : undefined, color: StrCast(this.layoutDoc.color, "inherit"), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", - border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, + border: this.layoutDoc.border ? StrCast(this.layoutDoc.border) : highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined, background: finalColor, opacity: finalOpacity, diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 305c04a90..a918526ed 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -52,6 +52,10 @@ export interface FieldViewProps { 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 360ead18e..100e2d61e 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,17 +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"), + color: StrCast(this.layoutDoc.color), + backgroundColor:this.backColor, + fontSize: NumCast(this.layoutDoc.fontSize) || "inherit", fontSize: NumCast(this.layoutDoc._fontSize) || "inherit", fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit", letterSpacing: StrCast(this.layoutDoc.letterSpacing), 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 134fe6e62..6b522f6d1 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -251,21 +251,59 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link)); } } - public highlightSearchTerms = (terms: string[]) => { + 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))); + if (this._searchIndex>1){ + this._searchIndex+=-2; + } + else if (this._searchIndex===1){ + this._searchIndex=length-1; + } + else if (this._searchIndex===0){ + this._searchIndex=length-2; + } + + 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 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 (this._editorView && (this._editorView as any).docView) { const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); @@ -684,7 +722,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: true }); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 516774f44..4976ffe43 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -103,6 +103,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 = ""; @@ -150,6 +151,17 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._mainCont.current!.scrollTop = this.layoutDoc._scrollTop || 0; this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => { if (search) { + this.search(Doc.SearchQuery(), false); + this._lastSearch = Doc.SearchQuery(); + } + else { + setTimeout(() => this._lastSearch === "mxytzlaf" && this.search("mxytzlaf", true), 200); // bcz: how do we clear search highlights? + this._lastSearch && (this._lastSearch = "mxytzlaf"); + } + }, { fireImmediately: true }); + + this._searchReactionDisposer2 = reaction(() => this.Document.searchMatch2, search => { + if (search) { this.search(Doc.SearchQuery(), true); this._lastSearch = Doc.SearchQuery(); } @@ -329,7 +341,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]); } @@ -337,6 +352,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 +421,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 +436,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..cd64d71ff 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -45,9 +45,11 @@ &.searchBox-filter { align-self: stretch; + button{ + transform:none; + } button:hover{ - transform:scale(1.0); - background:"#121721"; + transform:none; } } @@ -81,8 +83,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 +96,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 +130,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 +142,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 c9d29e485..8449f514a 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -8,29 +8,45 @@ import * as rp from 'request-promise'; import { Doc } 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 } 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'; + 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 +54,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 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,30 +94,87 @@ 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); - } - componentDidMount = action(() => { + 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){ + this.setupDocTypeButtons(); + this.setupKeyButtons(); + this.setupDefaultButtons(); + 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 @@ -94,7 +182,7 @@ export class SearchBox extends React.Component<SearchProps> { @action.bound onChange(e: React.ChangeEvent<HTMLInputElement>) { - this._searchString = e.target.value; + this.layoutDoc._searchString = e.target.value; this._openNoResults = false; this._results = []; @@ -108,6 +196,9 @@ export class SearchBox extends React.Component<SearchProps> { enter = (e: React.KeyboardEvent) => { if (e.key === "Enter") { + if (this._icons!==this._allIcons){ + runInAction(()=>{this.expandedBucket=false}); + } this.submitSearch(); } } @@ -134,10 +225,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,7 +254,7 @@ 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(); @@ -176,15 +268,15 @@ 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 && !blockedTypes.includes(layoutresult)){ if (layoutresult && this._icons.includes(layoutresult)) { finalDocs.push(doc); } + } }); return finalDocs; } @@ -277,28 +369,115 @@ export class SearchBox extends React.Component<SearchProps> { get fieldFiltersApplied() { return !(this._authorFieldStatus && this._titleFieldStatus); } + @observable expandedBucket:boolean=false; @action - submitSearch = async () => { - const query = this._searchString; + submitSearch = async (reset?:boolean) => { + this.checkIcons(); + if (reset){ + this.layoutDoc._searchString=""; + } + this.dataDoc[this.fieldKey] = new List<Doc>([]); + 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 (query !== "") { this._endIndex = 12; this._maxSearchIndex = 0; this._numTotalResults = -1; await this.getResults(query); - runInAction(() => { this._resultsOpen = true; this._searchbarOpen = true; this._openNoResults = true; this.resultsScrolled(); + }); } } + + @observable firststring: string = ""; + @observable secondstring: string = ""; + + @observable bucketcount:number[]=[]; + + @action private makenewbuckets(){ + console.log("new"); + let highcount=0; + let secondcount=0; + this.firststring=""; + this.secondstring=""; + this.buckets=[]; + this.bucketcount=[]; + this.dataDoc[this.fieldKey] = new List<Doc>([]); + for (var key in this.new_buckets){ + if (this.new_buckets[key]>highcount){ + secondcount===highcount; + this.secondstring=this.firststring; + highcount=this.new_buckets[key]; + this.firststring= key; + } + else if (this.new_buckets[key]>secondcount){ + secondcount=this.new_buckets[key]; + this.secondstring= key; + } + } + + let bucket = Docs.Create.StackingDocument([],{ _viewType:CollectionViewType.Stacking,title: `default bucket`}); + bucket._viewType === CollectionViewType.Stacking; + bucket._height=185; + bucket.bucketfield = "results"; + bucket.isBucket=true; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, bucket); + this.buckets!.push(bucket); + this.bucketcount[0]=0; + + if (this.firststring!==""){ + let firstbucket = Docs.Create.StackingDocument([],{ _viewType:CollectionViewType.Stacking,title: this.firststring }); + firstbucket._height=185; + + firstbucket._viewType === CollectionViewType.Stacking; + firstbucket.bucketfield = this.firststring; + firstbucket.isBucket=true; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, firstbucket); + this.buckets!.push(firstbucket); + this.bucketcount[1]=0; + + } + + if (this.secondstring!==""){ + let secondbucket = Docs.Create.StackingDocument([],{ _viewType:CollectionViewType.Stacking,title: this.secondstring }); + secondbucket._height=185; + secondbucket._viewType === CollectionViewType.Stacking; + secondbucket.bucketfield = this.secondstring; + secondbucket.isBucket=true; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, secondbucket); + this.buckets!.push(secondbucket); + this.bucketcount[2]=0; + } + + let webbucket = Docs.Create.StackingDocument([],{ _viewType:CollectionViewType.Stacking,title: this.secondstring }); + webbucket._height=185; + webbucket._viewType === CollectionViewType.Stacking; + webbucket.bucketfield = "webs"; + webbucket.isBucket=true; + const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${this.layoutDoc._searchString}`, { + _width: 200, _nativeHeight: 962, _nativeWidth: 800, isAnnotating: false, + title: "bing", UseCors: true + }); + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, webbucket); + Doc.AddDocToList(webbucket, this.props.fieldKey, textDoc); + + + } + + @observable buckets:Doc[]|undefined; getAllResults = async (query: string) => { return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 }); @@ -309,11 +488,11 @@ 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; - } + } getDataStatus() { return this._deletedDocsStatus; } @@ -321,17 +500,17 @@ export class SearchBox extends React.Component<SearchProps> { 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,13 +519,25 @@ 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 (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]); @@ -354,6 +545,7 @@ export class SearchBox extends React.Component<SearchProps> { this._results[index][1].push(...hlights); this._results[index][2].push(...line); } + } }); }); @@ -363,6 +555,9 @@ export class SearchBox extends React.Component<SearchProps> { await this._curRequest; } + if (this._numTotalResults>3 && this.expandedBucket===false){ + this.makenewbuckets(); + } this.resultsScrolled(); res(); }); @@ -371,9 +566,9 @@ export class SearchBox extends React.Component<SearchProps> { collectionRef = React.createRef<HTMLSpanElement>(); startDragCollection = async () => { - const res = await this.getAllResults(this.getFinalQuery(this._searchString)); - const filtered = this.filterDocsByType(res.docs); - const docs = filtered.map(doc => { + const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString))); + const filtered = this.filterDocsByType(res.docs); + const docs = filtered.map(doc => { const isProto = Doc.GetT(doc, "isPrototype", "boolean", true); if (isProto) { return Doc.MakeDelegate(doc); @@ -404,7 +599,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.Stacking , title: StrCast(this.layoutDoc._searchString), filterQuery: filter, searchQuery: StrCast(this.layoutDoc._searchString) }); } @action.bound @@ -427,6 +629,7 @@ 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; @@ -435,15 +638,20 @@ export class SearchBox extends React.Component<SearchProps> { @action resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => { if (!this._resultsRef.current) return; + const scrollY = e ? e.currentTarget.scrollTop : this._resultsRef.current ? this._resultsRef.current.scrollTop : 0; const itemHght = 53; const 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; if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) { this._visibleElements = [<div className="no-result">No Search Results</div>]; + //this._visibleDocuments= Docs.Create. + let noResult= Docs.Create.TextDocument("",{title:"noResult"}) + noResult.isBucket =false; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, noResult); return; } @@ -456,16 +664,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,31 +684,109 @@ 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} />; - this._isSearch[i] = "search"; - } + let lines = new List<string>(result[2]); + result[0]._height=46; + result[0].lines=lines; + result[0].highlighting=highlights.join(", "); + this._visibleDocuments[i] = result[0]; + this._isSearch[i] = "search"; + if (this._numTotalResults>3 && this.expandedBucket===false){ + let doctype = StrCast(result[0].type); + console.log(doctype); + if (doctype=== this.firststring){ + if (this.bucketcount[1]<3){ + result[0].parent= this.buckets![1]; + Doc.AddDocToList(this.buckets![1], this.props.fieldKey, result[0]); + this.bucketcount[1]+=1; + } + } + else if (doctype=== this.secondstring){ + if (this.bucketcount[2]<3){ + result[0].parent= this.buckets![2]; + Doc.AddDocToList(this.buckets![2], this.props.fieldKey, result[0]); + this.bucketcount[2]+=1; + } + } + else if (this.bucketcount[0]<3){ + //Doc.AddDocToList(this.buckets![0], this.props.fieldKey, result[0]); + //this.bucketcount[0]+=1; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); + } + } + else { + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); + } + } } 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} />; + let lines = new List<string>(result[2]); + 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"; + if (this._numTotalResults>3 && this.expandedBucket===false){ + + if (StrCast(result[0].type)=== this.firststring){ + if (this.bucketcount[1]<3){ + result[0].parent= this.buckets![1]; + Doc.AddDocToList(this.buckets![1], this.props.fieldKey, result[0]); + this.bucketcount[1]+=1; + } + } + else if (StrCast(result[0].type)=== this.secondstring){ + if (this.bucketcount[2]<3){ + result[0].parent= this.buckets![2]; + Doc.AddDocToList(this.buckets![2], this.props.fieldKey, result[0]); + this.bucketcount[2]+=1; + } + } + else if (this.bucketcount[0]<3){ + //Doc.AddDocToList(this.buckets![0], this.props.fieldKey, result[0]); + //this.bucketcount[0]+=1; + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); + } + } + else { + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); + } + } } } } } } + if (this._numTotalResults>3 && this.expandedBucket===false){ + if (this.buckets![0]){ + this.buckets![0]._height = this.bucketcount[0]*55 + 25; + } + if (this.buckets![1]){ + this.buckets![1]._height = this.bucketcount[1]*55 + 25; + } + if (this.buckets![2]){ + this.buckets![2]._height = this.bucketcount[2]*55 + 25; + } + } if (this._maxSearchIndex >= this._numTotalResults) { this._visibleElements.length = this._results.length; + this._visibleDocuments.length = this._results.length; this._isSearch.length = this._results.length; } } + findCommonElements(arr2:string[]) { + let arr1= ["layout", "data"]; + return arr1.some(item => arr2.includes(item)) + } + @computed get resFull() { return this._numTotalResults <= 8; } @@ -506,29 +795,48 @@ export class SearchBox extends React.Component<SearchProps> { //if true, any keywords can be used. if false, all keywords are required. @action.bound - handleWordQueryChange = () => { + handleWordQueryChange = async() => { this._basicWordStatus = !this._basicWordStatus; + if (this._basicWordStatus) { + 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 +844,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 +862,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; @@ -582,7 +891,6 @@ export class SearchBox extends React.Component<SearchProps> { } expandSection(thing: string) { - console.log("expand"); const element = document.getElementById(thing)!; // get the height of the element's inner content, regardless of its actual size const sectionHeight = element.scrollHeight; @@ -617,55 +925,335 @@ 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); + + @computed get docButtons() { + const nodeBtns = this.props.Document.nodeButtons; + let width = () => NumCast(this.props.Document._width); + // if (StrCast(this.props.Document.title)==="sidebar search stack"){ + width = MainView.Instance.flyoutWidthFunc; + + // } + if (nodeBtns instanceof Doc) { + return <div id="hi" style={{height:"100px",}}> + <DocumentView + docFilters={returnEmptyFilter} + Document={nodeBtns} + DataDoc={undefined} + LibraryPath={emptyPath} + addDocument={undefined} + addDocTab={returnFalse} + rootSelected={returnTrue} + pinToPres={emptyFunction} + onClick={undefined} + removeDocument={undefined} + ScreenToLocalTransform={this.getTransform} + ContentScaling={returnOne} + PanelWidth={width} + PanelHeight={() => 100} + renderDepth={0} + backgroundColor={returnEmptyString} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + NativeHeight={()=>100} + NativeWidth={width} + /> + </div>; + } + return (null); + } + + @computed get keyButtons() { + const nodeBtns = this.props.Document.keyButtons; + let width = () => NumCast(this.props.Document._width); + // if (StrCast(this.props.Document.title)==="sidebar search stack"){ + width = MainView.Instance.flyoutWidthFunc; + // } + if (nodeBtns instanceof Doc) { + return <div id="hi" style={{height:"35px",}}> + <DocumentView + docFilters={returnEmptyFilter} + Document={nodeBtns} + DataDoc={undefined} + LibraryPath={emptyPath} + addDocument={undefined} + addDocTab={returnFalse} + rootSelected={returnTrue} + pinToPres={emptyFunction} + onClick={undefined} + removeDocument={undefined} + ScreenToLocalTransform={this.getTransform} + ContentScaling={returnOne} + PanelWidth={width} + PanelHeight={() => 100} + renderDepth={0} + backgroundColor={returnEmptyString} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + NativeHeight={()=>100} + NativeWidth={width} + /> + </div>; + } + return (null); + } + @computed get defaultButtons() { + const defBtns = this.props.Document.defaultButtons; + let width = () => NumCast(this.props.Document._width); + // if (StrCast(this.props.Document.title)==="sidebar search stack"){ + width = MainView.Instance.flyoutWidthFunc; + // } + if (defBtns instanceof Doc) { + return <div id="hi" style={{height:"35px",}}> + <DocumentView + docFilters={returnEmptyFilter} + + Document={defBtns} + DataDoc={undefined} + LibraryPath={emptyPath} + addDocument={undefined} + addDocTab={returnFalse} + rootSelected={returnTrue} + pinToPres={emptyFunction} + onClick={undefined} + removeDocument={undefined} + ScreenToLocalTransform={this.getTransform} + ContentScaling={returnOne} + PanelWidth={width} + PanelHeight={() => 100} + renderDepth={0} + backgroundColor={returnEmptyString} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + NativeHeight={()=>100} + NativeWidth={width} + /> + </div>; + } + return (null); + } + + @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"; + } + } + } + + setupDocTypeButtons() { + let doc = this.props.Document; + const ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ ...opts, + dropAction: "alias", removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 100, _nativeHeight: 100, _width: 100, + _height: 100 })) as any as Doc; + doc.Audio = ficon({ onClick: ScriptField.MakeScript(`updateIcon("audio")`), title: "music button", icon: "music" }); + doc.Collection = ficon({ onClick: ScriptField.MakeScript(`updateIcon("collection")`), title: "col button", icon: "object-group" }); + doc.Image = ficon({ onClick: ScriptField.MakeScript(`updateIcon("image")`), title: "image button", icon: "image" }); + doc.Link = ficon({ onClick: ScriptField.MakeScript(`updateIcon("link")`), title: "link button", icon: "link" }); + doc.Pdf = ficon({ onClick: ScriptField.MakeScript(`updateIcon("pdf")`), title: "pdf button", icon: "file-pdf" }); + doc.Rtf = ficon({ onClick: ScriptField.MakeScript(`updateIcon("rtf")`), title: "text button", icon: "sticky-note" }); + doc.Video = ficon({ onClick: ScriptField.MakeScript(`updateIcon("video")`), title: "vid button", icon: "video" }); + doc.Web = ficon({ onClick: ScriptField.MakeScript(`updateIcon("web")`), title: "web button", icon: "globe-asia" }); + + let buttons = [doc.None as Doc, doc.Audio as Doc, doc.Collection as Doc, + doc.Image as Doc, doc.Link as Doc, doc.Pdf as Doc, doc.Rtf as Doc, doc.Video as Doc, doc.Web as Doc]; + + const dragCreators = Docs.Create.MasonryDocument(buttons, { + _width: 500, backgroundColor:"#121721", _autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _yMargin: 5 + }); + doc.nodeButtons= dragCreators; + this.checkIcons() + } + + + setupKeyButtons() { + let doc = this.props.Document; + const button = (opts: DocumentOptions) => new PrefetchProxy( Docs.Create.ButtonDocument({...opts, + _width: 35, _height: 30, + borderRounding: "16px", border:"1px solid grey", color:"white", hovercolor: "rgb(170, 170, 163)", letterSpacing: "2px", + _fontSize: 7, + }))as any as Doc; + doc.title=button({ backgroundColor:"grey", title: "Title", onClick:ScriptField.MakeScript("updateTitleStatus(self)")}); + doc.deleted=button({ title: "Deleted", onClick:ScriptField.MakeScript("updateDeletedStatus(self)")}); + doc.author = button({ backgroundColor:"grey", title: "Author", onClick:ScriptField.MakeScript("updateAuthorStatus(self)")}); + let buttons = [doc.title as Doc, doc.deleted as Doc, doc.author as Doc]; + + const dragCreators = Docs.Create.MasonryDocument(buttons, { + _width: 500, backgroundColor:"#121721", _autoHeight: true, columnWidth: 50, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons",_yMargin: 5 + //dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + }); + doc.keyButtons= dragCreators; + } + + setupDefaultButtons() { + let doc = this.props.Document; + const button = (opts: DocumentOptions) => new PrefetchProxy( Docs.Create.ButtonDocument({...opts, + _width: 35, _height: 30, + borderRounding: "16px", border:"1px solid grey", color:"white", + //hovercolor: "rgb(170, 170, 163)", + letterSpacing: "2px", + _fontSize: 7, + }))as any as Doc; + doc.keywords=button({ title: "Keywords", onClick:ScriptField.MakeScript("handleWordQueryChange(self)")}); + doc.keys=button({ title: "Keys", onClick:ScriptField.MakeScript(`handleKeyChange(self)`)}); + doc.nodes = button({ title: "Nodes", onClick:ScriptField.MakeScript("handleNodeChange(self)")}); + let buttons = [doc.keywords as Doc, doc.keys as Doc, doc.nodes as Doc]; + const dragCreators = Docs.Create.MasonryDocument(buttons, { + _width: 500, backgroundColor:"#121721", _autoHeight: true, columnWidth: 60, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons",_yMargin: 5 + //dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + }); + doc.defaultButtons= dragCreators; + } + @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() - 50; + } + selectElement = (doc: Doc) => { + //this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); + } + + addDocument = (doc: Doc) => { + return null; + } + //Make id layour document render() { + if (this.expandedBucket === true){ + this.props.Document._gridGap=5; + } + else { + this.props.Document._gridGap=10; + } + this.props.Document._searchDoc=true; 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} + <input value={StrCast(this.layoutDoc._searchString)} 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> + <button className="searchBox-barChild searchBox-filter" style={{transform:"none"}} title="Advanced Filtering Options" onClick={() => this.handleFilterChange()}><FontAwesomeIcon icon="ellipsis-v" color="white" /></button> </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 id={`filterhead${this.props.Document[Id]}`} className="filter-form" style={this._filterOpen && this._numTotalResults >0 ? {overflow:"visible"} : {overflow:"hidden"}}> + <div id={`filterhead2${this.props.Document[Id]}`} className="filter-header" > + {this.defaultButtons} </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 id={`node${this.props.Document[Id]}`} className="filter-body" style={this._nodeStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}> + {this.docButtons} </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 className="filter-key" id={`key${this.props.Document[Id]}`} style={this._keyStatus ? { borderTop: "grey 1px solid" } : { borderTop: "0px" }}> + {this.keyButtons} </div> </div> + <CollectionView {...this.props} + Document={this.props.Document} + PanelHeight={this.panelHeight} + moveDocument={returnFalse} + NativeHeight={()=>400} + childLayoutTemplate={this.childLayoutTemplate} + addDocument={undefined} + removeDocument={returnFalse} + focus={this.selectElement} + ScreenToLocalTransform={Transform.Identity} /> <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> ); } -}
\ 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.Stacking ? 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 74262d81a..c93611e9b 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 8c8720179..3438f00a3 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1167,4 +1167,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?: "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 fe39b84e6..696ad0288 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"; export enum Directory { @@ -286,25 +285,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 d55c2e198..19c98454c 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -221,7 +221,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) { @@ -235,25 +236,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 }; } @@ -275,10 +278,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 }; } } |