aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-10-17 22:02:19 -0400
committerbobzel <zzzman@gmail.com>2024-10-17 22:02:19 -0400
commit4b231bbaf21939144ed8639d35f022834a406e59 (patch)
tree7f675cbd90004742e14343921ca6237ea5d89066 /src/client/views/collections
parentdd93f5175064850c6c0e47f025cd7bbba1f23106 (diff)
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
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx32
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx32
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx51
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx5
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
7 files changed, 116 insertions, 14 deletions
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<number> => {
+ 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<FieldViewProps>, 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<CollectionStackedTimelineProps>() {
- // eslint-disable-next-line no-use-before-define
public static SelectingRegions: Set<CollectionStackedTimeline> = new Set();
public static StopSelecting() {
this.SelectingRegions.forEach(
@@ -171,7 +169,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
makeDocUnfiltered = (doc: Doc) => this.childDocList?.some(item => item === doc);
- getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+ getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(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<CollectionStack
pointerEvents: 'none',
}}>
<StackedTimelineAnchor
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
mark={d.anchor}
containerViewPath={this._props.containerViewPath}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 8a24db330..5738e7ce3 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -147,7 +147,6 @@ export class CollectionTimeView extends CollectionSubView() {
return (
<div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
fitContentsToBox={returnTrue}
@@ -257,7 +256,6 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
const pivotView = DocumentView.getDocumentView(pivotDoc);
if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
- // eslint-disable-next-line prefer-destructuring
pivotDoc._pivotField = filterVals[0];
}
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 9e9318c0a..c298bff22 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -58,7 +58,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
@computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => 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<PracticeUIProp
const setColor = (mode: practiceMode) => (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 : (
<div
className="FlashcardPracticeUI-practiceModes"
style={{
@@ -179,7 +179,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
</div>
);
}
- 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 (
<div className="FlashcardPracticeUI">
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<Partial<collection
return undefined;
};
- getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+ getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(res => {
if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false);
if (doc === this.Document) {