diff options
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 42 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarousel3DView.tsx | 40 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarouselView.tsx | 33 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/collections/TabDocView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 19 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 3 | ||||
-rw-r--r-- | src/extensions/ExtensionsTypings.ts | 8 | ||||
-rw-r--r-- | src/extensions/Extensions_Array.ts | 10 |
10 files changed, 78 insertions, 94 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f2e85cf7a..be3b9c3bb 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -372,6 +372,8 @@ export class DocumentOptions { config_panX?: NUMt = new NumInfo('panX saved as a view spec', false); config_panY?: NUMt = new NumInfo('panY saved as a view spec', false); config_zoom?: NUMt = new NumInfo('zoom saved as a view spec', false); + config_carousel_index?: NUMt = new NumInfo('saved carousel index', false); + config_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView'); config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false); presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document', false); presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false); diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 60305244c..729a1585a 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -9,25 +9,24 @@ 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, toList } from '../../../fields/Types'; +import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types'; import { URLField } from '../../../fields/URLField'; import { gptImageLabel } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoable } from '../../util/UndoManager'; +import { PinDocView } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; 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', @@ -599,22 +598,21 @@ export class CollectionCardView extends CollectionSubView() { }); focus = action((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._curDoc = target); + const docs = DocListCast(this.Document[this.fieldKey]); + if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) { + const foundDoc = DocCast( + anchor.config_curDoc, + docs.find(doc => doc === DocCast(anchor.annotationOn, anchor)) + ); + options.didMove = foundDoc !== this.curDoc() ? true : false; + options.didMove && (this._curDoc = foundDoc); + } return undefined; }); getAnchor = (addAsAnnotation: boolean) => { - const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); - anchor.config_curDoc = this.curDoc(); + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, 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); - } + addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; // pinned / linked anchor doc includes selected rows, graph titles, and graph colors @@ -625,15 +623,7 @@ export class CollectionCardView extends CollectionSubView() { } 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); - }; + addDocTab = this.addLinkedDocTab; /** * Actually renders all the cards diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 81e921724..081e2fe5a 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -1,24 +1,23 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; +import { computedFn } from 'mobx-utils'; import * as React from 'react'; 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, toList } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; +import { PinDocView } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; 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'); @@ -94,22 +93,18 @@ export class CollectionCarousel3DView extends CollectionSubView() { .scale(1 / this.centerScale); 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); + const docs = DocListCast(this.Document[this.fieldKey]); + if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) { + const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor)); + options.didMove = newIndex !== this.layoutDoc._carousel_index; + options.didMove && (this.layoutDoc._carousel_index = newIndex); + } return undefined; }; getAnchor = (addAsAnnotation: boolean) => { - const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); - anchor.config_carousel_index = this.layoutDoc._carousel_index; + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number }); 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); - } + addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; // pinned / linked anchor doc includes selected rows, graph titles, and graph colors @@ -120,16 +115,7 @@ export class CollectionCarousel3DView extends CollectionSubView() { } 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); - }; - + addDocTab = this.addLinkedDocTab; @computed get content() { const currentIndex = NumCast(this.layoutDoc._carousel_index); const displayDoc = (child: Doc, dxf: () => Transform) => ( diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index b043706fd..bfb58733d 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { StopEvent, returnOne, returnZero } from '../../../ClientUtils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; -import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; @@ -13,7 +13,6 @@ 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'; @@ -76,23 +75,19 @@ 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); + const docs = DocListCast(this.Document[this.fieldKey]); + if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) { + const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor)); + options.didMove = newIndex !== this.layoutDoc._carousel_index; + options.didMove && (this.layoutDoc._carousel_index = newIndex); + } return undefined; }; getAnchor = (addAsAnnotation: boolean) => { - const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); - anchor.config_carousel_index = this.carouselIndex; + const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, 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); - } + addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; // pinned / linked anchor doc includes selected rows, graph titles, and graph colors @@ -103,15 +98,7 @@ export class CollectionCarouselView extends CollectionSubView() { } 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); - }; + addDocTab = this.addLinkedDocTab; 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 diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 48aac3a68..9952863d7 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; @@ -27,6 +27,7 @@ import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldViewProps } from '../nodes/FieldView'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FlashcardPracticeUI } from './FlashcardPracticeUI'; +import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) @@ -130,6 +131,17 @@ export function CollectionSubView<X>() { @computed get childDocList() { return Cast(this.dataField, listSpec(Doc)); } + + addLinkedDocTab = (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); + }; + collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters); collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []); // child filters apply to the descendants of the documents in this collection diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 5bfdee1f5..1c5722217 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -285,6 +285,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { static Activate = (tabDoc: Doc) => { const tab = Array.from(CollectionDockingView.Instance?.tabMap ?? []).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); + if (tab.header.parent._activeContentItem === tab.contentItem) return false; tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) return tab !== undefined; }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index afd662b22..0241fc2dd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -40,7 +40,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, Active import { FieldViewProps } from '../../nodes/FieldView'; import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; -import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; import { AnnotationPalette } from '../../smartdraw/AnnotationPalette'; import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler'; @@ -1596,21 +1596,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } case undefined: case OpenWhere.lightbox: - { - const firstDoc = docs[0]; - if (this.layoutDoc._isLightbox) { - this._lightboxDoc = firstDoc; - return true; - } - if (firstDoc === this.Document || this.childDocList?.includes(firstDoc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(firstDoc)) { - if (firstDoc.hidden) firstDoc.hidden = false; - if (!location.includes(OpenWhereMod.always)) return true; - } + if (this.layoutDoc._isLightbox) { + this._lightboxDoc = docs[0]; + return true; } - break; + return this.addLinkedDocTab(docsIn, location); default: } - return this._props.addDocTab(docs, location); + return this._props.addDocTab(docsIn, location); }); getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData { const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 7448fa898..53d72e946 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -48,6 +48,7 @@ import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; import SlideEffect from './SlideEffect'; import { AnimationSettings, SpringSettings, SpringType, easeItems, effectItems, effectTimings, movementItems, presEffectDefaultTimings, springMappings, springPreviewColors } from './SpringUtils'; +import _ from 'lodash'; @observer export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @@ -708,7 +709,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) { - if (bestTarget.childFilters !== activeItem.config_docFilters) { + if (!_.isEqual(Array.from(StrListCast(bestTarget.childFilters)), Array.from(StrListCast(activeItem.config_docFilters)))) { bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]); changed = true; } diff --git a/src/extensions/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts index d6ffd3be3..fa8851bb3 100644 --- a/src/extensions/ExtensionsTypings.ts +++ b/src/extensions/ExtensionsTypings.ts @@ -1,6 +1,14 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ interface Array<T> { + /** + * returns the last element of the array or undefined + */ lastElement(): T; + /** + * if val is in the list, it returns its index, otherwise undefined; + * @param val + */ + getIndex(val: T): number | undefined; } interface String { diff --git a/src/extensions/Extensions_Array.ts b/src/extensions/Extensions_Array.ts index a50fb330f..d61585e28 100644 --- a/src/extensions/Extensions_Array.ts +++ b/src/extensions/Extensions_Array.ts @@ -1,14 +1,13 @@ export default class ArrayExtension { private readonly property: string; - private readonly body: <T>(this: Array<T>) => any; + private readonly body: <T>(this: Array<T>, args: unknown) => unknown; - constructor(property: string, body: <T>(this: Array<T>) => any) { + constructor(property: string, body: <T>(this: Array<T>, args: unknown) => unknown) { this.property = property; this.body = body; } assign() { - // eslint-disable-next-line no-extend-native Object.defineProperty(Array.prototype, this.property, { value: this.body, enumerable: false, @@ -28,6 +27,11 @@ const extensions = [ } return this[this.length - 1]; }), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + new ArrayExtension('getIndex', function (val: any) { + const index = this.indexOf(val); + return index === -1 ? undefined : index; + }), ]; function Assign() { |