diff options
Diffstat (limited to 'src/client/views/newlightbox')
18 files changed, 347 insertions, 386 deletions
diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index bce2b296f..3eb99f47a 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -1,34 +1,37 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { action } from 'mobx'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { OpenWhereMod } from '../../nodes/DocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; +import { OpenWhereMod } from '../../nodes/OpenWhere'; import { NewLightboxView } from '../NewLightboxView'; import './ButtonMenu.scss'; -import { IButtonMenu } from './utils'; -export const ButtonMenu = (props: IButtonMenu) => { +export function ButtonMenu() { return ( - <div className={`newLightboxButtonMenu-container`}> + <div className="newLightboxButtonMenu-container"> <div className="newLightboxView-navBtn" title="toggle fit width" onClick={e => { e.stopPropagation(); NewLightboxView.LightboxDoc!._fitWidth = !NewLightboxView.LightboxDoc!._fitWidth; - }}></div> + }} + /> <div className="newLightboxView-tabBtn" title="open in tab" onClick={e => { e.stopPropagation(); CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); NewLightboxView.SetNewLightboxDoc(undefined); - }}></div> + }} + /> <div className="newLightboxView-penBtn" title="toggle pen annotation" @@ -36,7 +39,8 @@ export const ButtonMenu = (props: IButtonMenu) => { onClick={e => { e.stopPropagation(); Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; - }}></div> + }} + /> <div className="newLightboxView-exploreBtn" title="toggle explore mode to navigate among documents only" @@ -44,7 +48,8 @@ export const ButtonMenu = (props: IButtonMenu) => { onClick={action(e => { e.stopPropagation(); SnappingManager.SetExploreMode(!SnappingManager.ExploreMode); - })}></div> + })} + /> </div> ); -}; +} diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx index a1d6375c4..f8c07cc43 100644 --- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx +++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx @@ -1,32 +1,34 @@ -import './ExploreView.scss'; -import { IBounds, IExploreView, emptyBounds } from './utils'; -import { IRecommendation } from '../components'; +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import * as React from 'react'; -import { NewLightboxView } from '../NewLightboxView'; import { StrCast } from '../../../../fields/Types'; +import { NewLightboxView } from '../NewLightboxView'; +import './ExploreView.scss'; +import { IExploreView, emptyBounds } from './utils'; -export const ExploreView = (props: IExploreView) => { +export function ExploreView(props: IExploreView) { const { recs, bounds = emptyBounds } = props; return ( - <div className={`exploreView-container`}> + <div className="exploreView-container"> {recs && recs.map(rec => { - const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x)); - const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y)); + const xBound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x)); + const yBound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y)); if (rec.embedding) { - const x = (rec.embedding.x / x_bound) * 50; - const y = (rec.embedding.y / y_bound) * 50; + const x = (rec.embedding.x / xBound) * 50; + const y = (rec.embedding.y / yBound) * 50; return ( - <div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}> + <div className="exploreView-doc" onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}> {rec.title} </div> ); - } else return null; + } + return null; })} - <div className={`exploreView-doc`} style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}> + <div className="exploreView-doc" style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}> {StrCast(NewLightboxView.LightboxDoc?.title)} </div> </div> ); -}; +} diff --git a/src/client/views/newlightbox/ExploreView/index.ts b/src/client/views/newlightbox/ExploreView/index.ts index bf94eedcd..f2ebf511f 100644 --- a/src/client/views/newlightbox/ExploreView/index.ts +++ b/src/client/views/newlightbox/ExploreView/index.ts @@ -1 +1 @@ -export * from './ExploreView'
\ No newline at end of file +export * from './ExploreView'; diff --git a/src/client/views/newlightbox/ExploreView/utils.ts b/src/client/views/newlightbox/ExploreView/utils.ts index 7d9cf226d..2d1bd75a9 100644 --- a/src/client/views/newlightbox/ExploreView/utils.ts +++ b/src/client/views/newlightbox/ExploreView/utils.ts @@ -1,20 +1,21 @@ -import { IRecommendation } from "../components"; +import { IRecommendation } from '../components'; export interface IExploreView { - recs?: IRecommendation[], - bounds?: IBounds + recs?: IRecommendation[]; + // eslint-disable-next-line no-use-before-define + bounds?: IBounds; } export const emptyBounds = { max_x: 0, max_y: 0, min_x: 0, - min_y: 0 -} + min_y: 0, +}; export interface IBounds { - max_x: number, - max_y: number, - min_x: number, - min_y: number -}
\ No newline at end of file + max_x: number; + max_y: number; + min_x: number; + min_y: number; +} diff --git a/src/client/views/newlightbox/Header/LightboxHeader.tsx b/src/client/views/newlightbox/Header/LightboxHeader.tsx index 76efe0185..882d28fba 100644 --- a/src/client/views/newlightbox/Header/LightboxHeader.tsx +++ b/src/client/views/newlightbox/Header/LightboxHeader.tsx @@ -4,21 +4,21 @@ import { BsBookmark, BsBookmarkFill } from 'react-icons/bs'; import { MdTravelExplore } from 'react-icons/md'; import { Doc } from '../../../../fields/Doc'; import { StrCast } from '../../../../fields/Types'; -import { LightboxView } from '../../LightboxView'; import { Colors } from '../../global/globalEnums'; +import { DocumentView } from '../../nodes/DocumentView'; import { NewLightboxView } from '../NewLightboxView'; import { EditableText } from '../components/EditableText'; import { getType } from '../utils'; import './LightboxHeader.scss'; import { INewLightboxHeader } from './utils'; -export const NewLightboxHeader = (props: INewLightboxHeader) => { +export function NewLightboxHeader(props: INewLightboxHeader) { const { height = 100, width } = props; - const [doc, setDoc] = React.useState<Doc | undefined>(LightboxView.LightboxDoc); + const [doc, setDoc] = React.useState<Doc | undefined>(DocumentView.LightboxDoc()); const [editing, setEditing] = React.useState<boolean>(false); const [title, setTitle] = React.useState<JSX.Element | null>(null); React.useEffect(() => { - let lbDoc = LightboxView.LightboxDoc; + const lbDoc = DocumentView.LightboxDoc(); setDoc(lbDoc); if (lbDoc) { setTitle( @@ -32,39 +32,28 @@ export const NewLightboxHeader = (props: INewLightboxHeader) => { /> ); } - }, [LightboxView.LightboxDoc]); + }, [DocumentView.LightboxDoc()]); const [saved, setSaved] = React.useState<boolean>(false); if (!doc) return null; - else - return ( - <div className={`newLightboxHeader-container`} onPointerDown={e => e.stopPropagation()} style={{ minHeight: height, height: height, width: width }}> - <div className={`title-container`}> - <div className={`lb-label`}>Title</div> - {title} - </div> - <div className={`type-container`}> - <div className={`lb-label`}>Type</div> - <div className={`type`}>{getType(StrCast(doc.type))}</div> - </div> - <div style={{ gridColumn: 2, gridRow: 1, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}> - <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill /> : <BsBookmark />} /> - <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill /> : <BsBookmark />} /> - </div> - <div style={{ gridColumn: 2, gridRow: 2, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}> - <Button - onClick={() => { - console.log(NewLightboxView.ExploreMode); - NewLightboxView.SetExploreMode(!NewLightboxView.ExploreMode); - }} - size={Size.XSMALL} - color={Colors.DARK_GRAY} - type={Type.SEC} - text={'t-SNE 2D Embeddings'} - icon={<MdTravelExplore />} - /> - </div> + return ( + <div className="newLightboxHeader-container" onPointerDown={e => e.stopPropagation()} style={{ minHeight: height, height: height, width: width }}> + <div className="title-container"> + <div className="lb-label">Title</div> + {title} </div> - ); -}; + <div className="type-container"> + <div className="lb-label">Type</div> + <div className="type">{getType(StrCast(doc.type))}</div> + </div> + <div style={{ gridColumn: 2, gridRow: 1, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}> + <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill /> : <BsBookmark />} /> + <IconButton size={Size.XSMALL} onClick={() => setSaved(!saved)} color={Colors.DARK_GRAY} icon={saved ? <BsBookmarkFill /> : <BsBookmark />} /> + </div> + <div style={{ gridColumn: 2, gridRow: 2, height: '100%', display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}> + <Button onClick={() => NewLightboxView.SetExploreMode(!NewLightboxView.ExploreMode)} size={Size.XSMALL} color={Colors.DARK_GRAY} type={Type.SEC} text="t-SNE 2D Embeddings" icon={<MdTravelExplore />} /> + </div> + </div> + ); +} diff --git a/src/client/views/newlightbox/Header/index.ts b/src/client/views/newlightbox/Header/index.ts index 090677c16..88a533585 100644 --- a/src/client/views/newlightbox/Header/index.ts +++ b/src/client/views/newlightbox/Header/index.ts @@ -1 +1 @@ -export * from './LightboxHeader'
\ No newline at end of file +export * from './LightboxHeader'; diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index 12b9870ca..c86ddb745 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -1,23 +1,19 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../Utils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../ClientUtils'; +import { emptyFunction } from '../../../Utils'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; -import { LinkManager } from '../../util/LinkManager'; -import { SelectionManager } from '../../util/SelectionManager'; +import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { GestureOverlay } from '../GestureOverlay'; -import { LightboxView } from '../LightboxView'; import { DefaultStyleProvider } from '../StyleProvider'; -import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; -import { TabDocView } from '../collections/TabDocView'; -import { DocumentView, OpenWhere } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { ExploreView } from './ExploreView'; import { IBounds, emptyBounds } from './ExploreView/utils'; import { NewLightboxHeader } from './Header'; @@ -25,11 +21,11 @@ import './NewLightboxView.scss'; import { RecommendationList } from './RecommendationList'; import { IRecommendation } from './components'; -enum LightboxStatus { - RECOMMENDATIONS = 'recommendations', - ANNOTATIONS = 'annotations', - NONE = 'none', -} +// enum LightboxStatus { +// RECOMMENDATIONS = 'recommendations', +// ANNOTATIONS = 'annotations', +// NONE = 'none', +// } interface LightboxViewProps { PanelWidth: number; @@ -46,10 +42,6 @@ type LightboxSavedState = { }; @observer export class NewLightboxView extends React.Component<LightboxViewProps> { - @computed public static get LightboxDoc() { - return this._doc; - } - private static LightboxDocTemplate = () => NewLightboxView._layoutTemplate; @observable private static _layoutTemplate: Opt<Doc> = undefined; @observable private static _layoutTemplateString: Opt<string> = undefined; @observable private static _doc: Opt<Doc> = undefined; @@ -62,59 +54,39 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { // keywords @observable private static _keywords: string[] = []; - @action public static SetKeywords(kw: string[]) { - this._keywords = kw; - } - @computed public static get Keywords() { - return this._keywords; - } - - // query @observable private static _query: string = ''; - @action public static SetQuery(query: string) { - this._query = query; - } - @computed public static get Query() { - return this._query; - } - - // keywords @observable private static _recs: IRecommendation[] = []; - @action public static SetRecs(recs: IRecommendation[]) { - this._recs = recs; - } - @computed public static get Recs() { - return this._recs; - } - - // bounds @observable private static _bounds: IBounds = emptyBounds; - @action public static SetBounds(bounds: IBounds) { - this._bounds = bounds; - } - @computed public static get Bounds() { - return this._bounds; - } - - // explore @observable private static _explore: Opt<boolean> = false; - @action public static SetExploreMode(status: Opt<boolean>) { - this._explore = status; - } - @computed public static get ExploreMode() { - return this._explore; - } - - // newLightbox sidebar status @observable private static _sidebarStatus: Opt<string> = ''; - @action public static SetSidebarStatus(sidebarStatus: Opt<string>) { - this._sidebarStatus = sidebarStatus; + static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = []; + private static LightboxDocTemplate = () => NewLightboxView._layoutTemplate; + public static GetSavedState(doc: Doc) { + return this.LightboxDoc === doc && this._savedState ? this._savedState : undefined; } - @computed public static get SidebarStatus() { - return this._sidebarStatus; + // adds a cookie to the newLightbox view - the cookie becomes part of a filter which will display any documents whose cookie metadata field matches this cookie + @action + public static SetCookie(cookie: string) { + if (this.LightboxDoc && cookie) { + this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); + } } + public static AddDocTab = (docsIn: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => { + DocumentView.DeselectAll(); + const doc = toList(docsIn).lastElement(); + return ( + doc && + NewLightboxView.SetNewLightboxDoc( + doc, + undefined, + [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort( + (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow) + ), + layoutTemplate + ) + ); + }; - static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = []; @action public static SetNewLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) { if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) { if (this._savedState.panX !== undefined) this.LightboxDoc._freeform_panX = this._savedState.panX; @@ -129,12 +101,12 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); } else { - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); - //TabDocView.PinDoc(doc, { hidePresBox: true }); + DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); + // DocumentView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); - if (doc !== LightboxView.LightboxDoc) { + if (doc !== DocumentView.LightboxDoc()) { this._savedState = { layout_fieldKey: StrCast(doc.layout_fieldKey), panX: Cast(doc.freeform_panX, 'number', null), @@ -151,7 +123,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), + .sort((a, b) => Doc.Links(a).length - Doc.Links(b).length), ]; } this._doc = doc; @@ -166,85 +138,34 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { public static IsNewLightboxDocView(path: DocumentView[]) { return (path ?? []).includes(this._docView!); } - @computed get leftBorder() { - return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); - } - @computed get topBorder() { - return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]); - } - newLightboxWidth = () => this.props.PanelWidth - 420; - newLightboxHeight = () => this.props.PanelHeight - 140; - newLightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1); - navBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => { - return ( - <div - className="newLightboxView-navBtn-frame" - style={{ - display: display(), - left, - width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), - bottom, - }}> - <div className="newLightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}> - <div style={{ height: 10 }}>{color}</div> - <FontAwesomeIcon icon={icon as any} size="3x" /> - </div> - </div> - ); - }; - public static GetSavedState(doc: Doc) { - return this.LightboxDoc === doc && this._savedState ? this._savedState : undefined; - } - - // adds a cookie to the newLightbox view - the cookie becomes part of a filter which will display any documents whose cookie metadata field matches this cookie - @action - public static SetCookie(cookie: string) { - if (this.LightboxDoc && cookie) { - this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); - } - } - public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => { - SelectionManager.DeselectAll(); - return NewLightboxView.SetNewLightboxDoc( - doc, - undefined, - [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...(NewLightboxView._future ?? [])].sort( - (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow) - ), - layoutTemplate - ); - }; - docFilters = () => NewLightboxView._docFilters || []; - addDocTab = NewLightboxView.AddDocTab; @action public static Next() { const doc = NewLightboxView._doc!; const target = (NewLightboxView._docTarget = this._future?.pop()); - const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); + const targetDocView = target && DocumentView.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); + const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target }); - } else { - if (!target && NewLightboxView.path.length) { - const saved = NewLightboxView._savedState; - if (LightboxView.LightboxDoc && saved) { - LightboxView.LightboxDoc._freeform_panX = saved.panX; - LightboxView.LightboxDoc._freeform_panY = saved.panY; - LightboxView.LightboxDoc._freeform_scale = saved.scale; - LightboxView.LightboxDoc._layout_scrollTop = saved.scrollTop; - } - const pop = NewLightboxView.path.pop(); - if (pop) { - NewLightboxView._doc = pop.doc; - NewLightboxView._docTarget = pop.target; - NewLightboxView._future = pop.future; - NewLightboxView._history = pop.history; - NewLightboxView._savedState = pop.saved; - } - } else { - NewLightboxView.SetNewLightboxDoc(target); + } else if (!target && NewLightboxView.path.length) { + const saved = NewLightboxView._savedState; + const lightboxDoc = DocumentView.LightboxDoc(); + if (lightboxDoc && saved) { + lightboxDoc._freeform_panX = saved.panX; + lightboxDoc._freeform_panY = saved.panY; + lightboxDoc._freeform_scale = saved.scale; + lightboxDoc._layout_scrollTop = saved.scrollTop; + } + const pop = NewLightboxView.path.pop(); + if (pop) { + NewLightboxView._doc = pop.doc; + NewLightboxView._docTarget = pop.target; + NewLightboxView._future = pop.future; + NewLightboxView._history = pop.history; + NewLightboxView._savedState = pop.saved; } + } else { + NewLightboxView.SetNewLightboxDoc(target); } } @@ -254,76 +175,120 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { NewLightboxView.SetNewLightboxDoc(undefined); return; } - const { doc, target } = NewLightboxView._history?.lastElement(); - const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const { doc, target } = NewLightboxView._history?.lastElement() ?? { doc: undefined, target: undefined }; + const docView = DocumentView.getLightboxDocumentView(target || doc); if (docView) { NewLightboxView._docTarget = target; - target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + target && DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { NewLightboxView.SetNewLightboxDoc(doc, target); } if (NewLightboxView._future?.lastElement() !== previous.target || previous.doc) NewLightboxView._future?.push(previous.target || previous.doc); } - @action - stepInto = () => { - NewLightboxView.path.push({ - doc: LightboxView.LightboxDoc, - target: NewLightboxView._docTarget, - future: NewLightboxView._future, - history: NewLightboxView._history, - saved: NewLightboxView._savedState, - }); - const coll = NewLightboxView._docTarget; - if (coll) { - const fieldKey = Doc.LayoutFieldKey(coll); - const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])]; - const links = LinkManager.Links(coll) - .map(link => LinkManager.getOppositeAnchor(link, coll)) - .filter(doc => doc) - .map(doc => doc!); - NewLightboxView.SetNewLightboxDoc(coll, undefined, contents.length ? contents : links); - } - }; + + @action public static SetKeywords(kw: string[]) { + this._keywords = kw; + } + @computed public static get Keywords() { + return this._keywords; + } + @computed public static get LightboxDoc() { + return this._doc; + } + + // query + @action public static SetQuery(query: string) { + this._query = query; + } + @computed public static get Query() { + return this._query; + } + + // keywords + @action public static SetRecs(recs: IRecommendation[]) { + this._recs = recs; + } + @computed public static get Recs() { + return this._recs; + } + + // bounds + @action public static SetBounds(bounds: IBounds) { + this._bounds = bounds; + } + @computed public static get Bounds() { + return this._bounds; + } + // newLightbox sidebar status + @action public static SetSidebarStatus(sidebarStatus: Opt<string>) { + this._sidebarStatus = sidebarStatus; + } + + // explore + @action public static SetExploreMode(status: Opt<boolean>) { + this._explore = status; + } + + addDocTab = NewLightboxView.AddDocTab; + + @computed public static get ExploreMode() { + return this._explore; + } + + @computed public static get SidebarStatus() { + return this._sidebarStatus; + } + @computed get leftBorder() { + return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); + } + @computed get topBorder() { + return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]); + } @computed get documentView() { - if (!LightboxView.LightboxDoc) return null; - else - return ( - <GestureOverlay isActive={true}> - <DocumentView - ref={action((r: DocumentView | null) => (NewLightboxView._docView = r !== null ? r : undefined))} - Document={LightboxView.LightboxDoc} - PanelWidth={this.newLightboxWidth} - PanelHeight={this.newLightboxHeight} - LayoutTemplate={NewLightboxView.LightboxDocTemplate} - isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected. - isContentActive={returnTrue} - styleProvider={DefaultStyleProvider} - ScreenToLocalTransform={this.newLightboxScreenToLocal} - renderDepth={0} - containerViewPath={returnEmptyDoclist} - childFilters={this.docFilters} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - addDocument={undefined} - removeDocument={undefined} - whenChildContentsActiveChanged={emptyFunction} - addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} - onBrowseClickScript={DocumentView.exploreMode} - focus={emptyFunction} - /> - </GestureOverlay> - ); + const lightboxDoc = DocumentView.LightboxDoc(); + if (!lightboxDoc) return null; + return ( + <GestureOverlay isActive> + <DocumentView + ref={action((r: DocumentView | null) => { + NewLightboxView._docView = r !== null ? r : undefined; + })} + Document={lightboxDoc} + PanelWidth={this.newLightboxWidth} + PanelHeight={this.newLightboxHeight} + LayoutTemplate={NewLightboxView.LightboxDocTemplate} + isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected. + isContentActive={returnTrue} + styleProvider={DefaultStyleProvider} + ScreenToLocalTransform={this.newLightboxScreenToLocal} + renderDepth={0} + containerViewPath={returnEmptyDoclist} + childFilters={this.docFilters} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + addDocument={undefined} + removeDocument={undefined} + whenChildContentsActiveChanged={emptyFunction} + addDocTab={this.addDocTab} + pinToPres={DocumentView.PinDoc} + focus={emptyFunction} + /> + </GestureOverlay> + ); } + newLightboxWidth = () => this.props.PanelWidth - 420; + newLightboxHeight = () => this.props.PanelHeight - 140; + newLightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1); + + docFilters = () => NewLightboxView._docFilters || []; - future = () => NewLightboxView._future; render() { - let newLightboxHeaderHeight = 100; - let downx = 0, - downy = 0; - return !LightboxView.LightboxDoc ? null : ( + const newLightboxHeaderHeight = 100; + let downx = 0; + let downy = 0; + return !DocumentView.LightboxDoc() ? null : ( <div className="newLightboxView-frame" onPointerDown={e => { @@ -335,7 +300,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { NewLightboxView.SetNewLightboxDoc(undefined); } }}> - <div className={`app-document`} style={{ gridTemplateColumns: `calc(100% - 400px) 400px` }}> + <div className="app-document" style={{ gridTemplateColumns: `calc(100% - 400px) 400px` }}> <div className="newLightboxView-contents" style={{ @@ -350,7 +315,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { {this.documentView} </div> ) : ( - <div className={`explore`}> + <div className="explore"> <ExploreView recs={NewLightboxView.Recs} bounds={NewLightboxView.Bounds} /> </div> )} @@ -363,6 +328,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> { } interface NewLightboxTourBtnProps { navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element; + // eslint-disable-next-line react/no-unused-prop-types future: () => Opt<Doc[]>; stepInto: () => void; } @@ -374,7 +340,7 @@ export class NewLightboxTourBtn extends React.Component<NewLightboxTourBtnProps> 0, 0, 'chevron-down', - () => (LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? '' : 'none'), + () => (DocumentView.LightboxDoc() /* && this.props.future()?.length */ ? '' : 'none'), e => { e.stopPropagation(); this.props.stepInto(); diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx index 1d502b73f..dc3339cd3 100644 --- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx @@ -1,3 +1,7 @@ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable guard-for-in */ import { IconButton, Size, Type } from 'browndash-components'; import * as React from 'react'; import { FaCaretDown, FaCaretUp } from 'react-icons/fa'; @@ -5,17 +9,15 @@ import { GrClose } from 'react-icons/gr'; import { DocListCast, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { StrCast } from '../../../../fields/Types'; -import { LightboxView } from '../../LightboxView'; import { Colors } from '../../global/globalEnums'; +import { DocumentView } from '../../nodes/DocumentView'; import { IBounds } from '../ExploreView/utils'; import { NewLightboxView } from '../NewLightboxView'; import { IRecommendation, Recommendation } from '../components'; import { IDocRequest, fetchKeywords, fetchRecommendations } from '../utils'; import './RecommendationList.scss'; -import { IRecommendationList } from './utils'; -export const RecommendationList = (props: IRecommendationList) => { - const { loading, keywords } = props; +export function RecommendationList() { const [loadingKeywords, setLoadingKeywords] = React.useState<boolean>(true); const [showMore, setShowMore] = React.useState<boolean>(false); const [keywordsLoc, setKeywordsLoc] = React.useState<string[]>([]); @@ -25,21 +27,22 @@ export const RecommendationList = (props: IRecommendationList) => { React.useEffect(() => { const getKeywords = async () => { - let text = StrCast(LightboxView.LightboxDoc?.text); + const text = StrCast(DocumentView.LightboxDoc()?.text); console.log('[1] fetching keywords'); const response = await fetchKeywords(text, 5, true); console.log('[2] response:', response); const kw = response.keywords; console.log(kw); NewLightboxView.SetKeywords(kw); - if (LightboxView.LightboxDoc) { + const lightboxDoc = DocumentView.LightboxDoc(); + if (lightboxDoc) { console.log('setting keywords on doc'); - LightboxView.LightboxDoc.keywords = new List<string>(kw); + lightboxDoc.keywords = new List<string>(kw); setKeywordsLoc(NewLightboxView.Keywords); } setLoadingKeywords(false); }; - let keywordsList = StrListCast(LightboxView.LightboxDoc!.keywords); + const keywordsList = StrListCast(DocumentView.LightboxDoc()!.keywords); if (!keywordsList || keywordsList.length < 2) { setLoadingKeywords(true); getKeywords(); @@ -57,14 +60,14 @@ export const RecommendationList = (props: IRecommendationList) => { console.log('fetching recommendations'); let query = 'undefined'; if (keywordsLoc) query = keywordsLoc.join(','); - let src = StrCast(NewLightboxView.LightboxDoc?.text); - let dashDocs: IDocRequest[] = []; + const src = StrCast(NewLightboxView.LightboxDoc?.text); + const dashDocs: IDocRequest[] = []; // get linked docs - let linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links); + const linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links); console.log('linked docs', linkedDocs); // get context docs (docs that are also in the collection) - // let contextDocs: Doc[] = DocListCast(DocCast(LightboxView.LightboxDoc?.context).data) - // let docId = LightboxView.LightboxDoc && LightboxView.LightboxDoc[Id] + // let contextDocs: Doc[] = DocListCast(DocCast(DocumentView.LightboxDoc()?.context).data) + // let docId = DocumentView.LightboxDoc() && DocumentView.LightboxDoc()[Id] // console.log("context docs", contextDocs) // contextDocs.forEach((doc: Doc) => { // if (docId !== doc[Id]){ @@ -79,10 +82,8 @@ export const RecommendationList = (props: IRecommendationList) => { console.log('dash docs', dashDocs); if (query !== undefined) { const response = await fetchRecommendations(src, query, [], true); - const num_recs = response.num_recommendations; - const recs = response.recommendations; - const keywords = response.keywords; - const response_bounds: IBounds = { + const theRecs = response.recommendations; + const responseBounds: IBounds = { max_x: response.max_x, max_y: response.max_y, min_x: response.min_x, @@ -93,22 +94,23 @@ export const RecommendationList = (props: IRecommendationList) => { // setKeywordsLoc(NewLightboxView.Keywords); // } // console.log(response_bounds) - NewLightboxView.SetBounds(response_bounds); + NewLightboxView.SetBounds(responseBounds); const recommendations: IRecommendation[] = []; - for (const key in recs) { + // eslint-disable-next-line no-restricted-syntax + for (const key in theRecs) { console.log(key); - const title = recs[key].title; - const url = recs[key].url; - const type = recs[key].type; - const text = recs[key].text; - const transcript = recs[key].transcript; - const previewUrl = recs[key].previewUrl; - const embedding = recs[key].embedding; - const distance = recs[key].distance; - const source = recs[key].source; - const related_concepts = recs[key].related_concepts; - const docId = recs[key].doc_id; - related_concepts.length >= 1 && + const { title } = theRecs[key]; + const { url } = theRecs[key]; + const { type } = theRecs[key]; + const { text } = theRecs[key]; + const { transcript } = theRecs[key]; + const { previewUrl } = theRecs[key]; + const { embedding } = theRecs[key]; + const { distance } = theRecs[key]; + const { source } = theRecs[key]; + const { related_concepts: relatedConcepts } = theRecs[key]; + const docId = theRecs[key].doc_id; + relatedConcepts.length >= 1 && recommendations.push({ title: title, data: url, @@ -119,14 +121,15 @@ export const RecommendationList = (props: IRecommendationList) => { embedding: embedding, distance: Math.round(distance * 100) / 100, source: source, - related_concepts: related_concepts, + related_concepts: relatedConcepts, docId: docId, }); } recommendations.sort((a, b) => { if (a.distance && b.distance) { return a.distance - b.distance; - } else return 0; + } + return 0; }); console.log('[rec]: ', recommendations); NewLightboxView.SetRecs(recommendations); @@ -138,12 +141,12 @@ export const RecommendationList = (props: IRecommendationList) => { return ( <div - className={`recommendationlist-container`} + className="recommendationlist-container" onPointerDown={e => { e.stopPropagation(); }}> - <div className={`header`}> - <div className={`title`}>Recommendations</div> + <div className="header"> + <div className="title">Recommendations</div> {NewLightboxView.LightboxDoc && ( <div style={{ fontSize: 10 }}> The recommendations are produced based on the text in the document{' '} @@ -153,65 +156,58 @@ export const RecommendationList = (props: IRecommendationList) => { . The following keywords are used to fetch the recommendations. </div> )} - <div className={`lb-label`}>Keywords</div> + <div className="lb-label">Keywords</div> {loadingKeywords ? ( - <div className={`keywords`}> + <div className="keywords"> <div className={`keyword ${loadingKeywords && 'loading'}`} /> <div className={`keyword ${loadingKeywords && 'loading'}`} /> <div className={`keyword ${loadingKeywords && 'loading'}`} /> <div className={`keyword ${loadingKeywords && 'loading'}`} /> </div> ) : ( - <div className={`keywords`}> + <div className="keywords"> {keywordsLoc && - keywordsLoc.map((word, ind) => { - return ( - <div className={`keyword`}> - {word} - <IconButton - type={Type.PRIM} - size={Size.XSMALL} - color={Colors.DARK_GRAY} - icon={<GrClose />} - onClick={() => { - let kw = keywordsLoc; - kw.splice(ind); - NewLightboxView.SetKeywords(kw); - }} - /> - </div> - ); - })} + keywordsLoc.map((word, ind) => ( + <div className="keyword"> + {word} + <IconButton + type={Type.PRIM} + size={Size.XSMALL} + color={Colors.DARK_GRAY} + icon={<GrClose />} + onClick={() => { + const kw = keywordsLoc; + kw.splice(ind); + NewLightboxView.SetKeywords(kw); + }} + /> + </div> + ))} </div> )} {!showMore ? ( <div - className={`lb-caret`} + className="lb-caret" onClick={() => { setShowMore(true); }}> More <FaCaretDown /> </div> ) : ( - <div className={`more`}> + <div className="more"> <div - className={`lb-caret`} + className="lb-caret" onClick={() => { setShowMore(false); }}> Less <FaCaretUp /> </div> - <div className={`lb-label`}>Type</div> - <div className={`lb-label`}>Sources</div> + <div className="lb-label">Type</div> + <div className="lb-label">Sources</div> </div> )} </div> - <div className={`recommendations`}> - {recs && - recs.map((rec: IRecommendation) => { - return <Recommendation {...rec} />; - })} - </div> + <div className="recommendations">{recs && recs.map((rec: IRecommendation) => <Recommendation {...rec} />)}</div> </div> ); -}; +} diff --git a/src/client/views/newlightbox/RecommendationList/index.ts b/src/client/views/newlightbox/RecommendationList/index.ts index f4555c1f2..226eafb7e 100644 --- a/src/client/views/newlightbox/RecommendationList/index.ts +++ b/src/client/views/newlightbox/RecommendationList/index.ts @@ -1 +1 @@ -export * from './RecommendationList'
\ No newline at end of file +export * from './RecommendationList'; diff --git a/src/client/views/newlightbox/RecommendationList/utils.ts b/src/client/views/newlightbox/RecommendationList/utils.ts index cdfff3258..fbf8ac19f 100644 --- a/src/client/views/newlightbox/RecommendationList/utils.ts +++ b/src/client/views/newlightbox/RecommendationList/utils.ts @@ -1,9 +1,8 @@ -import { IRecommendation } from "../components"; +import { IRecommendation } from '../components'; export interface IRecommendationList { - loading?: boolean, - keywords?: string[], - recs?: IRecommendation[] - getRecs?: any + loading?: boolean; + keywords?: string[]; + recs?: IRecommendation[]; + getRecs?: any; } - diff --git a/src/client/views/newlightbox/components/EditableText/index.ts b/src/client/views/newlightbox/components/EditableText/index.ts index e3367b175..444881a74 100644 --- a/src/client/views/newlightbox/components/EditableText/index.ts +++ b/src/client/views/newlightbox/components/EditableText/index.ts @@ -1 +1 @@ -export * from './EditableText' +export * from './EditableText'; diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx index 23027808f..375408d01 100644 --- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx +++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { IRecommendation } from './utils'; -import './Recommendation.scss'; -import { getType } from '../../utils'; import { FaEyeSlash } from 'react-icons/fa'; -import { NewLightboxView } from '../../NewLightboxView'; -import { DocumentManager } from '../../../../util/DocumentManager'; import { Doc } from '../../../../../fields/Doc'; import { Docs } from '../../../../documents/Documents'; +import { DocumentView } from '../../../nodes/DocumentView'; +import { NewLightboxView } from '../../NewLightboxView'; +import { getType } from '../../utils'; +import './Recommendation.scss'; +import { IRecommendation } from './utils'; export const Recommendation = (props: IRecommendation) => { const { title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId } = props; @@ -17,7 +17,7 @@ export const Recommendation = (props: IRecommendation) => { onClick={() => { let doc: Doc | null = null; if (source == 'Dash' && docId) { - const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement(); + const docView = DocumentView.getDocumentViewsById(docId).lastElement(); if (docView) { doc = docView.Document; } diff --git a/src/client/views/newlightbox/components/Recommendation/index.ts b/src/client/views/newlightbox/components/Recommendation/index.ts index 12ebf9d6e..4fce185ec 100644 --- a/src/client/views/newlightbox/components/Recommendation/index.ts +++ b/src/client/views/newlightbox/components/Recommendation/index.ts @@ -1,2 +1,2 @@ -export * from './utils' -export * from './Recommendation'
\ No newline at end of file +export * from './utils'; +export * from './Recommendation'; diff --git a/src/client/views/newlightbox/components/Recommendation/utils.ts b/src/client/views/newlightbox/components/Recommendation/utils.ts index 796ce0eb0..4a55d394e 100644 --- a/src/client/views/newlightbox/components/Recommendation/utils.ts +++ b/src/client/views/newlightbox/components/Recommendation/utils.ts @@ -1,23 +1,23 @@ -import { DocumentType } from "../../../../documents/DocumentTypes" +import { DocumentType } from '../../../../documents/DocumentTypes'; export interface IRecommendation { - loading?: boolean - type?: DocumentType | string, - data?: string, - title?: string, - text?: string, - source?: string, - previewUrl?: string, + loading?: boolean; + type?: DocumentType | string; + data?: string; + title?: string; + text?: string; + source?: string; + previewUrl?: string; transcript?: { - text: string, - start: number, - duration: number - }[], + text: string; + start: number; + duration: number; + }[]; embedding?: { - x: number, - y: number - }, - distance?: number, - related_concepts?: string[], - docId?: string -}
\ No newline at end of file + x: number; + y: number; + }; + distance?: number; + related_concepts?: string[]; + docId?: string; +} diff --git a/src/client/views/newlightbox/components/SkeletonDoc/index.ts b/src/client/views/newlightbox/components/SkeletonDoc/index.ts index 396b7272b..98379f404 100644 --- a/src/client/views/newlightbox/components/SkeletonDoc/index.ts +++ b/src/client/views/newlightbox/components/SkeletonDoc/index.ts @@ -1 +1 @@ -export * from './SkeletonDoc'
\ No newline at end of file +export * from './SkeletonDoc'; diff --git a/src/client/views/newlightbox/components/Template/index.ts b/src/client/views/newlightbox/components/Template/index.ts index 36b5f3f46..f5b83ad09 100644 --- a/src/client/views/newlightbox/components/Template/index.ts +++ b/src/client/views/newlightbox/components/Template/index.ts @@ -1 +1 @@ -export * from './Template'
\ No newline at end of file +export * from './Template'; diff --git a/src/client/views/newlightbox/components/index.ts b/src/client/views/newlightbox/components/index.ts index 3f9128690..1c266c5d8 100644 --- a/src/client/views/newlightbox/components/index.ts +++ b/src/client/views/newlightbox/components/index.ts @@ -1,3 +1,3 @@ -export * from './Template' -export * from './Recommendation' -export * from './SkeletonDoc'
\ No newline at end of file +export * from './Template'; +export * from './Recommendation'; +export * from './SkeletonDoc'; diff --git a/src/client/views/newlightbox/utils.ts b/src/client/views/newlightbox/utils.ts index c879718e3..a0d6b9f4a 100644 --- a/src/client/views/newlightbox/utils.ts +++ b/src/client/views/newlightbox/utils.ts @@ -1,5 +1,5 @@ +/* eslint-disable no-use-before-define */ import { DocumentType } from '../../documents/DocumentTypes'; -import { IRecommendation } from './components'; export interface IDocRequest { id: string; @@ -12,7 +12,7 @@ export const fetchRecommendations = async (src: string, query: string, docs?: ID console.log('[rec] making request'); if (dummy) { return { - recommendations: [], //dummyRecs, + recommendations: [], // dummyRecs, keywords: dummyKeywords, num_recommendations: 4, max_x: 100, @@ -79,6 +79,7 @@ export const getType = (type: DocumentType | string) => { } }; +/* const dummyRecs = { a: { title: 'Vannevar Bush - American Engineer', @@ -117,4 +118,6 @@ const dummyRecs = { }, }; +*/ + const dummyKeywords = ['user control', 'vannevar bush', 'hypermedia', 'hypertext']; |
