From c5c2c309cd88bbeb2f1b668cb040cffaac9c8470 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 4 Apr 2023 20:12:16 -0400 Subject: fixed using freeformview in sidebar of pdfs. fixed issues with dragging items out of schema and with selecting the schema view by clicking on headers. Fixed a lot of errors caused by using OmitKeys which masks type checking. fixed some pointerevent problems with treeview and freeformview --- .../views/collections/CollectionCarousel3DView.tsx | 7 +- .../views/collections/CollectionCarouselView.tsx | 8 +- .../collections/CollectionStackedTimeline.tsx | 54 +++----- .../views/collections/CollectionTreeView.tsx | 24 ++-- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 139 ++++++++++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 + .../collectionGrid/CollectionGridView.tsx | 9 +- .../collectionSchema/CollectionSchemaView.tsx | 99 +++------------ .../collectionSchema/SchemaColumnHeader.tsx | 10 +- 10 files changed, 146 insertions(+), 208 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 57ff1b292..a266c9207 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { OmitKeys, returnFalse, Utils } from '../../../Utils'; +import { returnFalse, returnZero, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { DocumentView } from '../nodes/DocumentView'; import { StyleProp } from '../StyleProvider'; @@ -43,7 +43,10 @@ export class CollectionCarousel3DView extends CollectionSubView() { const displayDoc = (childPair: { layout: Doc; data: Doc }) => { return (
this.currentTime; - @computed get renderDictation() { - const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); - return !dictation ? null : ( -
- -
- ); - } - // renders selection region on timeline @computed get selectionContainer() { const markerEnd = CollectionStackedTimeline.SelectingRegion === this ? this.currentTime : this._markerEnd; @@ -638,7 +606,6 @@ export class CollectionStackedTimeline extends CollectionSubView )} - {/* {this.renderDictation} */}
// renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, options: DocFocusOptions) => this.props.playLink(mark); + const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => { + this.props.playLink(mark); + return undefined; + }; return { anchor, view: ( (anchor.view = r))} Document={mark} DataDoc={undefined} + docViewPath={returnEmptyDoclist} pointerEvents={this.noEvents ? returnNone : undefined} styleProvider={this.props.styleProvider} renderDepth={this.props.renderDepth + 1} @@ -837,7 +810,16 @@ class StackedTimelineAnchor extends React.Component PanelHeight={height} fitWidth={returnTrue} ScreenToLocalTransform={screenXf} + addDocTab={returnFalse} + pinToPres={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} focus={focusFunc} + isContentActive={returnFalse} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + searchFilterDocs={returnEmptyDoclist} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} rootSelected={returnFalse} onClick={script} onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 553967b95..4a11e8f0b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -7,7 +7,7 @@ import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils'; +import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -384,12 +384,12 @@ export class CollectionTreeView extends CollectionSubView { + @computed get content() { const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; - return [ -
+ return ( +
{!this.buttonMenu && !this.noviceExplainer ? null : (
r && (this._headerHeight = Number(getComputedStyle(r).height.replace(/px/, ''))))}> {this.buttonMenu} @@ -428,9 +428,9 @@ export class CollectionTreeView extends CollectionSubView
-
, - ]; - }; +
+ ); + } render() { TraceMobx(); @@ -439,7 +439,11 @@ export class CollectionTreeView extends CollectionSubView {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? ( - {this.contentFunc} + {this.content} ) : ( - this.contentFunc() + this.content )}
); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 51624689e..bc25ad43a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -45,7 +45,7 @@ interface CollectionViewProps_ extends FieldViewProps { // property overrides for child documents childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) - childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) + childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childFitWidth?: (child: Doc) => boolean; childShowTitle?: () => string; childOpacity?: () => number; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aed3683d4..d39668a5d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { Bezier } from 'bezier-js'; import { Colors } from 'browndash-components'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { DateField } from '../../../../fields/DateField'; @@ -53,10 +53,14 @@ import { MarqueeView } from './MarqueeView'; import React = require('react'); export type collectionFreeformViewProps = { + noPointerWheel?: () => boolean; // turn off pointerwheel interactions (see PDFViewer) + NativeWidth?: () => number; + NativeHeight?: () => number; + originTopLeft?: boolean; annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: string; - scaleField?: string; + viewField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; getScrollHeight?: () => number | undefined; @@ -97,8 +101,14 @@ export class CollectionFreeFormView extends CollectionSubView (this.fitContentsToBox ? true : false); // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document. // 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.freeformData()?.bounds.cx ?? NumCast(this.Document._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); - panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); + panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1)); + panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1)); zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1)); contentTransform = () => - !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 - ? '' - : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; + this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; getTransform = () => this.cachedGetTransform.copy(); getLocalTransform = () => this.cachedGetLocalTransform.copy(); getContainerTransform = () => this.cachedGetContainerTransform.copy(); @@ -293,7 +301,7 @@ export class CollectionFreeFormView extends CollectionSubView { - options.docTransform = new Transform(-NumCast(this.rootDoc.panX) + NumCast(anchor.x), -NumCast(this.rootDoc.panY) + NumCast(anchor.y), 1); + options.docTransform = new Transform(-NumCast(this.rootDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.rootDoc[this.panYFieldKey]) + NumCast(anchor.y), 1); const res = this.props.focus(this.rootDoc, options); options.docTransform = undefined; return res; @@ -301,7 +309,7 @@ export class CollectionFreeFormView extends CollectionSubView { const xfToCollection = options?.docTransform ?? Transform.Identity(); - const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; + const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale || 0.75 : undefined); @@ -772,7 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView= 0.05 || localTransform.Scale > this.zoomScaling()) { const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20); this.props.Document[this.scaleFieldKey] = Math.abs(safeScale); - this.setPan(-localTransform.TranslateX / safeScale, NumCast(this.props.Document.scrollTop) * safeScale || -localTransform.TranslateY / safeScale); + this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.scrollTop) * safeScale) || -localTransform.TranslateY / safeScale); } }; @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom + if (this.props.noPointerWheel?.() || this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom PresBox.Instance?.pauseAutoPres(); if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; e.stopPropagation(); @@ -995,7 +1003,7 @@ export class CollectionFreeFormView extends CollectionSubView= panX + panelDim[0] / 2) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds - else if (ranges.xrange.max <= panX - panelDim[0] / 2) panX = ranges.xrange.min - panelDim[0] / 2; - if (ranges.yrange.min >= panY + panelDim[1] / 2) panY = ranges.yrange.max + panelDim[1] / 2; - else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2; + const panelWidMax = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelWidMin = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1); + const panelHgtMax = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1); + const panelHgtMin = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1); + if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this.props.originTopLeft ? 0 : panelWidMax / 2); + else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this.props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2); + if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this.props.originTopLeft ? 0 : panelHgtMax / 2); + else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this.props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2); } } if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) { @@ -1078,8 +1089,8 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) { this.setPan( - NumCast(this.layoutDoc._panX) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale - NumCast(this.layoutDoc._panY) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), + NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale + NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), nudgeTime, true ); @@ -1106,12 +1117,14 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).slice(); docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); - let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1; - if (zlast - docs.length > 100) { - for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; - zlast = docs.length + 1; + let zlast = docs.length ? Math.max(docs.length, NumCast(docs.lastElement().zIndex)) : 1; + if (docs.lastElement() !== doc) { + if (zlast - docs.length > 100) { + for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; + zlast = docs.length + 1; + } + doc.zIndex = zlast + 1; } - doc.zIndex = zlast + 1; } }; @@ -1134,8 +1147,8 @@ export class CollectionFreeFormView extends CollectionSubView { @@ -1158,8 +1171,8 @@ export class CollectionFreeFormView extends CollectionSubView screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0; @@ -1171,8 +1184,8 @@ export class CollectionFreeFormView extends CollectionSubView { if (cbounds) { const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; - const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; + const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])]; const pbounds = { x: cbounds.x - p[0] + c[0], y: cbounds.y - p[1] + c[1], @@ -1511,8 +1524,8 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(doc._height))) + 20; const dim = Math.ceil(Math.sqrt(docs.length)); docs.forEach((doc, i) => { - doc.x = NumCast(this.Document._panX) + (i % dim) * width - (width * dim) / 2; - doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - (height * dim) / 2; + doc.x = NumCast(this.Document[this.panXFieldKey]) + (i % dim) * width - (width * dim) / 2; + doc.y = NumCast(this.Document[this.panYFieldKey]) + Math.floor(i / dim) * height - (height * dim) / 2; }); }; @@ -1675,7 +1688,7 @@ export class CollectionFreeFormView extends CollectionSubView { - this.props.Document._panX = this.props.Document._panY = 0; + this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: 'compress-arrows-alt', @@ -1795,11 +1808,11 @@ export class CollectionFreeFormView extends CollectionSubView !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1); }); - children = () => { + get children() { this.incrementalRender(); - const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : []; + const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : []; return [...children, ...this.views, ]; - }; + } @computed get placeholder() { return ( @@ -1843,6 +1856,7 @@ export class CollectionFreeFormView extends CollectionSubView this.nativeDimScaling; private groupDropDisposer?: DragManager.DragDropDisposer; protected createGroupEventsTarget = (ele: HTMLDivElement) => { @@ -1912,7 +1927,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.createDashEventsTarget(r); // prevent wheel events from passivly propagating up through containers - r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); + !this.props.isAnnotationOverlay && r?.addEventListener('wheel', (e: WheelEvent) => this.props.isSelected() && e.preventDefault(), { passive: false }); }} onWheel={this.onPointerWheel} onClick={this.onClick} @@ -1985,7 +2000,8 @@ interface CollectionFreeFormViewPannableContentsProps { transform: () => string; zoomScaling: () => number; viewDefDivClick?: ScriptField; - children: () => JSX.Element[]; + children?: React.ReactNode | undefined; + //children: () => JSX.Element[]; transition?: string; presPaths: () => JSX.Element | null; presPinView?: boolean; @@ -2079,7 +2095,7 @@ class CollectionFreeFormViewPannableContents extends React.Component - {this.props.children()} + {this.props.children} {!this.props.brushView.width ? null : (
number; PanelHeight: () => number; isAnnotationOverlay?: boolean; + nativeDimScaling: () => number; zoomScaling: () => number; layoutDoc: Doc; cachedCenteringShiftX: number; @@ -2124,10 +2141,10 @@ class CollectionFreeFormBackgroundGrid extends React.Component { + // if (this.props.pointerEvents?.() === 'none') return; this._downX = this._lastX = e.clientX; this._downY = this._lastY = e.clientY; if (!(e.nativeEvent as any).marqueeHit) { @@ -345,6 +346,7 @@ export class MarqueeView extends React.Component { + if (this.props.pointerEvents?.() === 'none') return; if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { if (Doc.ActiveTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 9468c5f06..e8ae88ae5 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Doc, Opt } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -186,7 +186,10 @@ export class CollectionGridView extends CollectionSubView() { getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ( ); } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index d47c9762c..fd9bcf681 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,23 +1,20 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx'; +import { action, computed, observable, ObservableMap, untracked } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc'; +import { computedFn } from 'mobx-utils'; +import { Doc, Field, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; -import { RichTextField } from '../../../../fields/RichTextField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; -import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; -import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -215,9 +212,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addNewKey = (key: string, defaultVal: any) => { - this.childDocs.forEach(doc => (doc[key] = defaultVal)); - }; + addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal)); @undoBatch @action @@ -303,9 +298,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addRowRef = (doc: Doc, ref: HTMLDivElement) => { - this._rowEles.set(doc, ref); - }; + addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref); @action setColRef = (index: number, ref: HTMLDivElement) => { @@ -405,68 +398,12 @@ export class CollectionSchemaView extends CollectionSubView() { menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); - const layoutItems: ContextMenuProps[] = []; - const docItems: ContextMenuProps[] = []; - const dataDoc = this.props.DataDoc || this.props.Document; - - DocUtils.addDocumentCreatorMenuItems( - doc => { - FormattedTextBox.SelectOnLoad = StrCast(doc[Id]); - return this.addRow(doc); - }, - this.addRow, - x, - y, - true - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string') - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document)); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - return this.addRow(created); - } - }, - icon: 'compress-arrows-alt', - }) - ); - Array.from(Object.keys(Doc.GetProto(dataDoc))) - .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length) - .map(fieldKey => - docItems.push({ - description: ':' + fieldKey, - event: () => { - const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey }); - if (created) { - const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document; - if (container.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, container); - return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); - } - return this.addRow(created) || false; - } - }, - icon: 'compress-arrows-alt', - }) - ); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' }); - !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' }); + DocUtils.addDocumentCreatorMenuItems(doc => this.addRow(doc), this.addRow, x, y, true); + ContextMenu.Instance.setDefaultItem('::', (name: string): void => { Doc.GetProto(this.props.Document)[name] = ''; - const created = Docs.Create.TextDocument('', { title: name, _autoHeight: true }); - if (created) { - if (this.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(created, this.props.Document); - } - this.addRow(created); - } + this.addRow(Docs.Create.TextDocument('', { title: name, _autoHeight: true })); }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @@ -543,9 +480,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeColumnMenu = () => { - this._columnMenuIndex = undefined; - }; + closeColumnMenu = () => (this._columnMenuIndex = undefined); @action openFilterMenu = (index: number) => { @@ -596,19 +531,14 @@ export class CollectionSchemaView extends CollectionSubView() { getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); removeFieldFilters = (field: string) => { - this.getFieldFilters(field).forEach(filter => { - Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'); - }); + this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove')); }; onFilterKeyDown = (e: React.KeyboardEvent) => { + //prettier-ignore switch (e.key) { - case 'Enter': - this.closeFilterMenu(true); - break; - case 'Escape': - this.closeFilterMenu(false); - break; + case 'Enter' : this.closeFilterMenu(true); break; + case 'Escape': this.closeFilterMenu(false);break; } }; @@ -903,6 +833,7 @@ interface CollectionSchemaViewDocsProps { class CollectionSchemaViewDocs extends React.Component { tableWidthFunc = () => this.props.schema.tableWidth; rowHeightFunc = () => CollectionSchemaView._rowHeight; + childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -CollectionSchemaView._rowHeight - index * this.rowHeightFunc())); render() { return (
@@ -932,7 +863,7 @@ class CollectionSchemaViewDocs extends React.Component @action onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction); + setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false); }; render() { return ( -
{ - col && this.props.setColRef(this.props.columnIndex, col); - }}> +
col && this.props.setColRef(this.props.columnIndex, col)}>
this.props.resizeColumn(e, this.props.columnIndex)}>
{this.fieldKey}
-- cgit v1.2.3-70-g09d2 From e4b76f6c1dee63c49408c48d7b2bc98e62dc813a Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Apr 2023 12:19:01 -0400 Subject: adjusted general context menu items to always appear last. cleaned up makeLink api, --- src/client/documents/Documents.ts | 32 +++++----- src/client/util/DragManager.ts | 1 - src/client/util/LinkFollower.ts | 2 - src/client/views/ContextMenu.tsx | 12 ++-- src/client/views/DocumentButtonBar.tsx | 4 +- src/client/views/MainView.tsx | 1 + src/client/views/SidebarAnnos.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 28 ++++----- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 26 ++------ .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 70 +++++++++++++++------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 31 +++++----- src/client/views/nodes/VideoBox.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/search/SearchBox.tsx | 2 +- 21 files changed, 117 insertions(+), 118 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9cb480c4a..5623ca218 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -470,7 +470,7 @@ export namespace Docs { DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, forceReflow: true, nativeDimModifiable: true }, + options: { _height: 100, fitWidth: true, forceReflow: true, nativeDimModifiable: true }, }, ], [ @@ -934,13 +934,13 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } - export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) { + export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) { const linkDoc = InstanceFromProto( Prototypes.get(DocumentType.LINK), undefined, { - anchor1: source.doc, - anchor2: target.doc, + anchor1: source, + anchor2: target, ...options, }, id @@ -1294,16 +1294,14 @@ export namespace DocUtils { broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); - const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor(true) || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline'); - link && (link.followLinkLocation = OpenWhere.addRight); - return link; + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { linkDisplay: false, linkRelationship: 'recording annotation:linked recording', description: 'recording timeline' }); }); } - export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { - if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; - const sv = DocumentManager.Instance.getDocumentView(source.doc); - if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return; + export function MakeLink(source: Doc, target: Doc, linkSettings: { linkRelationship?: string; description?: string; linkDisplay?: boolean }, id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { + if (!linkSettings.linkRelationship) linkSettings.linkRelationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; + const sv = DocumentManager.Instance.getDocumentView(source); + if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target) return; if (target.doc === Doc.UserDoc()) return undefined; const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { @@ -1343,16 +1341,16 @@ export namespace DocUtils { target, { title: ComputedField.MakeFunction('generateLinkTitle(self)') as any, - 'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined, - 'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined, + 'anchor1-useLinkSmallAnchor': source.useLinkSmallAnchor ? true : undefined, + 'anchor2-useLinkSmallAnchor': target.useLinkSmallAnchor ? true : undefined, 'acl-Public': SharingPermissions.Augment, '_acl-Public': SharingPermissions.Augment, - linkDisplay: true, + linkDisplay: linkSettings.linkDisplay, _linkAutoMove: true, - linkRelationship, + linkRelationship: linkSettings.linkRelationship, _showCaption: 'description', _showTitle: 'linkRelationship', - description, + description: linkSettings.description, }, id ), @@ -1700,7 +1698,7 @@ export namespace DocUtils { _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); - const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', ''); + const pushpinLink = DocUtils.MakeLink(pushpin, doc, { linkRelationship: 'pushpin' }, ''); doc._timecodeToShow = undefined; return pushpin; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index cc116fb46..7d2aa813f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -168,7 +168,6 @@ export namespace DragManager { this.dropDocCreator = dropDocCreator; this.offset = [0, 0]; } - linkSourceDoc?: Doc; dropDocCreator: (annotationOn: Doc | undefined) => Doc; dropDocument?: Doc; offset: number[]; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 785018990..df61ecece 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -9,8 +9,6 @@ import { DocumentManager } from './DocumentManager'; import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; - -type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; /* * link doc: * - anchor1: doc diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 6a530e3ae..e4c3e864b 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -102,13 +102,11 @@ export class ContextMenu extends React.Component { } } @action - moveAfter(item: ContextMenuProps, after: ContextMenuProps) { - if (after && this.findByDescription(after.description)) { - const curInd = this._items.findIndex(i => i.description === item.description); - this._items.splice(curInd, 1); - const afterInd = this._items.findIndex(i => i.description === after.description); - this._items.splice(afterInd + 1, 0, item); - } + moveAfter(item: ContextMenuProps, after?: ContextMenuProps) { + const curInd = this._items.findIndex(i => i.description === item.description); + this._items.splice(curInd, 1); + const afterInd = after && this.findByDescription(after.description) ? this._items.findIndex(i => i.description === after.description) : this._items.length; + this._items.splice(afterInd, 0, item); } @action setDefaultItem(prefix: string, item: (name: string) => void) { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 9389fed01..3e1a0f265 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -416,7 +416,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const targetDoc = this.view0?.props.Document; return !targetDoc ? null : ( {`Open Context Menu`}
}> -
this.openContextMenu(e)}> +
@@ -569,7 +569,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const anchor = rootView.ComponentView?.getAnchor?.(false) ?? rootDoc; const trail = DocCast(anchor.presTrail) ?? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail), true); if (trail !== anchor.presTrail) { - DocUtils.MakeLink({ doc: anchor }, { doc: trail }, 'link trail'); + DocUtils.MakeLink(anchor, trail, { linkRelationship: 'link trail' }); anchor.presTrail = trail; } Doc.ActivePresentation = trail; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index befb0200d..b0888df68 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -270,6 +270,7 @@ export class MainView extends React.Component { fa.faLaptopCode, fa.faMale, fa.faCopy, + fa.faHome, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 48c0150e6..2d2b0f83e 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -74,7 +74,7 @@ export class SidebarAnnos extends React.Component { }); FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; - const link = DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on'); + const link = DocUtils.MakeLink(anchor, target, { linkRelationship: 'inline comment:comment on' }); link && (link.linkDisplay = false); const taggedContent = this.docFilters() diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 121260680..cb5be990d 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -441,7 +441,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed + de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { linkRelationship: 'doc annotation' }); // TODODO this is where in text links get passed e.stopPropagation(); } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); return false; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 64ec419ec..1e02fc9d4 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,14 +3,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -460,7 +460,7 @@ export class CollectionStackingView extends CollectionSubView { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped()) { - const subItems: ContextMenuProps[] = []; - subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); - subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); - subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' }); - ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' }); + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); + optionItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' }); + !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); } }; @@ -735,14 +737,6 @@ export class CollectionStackingView extends CollectionSubView
)} - {/* {this.chromeHidden || !this.props.isSelected() ? (null) : - } */}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5100d8d67..132ed6fb6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -335,7 +335,7 @@ export function CollectionSubView(moreProps?: X) { const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any; if (focusNode) { const anchor = srcWeb?.ComponentView?.getAnchor?.(true); - anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor }); + anchor && DocUtils.MakeLink(htmlDoc, anchor, {}); } } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 99b7549c0..75e76019e 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -359,7 +359,7 @@ export class TreeView extends React.Component { if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor(); const destDoc = this.doc; - DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', ''); + DocUtils.MakeLink(sourceDoc, destDoc, { linkRelationship: 'tree link' }); e.stopPropagation(); } const docDragData = de.complete.docDragData; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d39668a5d..fb0bd2a19 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -397,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' }); appearanceItems.push({ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`, @@ -1700,7 +1704,7 @@ export class CollectionFreeFormView extends CollectionSubView TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' }); - //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); + !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' }); appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' }); this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' }); @@ -1730,27 +1734,9 @@ export class CollectionFreeFormView extends CollectionSubView this.importDocument(e.clientX, e.clientY) }); !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' }); }; - importDocument = (x: number, y: number) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.zip'; - input.onchange = _e => { - input.files && - Doc.importDocument(input.files[0]).then(doc => { - if (doc instanceof Doc) { - const [xx, yy] = this.getTransform().transformPoint(x, y); - (doc.x = xx), (doc.y = yy); - this.props.addDocument?.(doc); - } - }); - }; - input.click(); - }; - @undoBatch @action transcribeStrokes = (math: boolean) => { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 043fe0bcf..0b4f76fa1 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -527,7 +527,7 @@ export class MarqueeView extends React.Component { + drop = (e: Event, de: DragManager.DropEvent) => { if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return; if (this.props.Document === Doc.ActiveDashboard) { alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.'); return; } const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData; - if (linkdrag) linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor(); - if (linkdrag?.linkSourceDoc) { - e.stopPropagation(); - if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) { - de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined); - } - if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) { - const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document; - de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]); + if (linkdrag) { + linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor(); + if (linkdrag.linkSourceDoc) { + e.stopPropagation(); + if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) { + de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined); + } + if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) { + const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc; + de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, undefined, [de.x, de.y - 50]); + } } } }; @@ -643,9 +645,9 @@ export class DocumentViewInternal extends DocComponent d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from'); if (!portalLink) { DocUtils.MakeLink( - { doc: this.props.Document }, - { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) }, - 'portal to:portal from' + this.props.Document, + Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }), + { linkRelationship: 'portal to:portal from' } ); } this.Document.followLinkLocation = OpenWhere.lightbox; @@ -653,6 +655,24 @@ export class DocumentViewInternal extends DocComponent { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.zip'; + input.onchange = _e => { + if (input.files) { + const batch = UndoManager.StartBatch('importing'); + Doc.importDocument(input.files[0]).then(doc => { + if (doc instanceof Doc) { + this.props.addDocTab(doc, OpenWhere.addRight); + batch.end(); + } + }); + } + }; + input.click(); + }; + @action onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => { if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) { @@ -704,9 +724,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' }); - LinkManager.Links(this.Document).length && - appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? 'Show' : 'Hide'} Link Button`, event: action(() => (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' }); - !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); + !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { const existingOnClick = cm.findByDescription('OnClick...'); @@ -774,16 +792,21 @@ export class DocumentViewInternal extends DocComponent Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' }); } - moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); - - (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); } - if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) { + !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' }); + } + const constantItems: ContextMenuProps[] = []; + + if (!Doc.IsSystem(this.rootDoc)) { + constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); + constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() }); + (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); + if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions) - moreItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); + constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' }); } - !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' }); + cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' }); } const help = cm.findByDescription('Help...'); const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; @@ -834,7 +857,8 @@ export class DocumentViewInternal extends DocComponent !isClick && batch.end(), () => { - this.toggleSidebar(); + onButton && this.toggleSidebar(); batch.end(); } ); @@ -440,17 +440,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform); specificContextMenu = (e: React.MouseEvent): void => { - const funcs: ContextMenuProps[] = []; - funcs.push({ - description: 'Toggle Sidebar Type', - event: () => (this.rootDoc.sidebarViewType = this.rootDoc.sidebarViewType === CollectionViewType.Freeform ? CollectionViewType.Stacking : CollectionViewType.Freeform), - icon: 'expand-arrows-alt', - }); - funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); - funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); - //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); - ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' }); + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' }); + !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); + //optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); + !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' }); + const help = cm.findByDescription('Help...'); + const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; + helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); + !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' }); }; @computed get renderTitleBox() { @@ -523,8 +526,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> @@ -568,7 +569,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, 'move', true)); }; @@ -1031,7 +1031,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || - DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); + DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!); newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); @@ -1273,7 +1273,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { makeLink = action((linkTo: Doc) => { const linkFrom = this.props.linkCreateAnchor?.(); if (linkFrom) { - const link = DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }); + const link = DocUtils.MakeLink(linkFrom, linkTo, {}); link && this.props.linkCreated?.(link); } }); -- cgit v1.2.3-70-g09d2 From 1edbb801fdd1cfecbd0838f382149fc2b38a4df1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Apr 2023 12:44:50 -0400 Subject: fixed dragging groups --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5623ca218..5039be2f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -166,6 +166,7 @@ export class DocumentOptions { _chromeHidden?: boolean; // whether the editing chrome for a document is hidden _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view) _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active) + enableDragWhenActive?: boolean; // allow dragging even if document contentts are active (e.g., tree, groups) _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged. _hideContextMenu?: boolean; // whether the context menu can be shown @@ -232,7 +233,6 @@ export class DocumentOptions { contextMenuIcons?: List; defaultDoubleClick?: 'ignore' | 'default'; // ignore double clicks, or deafult (undefined) means open document full screen waitForDoubleClickToClick?: 'always' | 'never' | 'default'; // whether a click function wait for double click to expire. 'default' undefined = wait only if there's a click handler, "never" = never wait, "always" = alway wait - enableDragWhenActive?: boolean; dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) description?: string; // added for links layout?: string | Doc; // default layout string for a document diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 0b4f76fa1..d443df0f3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -396,6 +396,7 @@ export class MarqueeView extends React.Component Date: Wed, 5 Apr 2023 14:36:37 -0400 Subject: made pinning from topbar available for all docs. --- src/client/util/CurrentUserUtils.ts | 8 +++---- src/client/views/DocumentButtonBar.tsx | 25 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 16 +++++++++----- src/client/views/nodes/trails/PresBox.tsx | 1 - src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 5 files changed, 28 insertions(+), 24 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 753cd1cb7..9571209b0 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -682,9 +682,9 @@ export class CurrentUserUtils { CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid, CollectionViewType.NoteTaking]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, - { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, toolType:"tab", funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, width: 20, scripts: { onClick: 'pinWithView(_readOnly_, altKey)'}}, - { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, toolType:"tab", ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected - { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: false, toolType:"tab", ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, + { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 20, scripts: { onClick: 'pinWithView(altKey)'}}, + { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected + { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform { title: "Z order", icon: "z", toolTip: "Bring Forward on Drag",btnType: ButtonType.ToggleButton, expertMode: false, toolType:CollectionViewType.Freeform, funcs: {}, scripts: { onClick: 'toggleRaiseOnDrag(_readOnly_)'}}, // Only when floating document is selected in freeform { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, @@ -693,7 +693,7 @@ export class CurrentUserUtils { { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: { linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available - { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(),expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available + { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected ]; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 3e1a0f265..e83ea00cf 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -289,7 +289,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV @observable subEndLink = ''; @computed get endLinkButton() { - const linkBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => { + const linkBtn = (pinLayout: boolean, pinContent: boolean, icon: IconProp) => { const tooltip = `Finish Link and Save ${this.subEndLink} data`; return !this.view0 ? null : ( {tooltip}
}> @@ -300,7 +300,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV key={icon.toString()} size="sm" icon={icon} - onPointerEnter={action(e => (this.subEndLink = (pinDocLayout ? 'Layout' : '') + (pinDocLayout && pinDocContent ? ' &' : '') + (pinDocContent ? ' Content' : '')))} + onPointerEnter={action(e => (this.subEndLink = (pinLayout ? 'Layout' : '') + (pinLayout && pinContent ? ' &' : '') + (pinContent ? ' Content' : '')))} onPointerLeave={action(e => (this.subEndLink = ''))} onClick={e => { const docs = this.props @@ -309,9 +309,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV .map(dv => dv!.rootDoc); this.view0 && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.view0.props.Document, true, this.view0, { - pinDocLayout, - pinDocContent, - pinData: !pinDocContent ? {} : { poslayoutview: true, dataannos: true, dataview: true }, + pinDocLayout: pinLayout, + pinData: !pinContent ? {} : { poslayoutview: true, dataannos: true, dataview: pinContent }, } as PinProps); e.stopPropagation(); @@ -337,7 +336,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV @computed get pinButton() { const targetDoc = this.view0?.props.Document; - const pinBtn = (pinDocLayout: boolean, pinDocContent: boolean, icon: IconProp) => { + const pinBtn = (pinLayoutView: boolean, pinContentView: boolean, icon: IconProp) => { const tooltip = `Pin Document and Save ${this.subPin} to trail`; return !tooltip ? null : ( {tooltip}
}> @@ -351,10 +350,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV onPointerEnter={action( e => (this.subPin = - (pinDocLayout ? 'Layout' : '') + - (pinDocLayout && pinDocContent ? ' &' : '') + - (pinDocContent ? ' Content View' : '') + - (pinDocLayout && pinDocContent ? '(shift+alt)' : pinDocLayout ? '(shift)' : pinDocContent ? '(alt)' : '')) + (pinLayoutView ? 'Layout' : '') + + (pinLayoutView && pinContentView ? ' &' : '') + + (pinContentView ? ' Content View' : '') + + (pinLayoutView && pinContentView ? '(shift+alt)' : pinLayoutView ? '(shift)' : pinContentView ? '(alt)' : '')) )} onPointerLeave={action(e => (this.subPin = ''))} onClick={e => { @@ -364,8 +363,8 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV .map(dv => dv!.rootDoc); TabDocView.PinDoc(docs, { pinAudioPlay: true, - pinDocLayout, - pinData: { dataview: true }, + pinDocLayout: pinLayoutView, + pinData: { dataview: pinContentView }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null), currentFrame: Cast(docs.lastElement()?.currentFrame, 'number', null), }); @@ -385,7 +384,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV .views() .filter(v => v) .map(dv => dv!.rootDoc); - TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinDocContent: e.altKey, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: {dataview: e.altKey}, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); e.stopPropagation(); }}>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index fb0bd2a19..22a2a429f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -2196,11 +2196,17 @@ ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) { if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent'; runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.())); }); -ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) { - !readOnly && - SelectionManager.Views().forEach(view => - TabDocView.PinDoc(view.rootDoc, { currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) }) - ); +ScriptingGlobals.add(function pinWithView(pinContent: boolean) { + SelectionManager.Views().forEach(view => + view.props.pinToPres(view.rootDoc, { + currentFrame: Cast(view.rootDoc.currentFrame, 'number', null), + pinData: { + poslayoutview: pinContent, + dataview: pinContent, + }, + pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()), + }) + ); }); ScriptingGlobals.add(function bringToFront() { SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc)); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 0afb36214..bfaae8069 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -59,7 +59,6 @@ export interface PinProps { hidePresBox?: boolean; pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected) pinDocLayout?: boolean; // pin layout info (width/height/x/y) - pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text) pinAudioPlay?: boolean; // pin audio annotation pinData?: pinDataTypes; } diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 698a2817e..9a74b5dba 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -312,7 +312,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @action updateCapturedViewContents = (presTargetDoc: Doc, activeItem: Doc) => { const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; - PresBox.pinDocView(activeItem, { pinDocContent: true, pinData: PresBox.pinDataTypes(target) }, target); + PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target); }; @computed get recordingIsInOverlay() { -- cgit v1.2.3-70-g09d2 From 8f7e99641f84ad15f34ba9e4a60b664ac93d2e5d Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Apr 2023 17:21:26 -0400 Subject: added an arrange feature for freeform collections. added btn width for linear buttons. --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 6 ++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 ++++++++++++++++++++++ .../collectionLinear/CollectionLinearView.tsx | 4 ++-- src/client/views/nodes/button/FontIconBox.tsx | 15 ++++++++++++-- 5 files changed, 43 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5039be2f6..c4014a752 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -315,6 +315,7 @@ export class DocumentOptions { linearViewExpandable?: boolean; // can linear view be expanded linearViewToggleButton?: string; // button to open close linear view group linearViewSubMenu?: boolean; + linearBtnWidth?: number; flexGap?: number; // Linear view flex gap flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse'; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9571209b0..9aceed366 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -41,6 +41,7 @@ interface Button { numBtnMax?: number; switchToggle?: boolean; width?: number; + linearBtnWidth?: number; toolType?: string; // type of pen tool expertMode?: boolean;// available only in expert mode btnList?: List; @@ -621,6 +622,7 @@ export class CurrentUserUtils { { title: "Snap\xA0Lines",icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"snap lines", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "View\xA0All", icon: "object-group",toolTip: "Fit all Docs to View",btnType: ButtonType.ToggleButton, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform + { title: "Arrange", icon: "window", toolTip: "Toggle Auto Arrange", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"arrange", funcs: {}, scripts: { onClick: 'showFreeform(self.toolType, _readOnly_)'}}, // Only when floating document is selected in freeform { title: "Reset", icon: "check", toolTip: "Reset View", btnType: ButtonType.ClickButton, expertMode: false, backgroundColor:"transparent", scripts: { onClick: 'resetView()'}}, // Only when floating document is selected in freeform ] } @@ -695,7 +697,7 @@ export class CurrentUserUtils { { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Only when Web is selected - { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected + { title: "Schema", icon: "Schema",linearBtnWidth:58,toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), expertMode: false, toolType:CollectionViewType.Schema, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearViewIsExpanded: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} } // Only when Schema is selected ]; } @@ -706,7 +708,7 @@ export class CurrentUserUtils { backgroundColor: params.backgroundColor ??"transparent", /// a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below). Otherwise, assume a transparent background color: Colors.WHITE, system: true, dontUndo: true, _nativeWidth: params.width ?? 30, _width: params.width ?? 30, - _height: 30, _nativeHeight: 30, + _height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth, toolType: params.toolType, expertMode: params.expertMode, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, _removeDropProperties: new List([ "_stayInCollection"]), diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 22a2a429f..9fe8f5f49 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1408,6 +1408,24 @@ export class CollectionFreeFormView extends CollectionSubView) { + if (this.layoutDoc._autoArrange) { + const sorted = this.childLayoutPairs.slice().sort((a, b) => (NumCast(a.layout.y) < NumCast(b.layout.y) ? -1 : 1)); + if (sorted.length > 1) { + const deltay = sorted.length > 1 ? NumCast(sorted[1].layout.y) - (NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height)) : 0; + const deltax = sorted.length > 1 ? NumCast(sorted[1].layout.x) - NumCast(sorted[0].layout.x) : 0; + + let lastx = NumCast(sorted[0].layout.x); + let lasty = NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height); + setTimeout( + action(() => + sorted.slice(1).forEach((pair, i) => { + lastx = pair.layout.x = lastx + deltax; + lasty = (pair.layout.y = lasty + deltay) + NumCast(pair.layout._height); + }) + ) + ); + } + } this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document }))); return [] as ViewDefResult[]; } @@ -1693,6 +1711,11 @@ export class CollectionFreeFormView extends CollectionSubView (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange), + icon: 'compress-arrows-alt', + }); if (this.props.setContentView === emptyFunction) { !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); return; diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index a062c65fc..c7d9b6619 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -44,7 +44,7 @@ export class CollectionLinearView extends CollectionSubView() { componentDidMount() { this._widthDisposer = reaction( - () => 5 + this.dimension() + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0), + () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0), width => this.childDocs.length && (this.layoutDoc._width = width), { fireImmediately: true } ); @@ -78,7 +78,7 @@ export class CollectionLinearView extends CollectionSubView() { } }; - dimension = () => NumCast(this.rootDoc._height); // 2 * the padding + dimension = () => NumCast(this.rootDoc._height); getTransform = (ele: Opt) => { if (!ele) return Transform.Identity(); const { scale, translateX, translateY } = Utils.GetScreenTransform(ele); diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 28e6eaf1c..8410fda18 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -587,32 +587,43 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); }); -ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snap lines' | 'clusters' | 'viewAll', checkResult?: boolean) { +ScriptingGlobals.add(function showFreeform(attr: 'grid' | 'snap lines' | 'clusters' | 'arrange' | 'viewAll', checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); // prettier-ignore - const map: Map<'grid' | 'snap lines' | 'clusters' | 'viewAll', { checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ + const map: Map<'grid' | 'snap lines' | 'clusters' | 'arrange'| 'viewAll', { undo: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc) => void;}> = new Map([ ['grid', { + undo: false, checkResult: (doc:Doc) => doc._backgroundGridShow, setDoc: (doc:Doc) => doc._backgroundGridShow = !doc._backgroundGridShow, }], ['snap lines', { + undo: false, checkResult: (doc:Doc) => doc.showSnapLines, setDoc: (doc:Doc) => doc._showSnapLines = !doc._showSnapLines, }], ['viewAll', { + undo: false, checkResult: (doc:Doc) => doc._fitContentsToBox, setDoc: (doc:Doc) => doc._fitContentsToBox = !doc._fitContentsToBox, }], ['clusters', { + undo: false, checkResult: (doc:Doc) => doc._useClusters, setDoc: (doc:Doc) => doc._useClusters = !doc._useClusters, }], + ['arrange', { + undo: true, + checkResult: (doc:Doc) => doc._autoArrange, + setDoc: (doc:Doc) => doc._autoArrange = !doc._autoArrange, + }], ]); if (checkResult) { return map.get(attr)?.checkResult(selected) ? Colors.MEDIUM_BLUE : 'transparent'; } + const batch = map.get(attr)?.undo ? UndoManager.StartBatch('set feature') : { end: () => {} }; SelectionManager.Docs().map(dv => map.get(attr)?.setDoc(dv)); + setTimeout(() => batch.end(), 100); }); ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: any, checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; -- cgit v1.2.3-70-g09d2 From 68c6c36af823255824a6b0692e8c33618c2d7ca2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 10 Apr 2023 11:32:44 -0400 Subject: fixed brushing of fonticon boxes with dropdowns. made line charts use computed values instead of observables --- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 14 +++- .../views/nodes/DataVizBox/components/Chart.scss | 2 +- .../nodes/DataVizBox/components/LineChart.tsx | 89 ++++++++++++---------- src/client/views/nodes/button/FontIconBox.tsx | 33 ++++++-- .../nodes/button/colorDropdown/ColorDropdown.tsx | 57 +++++++------- src/fields/Doc.ts | 2 +- 7 files changed, 120 insertions(+), 81 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9fe8f5f49..9d4a788db 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -664,8 +664,8 @@ export class CollectionFreeFormView extends CollectionSubView() { // 2 ways of doing it // @observable private pairs: { [key: string]: number | string | undefined }[] = []; // @observable private pairs: { [key: string]: FieldResult }[] = []; - @observable pairs: { [key: string]: string }[] = []; + static pairSet = new ObservableMap(); + @computed.struct get pairs() { + return DataVizBox.pairSet.get(StrCast(this.rootDoc.fileUpload)); + } private _chartRenderer: LineChart | undefined; // // another way would be store a schema that defines the type of data we are expecting from an imported doc @@ -93,6 +96,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; const margin = { top: 10, right: 50, bottom: 50, left: 50 }; + if (!this.pairs) return 'no data'; // prettier-ignore switch (this.dataVizView) { case DataVizView.TABLE: return ; @@ -109,8 +113,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } fetchData() { + if (DataVizBox.pairSet.has(StrCast(this.rootDoc.fileUpload))) return; + DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), []); fetch('/csvData?uri=' + this.dataUrl?.url.href) // - .then(res => res.json().then(action(res => !res.errno && (this.pairs = res)))); + .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(StrCast(this.rootDoc.fileUpload), res)))); } // handle changing the view using a button @@ -121,7 +127,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } render() { - return this.pairs.length == 0 ? ( + return !this.pairs?.length ? (
Loading...
) : (
{ private _disposers: { [key: string]: IReactionDisposer } = {}; private _lineChartRef: React.RefObject = React.createRef(); private _lineChartSvg: d3.Selection | undefined; - private _rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number } = {}; @observable _currSelected: SelectedDataPoint | undefined = undefined; - @observable _lineChartData: DataPoint[][] | undefined = undefined; // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates + @computed get _lineChartData() { + if (this.props.axes.length <= 1) return []; + return this.props.pairs + ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) })) + .sort((a, b) => (a.x < b.x ? -1 : 1)); + } + @computed get incomingLinks() { + return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + .filter(link => link.anchor1 !== this.props.rootDoc) // get links where this chart doc is the target of the link + .map(link => DocCast(link.anchor1)); // then return the source of the link + } + @computed get incomingSelected() { + return this.incomingLinks // all links that are pointing to this node + .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes + .filter(dvb => dvb) + .map(dvb => dvb.pairs?.filter(pair => pair['select' + dvb.rootDoc[Id]])) // get all the datapoints they have selected field set by incoming anchor + .lastElement(); + } + @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } { + return minMaxRange([this._lineChartData]); + } componentWillUnmount() { Array.from(Object.keys(this._disposers)).forEach(key => this._disposers[key]()); } componentDidMount = () => { - this._disposers.chartdata = reaction( - () => this.props.axes.slice(), - axes => { - if (axes.length > 1) { - this._lineChartData = [this.props.pairs?.map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1))]; - } - }, - { fireImmediately: true } - ); this._disposers.chartData = reaction( - () => ({ dataSet: this._lineChartData, axes: this.props.axes.slice(), w: this.props.width, h: this.props.height }), - vals => { - if (vals.dataSet) { - this._rangeVals = minMaxRange(vals.dataSet); - this.drawChart(vals.dataSet); + () => ({ dataSet: this._lineChartData, w: this.props.width, h: this.props.height }), + ({ dataSet, w, h }) => { + if (dataSet) { + this.drawChart([dataSet], this.rangeVals, w, h); + // redraw annotations when the chart data has changed, or the local or inherited selection has changed + this.clearAnnotations(); + this._currSelected && this.drawAnnotations(Number(this._currSelected.x), Number(this._currSelected.y), true); + this.incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); } }, { fireImmediately: true } @@ -87,18 +101,13 @@ export class LineChart extends React.Component { this._disposers.highlights = reaction( () => ({ selected: this._currSelected, - pairs: LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) - .filter(link => link.anchor1 !== this.props.rootDoc) // all links that are pointing to this node - .map(link => DocCast(link.anchor1)) // get the documents that are pointing to this node - .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes - .filter(dvb => dvb) - .map(dvb => dvb.pairs.filter(pair => pair['select' + dvb.rootDoc[Id]])) // get all the datapoints they have selected field set by incoming anchor - .lastElement(), + incomingSelected: this.incomingSelected, }), - ({ selected, pairs }) => { - this.clearAnnoations(); + ({ selected, incomingSelected }) => { + // redraw annotations when the chart data has changed, or the local or inherited selection has changed + this.clearAnnotations(); selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true); - pairs.forEach(pair => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); + incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); }, { fireImmediately: true } ); @@ -106,7 +115,7 @@ export class LineChart extends React.Component { // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that - clearAnnoations = () => { + clearAnnotations = () => { const elements = document.querySelectorAll('.datapoint'); for (let i = 0; i < elements.length; i++) { const element = elements[i]; @@ -207,12 +216,12 @@ export class LineChart extends React.Component { } // TODO: nda - can use d3.create() to create html element instead of appending - drawChart = (dataSet: DataPoint[][]) => { + drawChart = (dataSet: DataPoint[][], rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number }, width: number, height: number) => { // clearing tooltip and the current chart d3.select(this._lineChartRef.current).select('svg').remove(); d3.select(this._lineChartRef.current).select('.tooltip').remove(); - const { xMin, xMax, yMin, yMax } = this._rangeVals; + const { xMin, xMax, yMin, yMax } = rangeVals; if (xMin === undefined || xMax === undefined || yMin === undefined || yMax === undefined) { return; } @@ -226,16 +235,16 @@ export class LineChart extends React.Component { const svg = (this._lineChartSvg = d3 .select(this._lineChartRef.current) .append('svg') - .attr('width', `${this.width + margin.right + margin.left}`) - .attr('height', `${this.height + margin.top + margin.bottom}`) + .attr('width', `${width + margin.right + margin.left}`) + .attr('height', `${height + margin.top + margin.bottom}`) .append('g') .attr('transform', `translate(${margin.left}, ${margin.top})`)); // create x and y grids - xGrid(svg.append('g'), this.height, xScale); - yGrid(svg.append('g'), this.width, yScale); - xAxisCreator(svg.append('g'), this.height, xScale); - yAxisCreator(svg.append('g'), this.width, yScale); + xGrid(svg.append('g'), height, xScale); + yGrid(svg.append('g'), width, yScale); + xAxisCreator(svg.append('g'), height, xScale); + yAxisCreator(svg.append('g'), width, yScale); // draw the plot line const data = dataSet[0]; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 8410fda18..8eacfbc51 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -161,7 +161,13 @@ export class FontIconBox extends DocComponent() {
); return ( -
e.stopPropagation()} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> +
e.stopPropagation()} + onClick={action(() => { + this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; + Doc.UnBrushAllDocs(); + })}> {checkResult} {label} {this.rootDoc.dropDownOpen ? dropdown : null} @@ -198,7 +204,10 @@ export class FontIconBox extends DocComponent() { e.stopPropagation(); e.preventDefault(); }} - onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> + onClick={action(() => { + this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; + Doc.UnBrushAllDocs(); + })}> setValue(Number(e.target.value))))} />
setValue(Number(checkResult) + 1))}> @@ -215,6 +224,7 @@ export class FontIconBox extends DocComponent() { onClick={e => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; + Doc.UnBrushAllDocs(); }} />
@@ -237,7 +247,10 @@ export class FontIconBox extends DocComponent() {
(this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> + onClick={action(() => { + this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; + Doc.UnBrushAllDocs(); + })}> {this.Icon(color)} {!this.label || !FontIconBox.GetShowLabels() ? null : (
@@ -316,7 +329,14 @@ export class FontIconBox extends DocComponent() {
(this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen) : undefined}> + onClick={ + dropdown + ? () => { + this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen; + Doc.UnBrushAllDocs(); + } + : undefined + }> {dropdown ? null : }
{text && text[0].toUpperCase() + text.slice(1)}
{label} @@ -335,6 +355,7 @@ export class FontIconBox extends DocComponent() { onClick={e => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; + Doc.UnBrushAllDocs(); }} />
@@ -379,6 +400,7 @@ export class FontIconBox extends DocComponent() { style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(e => { this.colorPickerClosed = !this.colorPickerClosed; + setTimeout(() => Doc.UnBrushAllDocs()); e.stopPropagation(); })} onPointerDown={e => e.stopPropagation()}> @@ -397,6 +419,7 @@ export class FontIconBox extends DocComponent() { e.preventDefault(); e.stopPropagation(); this.colorPickerClosed = true; + Doc.UnBrushAllDocs(); })} />
@@ -816,7 +839,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | ' setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()), }], ['fillColor', { - checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ? Colors.MEDIUM_BLUE : 'transparent'), + checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ?? "transparent"), setInk: (doc: Doc) => (doc.fillColor = StrCast(value)), setMode: () => SetActiveFillColor(StrCast(value)), }], diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx index 7f414ddbb..74c3c563c 100644 --- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx +++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx @@ -12,7 +12,7 @@ export class ColorDropdown extends Component { const active: string = StrCast(this.props.rootDoc.dropDownOpen); const script: string = StrCast(this.props.rootDoc.script); - const scriptCheck: string = script + "(undefined, true)"; + const scriptCheck: string = script + '(undefined, true)'; const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result; const stroke: boolean = false; @@ -24,24 +24,21 @@ export class ColorDropdown extends Component { // strokeIcon = (
); // } - const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', - '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', - '#FFFFFF', '#f1efeb']; + const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb']; - const colorBox = (func: (color: ColorState) => void) => ; - const label = !this.props.label || !FontIconBox.GetShowLabels() ? (null) : -
- {this.props.label} -
; + const colorBox = (func: (color: ColorState) => void) => ; + const label = + !this.props.label || !FontIconBox.GetShowLabels() ? null : ( +
+ {this.props.label} +
+ ); - const dropdownCaret =
- -
; + const dropdownCaret = ( +
+ +
+ ); const click = (value: ColorState) => { const hex: string = value.hex; @@ -51,26 +48,30 @@ export class ColorDropdown extends Component { } }; return ( -
this.props.rootDoc.dropDownOpen = !this.props.rootDoc.dropDownOpen} + onClick={() => (this.props.rootDoc.dropDownOpen = !this.props.rootDoc.dropDownOpen)} onPointerDown={e => e.stopPropagation()}> -
+
{label} {/* {dropdownCaret} */} - {this.props.rootDoc.dropDownOpen ? + {this.props.rootDoc.dropDownOpen ? (
-
e.stopPropagation()} - onClick={e => e.stopPropagation()}> +
e.stopPropagation()} onClick={e => e.stopPropagation()}> {colorBox(click)}
-
{ e.stopPropagation(); this.props.rootDoc.dropDownOpen = false; }} /> +
{ + e.stopPropagation(); + this.props.rootDoc.dropDownOpen = false; + }} + />
- : null} + ) : null}
); } -} \ No newline at end of file +} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e89f5db52..1db56cbdd 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1394,7 +1394,7 @@ export namespace Doc { }); } export function UnBrushAllDocs() { - brushManager.BrushedDoc.clear(); + runInAction(() => brushManager.BrushedDoc.clear()); } export function getDocTemplate(doc?: Doc) { -- cgit v1.2.3-70-g09d2