From 4b231bbaf21939144ed8639d35f022834a406e59 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 17 Oct 2024 22:02:19 -0400 Subject: changed layout_isFlashcard to layout_flashcardType with value of 'flashcard' to make it easy to search for flashcards. made carousel views able to focus() on a Doc. added ability for carousel and card views to have anchors so they can be pinned and linked. fixed pinning collections to save filters --- .../views/collections/CollectionCardDeckView.tsx | 32 +++++++++++++- .../views/collections/CollectionCarousel3DView.tsx | 32 +++++++++++++- .../views/collections/CollectionCarouselView.tsx | 51 +++++++++++++++++++++- .../collections/CollectionStackedTimeline.tsx | 5 +-- .../views/collections/CollectionTimeView.tsx | 2 - .../views/collections/FlashcardPracticeUI.tsx | 6 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 7 files changed, 116 insertions(+), 14 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index b86dad9d7..60305244c 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -9,7 +9,7 @@ import { Animation, DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { URLField } from '../../../fields/URLField'; import { gptImageLabel } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -25,6 +25,9 @@ import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup'; import './CollectionCardDeckView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; +import { Docs } from '../../documents/Documents'; +import { PinDocView } from '../PinFuncs'; +import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; enum cardSortings { Time = 'time', @@ -604,6 +607,33 @@ export class CollectionCardView extends CollectionSubView() { index !== -1 && (this._curDoc = target); return undefined; }); + getAnchor = (addAsAnnotation: boolean) => { + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); + anchor.config_curDoc = this.curDoc(); + PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document); + if (addAsAnnotation) { + // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered + Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor); + } + return anchor; + }; + // pinned / linked anchor doc includes selected rows, graph titles, and graph colors + restoreView = (viewData: Doc) => { + if (viewData.config_curDoc !== undefined && this.curDoc() !== viewData.config_curDoc) { + this._curDoc = DocCast(viewData.config_curDoc); + return true; + } + return false; + }; + addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => { + const doc = toList(docsIn).lastElement(); + const where = location.split(':')[0]; + if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) { + if (doc.hidden) doc.hidden = false; + if (!location.includes(OpenWhereMod.always)) return true; + } + return this._props.addDocTab(docsIn, location); + }; /** * Actually renders all the cards diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index a71cc43ba..2be3ef48f 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -6,7 +6,7 @@ import { returnZero } from '../../../ClientUtils'; import { Utils } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; -import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; @@ -16,6 +16,9 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCarousel3DView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { computedFn } from 'mobx-utils'; +import { Docs } from '../../documents/Documents'; +import { PinDocView } from '../PinFuncs'; +import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; // eslint-disable-next-line @typescript-eslint/no-require-imports const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss'); @@ -96,6 +99,33 @@ export class CollectionCarousel3DView extends CollectionSubView() { index !== -1 && (this.layoutDoc._carousel_index = index); return undefined; }; + getAnchor = (addAsAnnotation: boolean) => { + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); + anchor.config_carousel_index = this.layoutDoc._carousel_index; + PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document); + if (addAsAnnotation) { + // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered + Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor); + } + return anchor; + }; + // pinned / linked anchor doc includes selected rows, graph titles, and graph colors + restoreView = (viewData: Doc) => { + if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) { + this.layoutDoc._carousel_index = viewData.config_carousel_index; + return true; + } + return false; + }; + addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => { + const doc = toList(docsIn).lastElement(); + const where = location.split(':')[0]; + if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) { + if (doc.hidden) doc.hidden = false; + if (!location.includes(OpenWhereMod.always)) return true; + } + return this._props.addDocTab(docsIn, location); + }; @computed get content() { const currentIndex = NumCast(this.layoutDoc._carousel_index); diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 1f2bc908f..b043706fd 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -3,12 +3,17 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { StopEvent, returnOne, returnZero } from '../../../ClientUtils'; -import { Doc, Opt } from '../../../fields/Doc'; -import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; +import { PinDocView } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; +import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import './CollectionCarouselView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; @@ -27,6 +32,9 @@ export class CollectionCarouselView extends CollectionSubView() { makeObservable(this); } + componentDidMount(): void { + this._props.setContentViewBox?.(this); + } componentWillUnmount() { this._dropDisposer?.(); } @@ -67,6 +75,44 @@ export class CollectionCarouselView extends CollectionSubView() { curDoc = () => this.carouselItems[this.carouselIndex]?.layout; + focus = (anchor: Doc, options: FocusViewOptions): Opt => { + const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]); + if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined; + options.didMove = true; + const target = DocCast(anchor.annotationOn) ?? anchor; + const index = docs.indexOf(target); + index !== -1 && (this.layoutDoc._carousel_index = index); + return undefined; + }; + + getAnchor = (addAsAnnotation: boolean) => { + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); + anchor.config_carousel_index = this.carouselIndex; + PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document); + if (addAsAnnotation) { + // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered + Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor); + } + return anchor; + }; + // pinned / linked anchor doc includes selected rows, graph titles, and graph colors + restoreView = (viewData: Doc) => { + if (viewData.config_carousel_index !== undefined && this.layoutDoc._carousel_index !== viewData.config_carousel_index) { + this.layoutDoc._carousel_index = viewData.config_carousel_index; + return true; + } + return false; + }; + addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => { + const doc = toList(docsIn).lastElement(); + const where = location.split(':')[0]; + if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) { + if (doc.hidden) doc.hidden = false; + if (!location.includes(OpenWhereMod.always)) return true; + } + return this._props.addDocTab(docsIn, location); + }; + captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string) => { // first look for properties on the document in the carousel, then fallback to properties on the container const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined; @@ -113,6 +159,7 @@ export class CollectionCarouselView extends CollectionSubView() { LayoutTemplateString={this._props.childLayoutString} TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)} childFilters={this.childDocFilters} + focus={this.focus} hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)} addDocument={this._props.addDocument} ScreenToLocalTransform={this.contentScreenToLocalXf} diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 486c826b6..c3047e5fb 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable no-use-before-define */ import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -59,7 +58,6 @@ export enum TrimScope { @observer export class CollectionStackedTimeline extends CollectionSubView() { - // eslint-disable-next-line no-use-before-define public static SelectingRegions: Set = new Set(); public static StopSelecting() { this.SelectingRegions.forEach( @@ -171,7 +169,7 @@ export class CollectionStackedTimeline extends CollectionSubView this.childDocList?.some(item => item === doc); - getView = async (doc: Doc, options: FocusViewOptions): Promise> => + getView = (doc: Doc, options: FocusViewOptions): Promise> => new Promise>(res => { if (doc.hidden) options.didMove = !(doc.hidden = false); const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv)); @@ -600,7 +598,6 @@ export class CollectionStackedTimeline extends CollectionSubView doc.title === 'Filter'); } // prettier-ignore - @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore + @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale); btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height)); @@ -127,7 +127,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray'); const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode); - return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : ( + return !this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? null : (
); } - tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct + tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._flashcardType) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct render() { return (
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d2bc8f2c2..afd662b22 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -389,7 +389,7 @@ export class CollectionFreeFormView extends CollectionSubView> => + getView = (doc: Doc, options: FocusViewOptions): Promise> => new Promise>(res => { if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false); if (doc === this.Document) { -- cgit v1.2.3-70-g09d2