diff options
Diffstat (limited to 'src')
33 files changed, 143 insertions, 158 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7d9cb36c5..0b15e3add 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -192,7 +192,7 @@ export class CurrentUserUtils { {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", }); }; const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 24, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts + textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts }); const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 4400b3576..6aba4a042 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,6 +1,6 @@ import { action, computed, makeObservable, observable } from 'mobx'; import * as React from 'react'; -import { emptyPath, returnFalse } from '../../Utils'; +import { returnFalse } from '../../Utils'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols'; @@ -41,7 +41,7 @@ export function DocComponent<P extends DocComponentProps>() { } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { - return this._props.Document[DocData] as Doc; + return this._props.Document[DocData]; } } return Component; @@ -55,16 +55,12 @@ export interface ViewBoxBaseProps { Document: Doc; TemplateDataDocument?: Doc; DocumentView?: () => DocumentView; - containerViewPath?: () => DocumentView[]; fieldKey: string; isSelected: () => boolean; isContentActive: () => boolean | undefined; ScreenToLocalTransform: () => Transform; renderDepth: number; } -function returnEmptyDocViewList() { - return [] as DocumentView[]; -} export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { class Component extends ObservableReactComponent<React.PropsWithChildren<P>> { ScreenToLocalBoxXf = () => this._props.ScreenToLocalTransform(); @@ -72,12 +68,6 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { get DocumentView() { return this._props.DocumentView; } - get docViewPath() { - return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList; - } - get containerViewPath() { - return this._props.containerViewPath; - } //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then get Document() { return this._props.Document; @@ -132,12 +122,6 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() get DocumentView() { return this._props.DocumentView; } - get docViewPath() { - return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList; - } - get containerViewPath() { - return this.DocumentView?.().containerViewPath ?? returnEmptyDocViewList; - } //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document() { return this._props.Document; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c96253405..8db972ef0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -344,10 +344,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora e, returnFalse, // don't care about move or up event, emptyFunction, // just care about whether we get a click event - e => UndoManager.RunInBatch( - () => SelectionManager.Docs.forEach(doc => - doc._pointerEvents = (doc._lockedPosition = !doc._lockedPosition)? 'none' : undefined ), - 'toggleBackground' ) // prettier-ignore + e => UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => Doc.toggleLockedPosition(doc)), 'toggleBackground') ); e.stopPropagation(); }; @@ -530,7 +527,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora doc.xPadding = NumCast(doc.xPadding) * scale.x; doc.yPadding = NumCast(doc.yPadding) * scale.y; } else { - const refCent = docView.screenToContentsTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) + const refCent = docView.screenToViewTransform().transformPoint(refPt[0], refPt[1]); // fixed reference point for resize (ie, a point that doesn't move) const [nwidth, nheight] = [docView.nativeWidth, docView.nativeHeight]; const [initWidth, initHeight] = [NumCast(doc._width, 1), NumCast(doc._height)]; @@ -689,7 +686,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora ); const bounds = this.ClippedBounds; - const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView; + const useLock = bounds.r - bounds.x > 135; const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? const rotation = SelectionManager.Views.length == 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cd6aeea41..d700ea020 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -56,13 +56,11 @@ import { CollectionLinearView } from './collections/collectionLinear'; import { LinkMenu } from './linking/LinkMenu'; import { AudioBox } from './nodes/AudioBox'; import { DocButtonState } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; -import { MapBox } from './nodes/MapBox/MapBox'; -import { RadialMenu } from './nodes/RadialMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @@ -898,6 +896,7 @@ export class MainView extends ObservableReactComponent<{}> { <div className="mainView-docButtons" style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor }} ref={this._docBtnRef}> <CollectionLinearView Document={Doc.MyDockedBtns} + docViewPath={returnEmptyDocViewList} fieldKey="data" dropAction="embed" styleProvider={DefaultStyleProvider} @@ -906,7 +905,6 @@ export class MainView extends ObservableReactComponent<{}> { isAnyChildContentActive={returnFalse} isContentActive={emptyFunction} isSelected={returnFalse} - containerViewPath={returnEmptyDoclist} moveDocument={this.moveButtonDoc} addDocument={this.addButtonDoc} addDocTab={DocumentViewInternal.addDocTabFunc} @@ -1021,7 +1019,6 @@ export class MainView extends ObservableReactComponent<{}> { <PreviewCursor /> <TaskCompletionBox /> <ContextMenu /> - <RadialMenu /> <AnchorMenu /> <MapAnchorMenu /> <DirectionsAnchorMenu /> diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index cfdc648b4..d0516336c 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -66,7 +66,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP const savedAnnoMap = savedAnnotations?.values() && Array.from(savedAnnotations?.values()).length ? savedAnnotations : this.props.savedAnnotations(); if (savedAnnoMap.size === 0) return undefined; const savedAnnos = Array.from(savedAnnoMap.values())[0]; - const doc = this.props.docView().Document; + const doc = this.props.Document; const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1); if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) { const anno = savedAnnos[0]; @@ -227,7 +227,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP dragComplete: e => { if (!e.aborted && e.linkDocument) { Doc.GetProto(e.linkDocument).link_relationship = 'cropped image'; - Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().Document.title; + Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.Document.title; Doc.GetProto(e.linkDocument).link_displayLine = false; } }, @@ -256,7 +256,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP // configure and show the annotation/link menu if a the drag region is big enough // copy the temporary marquee to allow for multiple selections (not currently available though). const copy = document.createElement('div'); - const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().Document._freeform_scale, 1); + const scale = (this.props.scaling?.() || 1) * NumCast(this.props.Document._freeform_scale, 1); ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any])); copy.className = 'marqueeAnnotator-annotationBox'; copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px'; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 491a72c08..1cb5ff249 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -20,7 +20,6 @@ import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; -import { DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; import { PropertiesView } from './PropertiesView'; @@ -51,21 +50,7 @@ export enum StyleProp { } function toggleLockedPosition(doc: Doc) { - UndoManager.RunInBatch( - () => - runInAction(() => { - doc._lockedPosition = !doc._lockedPosition; - doc._pointerEvents = doc._lockedPosition ? 'none' : undefined; - }), - 'toggleBackground' - ); -} - -export function testDocProps(toBeDetermined: any): toBeDetermined is DocumentViewProps { - return toBeDetermined?.isContentActive ? toBeDetermined : undefined; -} -export function testFieldProps(toBeDetermined: any): toBeDetermined is FieldViewProps { - return toBeDetermined?.isContentActive ? toBeDetermined : undefined; + UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground'); } export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { @@ -82,19 +67,17 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { // export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any { const remoteDocHeader = 'author;author_date;noMargin'; - const docProps = testDocProps(props) ? props : undefined; - const fieldProps = testFieldProps(props) ? props : undefined; const isCaption = property.includes(':caption'); const isAnchor = property.includes(':anchor'); const isNonTransparent = property.includes(':nonTransparent'); const isNonTransparentLevel = isNonTransparent ? Number(property.replace(/.*:nonTransparent([0-9]+).*/, '$1')) : 0; // property.includes(':nonTransparent'); const isContent = property.includes(':content'); const isAnnotated = property.includes(':annotated'); - const isInk = () => doc?._layout_isSvg && !docProps?.LayoutTemplateString; + const isInk = () => doc?._layout_isSvg && !props?.LayoutTemplateString; const isOpen = property.includes(':open'); const isEmpty = property.includes(':empty'); const boxBackground = property.includes(':box'); - const fieldKey = fieldProps?.fieldKey ? fieldProps.fieldKey + '_' : isCaption ? 'caption_' : ''; + const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : ''; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)); @@ -137,20 +120,19 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, return undefined; case StyleProp.DocContents:return undefined; case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey'; - case StyleProp.Opacity: return docProps?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); + case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._text_fontSize, StrCast(Doc.UserDoc().fontSize))); case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily))); case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight))); case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent')); case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption); - case StyleProp.TitleHeight: - return (props?.DocumentView?.().screenToViewTransform().Scale ?? 1) * NumCast(Doc.UserDoc().headerHeight,30) + case StyleProp.TitleHeight:return (props?.ScreenToLocalTransform().Scale ?? 1) * NumCast(Doc.UserDoc().headerHeight,30); case StyleProp.ShowTitle: return ( (doc && - !docProps?.LayoutTemplateString && + !props?.LayoutTemplateString && !doc.presentation_targetDoc && - !docProps?.LayoutTemplateString?.includes(KeyValueBox.name) && + !props?.LayoutTemplateString?.includes(KeyValueBox.name) && props?.layout_showTitle?.() !== '' && StrCast( doc._layout_showTitle, @@ -169,7 +151,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, if (doc?.type === DocumentType.FONTICON) return SettingsManager.userColor; const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; - const parView = props?.DocumentView?.(); const backColor = backgroundCol(); return backColor ? lightOrDark(backColor) : undefined; case StyleProp.BorderRounding: @@ -247,7 +228,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, doc?.layout_boxShadow, doc?._type_collection === CollectionViewType.Pile ? '4px 4px 10px 2px' - : lockedPosition() || doc?.isGroup || docProps?.LayoutTemplateString + : lockedPosition() || doc?.isGroup || props?.LayoutTemplateString ? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) : `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}` ); @@ -257,10 +238,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, default: return doc.z ? `#9c9396 ${StrCast(doc?.layout_boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow - : props?.DocumentView?.().containerViewPath?.().lastElement()?.Document._freeform_useClusters - ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + : props?.containerViewPath?.().lastElement()?.Document._freeform_useClusters + ? `${backgroundCol()} ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (props?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent : NumCast(doc.group, -1) !== -1 - ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + ? `gray ${StrCast(doc.layout_boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (props?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent : lockedPosition() ? undefined // if it's a background & has a cluster color, make the shadow spread really big : StrCast(doc.layout_fieldKey).includes('_inline') // if doc is an inline document in a text box @@ -271,8 +252,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, } } case StyleProp.PointerEvents: - if (StrCast(doc?.pointerEvents) && !docProps?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc - if (docProps?.DocumentView?.()._props.LayoutTemplateString?.includes(KeyValueBox.name)) return 'all'; + if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc + if (props?.LayoutTemplateString?.includes(KeyValueBox.name)) return 'all'; if (SnappingManager.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all'; if (props?.pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; @@ -281,22 +262,17 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, if (props?.isDocumentActive?.() && !props.treeViewDoc) return isInk() ? 'visiblePainted' : 'all'; return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active case StyleProp.Decorations: - const lock = () => { - if (props?.DocumentView?.().containerViewPath?.().lastElement()?.Document?._type_collection === CollectionViewType.Freeform) { - return doc?.pointerEvents !== 'none' ? null : ( + const lock = () => doc?.pointerEvents !== 'none' ? null : ( <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> <FontAwesomeIcon icon='lock' size="lg" /> </div> ); - } - }; const filter = () => { - const docView = props?.DocumentView?.(); const dashView = untracked(() => DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard)); const showFilterIcon = StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length ? 'green' // #18c718bd' //'hasFilter' - : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragDocsFilter).length || docProps?.childFiltersByRanges().length + : props?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragDocsFilter).length || props?.childFiltersByRanges().length ? 'orange' //'inheritsFilter' : undefined; return !showFilterIcon ? null : ( @@ -327,7 +303,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>, "this view inherits filters from one of its parents"} color={SettingsManager.userColor} background={showFilterIcon} - items={[ ...(dashView ? [dashView]: []), ...(docView?.containerViewPath?.()??[]), ...(docView ? [docView]:[])] + items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[])] .filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) .map(dv => ({ text: StrCast(dv?.Document.title), diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 92c8e1256..363db8850 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -238,7 +238,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { PanelWidth={width} PanelHeight={height} styleProvider={this.styleProvider} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} layout_fitWidth={this._props.childLayoutFitWidth} isContentActive={emptyFunction} onKey={this.onKeyDown} @@ -407,10 +407,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { @undoBatch onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { - const docView = fieldProps.DocumentView?.(); - if (docView && (e.ctrlKey || docView.Document._createDocOnCR) && ['Enter'].includes(e.key)) { + if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) { e.stopPropagation?.(); - const newDoc = Doc.MakeCopy(docView.Document, true); + const newDoc = Doc.MakeCopy(fieldProps.Document, true); Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SetSelectOnLoad(newDoc); return this.addDocument?.(newDoc); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 0a0218124..9384b7088 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -278,16 +278,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection }; @undoBatch onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { - const docView = fieldProps.DocumentView?.(); - if (docView && ['Enter'].includes(e.key) && e.ctrlKey) { + if (['Enter'].includes(e.key) && e.ctrlKey) { e.stopPropagation?.(); const below = !e.altKey && e.key !== 'Tab'; - const layout_fieldKey = StrCast(docView.LayoutFieldKey); - const newDoc = Doc.MakeCopy(docView.Document, true); - const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)]; + const layout_fieldKey = StrCast(fieldProps.fieldKey); + const newDoc = Doc.MakeCopy(fieldProps.Document, true); + const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)]; newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined; - if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) { - newDoc[layout_fieldKey] = docView.Document[layout_fieldKey]; + if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) { + newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SetSelectOnLoad(newDoc); @@ -327,7 +326,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection PanelHeight={panelHeight} pointerEvents={this.DocumentView?.()._props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView) styleProvider={this.styleProvider} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} layout_fitWidth={this.childFitWidth} isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive} onKey={this.onKeyDown} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 2e0a75281..d7c42a975 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -25,6 +25,7 @@ import { ViewBoxBaseComponent, ViewBoxBaseProps } from '../DocComponent'; import { LoadingBox } from '../nodes/LoadingBox'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { CollectionView, CollectionViewProps } from './CollectionView'; +import { returnEmptyDocViewList } from '../nodes/DocumentView'; export interface SubCollectionViewProps extends CollectionViewProps { isAnyChildContentActive: () => boolean; @@ -69,6 +70,9 @@ export function CollectionSubView<X>(moreProps?: X) { : Doc.GetProto(this._props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template } + get childContainerViewPath() { + return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList; + } // this returns whether either the collection is selected, or the template that it is part of is selected rootSelected = () => this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && this._props.rootSelected?.()); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 2441c67e6..f00e42360 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -29,7 +29,7 @@ import { LightboxView } from '../LightboxView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; import { Colors } from '../global/globalEnums'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView'; import { KeyValueBox } from '../nodes/KeyValueBox'; import { DashFieldView } from '../nodes/formattedText/DashFieldView'; import { PinProps, PresBox, PresMovement } from '../nodes/trails'; @@ -595,7 +595,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}> <CollectionFreeFormView Document={this._props.document} - containerViewPath={returnEmptyDoclist} + docViewPath={returnEmptyDocViewList} childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale isContentActive={emptyFunction} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 41720d0dd..9f40edee1 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -977,7 +977,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { hideResizeHandles={this.treeView.outlineMode} styleProvider={this.titleStyleProvider} onClickScriptDisable="never" // tree docViews have a script to show fields, etc. - containerViewPath={this.treeView.docViewPath} + containerViewPath={this.treeView.childContainerViewPath} treeViewDoc={this.treeView.Document} addDocument={undefined} addDocTab={this._props.addDocTab} @@ -1084,7 +1084,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> { ScreenToLocalTransform={this.docTransform} renderDepth={this._props.renderDepth + 1} treeViewDoc={this.treeView?.Document} - containerViewPath={this.treeView.docViewPath} + containerViewPath={this.treeView.childContainerViewPath} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3eaf1876c..c4358747b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1170,18 +1170,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @undoBatch onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { - const docView = fieldProps.DocumentView?.(); - if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { + if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { e.stopPropagation?.(); const below = !e.altKey && e.key !== 'Tab'; - const layout_fieldKey = StrCast(docView.LayoutFieldKey); - const newDoc = Doc.MakeCopy(docView.Document, true); - const dataField = docView.Document[Doc.LayoutFieldKey(newDoc)]; + const layout_fieldKey = StrCast(fieldProps.fieldKey); + const newDoc = Doc.MakeCopy(fieldProps.Document, true); + const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)]; newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined; - if (below) newDoc.y = NumCast(docView.Document.y) + NumCast(docView.Document._height) + 10; - else newDoc.x = NumCast(docView.Document.x) + NumCast(docView.Document._width) + 10; - if (layout_fieldKey !== 'layout' && docView.Document[layout_fieldKey] instanceof Doc) { - newDoc[layout_fieldKey] = docView.Document[layout_fieldKey]; + if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10; + else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10; + if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) { + newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey]; } Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SetSelectOnLoad(newDoc); diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 4f4c674ea..72bdb9b6b 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -189,7 +189,7 @@ export class CollectionLinearView extends CollectionSubView() { dontRegisterView={BoolCast(this.Document.childDontRegisterViews)} focus={emptyFunction} styleProvider={this._props.styleProvider} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} childFilters={this._props.childFilters} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 1fd514ccb..40b4151fe 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -259,7 +259,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { Document={childLayout} TemplateDataDocument={childLayout.resolvedDataDoc as Doc} styleProvider={this._props.styleProvider} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} LayoutTemplate={this._props.childLayoutTemplate} LayoutTemplateString={this._props.childLayoutString} renderDepth={this._props.renderDepth + 1} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 98e39cd36..04a4042f1 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -254,7 +254,7 @@ export class CollectionMultirowView extends CollectionSubView() { Document={layout} TemplateDataDocument={layout.resolvedDataDoc as Doc} styleProvider={this._props.styleProvider} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} LayoutTemplate={this._props.childLayoutTemplate} LayoutTemplateString={this._props.childLayoutString} renderDepth={this._props.renderDepth + 1} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 02131ae22..29d121974 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -210,8 +210,9 @@ border: 1px solid $medium-gray; overflow-x: hidden; overflow-y: auto; - padding: 5px; display: inline-flex; + padding: 0; + align-items: center; } .schema-row { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index e22a666c5..227274a53 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -971,7 +971,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV <DocumentView key={this._props.doc[Id]} {...this._props.schema._props} - containerViewPath={this._props.schema.docViewPath} + containerViewPath={this._props.schema.childContainerViewPath} LayoutTemplate={this._props.schema._props.childLayoutTemplate} LayoutTemplateString={SchemaRowBox.LayoutString(this._props.schema._props.fieldKey, this._props.index)} Document={this._props.doc} diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 8e1fba699..3f94e7fc1 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -3,7 +3,7 @@ import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; -import { CgClose } from 'react-icons/cg'; +import { CgClose, CgLock, CgLockUnlock } from 'react-icons/cg'; import { FaExternalLinkAlt } from 'react-icons/fa'; import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; @@ -121,6 +121,22 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRo pointerEvents: !this._props.isContentActive() ? 'none' : undefined, }}> <IconButton + tooltip="whether document interations are enabled" + icon={this.Document._lockedPosition ? <CgLockUnlock size="12px" /> : <CgLock size="12px" />} + size={Size.XSMALL} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(e => { + e.stopPropagation(); + Doc.toggleLockedPosition(this.Document); + }, 'Delete Row') + ) + }></IconButton> + <IconButton tooltip="close" icon={<CgClose size={'16px'} />} size={Size.XSMALL} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 83cabf355..73709c17e 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,6 +12,7 @@ import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { DocComponent } from '../DocComponent'; +import { ObservableReactComponent } from '../ObservableReactComponent'; import { StyleProp } from '../StyleProvider'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import './CollectionFreeFormDocumentView.scss'; @@ -37,7 +38,7 @@ export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentView CollectionFreeFormView: CollectionFreeFormView; } @observer -export class CollectionFreeFormDocumentViewWrapper extends DocComponent<CollectionFreeFormDocumentViewWrapperProps & { fieldKey: string }>() implements CollectionFreeFormDocumentViewProps { +export class CollectionFreeFormDocumentViewWrapper extends ObservableReactComponent<CollectionFreeFormDocumentViewWrapperProps> implements CollectionFreeFormDocumentViewProps { constructor(props: any) { super(props); makeObservable(this); @@ -59,6 +60,9 @@ export class CollectionFreeFormDocumentViewWrapper extends DocComponent<Collecti CollectionFreeFormView = this.props.CollectionFreeFormView; // needed for type checking RenderCutoffProvider = this.props.RenderCutoffProvider; // needed for type checking + get Document() { + return this._props.Document; + } @computed get WrapperKeys() { return Object.keys(this).filter(key => key.startsWith('w_')).map(key => key.replace('w_', '')) .map(key => ({upper:key, lower:key[0].toLowerCase() + key.substring(1)})); // prettier-ignore @@ -227,7 +231,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF }); } - @action public float = () => { + float = () => { const topDoc = this.Document; const containerDocView = this._props.containerViewPath?.().lastElement(); const screenXf = containerDocView?.screenToContentsTransform(); @@ -253,8 +257,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF nudge = (x: number, y: number) => { const [locX, locY] = this._props.ScreenToLocalTransform().transformDirection(x, y); - this._props.Document.x = this._props.w_X() + locX; - this._props.Document.y = this._props.w_Y() + locY; + this.Document.x = this._props.w_X() + locX; + this.Document.y = this._props.w_Y() + locY; }; screenToLocalTransform = () => this._props @@ -288,7 +292,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF zIndex: this._props.w_ZIndex?.(), display: this._props.w_Width?.() ? undefined : 'none', }}> - {this._props.RenderCutoffProvider(this._props.Document) ? ( + {this._props.RenderCutoffProvider(this.Document) ? ( <div style={{ position: 'absolute', width: this._props.PanelWidth(), height: this._props.PanelHeight(), background: 'lightGreen' }} /> ) : ( <DocumentView {...passOnProps} CollectionFreeFormDocumentView={this.returnThis} styleProvider={this.styleProvider} ScreenToLocalTransform={this.screenToLocalTransform} isGroupActive={this.isGroupActive} /> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index e707bb836..500eb52ac 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -11,7 +11,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import './ComparisonBox.scss'; -import { DocumentView } from './DocumentView'; +import { DocumentView, returnEmptyDocViewList } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { PinProps, PresBox } from './trails'; @@ -152,6 +152,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl if (property === StyleProp.PointerEvents) return 'none'; return this._props.styleProvider?.(doc, props, property); }; + @computed get childContainerViewPath() { + return this.DocumentView?.().docViewPath ?? returnEmptyDocViewList; + } moveDoc1 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_1'), true); moveDoc2 = (doc: Doc | Doc[], targetCol: Doc | undefined, addDoc: any) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc: Doc) => res && this.moveDoc(doc, addDoc, this.fieldKey + '_2'), true); remDoc1 = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((res, doc) => res && this.remDoc(doc, this.fieldKey + '_1'), true); @@ -178,7 +181,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl {...this._props} Document={targetDoc} TemplateDataDocument={undefined} - containerViewPath={this.docViewPath} + containerViewPath={this.childContainerViewPath} moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2} removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2} NativeWidth={returnZero} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 0d76959af..55859b92b 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -117,8 +117,6 @@ export class HTMLtag extends React.Component<HTMLtagProps> { @observer export class DocumentContentsView extends ObservableReactComponent< FieldViewProps & { - layout_fieldKey: string; - LayoutTemplateString?: string; onClick?: () => ScriptField; LayoutTemplate?: () => Opt<Doc>; } @@ -132,8 +130,8 @@ export class DocumentContentsView extends ObservableReactComponent< TraceMobx(); if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString; if (!this.layoutDoc) return '<p>awaiting layout</p>'; - if (this._props.layout_fieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString()); - const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layout_fieldKey ? this._props.layout_fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string'); + if (this._props.fieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString()); + const layout = Cast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.fieldKey ? this._props.fieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')], 'string'); if (layout === undefined) return this._props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(); if (typeof layout === 'string') return layout; return '<p>Loading layout</p>'; @@ -141,12 +139,12 @@ export class DocumentContentsView extends ObservableReactComponent< get layoutDoc() { // bcz: replaced this with below : is it correct? change was made to accommodate passing fieldKey's from a layout script - // const template: Doc = this._props.LayoutTemplate?.() || Doc.Layout(this._props.Document, this._props.layout_fieldKey ? Cast(this._props.Document[this._props.layout_fieldKey], Doc, null) : undefined); + // const template: Doc = this._props.LayoutTemplate?.() || Doc.Layout(this._props.Document, this._props.fieldKey ? Cast(this._props.Document[this._props.fieldKey], Doc, null) : undefined); const template: Doc = this._props.LayoutTemplate?.() || (this._props.LayoutTemplateString && this._props.Document) || - (this._props.layout_fieldKey && StrCast(this._props.Document[this._props.layout_fieldKey]) && this._props.Document) || - Doc.Layout(this._props.Document, this._props.layout_fieldKey ? Cast(this._props.Document[this._props.layout_fieldKey], Doc, null) : undefined); + (this._props.fieldKey && StrCast(this._props.Document[this._props.fieldKey]) && this._props.Document) || + Doc.Layout(this._props.Document, this._props.fieldKey ? Cast(this._props.Document[this._props.fieldKey], Doc, null) : undefined); return Doc.expandTemplateLayout(template, this._props.Document); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4e13b0ccb..444c300f3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -89,6 +89,9 @@ export enum OpenWhere { addRightKeyvalue = 'add:right:keyValue', } +export function returnEmptyDocViewList() { + return [] as DocumentView[]; +} export interface DocFocusOptions { willPan?: boolean; // determines whether to pan to target document willZoomCentered?: boolean; // determines whether to zoom in on target document. if zoomScale is 0, this just centers the document @@ -148,6 +151,7 @@ export interface DocComponentView { * */ export interface DocumentViewSharedProps { Document: Doc; + LayoutTemplateString?: string; TemplateDataDocument?: Doc; renderDepth: number; scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document @@ -161,6 +165,7 @@ export interface DocumentViewSharedProps { ignoreAutoHeight?: boolean; disableBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over. CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; + containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active setContentView?: (view: DocComponentView) => any; @@ -206,7 +211,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideLinkButton?: boolean; hideCaptions?: boolean; contentPointerEvents?: 'none' | 'all' | undefined; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents - LayoutTemplateString?: string; dontCenter?: 'x' | 'y' | 'xy'; childHideDecorationTitle?: boolean; childHideResizeHandles?: boolean; @@ -218,7 +222,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps { NativeWidth?: () => number; NativeHeight?: () => number; LayoutTemplate?: () => Opt<Doc>; - containerViewPath?: () => DocumentView[]; contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[]; onClick?: () => ScriptField; onDoubleClick?: () => ScriptField; @@ -235,13 +238,13 @@ export interface DocumentViewProps extends DocumentViewSharedProps { * these props correspond to things that the DocumentView creates and thus doesn't need to receive as a prop */ export interface DocumentViewInternalSharedProps { - DocumentView: () => DocumentView; select: (ctrlPressed: boolean, shiftPress?: boolean) => void; isSelected: () => boolean; + docViewPath: () => DocumentView[]; NativeDimScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal } export interface DocumentViewInternalProps extends DocumentViewProps, DocumentViewInternalSharedProps { - docViewPath: () => DocumentView[]; + docViewPublic: () => DocumentView; } @observer @@ -276,7 +279,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } // this makes mobx trace() statements more descriptive public get DocumentView() { - return this._props.DocumentView; + return this._props.docViewPublic; } public get ContentDiv() { @@ -426,15 +429,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } } - defaultRestoreTargetView = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => { - const targetMatch = - Doc.AreProtosEqual(anchor, this.Document) || // anchor is this document, so anchor's properties apply to this document - (DocCast(anchor)?.layout_unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.Document)) // the anchor is an layout_unrendered annotation on this document, so anchor properties apply to this document - ? true - : false; - return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; - }; - // switches text input focus to the title bar of the document (and displays the title bar if it hadn't been) setTitleFocus = () => { if (!StrCast(this.layoutDoc._layout_showTitle)) this.layoutDoc._layout_showTitle = 'title'; @@ -923,8 +917,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps }}> <DocumentContentsView key={1} - {...this._props} - fieldKey="" + {...this.styleProps} + fieldKey={this.finalLayoutKey} pointerEvents={this.contentPointerEvents} setContentView={this.setContentView} childFilters={this.childFilters} @@ -935,7 +929,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps rootSelected={this.rootSelected} onClick={this.onClickFunc} setTitleFocus={this.setTitleFocus} - layout_fieldKey={this.finalLayoutKey} /> {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints} </div> @@ -1699,7 +1692,7 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> { }}> <DocumentViewInternal {...this._props} - DocumentView={this.selfView} + docViewPublic={this.selfView} docViewPath={this.docViewPath} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss index 42e158584..0b195713d 100644 --- a/src/client/views/nodes/LabelBox.scss +++ b/src/client/views/nodes/LabelBox.scss @@ -18,6 +18,9 @@ display: inline-block; margin: auto; text-overflow: ellipsis; + > span { + max-height: 100%; // make sure top of text is in view, otherwise it would center on middle of large text span + } } .labelBox-params { @@ -29,4 +32,4 @@ width: 100%; background: lightgray; border: dimgray solid 1px; -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 934bce448..cc7c15a10 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,7 +1,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Field } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; @@ -43,7 +43,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp } @computed get Title() { - return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : typeof this.dataDoc[this.fieldKey] === 'string' ? StrCast(this.dataDoc[this.fieldKey]) : StrCast(this.Document.title); + return this.dataDoc.title_custom ? StrCast(this.Document.title) : this._props.label ? this._props.label : Field.toString(this.dataDoc[this.fieldKey] as Field); } protected createDropTarget = (ele: HTMLDivElement) => { diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 864c1955b..362a7def1 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -33,15 +33,16 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps & ViewBox } @computed get linkSource() { - return this.containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource); + return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource); } onPointerDown = (e: React.PointerEvent) => { const linkSource = this.linkSource; - linkSource && setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => { - if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false); - else this._props.select(false); - }); + linkSource && + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => { + if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false); + else this._props.select(false); + }); }; onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => { const cdiv = this._ref?.current?.parentElement; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 4221f464d..ba34255dc 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -29,12 +29,12 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get anchor1() { const anchor1 = DocCast(this.dataDoc.link_anchor_1); const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1; - return DocumentManager.Instance.getDocumentView(anchor_1, this.containerViewPath?.().lastElement()); + return DocumentManager.Instance.getDocumentView(anchor_1, this.DocumentView?.().containerViewPath?.().lastElement()); } @computed get anchor2() { const anchor2 = DocCast(this.dataDoc.link_anchor_2); const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2; - return DocumentManager.Instance.getDocumentView(anchor_2, this.containerViewPath?.().lastElement()); + return DocumentManager.Instance.getDocumentView(anchor_2, this.DocumentView?.().containerViewPath?.().lastElement()); } screenBounds = () => { if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) { @@ -66,7 +66,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { const a = (this.anchor1 ?? this.anchor2)!; const b = (this.anchor2 ?? this.anchor1)!; - const parxf = this.containerViewPath?.().lastElement().ComponentView as CollectionFreeFormView; + const parxf = this.DocumentView?.().containerViewPath?.().lastElement().ComponentView as CollectionFreeFormView; const this_xf = parxf?.screenToFreeformContentsXf ?? Transform.Identity; //this.ScreenToLocalTransform(); const a_invXf = a.screenToViewTransform().inverse(); const b_invXf = b.screenToViewTransform().inverse(); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 71c1b6a4c..7696a45a0 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -581,6 +581,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps }}> <PDFViewer {...this._props} + pdfBox={this} sidebarAddDoc={this.sidebarAddDocument} addDocTab={this.sidebarAddDocTab} layoutDoc={this.layoutDoc} diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 61576a498..4ae12505e 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -11,7 +11,7 @@ import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; import { Transform } from '../../../util/Transform'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DocFocusOptions, DocumentView } from '../DocumentView'; +import { DocFocusOptions, DocumentView, returnEmptyDocViewList } from '../DocumentView'; import { FormattedTextBox } from './FormattedTextBox'; var horizPadding = 3; // horizontal padding to container to allow cursor to show up on either side. @@ -178,6 +178,9 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn ele && (ele.style.backgroundColor = 'orange'); }; + @computed get childContainerViewPath() { + return this._textBox.DocumentView?.().docViewPath ?? returnEmptyDocViewList; + } componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.()); isContentActive = () => this._props.tbox._props.isContentActive() || this._props.tbox.isAnyChildContentActive?.(); @@ -207,7 +210,7 @@ export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewIn isDocumentActive={returnFalse} isContentActive={this.isContentActive} styleProvider={this._textBox._props.styleProvider} - containerViewPath={this._textBox.docViewPath ?? returnEmptyDoclist} + containerViewPath={this.childContainerViewPath} ScreenToLocalTransform={this.getDocTransform} addDocTab={this._textBox._props.addDocTab} pinToPres={returnFalse} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 3f2ab0ec5..dc9914637 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -148,7 +148,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi } createPivotForField = (e: React.MouseEvent) => { - let container = this._props.tbox.containerViewPath?.().lastElement(); + let container = this._props.tbox.DocumentView?.().containerViewPath?.().lastElement(); if (container) { const embedding = Doc.MakeEmbedding(container.Document); embedding._type_collection = CollectionViewType.Time; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 8cf01b9de..4bc79176e 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -50,7 +50,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { // the presentation view that renders this slide @computed get presBoxView() { - return this.containerViewPath?.().lastElement()?.ComponentView as PresBox; + return this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as PresBox; } // the presentation view document that renders this slide diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 89e5944f2..01590749e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -21,11 +21,12 @@ import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { LinkInfo } from '../nodes/LinkDocPreview'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { StyleProp, testDocProps } from '../StyleProvider'; +import { StyleProp } from '../StyleProvider'; import { AnchorMenu } from './AnchorMenu'; import { Annotation } from './Annotation'; import { GPTPopup } from './GPTPopup/GPTPopup'; import './PDFViewer.scss'; +import { PDFBox } from '../nodes/PDFBox'; const _global = (window /* browser */ || global) /* node */ as any; //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; @@ -33,6 +34,7 @@ const _global = (window /* browser */ || global) /* node */ as any; Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs'; interface IViewerProps extends FieldViewProps { + pdfBox: PDFBox; Document: Doc; dataDoc: Doc; layoutDoc: Doc; @@ -313,7 +315,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { this._ignoreScroll = false; if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio this._scrollTimer = setTimeout(() => { - DocUtils.MakeLinkToActiveAudio(() => this._props.DocumentView?.().ComponentView?.getAnchor!(true)!, false); + DocUtils.MakeLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); this._scrollTimer = undefined; }, 200); } @@ -419,7 +421,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { @action createTextAnnotation = (sel: Selection, selRange: Range) => { if (this._mainCont.current) { - this._mainCont.current.style.transform = `rotate(${NumCast(this._props.DocumentView!().screenToContentsTransform().RotateDeg)}deg)`; + this._mainCont.current.style.transform = `rotate(${NumCast(this._props.pdfBox.ScreenToLocalBoxXf().RotateDeg)}deg)`; const boundingRect = this._mainCont.current.getBoundingClientRect(); const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { @@ -499,9 +501,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [Utils.OpaqueBackgroundFilter])]; childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { - const docProps = testDocProps(props) ? props : undefined; if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; - const isInk = doc.layout_isSvg && !docProps?.LayoutTemplateString; + const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; return isInk ? 'visiblePainted' : 'all'; } return this._props.styleProvider?.(doc, props, property); @@ -589,7 +590,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { isNativeScaled={true} annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)} addDocument={this.addDocumentWrapper} - docView={this._props.DocumentView!} + docView={this._props.pdfBox.DocumentView!} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 188b2eba4..5828313c8 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -22,7 +22,7 @@ import { CollectionDockingView } from '../collections/CollectionDockingView'; import { CollectionLinearView } from '../collections/collectionLinear'; import { DashboardView } from '../DashboardView'; import { Colors } from '../global/globalEnums'; -import { DocumentViewInternal } from '../nodes/DocumentView'; +import { DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView'; import { DefaultStyleProvider } from '../StyleProvider'; import './TopBar.scss'; @@ -100,6 +100,7 @@ export class TopBar extends React.Component { <div className="collectionMenu-contMenuButtons" style={{ height: '100%' }}> <CollectionLinearView Document={selDoc} + docViewPath={returnEmptyDocViewList} fieldKey="data" dropAction="embed" styleProvider={DefaultStyleProvider} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d726f7064..9c2350bd0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1245,6 +1245,11 @@ export namespace Doc { : undefined; } + export function toggleLockedPosition(doc: Doc) { + doc._lockedPosition = !doc._lockedPosition; + doc._pointerEvents = doc._lockedPosition ? 'none' : undefined; + } + export function deiconifyView(doc: Doc) { StrCast(doc.layout_fieldKey).split('_')[1] === 'icon' && setNativeView(doc); } |