diff options
-rw-r--r-- | src/client/documents/Documents.ts | 9 | ||||
-rw-r--r-- | src/client/util/SearchUtil.ts | 55 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 8 | ||||
-rw-r--r-- | src/client/views/newlightbox/utils.ts | 121 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 2 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.scss | 1 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 27 |
7 files changed, 104 insertions, 119 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f1a0b37b3..ac418ecfe 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -81,6 +81,7 @@ export class FInfo { this.description = d; this.readOnly = readOnly ?? false; } + searchable = () => true; } class BoolInfo extends FInfo { fieldType? = 'boolean'; @@ -89,6 +90,7 @@ class BoolInfo extends FInfo { super(d); this.filterable = filterable; } + override searchable = () => false; } class NumInfo extends FInfo { fieldType? = 'number'; @@ -98,6 +100,7 @@ class NumInfo extends FInfo { this.values = values; this.filterable = filterable; } + override searchable = () => false; } class StrInfo extends FInfo { fieldType? = 'string'; @@ -116,34 +119,40 @@ class DocInfo extends FInfo { this.values = values; this.filterable = filterable; } + override searchable = () => false; } class DimInfo extends FInfo { fieldType? = 'enumeration'; values? = [DimUnit.Pixel, DimUnit.Ratio]; readOnly = false; filterable = false; + override searchable = () => false; } class PEInfo extends FInfo { fieldType? = 'enumeration'; values? = ['all', 'none']; readOnly = false; filterable = false; + override searchable = () => false; } class DAInfo extends FInfo { fieldType? = 'enumeration'; values? = ['embed', 'copy', 'move', 'same', 'proto', 'none']; readOnly = false; filterable = false; + override searchable = () => false; } class CTypeInfo extends FInfo { fieldType? = 'enumeration'; values? = Array.from(Object.keys(CollectionViewType)); readOnly = false; filterable = false; + override searchable = () => false; } class DTypeInfo extends FInfo { fieldType? = 'enumeration'; values? = Array.from(Object.keys(DocumentType)); + override searchable = () => false; } class DateInfo extends FInfo { fieldType? = 'date'; diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 68c981399..218667d3e 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -3,47 +3,21 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; +import { DocOptions, FInfo } from '../documents/Documents'; export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; - export function SearchCollection(collectionDoc: Opt<Doc>, query: string) { + export function SearchCollection(collectionDoc: Opt<Doc>, query: string, matchKeyNames: boolean) { const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.CONFIG, DocumentType.KVP, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; - const blockedKeys = [ - 'x', - 'y', - 'proto', - 'width', - 'layout_autoHeight', - 'acl-Override', - 'acl-Guest', - 'embedContainer', - 'zIndex', - 'height', - 'text_scrollHeight', - 'text_height', - 'cloneFieldFilter', - 'isDataDoc', - 'text_annotations', - 'dragFactory_count', - 'text_noTemplate', - 'proto_embeddings', - 'isSystem', - 'layout_fieldKey', - 'isBaseProto', - 'xMargin', - 'yMargin', - 'links', - 'layout', - 'layout_keyValue', - 'layout_fitWidth', - 'type_collection', - 'title_custom', - 'freeform_panX', - 'freeform_panY', - 'freeform_scale', - ]; - query = query.toLowerCase(); + const blockedKeys = matchKeyNames + ? [] + : Object.entries(DocOptions) + .filter(([key, info]: [string, FInfo]) => !info?.searchable()) + .map(([key]) => key); + + const exact = query.startsWith('='); + query = query.toLowerCase().split('=').lastElement(); const results = new ObservableMap<Doc, string[]>(); if (collectionDoc) { @@ -54,11 +28,10 @@ export namespace SearchUtil { if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth >= 0) { const hlights = new Set<string>(); SearchUtil.documentKeys(doc).forEach( - key => - (Field.toString(doc[key] as Field) + Field.toScriptString(doc[key] as Field)) - .toLowerCase() // - .includes(query) && hlights.add(key) - ); + key => (val => (exact ? val === query.toLowerCase() : val.includes(query.toLowerCase())))( + matchKeyNames ? key : Field.toString(doc[key] as Field)) + && hlights.add(key) + ); // prettier-ignore blockedKeys.forEach(key => hlights.delete(key)); if (Array.from(hlights.keys()).length > 0) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d19c9f07d..d3c196a60 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -304,10 +304,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection focus = (anchor: Doc, options: FocusViewOptions) => { if (this._lightboxDoc) return; if (anchor === this.Document) { - if (options.willZoomCentered && options.zoomScale) { - this.fitContentOnce(); - options.didMove = true; - } + // if (options.willZoomCentered && options.zoomScale) { + // this.fitContentOnce(); + // options.didMove = true; + // } } if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return; const xfToCollection = options?.docTransform ?? Transform.Identity(); diff --git a/src/client/views/newlightbox/utils.ts b/src/client/views/newlightbox/utils.ts index 6016abca4..c879718e3 100644 --- a/src/client/views/newlightbox/utils.ts +++ b/src/client/views/newlightbox/utils.ts @@ -1,121 +1,120 @@ -import { DocumentType } from "../../documents/DocumentTypes"; -import { IRecommendation } from "./components"; +import { DocumentType } from '../../documents/DocumentTypes'; +import { IRecommendation } from './components'; export interface IDocRequest { - id: string, - title: string, - text: string, - type: string + id: string; + title: string; + text: string; + type: string; } export const fetchRecommendations = async (src: string, query: string, docs?: IDocRequest[], dummy?: boolean) => { - console.log("[rec] making request") + console.log('[rec] making request'); if (dummy) { return { - "recommendations": dummyRecs, - "keywords": dummyKeywords, - "num_recommendations": 4, - "max_x": 100, - "max_y": 100, - "min_x": 0, - "min_y": 0 - + recommendations: [], //dummyRecs, + keywords: dummyKeywords, + num_recommendations: 4, + max_x: 100, + max_y: 100, + min_x: 0, + min_y: 0, }; } const response = await fetch('http://127.0.0.1:8000/recommend', { method: 'POST', headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' + Accept: 'application/json', + 'Content-Type': 'application/json', }, - body: JSON.stringify({ - "src": src, - "query": query, - "docs": docs - }) - }) + body: JSON.stringify({ + src: src, + query: query, + docs: docs, + }), + }); const data = await response.json(); - + return data; -} +}; export const fetchKeywords = async (text: string, n: number, dummy?: boolean) => { - console.log("[fetchKeywords]") + console.log('[fetchKeywords]'); if (dummy) { return { - "keywords": dummyKeywords + keywords: dummyKeywords, }; } const response = await fetch('http://127.0.0.1:8000/keywords', { method: 'POST', headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' + Accept: 'application/json', + 'Content-Type': 'application/json', }, - body: JSON.stringify({ - "text": text, - "n": n - }) - }) - const data = await response.json() + body: JSON.stringify({ + text: text, + n: n, + }), + }); + const data = await response.json(); return data; -} +}; export const getType = (type: DocumentType | string) => { - switch(type) { + switch (type) { case DocumentType.AUDIO: - return "Audio" + return 'Audio'; case DocumentType.VID: - return "Video" + return 'Video'; case DocumentType.PDF: - return "PDF" + return 'PDF'; case DocumentType.WEB: - return "Webpage" - case "YouTube": - return "Video" - case "HTML": - return "Webpage" + return 'Webpage'; + case 'YouTube': + return 'Video'; + case 'HTML': + return 'Webpage'; default: - return "Unknown: " + type + return 'Unknown: ' + type; } -} +}; const dummyRecs = { - "a": { + a: { title: 'Vannevar Bush - American Engineer', - previewUrl: 'https://cdn.britannica.com/98/23598-004-1E6A382E/Vannevar-Bush-Differential-Analyzer-1935.jpg', + previewUrl: 'https://cdn.britannica.com/98/23598-004-1E6A382E/Vannevar-Bush-Differential-Analyzer-1935.jpg', type: 'web', distance: 2.3, source: 'www.britannica.com', related_concepts: ['vannevar bush', 'knowledge'], embedding: { x: 0, - y: 0 - } + y: 0, + }, }, - "b": { + b: { title: "From Memex to hypertext: Vannevar Bush and the mind's machine", type: 'pdf', distance: 5.4, source: 'Google Scholar', related_concepts: ['memex', 'vannevar bush', 'hypertext'], }, - "c": { + c: { title: 'How the hyperlink changed everything | Small Thing Big Idea, a TED series', - previewUrl: 'https://pi.tedcdn.com/r/talkstar-photos.s3.amazonaws.com/uploads/b17d043f-2642-4117-a913-52204505513f/MargaretGouldStewart_2018V-embed.jpg?u%5Br%5D=2&u%5Bs%5D=0.5&u%5Ba%5D=0.8&u%5Bt%5D=0.03&quality=82w=640', + previewUrl: 'https://pi.tedcdn.com/r/talkstar-photos.s3.amazonaws.com/uploads/b17d043f-2642-4117-a913-52204505513f/MargaretGouldStewart_2018V-embed.jpg?u%5Br%5D=2&u%5Bs%5D=0.5&u%5Ba%5D=0.8&u%5Bt%5D=0.03&quality=82w=640', type: 'youtube', distance: 5.3, source: 'www.youtube.com', - related_concepts: ['User Control', 'Explanations'] + related_concepts: ['User Control', 'Explanations'], }, - "d": { + d: { title: 'Recommender Systems: Behind the Scenes of Machine Learning-Based Personalization', - previewUrl: 'https://sloanreview.mit.edu/wp-content/uploads/2018/10/MAG-Ransbotham-Ratings-Recommendations-1200X627-1200x627.jpg', + previewUrl: 'https://sloanreview.mit.edu/wp-content/uploads/2018/10/MAG-Ransbotham-Ratings-Recommendations-1200X627-1200x627.jpg', type: 'pdf', distance: 9.3, source: 'www.altexsoft.com', - related_concepts: ['User Control', 'Explanations'] - } -} + related_concepts: ['User Control', 'Explanations'], + }, +}; -const dummyKeywords = ['user control', 'vannevar bush', 'hypermedia', 'hypertext']
\ No newline at end of file +const dummyKeywords = ['user control', 'vannevar bush', 'hypermedia', 'hypertext']; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 4b242649a..3f793b85e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -184,7 +184,7 @@ export class LinkDocPreview extends ObservableReactComponent<LinkDocPreviewProps LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this._props.hrefs?.length) { const webDoc = - Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0]).keys()).lastElement() ?? + Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0], false).keys()).lastElement() ?? Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true }); DocumentManager.Instance.showDocument(webDoc, { openLocation: OpenWhere.lightbox, diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index 09e459f7d..94e64b952 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -8,6 +8,7 @@ background: none; z-index: 1000; padding: 0px; + overflow: auto; cursor: default; .searchBox-bar { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index c8caff9fa..0b664beaa 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -162,12 +162,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { * which the first letter is capitalized. This is used when displaying the type on the * right side of each search result. */ - static formatType(type: String): String { - if (type === 'pdf') { - return 'PDF'; - } else if (type === 'image') { - return 'Img'; - } + static formatType(type: string, colType: string): String { + switch (type) { + case DocumentType.PDF : return 'PDF'; + case DocumentType.IMG : return 'Img'; + case DocumentType.RTF : return 'Rtf'; + case DocumentType.COL : return 'Col:'+colType.substring(0,3); + } // prettier-ignore return type.charAt(0).toUpperCase() + type.substring(1, 3); } @@ -183,7 +184,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { @action searchCollection(query: string) { this._selectedResult = undefined; - this._results = SearchUtil.SearchCollection(CollectionDockingView.Instance?.Document, query); + this._results = SearchUtil.SearchCollection(CollectionDockingView.Instance?.Document, query, this._docTypeString === 'keys'); this.computePageRanks(); } @@ -357,11 +358,11 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { */ @computed public get selectOptions() { - const selectValues = ['all', DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.WEB, DocumentType.VID, DocumentType.AUDIO, DocumentType.COL]; + const selectValues = ['all', DocumentType.RTF, DocumentType.IMG, DocumentType.PDF, DocumentType.WEB, DocumentType.VID, DocumentType.AUDIO, DocumentType.COL, 'keys']; return selectValues.map(value => ( <option key={value} value={value}> - {SearchBox.formatType(value)} + {SearchBox.formatType(value, '')} </option> )); } @@ -387,10 +388,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { className += ' searchBox-results-scroll-view-result-selected'; } - const formattedType = SearchBox.formatType(StrCast(result[0].type)); + const formattedType = SearchBox.formatType(StrCast(result[0].type), StrCast(result[0].type_collection)); const title = result[0].title; - if (this._docTypeString === 'all' || this._docTypeString === result[0].type) { + if (this._docTypeString === 'keys' || this._docTypeString === 'all' || this._docTypeString === result[0].type) { validResults++; resultsJSX.push( <Tooltip key={result[0][Id]} placement={'right'} title={<div className="dash-tooltip">{title as string}</div>}> @@ -412,7 +413,9 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() { }} className={className}> <div className="searchBox-result-title">{title as string}</div> - <div className="searchBox-result-type">{formattedType}</div> + <div className="searchBox-result-type" style={{ color: SettingsManager.userVariantColor }}> + {formattedType} + </div> <div className="searchBox-result-keys" style={{ color: SettingsManager.userVariantColor }}> {result[1].join(', ')} </div> |