diff options
-rw-r--r-- | src/client/documents/Documents.ts | 23 | ||||
-rw-r--r-- | src/client/util/SearchUtil.ts | 4 | ||||
-rw-r--r-- | src/client/views/SearchBox.scss | 72 | ||||
-rw-r--r-- | src/client/views/SearchBox.tsx | 17 | ||||
-rw-r--r-- | src/client/views/SearchItem.tsx | 155 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss | 2 |
6 files changed, 228 insertions, 45 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ab61b915c..2da4c8d88 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -41,6 +41,7 @@ var path = require('path'); export interface DocumentOptions { x?: number; y?: number; + type?: string; ink?: InkField; width?: number; height?: number; @@ -73,7 +74,7 @@ export namespace DocUtils { let protoSrc = source.proto ? source.proto : source; let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); + let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1, type: "link" }); //let linkDoc = new Doc; linkDoc.proto!.title = "-link name-"; linkDoc.proto!.linkDescription = ""; @@ -154,54 +155,54 @@ export namespace Docs { function CreateImagePrototype(): Doc { let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0 }); + { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0, type: "image" }); return imageProto; } function CreateHistogramPrototype(): Doc { let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), - { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString() }); + { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: "histogram" }); return histoProto; } function CreateIconPrototype(): Doc { let iconProto = setupPrototypeOptions(iconProtoId, "ICON_PROTO", IconBox.LayoutString(), - { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); + { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE), type: "icon" }); return iconProto; } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150, backgroundColor: "#f1efeb" }); + { x: 0, y: 0, width: 300, height: 150, backgroundColor: "#f1efeb", type: "text" }); return textProto; } function CreatePdfPrototype(): Doc { let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); + { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1, type: "pdf" }); return pdfProto; } function CreateWebPrototype(): Doc { let webProto = setupPrototypeOptions(webProtoId, "WEB_PROTO", WebBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 300 }); + { x: 0, y: 0, width: 300, height: 300, type: "web" }); return webProto; } function CreateCollectionPrototype(): Doc { let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), - { panX: 0, panY: 0, scale: 1, width: 500, height: 500 }); + { panX: 0, panY: 0, scale: 1, width: 500, height: 500, type: "collection" }); return collProto; } function CreateKVPPrototype(): Doc { let kvpProto = setupPrototypeOptions(kvpProtoId, "KVP_PROTO", KeyValueBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150 }); + { x: 0, y: 0, width: 300, height: 150, type: "kvp" }); return kvpProto; } function CreateVideoPrototype(): Doc { let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"), - { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0 }); + { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0, type: "video" }); return videoProto; } function CreateAudioPrototype(): Doc { let audioProto = setupPrototypeOptions(audioProtoId, "AUDIO_PROTO", AudioBox.LayoutString(), - { x: 0, y: 0, width: 300, height: 150 }); + { x: 0, y: 0, width: 300, height: 150, type: "audio" }); return audioProto; } diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 28ec8ca14..27d27a3b8 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -23,4 +23,8 @@ export namespace SearchUtil { return Search(`proto_i:"${protoId}"`, true); // return Search(`{!join from=id to=proto_i}id:${protoId}`, true); } + + export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> { + return Search(`proto_i:"${doc[Id]}"`, true); + } }
\ No newline at end of file diff --git a/src/client/views/SearchBox.scss b/src/client/views/SearchBox.scss index b38e6091d..c8f139144 100644 --- a/src/client/views/SearchBox.scss +++ b/src/client/views/SearchBox.scss @@ -48,7 +48,6 @@ .filter-form { background: $dark-color; height: 400px; - width: 400px; position: relative; right: 1px; color: $light-color; @@ -71,15 +70,16 @@ top: 300px; display: flex; flex-direction: column; + margin-right: 72px; .search-item { + position: relative; + z-index: 1000; + pointer-events: auto; width: 500px; - height: 50px; background: $light-color-secondary; - display: flex; justify-content: space-between; align-items: center; - transition: all 0.1s; border-width: 0.11px; border-style: none; border-color: $intermediate-color; @@ -87,6 +87,19 @@ padding: 10px; white-space: nowrap; font-size: 13px; + height: 70px; + } + + .search-info { + display: flex; + justify-content: flex-end; + width: 100%; + } + + .main-search-info { + display: flex; + flex-direction: row; + width: 100%; } .search-item:hover { @@ -98,5 +111,56 @@ text-transform: uppercase; text-align: left; width: 8vw; + font-weight: bold; + } + + .more-search-info { + text-align: left; + } + + .link-count { + height: 25px; + width: 25px; + border-radius: 50%; + background: $dark-color; + color: $light-color-secondary; + display: flex; + justify-content: center; + align-items: center; + margin-right: 10px; + } + + .search-type { + width: 25PX; + height: 25PX; + display: flex; + justify-content: center; + align-items: center; + } + + + .searchBox-instances { + display: block; + background: $light-color-secondary; + opacity: 0; + width: 150px; + transition: all 0.2s ease; + color: black; + transform: translateX(150px); + } + + + .search-overview { + display: flex; + // float: left; + justify-content: flex-end; + height: 70px; + } + + .search-overview:hover { + .searchBox-instances { + transform: translateX(0px); + opacity: 1; + } } }
\ No newline at end of file diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 63d2065e2..0fd3c0904 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -22,6 +22,7 @@ import { SetupDrag } from '../util/DragManager'; import { Docs } from '../documents/Documents'; import { RouteStore } from '../../server/RouteStore'; import { NumCast } from '../../new_fields/Types'; +import { SearchUtil } from '../util/SearchUtil'; library.add(faSearch); library.add(faObjectGroup); @@ -37,6 +38,7 @@ export class SearchBox extends React.Component { @observable private _results: Doc[] = []; + @action.bound onChange(e: React.ChangeEvent<HTMLInputElement>) { this.searchString = e.target.value; @@ -72,6 +74,7 @@ export class SearchBox extends React.Component { } return docs; } + public static async convertDataUri(imageUri: string, returnedFilename: string) { try { let posting = DocServer.prepend(RouteStore.dataUriToImage); @@ -169,6 +172,18 @@ export class SearchBox extends React.Component { return Docs.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this.searchString}"` }); } + @action + getViews = async (doc: Doc) => { + console.log("getting view") + const results = await SearchUtil.GetViewsOfDocument(doc); + let toReturn: Doc[] = []; + await runInAction(() => { + toReturn = results; + }); + return toReturn; + } + + // Useful queries: // Delegates of a document: {!join from=id to=proto_i}id:{protoId} // Documents in a collection: {!join from=data_l to=id}id:{collectionProtoId} @@ -183,7 +198,7 @@ export class SearchBox extends React.Component { <input value={this.searchString} onChange={this.onChange} type="text" placeholder="Search..." className="searchBox-barChild searchBox-input" onKeyPress={this.enter} style={{ width: this._resultsOpen ? "500px" : undefined }} /> - {/* <button className="searchBox-barChild searchBox-filter" onClick={this.toggleFilterDisplay}>Filter</button> */} + <button className="searchBox-barChild searchBox-filter" onClick={this.toggleFilterDisplay}>Filter</button> {/* <FontAwesomeIcon icon="search" size="lg" className="searchBox-barChild searchBox-submit" /> */} </div> {this._resultsOpen ? ( diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 6901f60da..ae9bc350f 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -1,14 +1,27 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Cast, NumCast } from "../../new_fields/Types"; +import { FieldView, FieldViewProps } from './nodes/FieldView'; +import { computed, observable, action, runInAction } from "mobx"; +import { IconField } from "../../new_fields/IconField"; +import { listSpec } from "../../new_fields/Schema"; +import { Transform } from "../util/Transform"; +import { ObjectField } from "../../new_fields/ObjectField"; +import { RichTextField } from "../../new_fields/RichTextField"; import { Doc } from "../../new_fields/Doc"; import { DocumentManager } from "../util/DocumentManager"; import { SetupDrag } from "../util/DragManager"; +import { SearchUtil } from "../util/SearchUtil"; +import { Id } from "../../new_fields/FieldSymbols"; +import { CollectionDockingView } from "./collections/CollectionDockingView"; +import { observer } from "mobx-react"; +import { CollectionViewType } from "./collections/CollectionBaseView"; - -export interface SearchProps { +export interface SearchItemProps { doc: Doc; + // addDocTab(doc: Doc, location: string): void } library.add(faCaretUp); @@ -16,33 +29,87 @@ library.add(faObjectGroup); library.add(faStickyNote); library.add(faFilePdf); library.add(faFilm); +library.add(faMusic); +library.add(faLink); +library.add(faChartBar); +library.add(faGlobeAsia); -export class SearchItem extends React.Component<SearchProps> { +@observer +export class SelectorContextMenu extends React.Component<SearchItemProps> { + @observable private _docs: { col: Doc, target: Doc }[] = []; + @observable private _otherDocs: { col: Doc, target: Doc }[] = []; - onClick = () => { - DocumentManager.Instance.jumpToDocument(this.props.doc); + constructor(props: SearchItemProps) { + super(props); + + this.fetchDocuments(); } - //needs help - // @computed get layout(): string { const field = Cast(this.props.doc[fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; } + async fetchDocuments() { + console.log("fetching"); + let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc); + const docs = await SearchUtil.Search(`data_l:"${this.props.doc[Id]}"`, true); + const map: Map<Doc, Doc> = new Map; + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); + docs.forEach(doc => map.delete(doc)); + runInAction(() => { + console.log("action running") + this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: this.props.doc })); + this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target })); + }); + } + getOnClick({ col, target }: { col: Doc, target: Doc }) { + return () => { + console.log("returning!") + // col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; + // if (NumCast(col.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + // const newPanX = NumCast(target.x) + NumCast(target.width) / NumCast(target.zoomBasis, 1) / 2; + // const newPanY = NumCast(target.y) + NumCast(target.height) / NumCast(target.zoomBasis, 1) / 2; + // col.panX = newPanX; + // col.panY = newPanY; + // } + // this.props.addDocTab(col, "inTab"); + }; + } - public static DocumentIcon(layout: string) { - let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf : - layout.indexOf("ImageBox") !== -1 ? faImage : - layout.indexOf("Formatted") !== -1 ? faStickyNote : - layout.indexOf("Video") !== -1 ? faFilm : - layout.indexOf("Collection") !== -1 ? faObjectGroup : - faCaretUp; - return <FontAwesomeIcon icon={button} className="documentView-minimizedIcon" />; + render() { + return ( + <> + <p>Contexts:</p> + {this._docs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} + {this._otherDocs.length ? <hr></hr> : null} + {this._otherDocs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} + </> + ); } - onPointerEnter = (e: React.PointerEvent) => { - this.props.doc.libraryBrush = true; - Doc.SetOnPrototype(this.props.doc, "protoBrush", true); +} + +@observer +export class SearchItem extends React.Component<SearchItemProps> { + + @observable _selected: boolean = false; + @observable hover = false; + + onClick = () => { + DocumentManager.Instance.jumpToDocument(this.props.doc); } - onPointerLeave = (e: React.PointerEvent) => { - this.props.doc.libraryBrush = false; - Doc.SetOnPrototype(this.props.doc, "protoBrush", false); + + public DocumentIcon() { + let layoutresult = Cast(this.props.doc.type, "string", ""); + + let button = layoutresult.indexOf("pdf") !== -1 ? faFilePdf : + layoutresult.indexOf("image") !== -1 ? faImage : + layoutresult.indexOf("text") !== -1 ? faStickyNote : + layoutresult.indexOf("video") !== -1 ? faFilm : + layoutresult.indexOf("collection") !== -1 ? faObjectGroup : + layoutresult.indexOf("audio") !== -1 ? faMusic : + layoutresult.indexOf("link") !== -1 ? faLink : + layoutresult.indexOf("histogram") !== -1 ? faChartBar : + layoutresult.indexOf("web") !== -1 ? faGlobeAsia : + faCaretUp; + return <FontAwesomeIcon icon={button} size="2x" />; } collectionRef = React.createRef<HTMLDivElement>(); @@ -55,14 +122,46 @@ export class SearchItem extends React.Component<SearchProps> { return Doc.MakeAlias(doc); } } + + linkCount = () => { + return Cast(this.props.doc.linkedToDocs, listSpec(Doc), []).length + Cast(this.props.doc.linkedFromDocs, listSpec(Doc), []).length; + } + + // @action + // onMouseLeave = () => { + // this.hover = false; + // } + + // @action + // onMouseEnter = () => { + // this.hover = true; + // } + render() { return ( - <div className="search-item" ref={this.collectionRef} id="result" - onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} - onClick={this.onClick} onPointerDown={SetupDrag(this.collectionRef, this.startDocDrag)} > - <div className="search-title" id="result" >title: {this.props.doc.title}</div> - {/* <div className="search-type" id="result" >Type: {this.props.doc.layout}</div> */} - {/* <div className="search-type" >{SearchItem.DocumentIcon(this.layout)}</div> */} + <div className="search-overview"> + {/* onMouseEnter={this.onMouseEnter} + onMouseLeave={this.onMouseLeave}> */} + <div className="searchBox-instances"> + <SelectorContextMenu {...this.props} /> + </div> + {/* {this.hover ? ( + <div className="searchBox-instances"> + <SelectorContextMenu {...this.props} /> + </div> + ) : null} */} + <div className="search-item" ref={this.collectionRef} id="result" onClick={this.onClick} onPointerDown={SetupDrag(this.collectionRef, this.startDocDrag)} > + <div className="main-search-info"> + <div className="search-title" id="result" >{this.props.doc.title}</div> + <div className="search-info"> + <div className="link-count">{this.linkCount()}</div> + <div className="search-type" >{this.DocumentIcon()}</div> + </div> + </div> + <div className="more-search-info"> + <div className="found">Where Found: (i.e. title, body, etc)</div> + </div> + </div> </div> ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss index 30e158603..a80e09fa8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss @@ -1,4 +1,4 @@ -.collectionfreeformlinksview-svgCanvas{ +p.collectionfreeformlinksview-svgCanvas{ transform: translate(-10000px,-10000px); position: absolute; top: 0; |