diff options
Diffstat (limited to 'src/client/views/collections')
27 files changed, 298 insertions, 255 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 756b37f99..50de7c601 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -23,7 +23,8 @@ import { undoable, UndoManager } from '../../util/UndoManager'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; +import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCardDeckView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss index 544b3e262..962b590c8 100644 --- a/src/client/views/collections/CollectionCarouselView.scss +++ b/src/client/views/collections/CollectionCarouselView.scss @@ -1,12 +1,16 @@ .collectionCarouselView-outer { height: 100%; - position: relative; + position: absolute; overflow: hidden; display: flex; + transform-origin: top left; + .collectionCarouselView-caption { height: 50; display: inline-block; width: 100%; + bottom: 0; + position: absolute; } .collectionCarouselView-image { height: calc(100% - 50px); diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index a7d217076..2d3f8cb0e 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -115,12 +115,14 @@ export class CollectionCarouselView extends CollectionSubView() { ? false : undefined; // prettier-ignore onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); + renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => { return ( <DocumentView {...this._props} ref={overlayFunc} Document={doc} + TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined} NativeWidth={returnZero} NativeHeight={returnZero} fitWidth={this._props.childLayoutFitWidth} @@ -136,7 +138,6 @@ export class CollectionCarouselView extends CollectionSubView() { renderDepth={this._props.renderDepth + 1} LayoutTemplate={this._props.childLayoutTemplate} LayoutTemplateString={this._props.childLayoutString} - TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)} childFilters={this.childDocFilters} focus={this.focus} hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)} @@ -182,7 +183,15 @@ export class CollectionCarouselView extends CollectionSubView() { } @computed get content() { - const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined }; + const captionProps = { + ...this._props, // + NativeScaling: returnOne, + PanelWidth: this.captionWidth, + fieldKey: 'caption', + setHeight: undefined, + setContentView: undefined, + noSidebar: true, + }; const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption); return !this.curDoc() ? null : ( <> @@ -242,11 +251,9 @@ export class CollectionCarouselView extends CollectionSubView() { color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string, left: NumCast(this.layoutDoc._xMargin), top: NumCast(this.layoutDoc._yMargin), - transformOrigin: 'top left', transform: `scale(${this.nativeScaling()})`, width: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._xMargin)) / this.nativeScaling()}px)`, height: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._yMargin)) / this.nativeScaling()}px)`, - position: 'relative', }}> {this.content} {this.navButtons} diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index c79610595..1576a8e4a 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -23,6 +23,7 @@ import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import './CollectionMenu.scss'; import { CollectionLinearView } from './collectionLinear'; +import { SettingsManager } from '../../util/SettingsManager'; interface CollectionMenuProps { panelHeight: () => number; @@ -118,7 +119,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { <div className="hardCodedButtons"> <Toggle toggleType={ToggleType.BUTTON} - type={Type.PRIM} + type={Type.TERT} color={SnappingManager.userColor} onClick={this.props.toggleTopBar} toggleStatus={this.props.topBarHeight() > 0} @@ -127,7 +128,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { /> <Toggle toggleType={ToggleType.BUTTON} - type={Type.PRIM} + type={Type.TERT} color={SnappingManager.userColor} onClick={this._props.togglePropertiesFlyout} toggleStatus={SnappingManager.PropertiesWidth > 0} @@ -138,19 +139,17 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { ); // dash col linear view buttons - const contMenuButtons = ( + return ( <div className="collectionMenu-container" style={{ - background: SnappingManager.userBackgroundColor, + background: SettingsManager.userBackgroundColor, // borderColor: SettingsManager.userColor }}> {this.contMenuButtons} {hardCodedButtons} </div> ); - - return contMenuButtons; } } diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 01695dbaf..173147f64 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -258,11 +258,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { const noteTakingDocTransform = () => this.getDocTransform(doc, dref); return ( <DocumentView - ref={r => { - dref = r || undefined; - }} + ref={r => (dref = r || undefined)} Document={doc} - TemplateDataDocument={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)} + TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined} pointerEvents={this.blockPointerEventsWhenDragging} renderDepth={this._props.renderDepth + 1} PanelWidth={width} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index fd48a9dc1..4f60acb18 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -112,7 +112,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection @computed get columnWidth() { const availableWidth = this._props.PanelWidth() - 2 * this.xMargin; const cwid = availableWidth / (NumCast(this.Document._layout_columnCount) || this._props.PanelWidth() / NumCast(this.Document._layout_columnWidth, this._props.PanelWidth() / 4)); - return Math.min(availableWidth, this.isStackingView ? Number.MAX_VALUE : cwid - this.gridGap); + return Math.min(availableWidth, this.isStackingView ? availableWidth / (this.numGroupColumns || 1) : cwid - this.gridGap); } @computed get NodeWidth() { @@ -675,12 +675,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection } return35 = () => 35; + @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore @computed get buttonMenu() { - const menuDoc = DocCast(this.layoutDoc.layout_headerButton); - return !menuDoc ? null : ( - <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}> + return !this.menuBtnDoc ? null : ( + <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}> <DocumentView - Document={menuDoc} + Document={this.menuBtnDoc} isContentActive={this.isContentActive} isDocumentActive={this.isContentActive} addDocument={this._props.addDocument} @@ -728,13 +728,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection SetValue: this.addGroup, contents: '+ ADD A GROUP', }; - const buttonMenu = this.layoutDoc.layout_headerButton; const noviceExplainer = this.layoutDoc.layout_explainer; return ( <> - {buttonMenu || noviceExplainer ? ( + {this.menuBtnDoc || noviceExplainer ? ( <div className="documentButtonMenu"> - {buttonMenu ? this.buttonMenu : null} + {this.menuBtnDoc ? this.buttonMenu : null} {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null} </div> ) : null} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 3b9d167c6..66839ba7f 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { RichTextField } from '../../../fields/RichTextField'; import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, NumCast } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; @@ -26,6 +26,7 @@ import { EditableView } from '../EditableView'; import { DocumentView } from '../nodes/DocumentView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import './CollectionStackingView.scss'; +import { DocData } from '../../../fields/DocSymbols'; // So this is how we are storing a column interface CSVFieldColumnProps { @@ -270,7 +271,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< event: () => { const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey }); if (created) { - const container = this._props.Doc.resolvedDataDoc ? Doc.GetProto(this._props.Doc) : this._props.Doc; + const container = DocCast(this._props.Doc.rootDocument)?.[DocData] ? Doc.GetProto(this._props.Doc) : this._props.Doc; if (container.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, container); return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); @@ -304,10 +305,8 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< @computed get innards() { TraceMobx(); const key = this._props.pivotField; - const headings = this._props.headings(); const heading = this._heading; const columnYMargin = this._props.headingObject ? 0 : this._props.yMargin; - const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); const noValueHeader = `NO ${key.toUpperCase()} VALUE`; const evContents = heading || (this._props?.type === 'number' ? '0' : noValueHeader); const headingView = this._props.headingObject ? ( @@ -317,7 +316,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< ref={this._headerRef} style={{ marginTop: this._props.yMargin, - width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1), + width: this._props.columnWidth }}> {/* the default bucket (no key value) has a tooltip that describes what it is. Further, it does not have a color and cannot be deleted. */} @@ -360,7 +359,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< margin: 'auto', marginTop: this._props.dontCenter.includes('y') ? undefined : 'auto', marginBottom: this._props.dontCenter.includes('y') ? undefined : 'auto', - width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1), + width: this._props.columnWidth, }}> <div key={`${heading}-stack`} @@ -368,7 +367,6 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< style={{ padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`, margin: this._props.dontCenter.includes('x') ? undefined : 'auto', - // width: 'max-content', // singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`, height: 'max-content', position: 'relative', gridGap: this._props.gridGap, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a43cf0755..375c0fe53 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -13,6 +13,7 @@ import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from ' import { WebField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; +import { Upload } from '../../../server/SharedMediaTypes'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; import { DocUtils } from '../../documents/DocUtils'; @@ -24,11 +25,11 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; -import { FlashcardPracticeUI } from './FlashcardPracticeUI'; import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; -import { Upload } from '../../../server/SharedMediaTypes'; +import { FlashcardPracticeUI } from './FlashcardPracticeUI'; export enum docSortings { Time = 'time', @@ -108,11 +109,11 @@ export function CollectionSubView<X>() { } get dataDoc() { - return this._props.TemplateDataDocument instanceof Doc && this.layoutDoc.isTemplateForField // - ? this._props.TemplateDataDocument[DocData] - : this.layoutDoc.resolvedDataDoc - ? this._props.Document - : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template + return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField // + ? Doc.GetProto(this._props.TemplateDataDocument) + : this.Document.rootDocument + ? this.Document + : this.Document[DocData]; // if the layout document has a rootDocument, then we don't want to get its parent which would be the unexpanded template } get childContainerViewPath() { @@ -131,9 +132,9 @@ export function CollectionSubView<X>() { hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout); @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] { - const { Document: Document, TemplateDataDocument } = this._props; + const { TemplateDataDocument } = this._props; const validPairs = this.childDocs - .map(doc => Doc.GetLayoutDataDocPair(Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc)) + .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc)) .filter( pair => // filter out any documents that have a proto that we don't have permissions to @@ -299,7 +300,7 @@ export function CollectionSubView<X>() { const dragData = de.complete.docDragData; if (dragData) { const sourceDragAction = dragData.dropAction; - const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._renderDoc); + const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this.Document); dragData.dropAction = !sameCollection // if doc from another tree ? sourceDragAction || targetDropAction // then use the source's dragAction otherwise the target's : sourceDragAction === dropActionType.inPlace // if source drag is inPlace @@ -589,7 +590,7 @@ export function CollectionSubView<X>() { /** * How much the content of the collection is being scaled based on its nesting and its fit-to-width settings */ - @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale; } // prettier-ignore + @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (!this._props.fitWidth?.(this.Document) ? this._props.NativeDimScaling?.()||1: 1); } // prettier-ignore /** * The maximum size a UI widget can be in collection coordinates based on not wanting the widget to visually obscure too much of the collection * This takes the desired screen space size and converts into collection coordinates. It then returns the smaller of the converted @@ -602,7 +603,12 @@ export function CollectionSubView<X>() { */ @computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore - screenXPadding = () => (this.uiBtnScaling * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale; + screenXPadding = (docView?: DocumentView) => { + if (!docView) return 0; + const diff = this._props.PanelWidth() - docView.PanelWidth(); + const xpad1 = this.uiBtnScaling * (this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) - diff / 2; // this._sideBtnWidth; + return xpad1 / (docView.NativeDimScaling?.() || 1); + }; filteredChildDocs = () => this.childLayoutPairs.map(pair => pair.layout); childDocsFunc = () => this.childDocs; @action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1960e12bd..962fbdcd7 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -8,7 +8,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; @@ -187,7 +187,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree @action addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => { const addDocRelativeTo = (adocs: Doc | Doc[]) => (adocs as Doc[]).reduce((flg, doc) => flg && Doc.AddDocToList(this.Document[DocData], this._props.fieldKey, doc, relativeTo, before), true); - if (this.Document.resolvedDataDoc instanceof Promise) return false; + if (this.Document.rootDocument instanceof Promise) return false; const doclist = toList(docs); const res = relativeTo === undefined ? this._props.addDocument?.(doclist) || false : addDocRelativeTo(doclist); res && @@ -200,7 +200,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree onContextMenu = (): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout const layoutItems: ContextMenuProps[] = []; - const menuDoc = ScriptCast(Cast(this.layoutDoc.layout_headerButton, Doc, null)?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc; + const menuDoc = ScriptCast(this.menuBtnDoc?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc; menuDoc && layoutItems.push({ description: 'Create new folder', event: () => CollectionTreeView.addTreeFolder(this.Document), icon: 'paint-brush' }); if (!Doc.noviceMode) { layoutItems.push({ @@ -347,14 +347,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree } return35 = () => 35; + @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore @computed get buttonMenu() { - const menuDoc = Cast(this.layoutDoc.layout_headerButton, Doc, null); // To create a multibutton menu add a CollectionLinearView - return !menuDoc ? null : ( - <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}> + return !this.menuBtnDoc ? null : ( + <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}> <DocumentView - Document={menuDoc} - TemplateDataDocument={menuDoc} + Document={this.menuBtnDoc} + TemplateDataDocument={this.menuBtnDoc} isContentActive={this._props.isContentActive} isDocumentActive={returnTrue} addDocument={this._props.addDocument} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7ba8989ce..c9af92a1b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -130,7 +130,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr { description: 'Calendar', event: () => func(CollectionViewType.Calendar), icon: 'columns' }, { description: 'Pivot', event: () => func(CollectionViewType.Pivot), icon: 'columns' }, { description: 'Time', event: () => func(CollectionViewType.Time), icon: 'columns' }, - { description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' }, { description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' }, ]; @@ -162,7 +161,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr if (this.Document.childClickedOpenTemplateView instanceof Doc) { optionItems.push({ description: 'View Child Detailed Layout', event: () => this._props.addDocTab(this.Document.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' }); } - !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox; }, icon: 'project-diagram' }); // prettier-ignore + !Doc.noviceMode && optionItems.push({ description: `${this.dataDoc.$isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.dataDoc.$isLightbox = !this.dataDoc.$isLightbox; }, icon: 'project-diagram' }); // prettier-ignore !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' }); diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx index c071c5fb8..f24a8acb7 100644 --- a/src/client/views/collections/FlashcardPracticeUI.tsx +++ b/src/client/views/collections/FlashcardPracticeUI.tsx @@ -7,13 +7,16 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnFalse, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import './FlashcardPracticeUI.scss'; +import { StyleProp } from '../StyleProp'; +import { FieldViewProps } from '../nodes/FieldView'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; export enum practiceMode { PRACTICE = 'practice', @@ -60,8 +63,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp @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_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)); + btnHeight = () => NumCast(this.filterDoc?.height); + btnWidth = () => (!this.filterDoc ? 1 : NumCast(this.filterDoc._width)); /** * Sets the practice mode answer style for flashcards @@ -131,7 +134,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp <div className="FlashcardPracticeUI-practiceModes" style={{ - transform: this._props.ScreenToLocalBoxXf().Scale >= 1 ? undefined : `translateY(${this.btnHeight() / this._props.ScreenToLocalBoxXf().Scale - this.btnHeight()}px)`, + background: SnappingManager.userVariantColor, }}> <MultiToggle tooltip="Practice flashcards one at a time" @@ -139,7 +142,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp color={SnappingManager.userColor} background={SnappingManager.userVariantColor} multiSelect={false} - isToggle={false} toggleStatus={!!this.practiceMode} label="Practice" items={[ @@ -159,7 +161,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp color={SnappingManager.userColor} background={SnappingManager.userVariantColor} multiSelect={false} - isToggle={false} toggleStatus={!!this.practiceMode} label={StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)} items={[ @@ -179,6 +180,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp </div> ); } + childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => { + if (doc instanceof Doc && property === StyleProp.BackgroundColor) { + return SnappingManager.userVariantColor; + } + return this._props.docViewProps().styleProvider?.(doc, props, property); + }; tryFilterOut = (doc: Doc) => (this.practiceMode && doc?._layout_flashcardType && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct render() { return ( @@ -186,24 +193,27 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp {this.emptyMessage} {this.practiceButtons} {this._props.layoutDoc._chromeHidden ? null : ( - <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnHeight(), transform: `scale(${this._props.uiBtnScaling})` }}> + <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnWidth(), transform: `scale(${this._props.uiBtnScaling})` }}> {!this.filterDoc ? null : ( - <DocumentView - {...this._props.docViewProps()} - Document={this.filterDoc} - TemplateDataDocument={undefined} - PanelWidth={this.btnWidth} - PanelHeight={this.btnHeight} - NativeWidth={returnZero} - NativeHeight={returnZero} - hideDecorations={BoolCast(this._props.layoutDoc.layout_hideDecorations)} - hideCaptions={true} - hideFilterStatus={true} - renderDepth={this._props.renderDepth + 1} - fitWidth={undefined} - showTags={false} - setContentViewBox={undefined} - /> + <div style={{ background: SnappingManager.userVariantColor }}> + <DocumentView + {...this._props.docViewProps()} + Document={this.filterDoc} + TemplateDataDocument={undefined} + PanelWidth={this.btnWidth} + PanelHeight={this.btnHeight} + NativeWidth={returnZero} + NativeHeight={returnZero} + hideDecorations={BoolCast(this._props.layoutDoc.layout_hideDecorations)} + hideCaptions={true} + hideFilterStatus={true} + renderDepth={this._props.renderDepth + 1} + styleProvider={this.childStyleProvider} + fitWidth={undefined} + showTags={false} + setContentViewBox={undefined} + /> + </div> )} {this.practiceModesMenu} </div> diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss index 397e35ca9..931cdac2b 100644 --- a/src/client/views/collections/TabDocView.scss +++ b/src/client/views/collections/TabDocView.scss @@ -4,7 +4,6 @@ height: 100%; width: 100%; } - input.lm_title:focus, input.lm_title { max-width: unset !important; @@ -22,7 +21,6 @@ input.lm_title { .lm_iconWrap { display: flex; color: black; - width: 15px; height: 15px; align-items: center; align-self: center; @@ -30,6 +28,16 @@ input.lm_title { margin: 3px; border-radius: 20%; + width: auto; + svg:nth-of-type(2) { + display: none; + } + &:hover { + svg:nth-of-type(2) { + display: block; + } + } + .moreInfoDot { background-color: white; border-radius: 100%; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 620be2726..568a08792 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -10,7 +10,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, DashColor, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocLayout } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; @@ -41,6 +41,7 @@ import { CollectionDockingView } from './CollectionDockingView'; import { CollectionView } from './CollectionView'; import './TabDocView.scss'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; +import { Tooltip } from '@mui/material'; interface TabMinimapViewProps { doc: Doc; @@ -311,7 +312,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { @observable _view: DocumentView | undefined = undefined; @observable _forceInvalidateScreenToLocal = 0; // screentolocal is computed outside of react using a dom resize ovbserver. this hack allows the resize observer to trigger a react update - @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } // prettier-ignore + @computed get layoutDoc() { return this._document?.[DocLayout]; } // prettier-ignore @computed get isUserActivated() { return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } // prettier-ignore @computed get isContentActive() { return this.isUserActivated || this._hovering; } // prettier-ignore @@ -353,7 +354,6 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { if (tab.element[0].children[1].children.length === 1) { iconWrap.className = 'lm_iconWrap lm_moreInfo'; - iconWrap.title = 'click for menu, drag to embed in document'; const dragBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, @@ -377,7 +377,26 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> { ); }; - const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />; + const docIcon = ( + <> + <Tooltip title="click for menu, drag to embed in document"> + <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} /> + </Tooltip> + <Tooltip title="click to open in lightbox"> + <FontAwesomeIcon + onPointerDown={dragBtnDown} + icon="external-link-alt" + onClick={() => { + if (doc.layout_fieldKey === 'layout_icon') { + const odoc = Doc.GetEmbeddings(doc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(doc); + Doc.deiconifyView(odoc); + } + this.addDocTab(doc, OpenWhere.lightboxAlways); + }} + /> + </Tooltip> + </> + ); const closeIcon = <FontAwesomeIcon icon="eye" />; ReactDOM.createRoot(iconWrap).render(docIcon); ReactDOM.createRoot(closeWrap).render(closeIcon); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index cb7da8c84..4dc937864 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocLayout } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; @@ -156,7 +156,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { return this.Document[DocData]; } @computed get layoutDoc() { - return Doc.Layout(this.Document); + return this.Document[DocLayout]; } @computed get fieldKey() { return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document)); @@ -594,7 +594,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { const key = match[1]; const assign = match[2]; const val = match[3]; - Doc.SetField(doc, key, assign + val, false); + Doc.SetField(doc, key, assign + val); return true; } return false; @@ -1309,7 +1309,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView._props.parentTreeView : undefined); const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false); - const childLayout = Doc.Layout(pair.layout); + const childLayout = pair.layout[DocLayout]; const rowHeight = () => { const aspect = Doc.NativeAspect(childLayout); return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts index 3838852dd..903d92c90 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts @@ -31,16 +31,14 @@ export class CollectionFreeFormClusters { get selectDocuments() { return this._view.selectDocuments; } // prettier-ignore static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { - const doc2Layout = Doc.Layout(doc2); - const doc1Layout = Doc.Layout(doc1); const x2 = NumCast(doc2.x) - clusterDistance; const y2 = NumCast(doc2.y) - clusterDistance; - const w2 = NumCast(doc2Layout._width) + clusterDistance; - const h2 = NumCast(doc2Layout._height) + clusterDistance; + const w2 = NumCast(doc2._width) + clusterDistance; + const h2 = NumCast(doc2._height) + clusterDistance; const x = NumCast(doc1.x) - clusterDistance; const y = NumCast(doc1.y) - clusterDistance; - const w = NumCast(doc1Layout._width) + clusterDistance; - const h = NumCast(doc1Layout._height) + clusterDistance; + const w = NumCast(doc1._width) + clusterDistance; + const h = NumCast(doc1._height) + clusterDistance; return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); } handlePointerDown(probe: number[]) { @@ -49,12 +47,11 @@ export class CollectionFreeFormClusters { .reduce((cluster, cd) => { const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1); if (grouping !== -1) { - const layoutDoc = Doc.Layout(cd); const cx = NumCast(cd.x) - this._clusterDistance / 2; const cy = NumCast(cd.y) - this._clusterDistance / 2; - const cw = NumCast(layoutDoc._width) + this._clusterDistance; - const ch = NumCast(layoutDoc._height) + this._clusterDistance; - return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster; + const cw = NumCast(cd._width) + this._clusterDistance; + const ch = NumCast(cd._height) + this._clusterDistance; + return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster; } return cluster; }, -1); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 4ea1de680..158bac7ba 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -1,5 +1,6 @@ /* eslint-disable no-use-before-define */ import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc'; +import { DocLayout } from '../../../../fields/DocSymbols'; import { Id, ToString } from '../../../../fields/FieldSymbols'; import { ObjectField } from '../../../../fields/ObjectField'; import { RefField } from '../../../../fields/RefField'; @@ -237,7 +238,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do payload: val, }); val?.docs.forEach((doc, i) => { - const layoutDoc = Doc.Layout(doc); + const layoutDoc = doc[DocLayout]; let wid = pivotAxisWidth; let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1); if (hgt > pivotAxisWidth) { @@ -249,7 +250,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do y: -y + (pivotAxisWidth - hgt) / 2, width: wid, height: hgt, - backgroundColor: StrCast(layoutDoc.backgroundColor, 'white'), + backgroundColor: StrCast(doc.backgroundColor, 'white'), pair: { layout: doc }, replica: val.replicas[i], }); @@ -362,7 +363,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: function layoutDocsAtTime(keyDocs: Doc[], key: number) { keyDocs.forEach(doc => { const stack = findStack(x, stacking); - const layoutDoc = Doc.Layout(doc); + const layoutDoc = doc[DocLayout]; let wid = pivotAxisWidth; let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1); if (hgt > pivotAxisWidth) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index cce0ff684..6c47a71b0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -41,7 +41,7 @@ transition: background-color 1s ease 0s; } .collectionfreeformview-mask { - mix-blend-mode: multiply; + mix-blend-mode: hard-light; background-color: rgba(0, 0, 0, 0.8); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7fd5d71fd..626538976 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -11,7 +11,7 @@ import ReactLoading from 'react-loading'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; -import { DocData, Height, Width } from '../../../../fields/DocSymbols'; +import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1)); panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1)); - zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1)); + zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1)); PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`); ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy(); getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); @@ -440,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; docDragData.droppedDocuments.forEach((d, i) => { - const layoutDoc = Doc.Layout(d); + const layoutDoc = d[DocLayout]; const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate); if (this.Document._currentFrame !== undefined) { CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); @@ -1440,9 +1440,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { - const layoutdoc = Doc.Layout(doc); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); - const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height)); + const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height)); const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] }; if (scale !== undefined) { @@ -1489,9 +1488,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection * @returns whether the new text doc was created and added successfully */ createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => { - const textDoc = DocCast(textBox.Document.rootDocument, textBox.Document); + const textDoc = DocCast(textBox.Document); const newDoc = Doc.MakeCopy(textDoc, true); - newDoc['$' + Doc.LayoutFieldKey(newDoc, textBox._props.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style + newDoc['$' + Doc.LayoutFieldKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); DocumentView.SetSelectOnLoad(newDoc); @@ -1588,7 +1587,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } case undefined: case OpenWhere.lightbox: - if (this.layoutDoc._isLightbox) { + if (this.dataDoc.$isLightbox) { this._lightboxDoc = docs[0]; return true; } @@ -1600,14 +1599,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection 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); const childDoc = pair.layout; - const childDocLayout = Doc.Layout(childDoc); const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed - const { z, zIndex } = childDoc; + const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { z, zIndex, stroke_isInkMask } = childDoc; const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber); const { x, y, autoDim, _width, _height, opacity, _rotation } = layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value) - ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } + ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber); // prettier-ignore const rotation = Cast(_rotation,'number', @@ -1625,8 +1624,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection zIndex: Cast(zIndex, 'number'), width: _width, height: _height, - transition: StrCast(childDocLayout.dataTransition), - showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags), + transition: StrCast(childDoc.dataTransition), + showTags: BoolCast(childDoc.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags), pointerEvents: Cast(childDoc.pointerEvents, 'string', null), pair, replica: '', @@ -1719,7 +1718,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection elements.push({ ele: this.getChildDocView(entry[1]), bounds: entry[1].opacity === 0 ? { payload: undefined, type: '', ...entry[1], width: 0, height: 0 } : { payload: undefined, type: '', ...entry[1] }, - inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, + inkMask: BoolCast(entry[1].pair.layout?.$stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, }) ); diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx index b40189d76..c7aa410c6 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx @@ -11,7 +11,7 @@ import { emptyFunction } from '../../../../Utils'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { DocData } from '../../../../fields/DocSymbols'; import { List } from '../../../../fields/List'; -import { DocCast, ImageCast, NumCast, StrCast } from '../../../../fields/Types'; +import { DocCast, ImageCastToNameType, NumCast, StrCast } from '../../../../fields/Types'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; @@ -187,7 +187,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() { ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); })}> {FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => { - const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url.href.split('.') ?? ['-missing-', '.png']; + const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]) ?? ['-missing-', '.png']; return ( <div className="image-wrapper" diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx index c983d7c26..7c8ccb92d 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx +++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx @@ -10,7 +10,7 @@ import { imageUrlToBase64 } from '../../../../ClientUtils'; import { Utils, numberRange } from '../../../../Utils'; import { Doc, NumListCast, Opt } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; -import { ImageCast } from '../../../../fields/Types'; +import { ImageCastToNameType, ImageCastWithSuffix } from '../../../../fields/Types'; import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; @@ -165,8 +165,8 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() { const imageInfos = this._selectedImages.map(async doc => { if (!doc.$tags_chat) { - const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.'); - return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 => + const url = ImageCastWithSuffix(doc[Doc.LayoutFieldKey(doc)], '_o') ?? ''; + return imageUrlToBase64(url).then(hrefBase64 => !hrefBase64 ? undefined : gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels => ({ doc, labels }))) ; // prettier-ignore @@ -199,13 +199,11 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() { groupImagesInBox = action(async () => { this.startLoading(); - for (const doc of this._selectedImages) { - for (let index = 0; index < (doc.$tags_chat as List<string>).length; index++) { - const label = (doc.$tags_chat as List<string>)[index]; - const embedding = await gptGetEmbedding(label); - doc[`$tags_embedding_${index + 1}`] = new List<number>(embedding); - } - } + await Promise.all( + this._selectedImages + .map(doc => ({ doc, labels: doc.$tags_chat as List<string> })) + .map(({ doc, labels }) => labels.map((label, index) => gptGetEmbedding(label).then(embedding => (doc[`$tags_embedding_${index + 1}`] = new List<number>(embedding))))) + ); const labelToEmbedding = new Map<string, number[]>(); // Create embeddings for the labels. @@ -312,7 +310,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() { {this._displayImageInformation ? ( <div className="image-information-list"> {this._selectedImages.map(doc => { - const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.'); + const [name, type] = ImageCastToNameType(doc[Doc.LayoutFieldKey(doc)]); return ( <div className="image-information" style={{ borderColor: SettingsManager.userColor }} key={Utils.GenerateGuid()}> <img diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index eaa8826ed..5fbf72f39 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -8,7 +8,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; -import { Cast, NumCast, StrCast } from '../../../../fields/Types'; +import { Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { GetEffectiveAcl } from '../../../../fields/util'; import { DocUtils } from '../../../documents/DocUtils'; @@ -18,7 +18,6 @@ import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManag import { Transform } from '../../../util/Transform'; import { UndoManager, undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; -import { MainView } from '../../MainView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { MarqueeViewBounds } from '../../PinFuncs'; import { PreviewCursor } from '../../PreviewCursor'; @@ -442,7 +441,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps if (groupButton) { this._selectedDocs = this.marqueeSelect(false, DocumentType.IMG); ImageLabelBoxData.Instance.setData(this._selectedDocs); - MainView.Instance.expandFlyout(groupButton); + ScriptCast(groupButton.onClick)?.script.run({ this: groupButton }); } }; @@ -591,8 +590,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps marqueeSelect = (selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) => { const selection: Doc[] = []; const selectFunc = (doc: Doc) => { - const layoutDoc = Doc.Layout(doc); - const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) }; + const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }; if (!this._lassoFreehand) { intersectRect(bounds, this.Bounds) && selection.push(doc); } else { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index e7f1de7f1..a7603a45b 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -182,17 +182,17 @@ export class CollectionGridView extends CollectionSubView() { isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined); /** * - * @param layout + * @param childLayout * @param dxf the x- and y-translations of the decorations box as a transform i.e. this.lookupIndividualTransform * @param width * @param height * @returns the `ContentFittingDocumentView` of the node */ - getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => ( + getDisplayDoc = (childLayout: Doc, dxf: () => Transform, width: () => number, height: () => number) => ( <DocumentView {...this._props} - Document={layout} - TemplateDataDocument={layout.resolvedDataDoc as Doc} + Document={childLayout} + TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined} NativeWidth={returnZero} NativeHeight={returnZero} fitWidth={this._props.childLayoutFitWidth} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 8aae24df0..adaceb053 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,14 +1,17 @@ +import { Button, IconButton } from '@dash/components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Button, IconButton } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; +import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { FaChevronRight } from 'react-icons/fa'; import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { DragManager } from '../../../util/DragManager'; +import { dropActionType } from '../../../util/DropActionTypes'; import { SettingsManager } from '../../../util/SettingsManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; @@ -16,8 +19,6 @@ import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView' import './CollectionMulticolumnView.scss'; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; -import { dropActionType } from '../../../util/DropActionTypes'; -import { SnappingManager } from '../../../util/SnappingManager'; interface WidthSpecifier { magnitude: number; @@ -197,16 +198,14 @@ export class CollectionMulticolumnView extends CollectionSubView() { * documents before the target. */ private lookupIndividualTransform = (layout: Doc) => { - if (this.columnUnitLength === undefined) { - return Transform.Identity(); // we're still waiting on promises to resolve - } - let offset = 0; - // eslint-disable-next-line no-restricted-syntax - for (const { layout: candidate } of this.childLayoutPairs) { - if (candidate === layout) { - return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0); + if (this.columnUnitLength !== undefined) { + let offset = 0; + for (const { layout: candidate } of this.childLayoutPairs) { + if (candidate === layout) { + return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0); + } + offset += this.lookupPixels(candidate) + resizerWidth; } - offset += this.lookupPixels(candidate) + resizerWidth; } return Transform.Identity(); }; @@ -262,50 +261,51 @@ export class CollectionMulticolumnView extends CollectionSubView() { ? true : undefined; }; - getDisplayDoc = (childLayout: Doc) => { - const width = () => this.lookupPixels(childLayout); - const height = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0); - const dxf = () => - this.lookupIndividualTransform(childLayout) + childHeight = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0); + childWidth = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc)); + childXf = computedFn( + (childDoc: Doc) => () => + this.lookupIndividualTransform(childDoc) .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin)) - .scale(this._props.NativeDimScaling?.() || 1); - return ( - <DocumentView - Document={childLayout} - TemplateDataDocument={childLayout.resolvedDataDoc as Doc} - styleProvider={this._props.styleProvider} - containerViewPath={this.childContainerViewPath} - LayoutTemplate={this._props.childLayoutTemplate} - LayoutTemplateString={this._props.childLayoutString} - renderDepth={this._props.renderDepth + 1} - PanelWidth={width} - PanelHeight={height} - rootSelected={this.rootSelected} - dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType} - onClickScript={this.onChildClickHandler} - onDoubleClickScript={this.onChildDoubleClickHandler} - suppressSetHeight - ScreenToLocalTransform={dxf} - isContentActive={this.isChildContentActive} - isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} - hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)} - hideDecorationTitle={this._props.childHideDecorationTitle} - fitContentsToBox={this._props.fitContentsToBox} - focus={this._props.focus} - childFilters={this.childDocFilters} - childFiltersByRanges={this.childDocRangeFilters} - searchFilterDocs={this.searchFilterDocs} - dontRegisterView={this._props.dontRegisterView} - addDocument={this._props.addDocument} - moveDocument={this._props.moveDocument} - removeDocument={this._props.removeDocument} - whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} - addDocTab={this._props.addDocTab} - pinToPres={this._props.pinToPres} - dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'} - /> - ); - }; + .scale(this._props.NativeDimScaling?.() || 1) + ); + getDisplayDoc = (childLayout: Doc) => ( + <DocumentView + Document={childLayout} + TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined} + styleProvider={this._props.styleProvider} + containerViewPath={this.childContainerViewPath} + LayoutTemplate={this._props.childLayoutTemplate} + LayoutTemplateString={this._props.childLayoutString} + renderDepth={this._props.renderDepth + 1} + PanelWidth={this.childWidth(childLayout)} + PanelHeight={this.childHeight} + rootSelected={this.rootSelected} + dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType} + onClickScript={this.onChildClickHandler} + onDoubleClickScript={this.onChildDoubleClickHandler} + suppressSetHeight + ScreenToLocalTransform={this.childXf(childLayout)} + isContentActive={this.isChildContentActive} + isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} + hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)} + hideDecorationTitle={this._props.childHideDecorationTitle} + fitContentsToBox={this._props.fitContentsToBox} + focus={this._props.focus} + childFilters={this.childDocFilters} + childFiltersByRanges={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + dontRegisterView={this._props.dontRegisterView} + addDocument={this._props.addDocument} + moveDocument={this._props.moveDocument} + removeDocument={this._props.removeDocument} + whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} + addDocTab={this._props.addDocTab} + pinToPres={this._props.pinToPres} + dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'} + /> + ); + /** * @returns the resolved list of rendered child documents, displayed * at their resolved pixel widths, each separated by a resizer. @@ -315,7 +315,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { const collector: JSX.Element[] = []; this.childLayouts.forEach((layout, i) => { collector.push( - // eslint-disable-next-line react/no-array-index-key <Tooltip title={'Doc: ' + StrCast(layout.title)} key={'wrapper' + i}> <div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}> {this.getDisplayDoc(layout)} @@ -327,7 +326,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { </Tooltip>, <ResizeBar width={resizerWidth} - // eslint-disable-next-line react/no-array-index-key key={'resizer' + i} styleProvider={this._props.styleProvider} isContentActive={this._props.isContentActive} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index bda8e91ac..27c41583c 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -1,5 +1,6 @@ import { action, computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; +import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { Doc, DocListCast } from '../../../../fields/Doc'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; @@ -178,16 +179,14 @@ export class CollectionMultirowView extends CollectionSubView() { * documents before the target. */ private lookupIndividualTransform = (layout: Doc) => { - if (this.rowUnitLength === undefined) { - return Transform.Identity(); // we're still waiting on promises to resolve - } - let offset = 0; - // eslint-disable-next-line no-restricted-syntax - for (const { layout: candidate } of this.childLayoutPairs) { - if (candidate === layout) { - return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1)); + if (this.rowUnitLength !== undefined) { + let offset = 0; + for (const { layout: candidate } of this.childLayoutPairs) { + if (candidate === layout) { + return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1)); + } + offset += this.lookupPixels(candidate) + resizerHeight; } - offset += this.lookupPixels(candidate) + resizerHeight; } return Transform.Identity(); // type coersion, this case should never be hit }; @@ -243,49 +242,51 @@ export class CollectionMultirowView extends CollectionSubView() { ? true : undefined; }; - getDisplayDoc = (layout: Doc) => { - const height = () => this.lookupPixels(layout); - const width = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0); - const dxf = () => - this.lookupIndividualTransform(layout) + childHeight = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc)); + childWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0); + childXf = computedFn( + (childDoc: Doc) => () => + this.lookupIndividualTransform(childDoc) .translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin)) - .scale(this._props.NativeDimScaling?.() || 1); - return ( - <DocumentView - Document={layout} - TemplateDataDocument={layout.resolvedDataDoc as Doc} - styleProvider={this._props.styleProvider} - containerViewPath={this.childContainerViewPath} - LayoutTemplate={this._props.childLayoutTemplate} - LayoutTemplateString={this._props.childLayoutString} - renderDepth={this._props.renderDepth + 1} - PanelWidth={width} - PanelHeight={height} - rootSelected={this.rootSelected} - dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType} - onClickScript={this.onChildClickHandler} - onDoubleClickScript={this.onChildDoubleClickHandler} - ScreenToLocalTransform={dxf} - isContentActive={this.isChildContentActive} - isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} - hideResizeHandles={!!(layout.layout_fitWidth || this._props.childHideResizeHandles)} - hideDecorationTitle={this._props.childHideDecorationTitle} - fitContentsToBox={this._props.fitContentsToBox} - focus={this._props.focus} - childFilters={this.childDocFilters} - childFiltersByRanges={this.childDocRangeFilters} - searchFilterDocs={this.searchFilterDocs} - dontRegisterView={this._props.dontRegisterView} - addDocument={this._props.addDocument} - moveDocument={this._props.moveDocument} - removeDocument={this._props.removeDocument} - whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} - addDocTab={this._props.addDocTab} - pinToPres={this._props.pinToPres} - dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'} - /> - ); - }; + .scale(this._props.NativeDimScaling?.() || 1) + ); + + getDisplayDoc = (childLayout: Doc) => ( + <DocumentView + Document={childLayout} + TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined} + styleProvider={this._props.styleProvider} + containerViewPath={this.childContainerViewPath} + LayoutTemplate={this._props.childLayoutTemplate} + LayoutTemplateString={this._props.childLayoutString} + renderDepth={this._props.renderDepth + 1} + PanelWidth={this.childWidth} + PanelHeight={this.childHeight(childLayout)} + rootSelected={this.rootSelected} + dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType} + onClickScript={this.onChildClickHandler} + onDoubleClickScript={this.onChildDoubleClickHandler} + ScreenToLocalTransform={this.childXf(childLayout)} + isContentActive={this.isChildContentActive} + isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} + hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)} + hideDecorationTitle={this._props.childHideDecorationTitle} + fitContentsToBox={this._props.fitContentsToBox} + focus={this._props.focus} + childFilters={this.childDocFilters} + childFiltersByRanges={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + dontRegisterView={this._props.dontRegisterView} + addDocument={this._props.addDocument} + moveDocument={this._props.moveDocument} + removeDocument={this._props.removeDocument} + whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} + addDocTab={this._props.addDocTab} + pinToPres={this._props.pinToPres} + dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'} + /> + ); + /** * @returns the resolved list of rendered child documents, displayed * at their resolved pixel widths, each separated by a resizer. diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 05670562e..5803acca0 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -200,7 +200,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._props.setContentViewBox?.(this); document.addEventListener('keydown', this.onKeyDown); - Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); + Object.entries(this._documentOptions).forEach(pair => this.fieldInfos.set(pair[0], pair[1] as FInfo)); this._keysDisposer = observe( this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>, change => { diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx index daffdf1f5..9ad94cb31 100644 --- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx +++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx @@ -85,6 +85,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro () => this._editing, editing => { if (editing) { + this.setContent((this._unrenderedContent = this._props.GetValue() ?? '')); this.setupRefSelect(this.refSelectConditionMet); } else { this._overlayDisposer?.(); @@ -99,7 +100,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro () => this._props.GetValue(), fieldVal => { this._unrenderedContent = fieldVal ?? ''; - this.finalizeEdit(false, false, false); + this._editing && this.finalizeEdit(false, false, false); } ); } diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index e6fe46638..173984dc7 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -32,6 +32,7 @@ import { FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaCellField } from './SchemaCellField'; +import { DocLayout } from '../../../../fields/DocSymbols'; /** * SchemaTableCells make up the majority of the visual representation of the SchemaView. @@ -104,13 +105,14 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro public static renderProps(props: SchemaTableCellProps) { const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props; let protoCount = 0; - let doc: Doc | undefined = Document; + const layoutDoc = fieldKey.startsWith('_') ? Document[DocLayout] : Document; + let doc = Document; while (doc) { if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break; protoCount++; doc = DocCast(doc.proto); } - const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells + const color = layoutDoc !== Document ? 'red' : protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells const textDecoration = ''; const fieldProps: FieldViewProps = { childFilters: returnEmptyFilter, @@ -130,7 +132,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro focus: emptyFunction, addDocTab: SchemaTableCell.addFieldDoc, pinToPres: returnZero, - Document: DocCast(Document.rootDocument, Document), + Document: Document, fieldKey: fieldKey, PanelWidth: columnWidth, PanelHeight: props.rowHeight, @@ -175,7 +177,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro const inQuotes = (strField: string) => { return (strField.startsWith('`') && strField.endsWith('`')) || (strField.startsWith("'") && strField.endsWith("'")) || (strField.startsWith('"') && strField.endsWith('"')); }; - if (!inQuotes(this._submittedValue) && inQuotes(modField)) modField = modField.substring(1, modField.length - 1); + const submittedValue = this._submittedValue.startsWith(eqSymbol) ? this._submittedValue.slice(eqSymbol.length) : this._submittedValue; + if (!inQuotes(submittedValue) && inQuotes(modField)) modField = modField.substring(1, modField.length - 1); return eqSymbol + modField; }; @@ -211,8 +214,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro this._props.finishEdit?.(); return true; } - const hasNoLayout = Doc.IsDataProto(fieldProps.Document) ? true : undefined; // the "delegate" is a a data document so never write to it's proto - const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, hasNoLayout); + const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey, value); this._submittedValue = value; this._props.finishEdit?.(); return ret; |
