From a37559a417b8a19ee56ac3b2f5dd46ef70ca53a6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 23 Apr 2020 20:52:09 -0400 Subject: fixed loading of notes with template types. changed noteTypes to ["template-notes"]. Added pass through free form layout engine. --- .../CollectionFreeFormLayoutEngines.tsx | 22 ++++++++++++++++ .../collectionFreeForm/CollectionFreeFormView.scss | 2 ++ .../collectionFreeForm/CollectionFreeFormView.tsx | 30 +++++++++++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 25 +++++++++++++++++- 4 files changed, 66 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 0a5ea3baf..1ec0542a3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -75,6 +75,28 @@ interface PivotColumn { filters: string[]; } +export function computerPassLayout( + poolData: Map, + pivotDoc: Doc, + childDocs: Doc[], + filterDocs: Doc[], + childPairs: { layout: Doc, data?: Doc }[], + panelDim: number[], + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] +) { + const docMap = new Map(); + childDocs.forEach((doc, i) => { + docMap.set(doc, { + type: "doc", + x: NumCast(doc.x), + y: NumCast(doc.y), + width: doc[WidthSym](), + height: doc[HeightSym](), + payload: undefined + }); + }); + return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c))); +} export function computerStarburstLayout( poolData: Map, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index a00311a9c..4e4e85e13 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -9,6 +9,8 @@ height: 100%; transform-origin: left top; border-radius: inherit; + touch-action: none; + border-radius: inherit; } .collectionfreeformview-ease { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5967f36f9..e8738b292 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -37,7 +37,7 @@ import { pageSchema } from "../../nodes/ImageBox"; import PDFMenu from "../../pdf/PDFMenu"; import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; -import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult, computerStarburstLayout } from "./CollectionFreeFormLayoutEngines"; +import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult, computerStarburstLayout, computerPassLayout } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; @@ -94,6 +94,7 @@ export class CollectionFreeFormView extends CollectionSubView e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); } @@ -104,8 +105,9 @@ export class CollectionFreeFormView extends CollectionSubView this.props.Document.panTransformType === "Ease"; private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0; private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0; - private zoomScaling = () => (1 / this.parentScaling) * (this.fitToContent ? - Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) : + private zoomScaling = () => (this.fitToContentScaling / this.parentScaling) * (this.fitToContent ? + Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), + this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) : this.Document.scale || 1) private centeringShiftX = () => !this.nativeWidth && !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling : 0; // shift so pan position is at center of window for non-overlay collections @@ -971,7 +973,7 @@ export class CollectionFreeFormView extends CollectionSubView(); const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.(); switch (engine) { - case "pass": break; + case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) } case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) }; case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) }; case "starburst": return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) }; @@ -993,6 +995,7 @@ export class CollectionFreeFormView extends CollectionSubView this._cachedPool.set(k, newPool.get(k))); const elements: ViewDefResult[] = computedElementData.slice(); + const engine = this.props.layoutEngine?.() || this.props.Document._layoutEngine; this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair => elements.push({ ele: { if (this.layoutDoc._layoutEngine === undefined) { - this.layoutDoc._layoutEngine = "starburst"; - this.layoutDoc.overflow = "visible"; + Doc.makeStarburst(this.layoutDoc); } else { - - this.layoutDoc._layoutEngine = "pass"; + this.layoutDoc._layoutEngine = undefined; this.layoutDoc.overflow = "hidden"; + this.layoutDoc._fitToBox = undefined; } - }); layoutDocsInGrid = () => { UndoManager.RunInBatch(() => { @@ -1141,7 +1142,7 @@ export class CollectionFreeFormView extends CollectionSubView - {this.children} @@ -1216,6 +1217,7 @@ interface CollectionFreeFormViewPannableContentsProps { zoomScaling: () => number; easing: () => boolean; children: () => JSX.Element[]; + shifted: boolean; } @observer @@ -1227,7 +1229,11 @@ class CollectionFreeFormViewPannableContents extends React.Component + return
{this.props.children()}
; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 454c3a5f2..2518ac629 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -20,6 +20,7 @@ import { CognitiveServices } from "../../../cognitive_services/CognitiveServices import { RichTextField } from "../../../../new_fields/RichTextField"; import { CollectionView } from "../CollectionView"; import { FormattedTextBox } from "../../nodes/FormattedTextBox"; +import { ScriptField } from "../../../../new_fields/ScriptField"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -332,6 +333,25 @@ export class MarqueeView extends React.Component { + const bounds = this.Bounds; + const selected = this.marqueeSelect(false); + selected.map(d => { + this.props.removeDocument(d); + d.x = NumCast(d.x) - bounds.left - bounds.width / 2; + d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + return d; + }); + const newCollection = this.getCollection(selected, false); + this.props.addDocument(newCollection); + this.props.selectDocuments([newCollection], []); + MarqueeOptionsMenu.Instance.fadeOut(true); + this.hideMarquee(); + Doc.makeStarburst(newCollection); + } + @action collection = (e: KeyboardEvent | React.PointerEvent | undefined) => { const bounds = this.Bounds; @@ -476,7 +496,7 @@ export class MarqueeView extends React.Component Date: Fri, 24 Apr 2020 00:06:48 -0400 Subject: moved iconify code into Doc from DocumentView. fixed up starburst a bit. --- .../apis/google_docs/GooglePhotosClientUtils.ts | 21 +++--- src/client/util/RichTextSchema.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 16 +--- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 15 ++-- .../CollectionFreeFormLayoutEngines.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 +++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 24 ++++-- src/client/views/nodes/DocumentView.tsx | 85 ++++++---------------- src/client/views/nodes/FormattedTextBox.tsx | 25 +++---- src/client/views/pdf/PDFViewer.tsx | 43 ++++++----- src/new_fields/Doc.ts | 85 ++++++++++++++++++++-- src/new_fields/RichTextUtils.ts | 32 ++++---- 13 files changed, 208 insertions(+), 177 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 7c4137f59..8c0149a89 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -1,19 +1,18 @@ -import { Utils } from "../../../Utils"; -import { ImageField } from "../../../new_fields/URLField"; -import { Cast, StrCast } from "../../../new_fields/Types"; -import { Doc, Opt, DocListCastAsync } from "../../../new_fields/Doc"; +import { AssertionError } from "assert"; +import { EditorState } from "prosemirror-state"; +import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import Photos = require('googlephotos'); import { RichTextField } from "../../../new_fields/RichTextField"; import { RichTextUtils } from "../../../new_fields/RichTextUtils"; -import { EditorState } from "prosemirror-state"; -import { FormattedTextBox } from "../../views/nodes/FormattedTextBox"; +import { Cast, StrCast } from "../../../new_fields/Types"; +import { ImageField } from "../../../new_fields/URLField"; +import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes"; +import { Utils } from "../../../Utils"; import { Docs, DocumentOptions } from "../../documents/Documents"; -import { NewMediaItemResult, MediaItem } from "../../../server/apis/google/SharedTypes"; -import { AssertionError } from "assert"; -import { DocumentView } from "../../views/nodes/DocumentView"; import { Networking } from "../../Network"; +import { FormattedTextBox } from "../../views/nodes/FormattedTextBox"; import GoogleAuthenticationManager from "../GoogleAuthenticationManager"; +import Photos = require('googlephotos'); export namespace GooglePhotos { @@ -340,7 +339,7 @@ export namespace GooglePhotos { const url = data.url.href; const target = Doc.MakeAlias(source); const description = parseDescription(target, descriptionKey); - await DocumentView.makeCustomViewClicked(target, Docs.Create.FreeformDocument); + await Doc.makeCustomViewClicked(target, Docs.Create.FreeformDocument); media.push({ url, description }); } if (media.length) { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index d23962d5c..b88a7b017 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -780,7 +780,7 @@ export class DashDocView { if (dashDocBase instanceof Doc) { const aliasedDoc = Doc.MakeAlias(dashDocBase, docid + alias); aliasedDoc.layoutKey = "layout"; - node.attrs.fieldKey && DocumentView.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined); + node.attrs.fieldKey && Doc.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined); self.doRender(aliasedDoc, removeDoc, node, view, getPos); } }); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e4ceb75bd..312acd5b2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -193,7 +193,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0], selectedDocs[0].props.LibraryPath); - CollectionDockingView.AddRightSplit(selectedDocs[0].props.Document, selectedDocs[0].props.LibraryPath); + CollectionDockingView.AddRightSplit(Doc.MakeAlias(selectedDocs[0].props.Document), selectedDocs[0].props.LibraryPath); } } SelectionManager.DeselectAll(); @@ -202,19 +202,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onIconifyClick = (e: PointerEvent): void => { if (e.button === 0) { - const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd); - selectedDocs.map(dv => { - const layoutKey = Cast(dv.props.Document.layoutKey, "string", null); - const collapse = layoutKey !== "layout_icon"; - if (collapse) { - dv.switchViews(collapse, "icon"); - if (layoutKey && layoutKey !== "layout") dv.props.Document.deiconifyLayout = layoutKey.replace("layout_", ""); - } else { - const deiconifyLayout = Cast(dv.props.Document.deiconifyLayout, "string", null); - dv.switchViews(deiconifyLayout ? true : false, deiconifyLayout); - dv.props.Document.deiconifyLayout = undefined; - } - }); + SelectionManager.SelectedDocuments().forEach(dv => dv?.iconify()); } SelectionManager.DeselectAll(); } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e3c749a4d..4c84a22ad 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -169,7 +169,7 @@ Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) { template = Cast(template.dragFactory, Doc, null); } const templateTitle = StrCast(template?.title); - return templateTitle && DocumentView.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template); + return templateTitle && Doc.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template); }); Scripting.addGlobal(function templateIsUsed(templateDoc: Doc, selDoc: Doc) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index a052d045c..ca2004b79 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -3,13 +3,14 @@ import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc'; +import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; +import { RichTextField } from '../../../new_fields/RichTextField'; import { Document, listSpec } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; -import { emptyFunction, emptyPath, returnFalse, Utils, returnOne, returnZero, returnTransparent, returnTrue, simulateMouseClick } from '../../../Utils'; +import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; @@ -24,16 +25,14 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import { ImageBox } from '../nodes/ImageBox'; import { KeyValueBox } from '../nodes/KeyValueBox'; -import { ScriptBox } from '../ScriptBox'; import { Templates } from '../Templates'; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; +import { CollectionViewType } from './CollectionView'; import React = require("react"); -import { CollectionViewType, CollectionView } from './CollectionView'; -import { RichTextField } from '../../../new_fields/RichTextField'; -import { DocumentView } from '../nodes/DocumentView'; export interface TreeViewProps { @@ -768,7 +767,7 @@ export class CollectionTreeView extends CollectionSubView UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" + description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 1ec0542a3..57f62347a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -113,8 +113,8 @@ export function computerStarburstLayout( const deg = i / childDocs.length * Math.PI * 2; docMap.set(doc, { type: "doc", - x: Math.sin(deg) * burstDim[0] / 3 - NumCast(pivotDoc.starburstX), - y: Math.cos(deg) * burstDim[1] / 3 - NumCast(pivotDoc.starburstY), + x: Math.cos(deg) * (burstDim[0] / 3) - doc[WidthSym]() / 2, + y: Math.sin(deg) * (burstDim[1] / 3) - doc[HeightSym]() / 2, width: doc[WidthSym](), height: doc[HeightSym](), payload: undefined diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e8738b292..63199ccbb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -885,13 +885,20 @@ export class CollectionFreeFormView extends CollectionSubView { + addDocTab = action((doc: Doc, where: string) => { + if (where === "inParent") { + const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y)); + doc.x = pt[0]; + doc.y = pt[1]; + this.props.addDocument(doc); + return true; + } if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { this.dataDoc[this.props.fieldKey] = new List([doc]); return true; } return this.props.addDocTab(doc, where); - } + }) getCalculatedPositions(params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): PoolData { const result = this.Document.arrangeScript?.script.run(params, console.log); if (result?.success) { @@ -1031,15 +1038,15 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.layoutDoc._layoutEngine === undefined) { - Doc.makeStarburst(this.layoutDoc); - } else { - this.layoutDoc._layoutEngine = undefined; - this.layoutDoc.overflow = "hidden"; - this.layoutDoc._fitToBox = undefined; - } - }); + promoteCollection = undoBatch(action(() => { + this.childDocs.forEach(doc => { + const scr = this.getTransform().inverse().transformPoint(NumCast(doc.x), NumCast(doc.y)); + doc.x = scr?.[0]; + doc.y = scr?.[1]; + this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc); + }) + this.props.ContainingCollectionView?.removeDocument(this.props.Document); + })); layoutDocsInGrid = () => { UndoManager.RunInBatch(() => { const docs = this.childLayoutPairs; @@ -1073,7 +1080,7 @@ export class CollectionFreeFormView extends CollectionSubView this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); - optionItems.push({ description: "Arrange Starburst", event: this.layoutStarburst, icon: "table" }); + this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" }); optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 2518ac629..d8e73a820 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -335,21 +335,33 @@ export class MarqueeView extends React.Component { - const bounds = this.Bounds; const selected = this.marqueeSelect(false); - selected.map(d => { + SelectionManager.DeselectAll(); + + let w = 0, h = 0; + selected.forEach((d, i) => { + Doc.iconify(d); + w = Math.max(d[WidthSym](), w); + h = Math.max(d[HeightSym](), h); + }); + selected.forEach((d, i) => { this.props.removeDocument(d); - d.x = NumCast(d.x) - bounds.left - bounds.width / 2; - d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; + d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - return d; }); const newCollection = this.getCollection(selected, false); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; + newCollection._width = newCollection._height = 110; + //newCollection.borderRounding = "40px"; + newCollection.jitterRotation = 10; + newCollection._backgroundColor = "yellow"; this.props.addDocument(newCollection); this.props.selectDocuments([newCollection], []); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); - Doc.makeStarburst(newCollection); + Doc.pileup(newCollection); } @action diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6c8e44e95..cc8773ed5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -307,7 +307,7 @@ export class DocumentView extends DocComponent(Docu UndoManager.RunInBatch(func, "on click"); } else func(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"); + UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"); //ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click"); } else if (this.Document.isLinkButton) { DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); @@ -403,6 +403,19 @@ export class DocumentView extends DocComponent(Docu } } + public iconify() { + const layoutKey = Cast(this.props.Document.layoutKey, "string", null); + const collapse = layoutKey !== "layout_icon"; + if (collapse) { + this.switchViews(collapse, "icon"); + if (layoutKey && layoutKey !== "layout") this.props.Document.deiconifyLayout = layoutKey.replace("layout_", ""); + } else { + const deiconifyLayout = Cast(this.props.Document.deiconifyLayout, "string", null); + this.switchViews(deiconifyLayout ? true : false, deiconifyLayout); + this.props.Document.deiconifyLayout = undefined; + } + } + @action handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); @@ -547,56 +560,6 @@ export class DocumentView extends DocComponent(Docu @undoBatch deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument?.(this.props.Document); } - // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - static makeCustomViewClicked = (doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { - const batch = UndoManager.StartBatch("makeCustomViewClicked"); - runInAction(() => { - doc.layoutKey = "layout_" + templateSignature; - if (doc[doc.layoutKey] === undefined) { - DocumentView.createCustomView(doc, creator, templateSignature, docLayoutTemplate); - } - }); - batch.end(); - } - static findTemplate(templateName: string, type: string, signature: string) { - let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); - const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); - // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized - // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); - return docLayoutTemplate; - } - static createCustomView = (doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) => { - const templateName = templateSignature.replace(/\(.*\)/, ""); - docLayoutTemplate = docLayoutTemplate || DocumentView.findTemplate(templateName, StrCast(doc.type), templateSignature); - - const customName = "layout_" + templateSignature; - const _width = NumCast(doc._width); - const _height = NumCast(doc._height); - const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; - - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { - fieldTemplate = Docs.Create.TextDocument("", options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); - } - const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); - docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); - } @undoBatch toggleLinkButtonBehavior = (): void => { @@ -667,15 +630,6 @@ export class DocumentView extends DocComponent(Docu this.Document.isLinkButton = true; } - @undoBatch - @action - setCustomView = (custom: boolean, layout: string): void => { - Doc.setNativeView(this.props.Document); - if (custom) { - DocumentView.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); - } - } - @undoBatch @action toggleBackground = (temporary: boolean): void => { @@ -747,7 +701,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); - onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocumentView.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); + onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); const funcs: ContextMenuProps[] = []; @@ -1114,7 +1068,14 @@ export class DocumentView extends DocComponent(Docu @computed get ignorePointerEvents() { return this.props.pointerEvents === false || (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } - + @undoBatch + @action + setCustomView = (custom: boolean, layout: string): void => { + Doc.setNativeView(this.props.Document); + if (custom) { + Doc.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); + } + } @observable _animate = 0; switchViews = action((custom: boolean, view: string) => { SelectionManager.SetIsDragging(true); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 6ed2a1b9e..f71343176 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -13,22 +13,24 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from " import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; -import { DataSym, Doc, DocListCastAsync, Field, HeightSym, Opt, WidthSym, DocListCast } from "../../../new_fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; import { documentSchema } from '../../../new_fields/documentSchemas'; import { Id } from '../../../new_fields/FieldSymbols'; import { InkTool } from '../../../new_fields/InkField'; +import { PrefetchProxy } from '../../../new_fields/Proxy'; import { RichTextField } from "../../../new_fields/RichTextField"; import { RichTextUtils } from '../../../new_fields/RichTextUtils'; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, StrCast, BoolCast, DateCast } from "../../../new_fields/Types"; +import { Cast, DateCast, NumCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from '../../../new_fields/util'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, Utils, returnTrue, returnZero } from '../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils } from '../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DictationManager } from '../../util/DictationManager'; import { DragManager } from "../../util/DragManager"; +import { makeTemplate } from '../../util/DropConverter'; import buildKeymap from "../../util/ProsemirrorExampleTransfer"; import RichTextMenu from '../../util/RichTextMenu'; import { RichTextRules } from "../../util/RichTextRules"; @@ -46,9 +48,6 @@ import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment'; import React = require("react"); -import { PrefetchProxy } from '../../../new_fields/Proxy'; -import { makeTemplate } from '../../util/DropConverter'; -import { DocumentView } from './DocumentView'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -436,24 +435,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null); DocListCast(noteTypesDoc?.data).forEach(note => { changeItems.push({ - description: StrCast(note.title), event: () => { + description: StrCast(note.title), event: undoBatch(() => { Doc.setNativeView(this.props.Document); - DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); - }, icon: "eye" + Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note); + }), icon: "eye" }); }); - changeItems.push({ description: "FreeForm", event: () => DocumentView.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }); + changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" }); !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); const open = cm.findByDescription("Open New Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ - description: "FreeForm", event: () => { + description: "FreeForm", event: undoBatch(() => { const alias = Doc.MakeAlias(this.rootDoc); - DocumentView.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform"); + Doc.makeCustomViewClicked(alias, Docs.Create.FreeformDocument, "freeform"); this.props.addDocTab(alias, "onRight"); - }, icon: "eye" + }), icon: "eye" }); !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 46b60b554..acaa4363e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,37 +1,36 @@ -import { action, computed, IReactionDisposer, observable, reaction, trace, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; +import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc"; -import { Id, Copy } from "../../../new_fields/FieldSymbols"; +import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; +import { documentSchema } from "../../../new_fields/documentSchemas"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { InkTool } from "../../../new_fields/InkField"; import { List } from "../../../new_fields/List"; -import { makeInterface, createSchema } from "../../../new_fields/Schema"; -import { ScriptField, ComputedField } from "../../../new_fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { smoothScroll, Utils, emptyFunction, returnOne, intersectRect, addStyleSheet, addStyleSheetRule, clearStyleSheetRules, returnZero, emptyPath } from "../../../Utils"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { PdfField } from "../../../new_fields/URLField"; +import { TraceMobx } from "../../../new_fields/util"; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; +import { DocumentType } from "../../documents/DocumentTypes"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; -import { Transform } from "../../util/Transform"; -import PDFMenu from "./PDFMenu"; -import "./PDFViewer.scss"; -import React = require("react"); -import * as rp from "request-promise"; -import { CollectionView } from "../collections/CollectionView"; -import Annotation from "./Annotation"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { SelectionManager } from "../../util/SelectionManager"; +import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionView } from "../collections/CollectionView"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { documentSchema } from "../../../new_fields/documentSchemas"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; -import { InkTool } from "../../../new_fields/InkField"; -import { TraceMobx } from "../../../new_fields/util"; -import { PdfField } from "../../../new_fields/URLField"; -import { DocumentView } from "../nodes/DocumentView"; +import Annotation from "./Annotation"; +import PDFMenu from "./PDFMenu"; +import "./PDFViewer.scss"; +import React = require("react"); const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -574,7 +573,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent([clipDoc]); clipDoc.rootDocument = targetDoc; - DocumentView.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined); + Doc.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined); targetDoc.layoutKey = "layout"; // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title }); // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy](); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1373431b8..cc929c040 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -17,7 +17,8 @@ import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types"; import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util"; -import { Docs } from "../client/documents/Documents"; +import { Docs, DocumentOptions } from "../client/documents/Documents"; +import { PdfField, VideoField, AudioField, ImageField } from "./URLField"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -913,18 +914,86 @@ export namespace Doc { return false; } - export function makeStarburst(newCollection: Doc) { - newCollection._layoutEngine = "starburst"; - newCollection._fitToBox = true; + // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) + export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { + const batch = UndoManager.StartBatch("makeCustomViewClicked"); + runInAction(() => { + doc.layoutKey = "layout_" + templateSignature; + if (doc[doc.layoutKey] === undefined) { + createCustomView(doc, creator, templateSignature, docLayoutTemplate); + } + }); + batch.end(); + } + export function findTemplate(templateName: string, type: string, signature: string) { + let docLayoutTemplate: Opt; + const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); + const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); + // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized + // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); + return docLayoutTemplate; + } + export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { + const templateName = templateSignature.replace(/\(.*\)/, ""); + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.type), templateSignature); + + const customName = "layout_" + templateSignature; + const _width = NumCast(doc._width); + const _height = NumCast(doc._height); + const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; + + let fieldTemplate: Opt; + if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { + fieldTemplate = Docs.Create.TextDocument("", options); + } else if (doc.data instanceof PdfField) { + fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); + } else if (doc.data instanceof VideoField) { + fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof AudioField) { + fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof ImageField) { + fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); + } + const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + } + export function makeCustomView(doc: Doc, custom: boolean, layout: string) { + Doc.setNativeView(doc); + if (custom) { + makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); + } + } + export function iconify(doc: Doc) { + const layoutKey = Cast(doc.layoutKey, "string", null); + Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined); + if (layoutKey && layoutKey !== "layout") doc.deiconifyLayout = layoutKey.replace("layout_", ""); + } + + export function pileup(newCollection: Doc) { + newCollection._layoutEngine = "pass"; newCollection.overflow = "visible"; const script = "if (self._layoutEngine === 'starburst') {" + - " self._layoutEngine = 'pass';" + " self.overflow = undefined;" + - " self.fitToContentScaling=undefined;" + + " self.x = self.x + self._width/2 - 55; " + + " self.y = self.y + self._height/2 - 55; " + + " self._width = self._height = 110;" + + " self._layoutEngine = 'pass';" + " } else {" + - " self._layoutEngine = 'starburst';" + " self.overflow = 'visible';" + - " self.fitToContentScaling=10;" + + " !self.starburstRadius && (self.starburstRadius = 500);" + + " if (self._layoutEngine === 'pass') { " + + " self.x = self.x + self._width/2 - 12.5; " + + " self.y = self.y + self._height/2 - 12.5; " + + " }; " + + " self._width = self._height = 25;" + + " self._layoutEngine = 'starburst';" + " };"; newCollection.onClick = ScriptField.MakeScript(script, { self: Doc.name }); } diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts index c211b3d3c..635fd053d 100644 --- a/src/new_fields/RichTextUtils.ts +++ b/src/new_fields/RichTextUtils.ts @@ -1,23 +1,21 @@ -import { EditorState, Transaction, TextSelection } from "prosemirror-state"; -import { Node, Fragment, Mark } from "prosemirror-model"; -import { RichTextField } from "./RichTextField"; +import { AssertionError } from "assert"; import { docs_v1 } from "googleapis"; -import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils"; -import { FormattedTextBox } from "../client/views/nodes/FormattedTextBox"; -import { Opt, Doc } from "./Doc"; -import Color = require('color'); +import { Fragment, Mark, Node } from "prosemirror-model"; import { sinkListItem } from "prosemirror-schema-list"; -import { Utils } from "../Utils"; -import { Docs } from "../client/documents/Documents"; -import { schema } from "../client/util/RichTextSchema"; +import { EditorState, TextSelection, Transaction } from "prosemirror-state"; +import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils"; import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils"; import { DocServer } from "../client/DocServer"; -import { Cast, StrCast } from "./Types"; -import { Id } from "./FieldSymbols"; -import { DocumentView } from "../client/views/nodes/DocumentView"; -import { AssertionError } from "assert"; +import { Docs } from "../client/documents/Documents"; import { Networking } from "../client/Network"; -import { extname } from "path"; +import { schema } from "../client/util/RichTextSchema"; +import { FormattedTextBox } from "../client/views/nodes/FormattedTextBox"; +import { Utils } from "../Utils"; +import { Doc, Opt } from "./Doc"; +import { Id } from "./FieldSymbols"; +import { RichTextField } from "./RichTextField"; +import { Cast, StrCast } from "./Types"; +import Color = require('color'); export namespace RichTextUtils { @@ -274,7 +272,7 @@ export namespace RichTextUtils { const backingDocId = StrCast(textNote[guid]); if (!backingDocId) { const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 }); - DocumentView.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument); + Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument); docid = backingDoc[Id]; textNote[guid] = docid; } else { @@ -403,7 +401,7 @@ export namespace RichTextUtils { let exported = (await Cast(linkDoc.anchor2, Doc))!; if (!exported.customLayout) { exported = Doc.MakeAlias(exported); - DocumentView.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); + Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); linkDoc.anchor2 = exported; } url = Utils.shareUrl(exported[Id]); -- cgit v1.2.3-70-g09d2 From a51439a6569079ca2652ce28745317fecce3a353 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 10:53:51 -0400 Subject: changing how multiple images are imported -- now a pile. imagebox chooses render quality based on image width. --- src/client/views/collections/CollectionSubView.tsx | 11 +++++++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 22 ++-------------------- src/client/views/nodes/ImageBox.tsx | 4 ++-- src/new_fields/Doc.ts | 21 ++++++++++++++++++++- 4 files changed, 33 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index ca38a21b8..78ba3d6e6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -398,8 +398,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: generatedDocuments.push(doc); } if (generatedDocuments.length) { - generatedDocuments.forEach(addDocument); - completed && completed(); + const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d)); + if (set) { + const pile = Docs.Create.FreeformDocument(generatedDocuments, { ...options, title: "pile", _LODdisable: true, }); + Doc.pileup(pile, generatedDocuments); + addDocument(pile); + } else { + generatedDocuments.forEach(addDocument); + } + completed?.(); } else { if (text && !text.includes("https://")) { addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d8e73a820..ab8174ba1 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -337,31 +337,13 @@ export class MarqueeView extends React.Component { const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - - let w = 0, h = 0; - selected.forEach((d, i) => { - Doc.iconify(d); - w = Math.max(d[WidthSym](), w); - h = Math.max(d[HeightSym](), h); - }); - selected.forEach((d, i) => { - this.props.removeDocument(d); - d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; - d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; - d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - }); + selected.forEach(d => this.props.removeDocument(d)); const newCollection = this.getCollection(selected, false); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; - newCollection._width = newCollection._height = 110; - //newCollection.borderRounding = "40px"; - newCollection.jitterRotation = 10; - newCollection._backgroundColor = "yellow"; + Doc.pileup(newCollection, selected); this.props.addDocument(newCollection); this.props.selectDocuments([newCollection], []); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); - Doc.pileup(newCollection); } @action diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index cf6a7ba5b..e189f5814 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -223,8 +223,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent { + Doc.iconify(d); + w = Math.max(d[WidthSym](), w); + h = Math.max(d[HeightSym](), h); + }); + h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable + selected.forEach((d, i) => { + d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; + d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; + d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; + newCollection._width = newCollection._height = 110; + //newCollection.borderRounding = "40px"; + newCollection.jitterRotation = 10; + newCollection._backgroundColor = "gray"; + newCollection._layoutEngine = "pass"; newCollection.overflow = "visible"; const script = "if (self._layoutEngine === 'starburst') {" + -- cgit v1.2.3-70-g09d2 From edf82f7a686169ea279627bac1c661aaa3459ec9 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 11:44:03 -0400 Subject: more starburst tweaks. --- .../views/collections/CollectionTimeView.tsx | 2 +- .../CollectionFreeFormLayoutEngines.tsx | 5 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +-- src/client/views/nodes/DocumentBox.tsx | 2 +- src/new_fields/Doc.ts | 43 +++++++++++++--------- 5 files changed, 33 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 06ebf6d2d..63498cea1 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -29,7 +29,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @observable _childClickedScript: Opt; @observable _viewDefDivClick: Opt; async componentDidMount() { - const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || DocumentView.findTemplate("detailView", StrCast(this.props.Document.type), ""); + const detailView = (await DocCastAsync(this.props.Document.childDetailView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), ""); const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; runInAction(() => { this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 57f62347a..7152ff242 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -108,7 +108,8 @@ export function computerStarburstLayout( viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { const docMap = new Map(); - const burstDim = [NumCast(pivotDoc.starburstRadius, panelDim[0]), NumCast(pivotDoc.starburstRadius, panelDim[1])] + const burstDim = [NumCast(pivotDoc.starburstRadius, panelDim[0]), NumCast(pivotDoc.starburstRadius, panelDim[1])]; + const scaleDim = [NumCast(pivotDoc._starburstWidth, burstDim[0]) * 5, NumCast(pivotDoc._starburstHeight, burstDim[1]) * 5]; childDocs.forEach((doc, i) => { const deg = i / childDocs.length * Math.PI * 2; docMap.set(doc, { @@ -120,7 +121,7 @@ export function computerStarburstLayout( payload: undefined }); }); - return normalizeResults(burstDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c))); + return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c))); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 63199ccbb..1f0bc89c5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -898,7 +898,7 @@ export class CollectionFreeFormView extends CollectionSubView(); const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.(); switch (engine) { - case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) } + case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) }; case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) }; case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) }; case "starburst": return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) }; @@ -1044,7 +1044,7 @@ export class CollectionFreeFormView extends CollectionSubView { diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index 7583aa070..0111cadd5 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -111,7 +111,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { - DocumentView.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName); + Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName); Doc.expandTemplateLayout(Cast(containedDoc["layout_" + childTemplateName], Doc, null), containedDoc, undefined); }, 0); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 52c3ce141..48606833c 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -998,23 +998,7 @@ export namespace Doc { newCollection._layoutEngine = "pass"; newCollection.overflow = "visible"; - const script = "if (self._layoutEngine === 'starburst') {" + - " self.overflow = undefined;" + - " self.x = self.x + self._width/2 - 55; " + - " self.y = self.y + self._height/2 - 55; " + - " self._width = self._height = 110;" + - " self._layoutEngine = 'pass';" + - " } else {" + - " self.overflow = 'visible';" + - " !self.starburstRadius && (self.starburstRadius = 500);" + - " if (self._layoutEngine === 'pass') { " + - " self.x = self.x + self._width/2 - 12.5; " + - " self.y = self.y + self._height/2 - 12.5; " + - " }; " + - " self._width = self._height = 25;" + - " self._layoutEngine = 'starburst';" + - " };"; - newCollection.onClick = ScriptField.MakeScript(script, { self: Doc.name }); + newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self)", { self: Doc.name }); } @@ -1071,4 +1055,27 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo return docs.length ? new List(docs) : prevValue; }); Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); -Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file +Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); +Scripting.addGlobal(function toggleStarburst(self: Doc) { + if (self._layoutEngine === 'starburst') { + const defaultSize = 110; + self.overflow = undefined; + self.x = NumCast(self.x) + self[WidthSym]() / 2 - NumCast(self._starburstWidth, defaultSize) / 2; + self.y = NumCast(self.y) + self[HeightSym]() / 2 - NumCast(self._starburstHeight, defaultSize) / 2; + self._width = NumCast(self._starburstWidth, defaultSize); + self._height = NumCast(self._starburstHeight, defaultSize); + self._layoutEngine = 'pass'; + } else { + const defaultSize = 25; + self.overflow = 'visible'; + !self.starburstRadius && (self.starburstRadius = 200); + if (self._layoutEngine === 'pass') { + self.x = NumCast(self.x) + self[WidthSym]() / 2 - defaultSize / 2; + self.y = NumCast(self.y) + self[HeightSym]() / 2 - defaultSize / 2; + self._starburstWidth = self._width; + self._starburstHeight = self._height; + } + self._width = self._height = defaultSize; + self._layoutEngine = 'starburst'; + } +}); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 81229ca27c6e21d4b5f0ee3a5168c766dcfb954a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 13:28:05 -0400 Subject: fixed presbox navigation bar layout height. --- src/client/views/collections/CollectionView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- src/client/views/nodes/PresBox.scss | 5 +++++ 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 4f1242c33..47e79f088 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -201,7 +201,7 @@ export class CollectionView extends Touchable { ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" }); } subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" }); - subItems.push({ description: "Treeview", event: () => func(CollectionViewType.Tree), icon: "tree" }); + subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" }); subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" }); subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" }); subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" }); @@ -222,7 +222,7 @@ export class CollectionView extends Touchable { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); - this.setupViewTypes("Open New Perspective...", vtype => { + this.setupViewTypes("New Perspective...", vtype => { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; this.props.addDocTab(newRendition, "onRight"); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cc8773ed5..4bbdde3ea 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -687,11 +687,11 @@ export class DocumentView extends DocComponent(Docu layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); !existing && cm.addItem({ description: "Options...", subitems: layoutItems, icon: "compass" }); - const open = cm.findByDescription("Open New Perspective..."); + const open = cm.findByDescription("New Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); templateDoc && openItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); + !open && cm.addItem({ description: "New Perspective...", subitems: openItems, icon: "external-link-alt" }); const existingOnClick = cm.findByDescription("OnClick..."); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f71343176..a018f17e8 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -444,7 +444,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" }); !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); - const open = cm.findByDescription("Open New Perspective..."); + const open = cm.findByDescription("New Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ @@ -454,7 +454,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.props.addDocTab(alias, "onRight"); }), icon: "eye" }); - !open && cm.addItem({ description: "Open New Perspective...", subitems: openItems, icon: "external-link-alt" }); + !open && cm.addItem({ description: "New Perspective...", subitems: openItems, icon: "external-link-alt" }); } diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index 6676d8cd2..78c19f351 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -11,6 +11,11 @@ overflow: hidden; transition: 0.7s opacity ease; + .presBox-listCont { + position: absolute; + height: calc(100% - 25px); + width: 100%; + } .presBox-buttons { padding: 10px; width: 100%; -- cgit v1.2.3-70-g09d2 From 95efa332e0fd7ba9dfddf45c71f02a37052ca24e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 15:41:57 -0400 Subject: more starburst fixes --- .../CollectionFreeFormLayoutEngines.tsx | 14 +++++----- src/client/views/nodes/DocumentView.tsx | 2 +- src/new_fields/Doc.ts | 31 +++++++++++----------- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 7152ff242..566a6788b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -108,16 +108,18 @@ export function computerStarburstLayout( viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { const docMap = new Map(); - const burstDim = [NumCast(pivotDoc.starburstRadius, panelDim[0]), NumCast(pivotDoc.starburstRadius, panelDim[1])]; - const scaleDim = [NumCast(pivotDoc._starburstWidth, burstDim[0]) * 5, NumCast(pivotDoc._starburstHeight, burstDim[1]) * 5]; + const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])]; + const docScale = NumCast(pivotDoc._starburstDocScale); + const docSize = docScale * 100; // assume a icon sized at 100 + const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize]; childDocs.forEach((doc, i) => { const deg = i / childDocs.length * Math.PI * 2; docMap.set(doc, { type: "doc", - x: Math.cos(deg) * (burstDim[0] / 3) - doc[WidthSym]() / 2, - y: Math.sin(deg) * (burstDim[1] / 3) - doc[HeightSym]() / 2, - width: doc[WidthSym](), - height: doc[HeightSym](), + x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * doc[WidthSym]() / 2, + y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * doc[HeightSym]() / 2, + width: docScale * doc[WidthSym](), + height: docScale * doc[HeightSym](), payload: undefined }); }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4bbdde3ea..06bd40992 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -408,7 +408,7 @@ export class DocumentView extends DocComponent(Docu const collapse = layoutKey !== "layout_icon"; if (collapse) { this.switchViews(collapse, "icon"); - if (layoutKey && layoutKey !== "layout") this.props.Document.deiconifyLayout = layoutKey.replace("layout_", ""); + if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.props.Document.deiconifyLayout = layoutKey.replace("layout_", ""); } else { const deiconifyLayout = Cast(this.props.Document.deiconifyLayout, "string", null); this.switchViews(deiconifyLayout ? true : false, deiconifyLayout); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 48606833c..18ffc38f5 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -973,7 +973,7 @@ export namespace Doc { export function iconify(doc: Doc) { const layoutKey = Cast(doc.layoutKey, "string", null); Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined); - if (layoutKey && layoutKey !== "layout") doc.deiconifyLayout = layoutKey.replace("layout_", ""); + if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); } export function pileup(newCollection: Doc, selected: Doc[]) { @@ -998,7 +998,7 @@ export namespace Doc { newCollection._layoutEngine = "pass"; newCollection.overflow = "visible"; - newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self)", { self: Doc.name }); + newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self, this)", { self: Doc.name, this: Doc.name }); } @@ -1056,26 +1056,27 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo }); Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); -Scripting.addGlobal(function toggleStarburst(self: Doc) { +Scripting.addGlobal(function toggleStarburst(self: Doc, tLayout: Doc) { if (self._layoutEngine === 'starburst') { const defaultSize = 110; - self.overflow = undefined; - self.x = NumCast(self.x) + self[WidthSym]() / 2 - NumCast(self._starburstWidth, defaultSize) / 2; - self.y = NumCast(self.y) + self[HeightSym]() / 2 - NumCast(self._starburstHeight, defaultSize) / 2; - self._width = NumCast(self._starburstWidth, defaultSize); - self._height = NumCast(self._starburstHeight, defaultSize); + tLayout.overflow = undefined; + self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - NumCast(tLayout._starburstPileWidth, defaultSize) / 2; + self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - NumCast(tLayout._starburstPileHeight, defaultSize) / 2; + tLayout._width = NumCast(tLayout._starburstPileWidth, defaultSize); + tLayout._height = NumCast(tLayout._starburstPileHeight, defaultSize); self._layoutEngine = 'pass'; } else { const defaultSize = 25; - self.overflow = 'visible'; - !self.starburstRadius && (self.starburstRadius = 200); + tLayout.overflow = 'visible'; + !tLayout._starburstRadius && (tLayout._starburstRadius = 500); + !tLayout._starburstDocScale && (tLayout._starburstDocScale = 2.5); if (self._layoutEngine === 'pass') { - self.x = NumCast(self.x) + self[WidthSym]() / 2 - defaultSize / 2; - self.y = NumCast(self.y) + self[HeightSym]() / 2 - defaultSize / 2; - self._starburstWidth = self._width; - self._starburstHeight = self._height; + self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - defaultSize / 2; + self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - defaultSize / 2; + tLayout._starburstPileWidth = tLayout[WidthSym](); + tLayout._starburstPileHeight = tLayout[HeightSym](); } - self._width = self._height = defaultSize; + tLayout._width = tLayout._height = defaultSize; self._layoutEngine = 'starburst'; } }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f5b6837dba732780930594e76a3676a4264639d7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 19:56:02 -0400 Subject: fixed problems with PivotView -- docs that show up when they shouldn't and not being able to click the pivot background sometimes. --- .../CollectionFreeFormLayoutEngines.tsx | 15 ++++++++++++--- .../collectionFreeForm/CollectionFreeFormView.scss | 9 +++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 7 ++++--- 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 566a6788b..9f7919ada 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -136,6 +136,7 @@ export function computePivotLayout( panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { + const docMap = new Map(); const fieldKey = "data"; const pivotColumnGroups = new Map, PivotColumn>(); @@ -145,6 +146,16 @@ export function computePivotLayout( if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val] }); pivotColumnGroups.get(val)!.docs.push(doc); + } else { + docMap.set(doc, { + type: "doc", + x: 0, + y: 0, + zIndex: -99, + width: 0, + height: 0, + payload: undefined + }) } } let nonNumbers = 0; @@ -193,7 +204,6 @@ export function computePivotLayout( } } - const docMap = new Map(); const groupNames: ViewDefBounds[] = []; const expander = 1.05; @@ -366,10 +376,9 @@ export function computeTimelineLayout( function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, poolData: Map, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[], extraDocs: Doc[]): ViewDefResult[] { - const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds); const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as ViewDefBounds); - const aggBounds = aggregateBounds(docEles.concat(grpEles), 0, 0); + const aggBounds = aggregateBounds(docEles.concat(grpEles).filter(e => e.zIndex !== -99), 0, 0); aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x); const wscale = panelDim[0] / (aggBounds.r - aggBounds.x); let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 4e4e85e13..60c39c825 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -13,6 +13,15 @@ border-radius: inherit; } +.collectionfreeformview-viewdef { + > .collectionFreeFormDocumentView-container { + pointer-events: none; + .contentFittingDocumentDocumentView-previewDoc { + pointer-events: all; + } + } +} + .collectionfreeformview-ease { transition: transform 500ms; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1f0bc89c5..b3cf2b97a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1010,7 +1010,7 @@ export class CollectionFreeFormView extends CollectionSubView + easing={this.easing} viewDefDivClick={this.props.viewDefDivClick} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> {this.children} ; @@ -1223,6 +1223,7 @@ interface CollectionFreeFormViewPannableContentsProps { panY: () => number; zoomScaling: () => number; easing: () => boolean; + viewDefDivClick?: ScriptField; children: () => JSX.Element[]; shifted: boolean; } @@ -1230,7 +1231,7 @@ interface CollectionFreeFormViewPannableContentsProps { @observer class CollectionFreeFormViewPannableContents extends React.Component{ render() { - const freeformclass = "collectionfreeformview" + (this.props.easing() ? "-ease" : "-none"); + const freeformclass = "collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : (this.props.easing() ? "-ease" : "-none")); const cenx = this.props.centeringShiftX(); const ceny = this.props.centeringShiftY(); const panx = -this.props.panX(); -- cgit v1.2.3-70-g09d2 From 896c0fef965458bd23ff098c7c95b09799528033 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Apr 2020 23:00:26 -0400 Subject: changed pivot view to support lists of values for keys (docs can appear mulltiple times as replicas) --- .../CollectionFreeFormLayoutEngines.tsx | 122 +++++++++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 47 ++++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 +- 3 files changed, 100 insertions(+), 74 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 9f7919ada..d8bd18ec7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -9,6 +9,7 @@ import React = require("react"); import { Id, ToString } from "../../../../new_fields/FieldSymbols"; import { ObjectField } from "../../../../new_fields/ObjectField"; import { RefField } from "../../../../new_fields/RefField"; +import { listSpec } from "../../../../new_fields/Schema"; export interface ViewDefBounds { type: string; @@ -24,6 +25,8 @@ export interface ViewDefBounds { highlight?: boolean; color?: string; payload: any; + replica?: string; + pair?: { layout: Doc, data?: Doc }; } export interface PoolData { @@ -36,6 +39,8 @@ export interface PoolData { color?: string; transition?: string; highlight?: boolean; + replica?: string; + pair: { layout: Doc, data?: Doc } } export interface ViewDefResult { @@ -72,100 +77,111 @@ function getTextWidth(text: string, font: string): number { interface PivotColumn { docs: Doc[]; + replicas: string[]; filters: string[]; } export function computerPassLayout( poolData: Map, pivotDoc: Doc, - childDocs: Doc[], filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); - childDocs.forEach((doc, i) => { - docMap.set(doc, { + const docMap = new Map(); + childPairs.forEach(({ layout, data }, i) => { + docMap.set(layout[Id], { type: "doc", - x: NumCast(doc.x), - y: NumCast(doc.y), - width: doc[WidthSym](), - height: doc[HeightSym](), - payload: undefined + x: NumCast(layout.x), + y: NumCast(layout.y), + width: layout[WidthSym](), + height: layout[HeightSym](), + payload: undefined, + pair: { layout, data }, + replica: "" }); }); - return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c))); + return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); } export function computerStarburstLayout( poolData: Map, pivotDoc: Doc, - childDocs: Doc[], filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); + const docMap = new Map(); const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])]; const docScale = NumCast(pivotDoc._starburstDocScale); const docSize = docScale * 100; // assume a icon sized at 100 const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize]; - childDocs.forEach((doc, i) => { - const deg = i / childDocs.length * Math.PI * 2; - docMap.set(doc, { + childPairs.forEach((pair, i) => { + const deg = i / childPairs.length * Math.PI * 2; + docMap.set(pair.layout[Id], { type: "doc", - x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * doc[WidthSym]() / 2, - y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * doc[HeightSym]() / 2, - width: docScale * doc[WidthSym](), - height: docScale * doc[HeightSym](), - payload: undefined + x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * pair.layout[WidthSym]() / 2, + y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * pair.layout[HeightSym]() / 2, + width: docScale * pair.layout[WidthSym](), + height: docScale * pair.layout[HeightSym](), + payload: undefined, + replica: "" }); }); - return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childDocs.filter(c => !filterDocs.includes(c))); + return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); } export function computePivotLayout( poolData: Map, pivotDoc: Doc, - childDocs: Doc[], filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); + const docMap = new Map(); const fieldKey = "data"; const pivotColumnGroups = new Map, PivotColumn>(); const pivotFieldKey = toLabel(pivotDoc._pivotField); for (const doc of filterDocs) { + const lval = Cast(doc[pivotFieldKey], listSpec("string"), null); const val = Field.toString(doc[pivotFieldKey] as Field); - if (val) { - !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val] }); + if (lval) { + lval.forEach((val, i) => { + !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); + pivotColumnGroups.get(val)!.docs.push(doc); + pivotColumnGroups.get(val)!.replicas.push(i.toString()); + }); + } else if (val) { + !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); pivotColumnGroups.get(val)!.docs.push(doc); + pivotColumnGroups.get(val)!.replicas.push(""); } else { - docMap.set(doc, { + docMap.set(doc[Id], { type: "doc", x: 0, y: 0, zIndex: -99, width: 0, height: 0, - payload: undefined + payload: undefined, + pair: { layout: doc }, + replica: "" }) } } let nonNumbers = 0; - childDocs.map(doc => { - const num = toNumber(doc[pivotFieldKey]); + childPairs.map(pair => { + const num = toNumber(pair.layout[pivotFieldKey]); if (num === undefined || Number.isNaN(num)) { nonNumbers++; } }); - const pivotNumbers = nonNumbers / childDocs.length < .1; + const pivotNumbers = nonNumbers / childPairs.length < .1; if (pivotColumnGroups.size > 10) { const arrayofKeys = Array.from(pivotColumnGroups.keys()); const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort(); @@ -177,6 +193,7 @@ export function computePivotLayout( const newgrp = pivotColumnGroups.get(sortedKeys[j])!; curgrp.docs.push(...newgrp.docs); curgrp.filters.push(...newgrp.filters); + curgrp.replicas.push(...newgrp.replicas); pivotColumnGroups.delete(sortedKeys[j]); } } @@ -226,7 +243,7 @@ export function computePivotLayout( fontSize, payload: val }); - for (const doc of val.docs) { + val.docs.forEach((doc, i) => { const layoutDoc = Doc.Layout(doc); let wid = pivotAxisWidth; let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth; @@ -234,27 +251,29 @@ export function computePivotLayout( hgt = pivotAxisWidth; wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } - docMap.set(doc, { + docMap.set(doc[Id] + (val.replicas || ""), { type: "doc", x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0), y: -y + (pivotAxisWidth - hgt) / 2, width: wid, height: hgt, - payload: undefined + payload: undefined, + pair: { layout: doc }, + replica: val.replicas[i] }); xCount++; if (xCount >= numCols) { xCount = 0; y += pivotAxisWidth * expander; } - } + }); x += pivotAxisWidth * (numCols * expander + gap); }); const dividers = sortedPivotKeys.map((key, i) => ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters })); groupNames.push(...dividers); - return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childDocs.filter(c => !filterDocs.includes(c))); + return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); } function toNumber(val: FieldResult) { @@ -264,7 +283,6 @@ function toNumber(val: FieldResult) { export function computeTimelineLayout( poolData: Map, pivotDoc: Doc, - childDocs: Doc[], filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], @@ -272,7 +290,7 @@ export function computeTimelineLayout( ) { const fieldKey = "data"; const pivotDateGroups = new Map(); - const docMap = new Map(); + const docMap = new Map(); const groupNames: ViewDefBounds[] = []; const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field); const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]); @@ -351,7 +369,7 @@ export function computeTimelineLayout( } const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; - return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childDocs.filter(c => !filterDocs.includes(c))); + return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childPairs.filter(c => !filterDocs.includes(c.layout))); function layoutDocsAtTime(keyDocs: Doc[], key: number) { keyDocs.forEach(doc => { @@ -363,43 +381,51 @@ export function computeTimelineLayout( hgt = pivotAxisWidth; wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } - docMap.set(doc, { + docMap.set(doc[Id], { type: "doc", x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2, - zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt / (Math.max(stack, 1)), payload: undefined + zIndex: (curTime === key ? 1000 : zind++), + highlight: curTime === key, + width: wid / (Math.max(stack, 1)), + height: hgt / (Math.max(stack, 1)), + payload: undefined, + pair: { layout: doc }, + replica: "" }); stacking[stack] = x + pivotAxisWidth; }); } } -function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, +function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, poolData: Map, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[], - extraDocs: Doc[]): ViewDefResult[] { + extraDocs: { data?: Doc, layout: Doc }[]): ViewDefResult[] { const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds); - const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as ViewDefBounds); + const docEles = Array.from(docMap.entries()).map(ele => ele[1]); const aggBounds = aggregateBounds(docEles.concat(grpEles).filter(e => e.zIndex !== -99), 0, 0); aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x); const wscale = panelDim[0] / (aggBounds.r - aggBounds.x); let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale; if (Number.isNaN(scale)) scale = 1; - childPairs.filter(d => docMap.get(d.layout)).map(pair => { - const newPosRaw = docMap.get(pair.layout); + Array.from(docMap.entries()).filter(ele => ele[1].pair).map(ele => { + const newPosRaw = ele[1]; if (newPosRaw) { const newPos = { x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, + replica: newPosRaw.replica, highlight: newPosRaw.highlight, zIndex: newPosRaw.zIndex, width: (newPosRaw.width || 0) * scale, - height: newPosRaw.height! * scale + height: newPosRaw.height! * scale, + pair: ele[1].pair! }; - poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }); + poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "transform 1s", ...newPos }); } }); - extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 })); + extraDocs.map(ed => poolData.set(ed.layout[Id], { x: 0, y: 0, zIndex: -99, pair: ed })); return viewDefsToJSX(extras.concat(groupNames).map(gname => ({ type: gname.type, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b3cf2b97a..5555d9e84 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -85,8 +85,8 @@ export class CollectionFreeFormView extends CollectionSubView(); - private _cachedPool: Map = new Map(); + private _layoutPoolData = new ObservableMap(); + private _cachedPool: Map = new Map(); @observable private _pullCoords: number[] = [0, 0]; @observable private _pullDirection: string = ""; @@ -728,7 +728,7 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout instanceof Doc).map(pair => pair.layout); - const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc)).map(doc => this.childDataProvider(doc)); + const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc, "")).map(doc => this.childDataProvider(doc, "")); if (measuredDocs.length) { const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) => // computes range of content ({ @@ -899,16 +899,16 @@ export class CollectionFreeFormView extends CollectionSubView) { + return this._layoutPoolData.get(doc[Id] + (replica || "")); }.bind(this)); doEngineLayout(poolData: Map, engine: ( poolData: Map, pivotDoc: Doc, - childDocs: Doc[], filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]) { - return engine(poolData, this.props.Document, this.childDocs, this.childDocs, + return engine(poolData, this.props.Document, this.childDocs, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } @@ -970,14 +969,14 @@ export class CollectionFreeFormView extends CollectionSubView this.isCurrent(pair.layout)).map((pair, i) => { - const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state }); + const pos = this.getCalculatedPositions({ pair, index: i, collection: this.Document, docs: layoutDocs, state }); poolData.set(pair.layout[Id], pos); }); return elements; } @computed get doInternalLayoutComputation() { - const newPool = new Map(); + const newPool = new Map(); const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.(); switch (engine) { case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) }; @@ -992,22 +991,22 @@ export class CollectionFreeFormView extends CollectionSubView - Array.from(newPool.keys()).map(key => { - const lastPos = this._cachedPool.get(key); // last computed pos - const newPos = newPool.get(key); + Array.from(newPool.entries()).map(entry => { + const lastPos = this._cachedPool.get(entry[0]); // last computed pos + const newPos = entry[1]; if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex || newPos.width !== lastPos.width || newPos.height !== lastPos.height) { - this._layoutPoolData.set(key, newPos); + this._layoutPoolData.set(entry[0], newPos); } })); this._cachedPool.clear(); - Array.from(newPool.keys()).forEach(k => this._cachedPool.set(k, newPool.get(k))); + Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1])); const elements: ViewDefResult[] = computedElementData.slice(); - const engine = this.props.layoutEngine?.() || this.props.Document._layoutEngine; - this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair => + Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry => elements.push({ ele: , - bounds: this.childDataProvider(pair.layout) + bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) })); return elements; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 4b282b0c9..baeac311e 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,7 +12,7 @@ import { TraceMobx } from "../../../new_fields/util"; import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { - dataProvider?: (doc: Doc) => { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string } | undefined; + dataProvider?: (doc: Doc, replica?: string) => { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string, replica: string } | undefined; x?: number; y?: number; z?: number; @@ -23,6 +23,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { jitterRotation: number; transition?: string; fitToBox?: boolean; + replica?: string; } @observer @@ -46,7 +47,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Sat, 25 Apr 2020 03:42:51 -0400 Subject: hopefully final cleanup of pile up collection view. added doubleTap support in setupMoveUpEvents --- src/Utils.ts | 12 ++- src/client/documents/Documents.ts | 4 + .../views/collections/CollectionPileView.scss | 8 ++ .../views/collections/CollectionPileView.tsx | 97 ++++++++++++++++++++++ src/client/views/collections/CollectionSubView.tsx | 4 +- .../views/collections/CollectionTimeView.tsx | 3 +- src/client/views/collections/CollectionView.tsx | 5 +- .../CollectionFreeFormLayoutEngines.tsx | 95 ++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++-- .../collections/collectionFreeForm/MarqueeView.tsx | 16 ++-- src/new_fields/Doc.ts | 35 ++------ 11 files changed, 189 insertions(+), 109 deletions(-) create mode 100644 src/client/views/collections/CollectionPileView.scss create mode 100644 src/client/views/collections/CollectionPileView.tsx (limited to 'src/client/views/collections') diff --git a/src/Utils.ts b/src/Utils.ts index 9acdc8731..ad12c68a1 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -504,8 +504,9 @@ export function setupMoveUpEvents( e: React.PointerEvent, moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, upEvent: (e: PointerEvent) => void, - clickEvent: (e: PointerEvent) => void, - stopPropagation: boolean = true + clickEvent: (e: PointerEvent, doubleTap?: boolean) => void, + stopPropagation: boolean = true, + stopMovePropagation: boolean = true ) { (target as any)._downX = (target as any)._lastX = e.clientX; (target as any)._downY = (target as any)._lastY = e.clientY; @@ -520,12 +521,15 @@ export function setupMoveUpEvents( } (target as any)._lastX = e.clientX; (target as any)._lastY = e.clientY; - e.stopPropagation(); + stopMovePropagation && e.stopPropagation(); }; + (target as any)._doubleTap = false; const _upEvent = (e: PointerEvent): void => { + (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < 300); + (target as any)._lastTap = Date.now(); upEvent(e); if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) { - clickEvent(e); + clickEvent(e, (target as any)._doubleTap); } document.removeEventListener("pointermove", _moveEvent); document.removeEventListener("pointerup", _upEvent); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 578d337d6..1268c97b0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -612,6 +612,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id); } + export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Pile }, id); + } + export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id); } diff --git a/src/client/views/collections/CollectionPileView.scss b/src/client/views/collections/CollectionPileView.scss new file mode 100644 index 000000000..ac874b663 --- /dev/null +++ b/src/client/views/collections/CollectionPileView.scss @@ -0,0 +1,8 @@ +.collectionPileView { + display: flex; + flex-direction: row; + position: absolute; + height: 100%; + width: 100%; + overflow: visible; +} diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx new file mode 100644 index 000000000..511efe0a7 --- /dev/null +++ b/src/client/views/collections/CollectionPileView.tsx @@ -0,0 +1,97 @@ +import { action, computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; +import { ContextMenu } from "../ContextMenu"; +import { ContextMenuProps } from "../ContextMenuItem"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; +import { CollectionSubView } from "./CollectionSubView"; +import "./CollectionPileView.scss"; +import React = require("react"); +import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils"; +import { SelectionManager } from "../../util/SelectionManager"; + +@observer +export class CollectionPileView extends CollectionSubView(doc => doc) { + _lastTap = 0; + _doubleTap: boolean | undefined = false; + _originalChrome: string = ""; + @observable _contentsActive = true; + @observable _layoutEngine = "pass"; + @observable _collapsed: boolean = false; + @observable _childClickedScript: Opt; + componentDidMount() { + this._originalChrome = StrCast(this.layoutDoc._chromeStatus); + this.layoutDoc._chromeStatus = "disabled"; + this.layoutDoc.hideFilterView = true; + } + componentWillUnmount() { + this.layoutDoc.hideFilterView = false; + this.layoutDoc._chromeStatus = this._originalChrome; + } + + layoutEngine = () => this._layoutEngine; + + @computed get contents() { + return
+ +
; + } + + specificMenu = (e: React.MouseEvent) => { + const layoutItems: ContextMenuProps[] = []; + const doc = this.props.Document; + + ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); + } + + toggleStarburst = action(() => { + if (this._layoutEngine === 'starburst') { + const defaultSize = 110; + this.layoutDoc.overflow = undefined; + this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; + this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; + this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); + this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize); + this._layoutEngine = 'pass'; + } else { + const defaultSize = 25; + this.layoutDoc.overflow = 'visible'; + !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500); + !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); + if (this._layoutEngine === 'pass') { + this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2; + this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - defaultSize / 2; + this.layoutDoc._starburstPileWidth = this.layoutDoc[WidthSym](); + this.layoutDoc._starburstPileHeight = this.layoutDoc[HeightSym](); + } + this.layoutDoc._width = this.layoutDoc._height = defaultSize; + this._layoutEngine = 'starburst'; + } + }); + + pointerDown = (e: React.PointerEvent) => { + // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header + setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false, false); // this sets _doubleTap + } + + onClick = (e: React.MouseEvent) => { + if (e.button === 0 && (this._doubleTap || this.layoutEngine() === "starburst")) { + SelectionManager.DeselectAll(); + this.toggleStarburst(); + e.stopPropagation(); + } else if (this.layoutEngine() === "pass") { + runInAction(() => this._contentsActive = false); + setTimeout(action(() => this._contentsActive = true), 300); + } + } + + render() { + + return
+ {this.contents} +
; + } +} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 78ba3d6e6..c530b24fb 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -400,9 +400,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: if (generatedDocuments.length) { const set = generatedDocuments.length > 1 && generatedDocuments.map(d => Doc.iconify(d)); if (set) { - const pile = Docs.Create.FreeformDocument(generatedDocuments, { ...options, title: "pile", _LODdisable: true, }); - Doc.pileup(pile, generatedDocuments); - addDocument(pile); + addDocument(Doc.pileup(generatedDocuments, options.x!, options.y!)); } else { generatedDocuments.forEach(addDocument); } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 63498cea1..045134225 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -19,7 +19,6 @@ const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; import React = require("react"); -import { DocumentView } from "../nodes/DocumentView"; @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { @@ -84,7 +83,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } @computed get contents() { - return
+ return
; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 47e79f088..f82c6d8a6 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -46,6 +46,7 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { ObjectField } from '../../../new_fields/ObjectField'; import CollectionMapView from './CollectionMapView'; import { Transform } from 'prosemirror-transform'; +import { CollectionPileView } from './CollectionPileView'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -67,7 +68,8 @@ export enum CollectionViewType { Carousel = "carousel", Linear = "linear", Staff = "staff", - Map = "map" + Map = "map", + Pile = "pileup" } export interface CollectionRenderProps { @@ -169,6 +171,7 @@ export class CollectionView extends Touchable { case CollectionViewType.Multicolumn: return (); case CollectionViewType.Multirow: return (); case CollectionViewType.Linear: { return (); } + case CollectionViewType.Pile: { return (); } case CollectionViewType.Carousel: { return (); } case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index d8bd18ec7..9ca747113 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -13,10 +13,11 @@ import { listSpec } from "../../../../new_fields/Schema"; export interface ViewDefBounds { type: string; - text?: string; + payload: any; x: number; y: number; z?: number; + text?: string; zIndex?: number; width?: number; height?: number; @@ -24,14 +25,13 @@ export interface ViewDefBounds { fontSize?: number; highlight?: boolean; color?: string; - payload: any; replica?: string; pair?: { layout: Doc, data?: Doc }; } export interface PoolData { - x?: number; - y?: number; + x: number; + y: number; z?: number; zIndex?: number; width?: number; @@ -40,7 +40,7 @@ export interface PoolData { transition?: string; highlight?: boolean; replica?: string; - pair: { layout: Doc, data?: Doc } + pair: { layout: Doc, data?: Doc }; } export interface ViewDefResult { @@ -84,96 +84,88 @@ interface PivotColumn { export function computerPassLayout( poolData: Map, pivotDoc: Doc, - filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); + const docMap = new Map(); childPairs.forEach(({ layout, data }, i) => { docMap.set(layout[Id], { - type: "doc", x: NumCast(layout.x), y: NumCast(layout.y), width: layout[WidthSym](), height: layout[HeightSym](), - payload: undefined, pair: { layout, data }, replica: "" }); }); - return normalizeResults(panelDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); + return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []); } export function computerStarburstLayout( poolData: Map, pivotDoc: Doc, - filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); + const docMap = new Map(); const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])]; const docScale = NumCast(pivotDoc._starburstDocScale); const docSize = docScale * 100; // assume a icon sized at 100 const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize]; - childPairs.forEach((pair, i) => { + childPairs.forEach(({ layout, data }, i) => { const deg = i / childPairs.length * Math.PI * 2; - docMap.set(pair.layout[Id], { - type: "doc", - x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * pair.layout[WidthSym]() / 2, - y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * pair.layout[HeightSym]() / 2, - width: docScale * pair.layout[WidthSym](), - height: docScale * pair.layout[HeightSym](), - payload: undefined, + docMap.set(layout[Id], { + x: Math.cos(deg) * (burstRadius[0] / 3) - docScale * layout[WidthSym]() / 2, + y: Math.sin(deg) * (burstRadius[1] / 3) - docScale * layout[HeightSym]() / 2, + width: docScale * layout[WidthSym](), + height: docScale * layout[HeightSym](), + pair: { layout, data }, replica: "" }); }); - return normalizeResults(scaleDim, 12, childPairs, docMap, poolData, viewDefsToJSX, [], 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); + return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []); } export function computePivotLayout( poolData: Map, pivotDoc: Doc, - filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { - const docMap = new Map(); + const docMap = new Map(); const fieldKey = "data"; const pivotColumnGroups = new Map, PivotColumn>(); const pivotFieldKey = toLabel(pivotDoc._pivotField); - for (const doc of filterDocs) { - const lval = Cast(doc[pivotFieldKey], listSpec("string"), null); - const val = Field.toString(doc[pivotFieldKey] as Field); + childPairs.map(pair => { + const lval = Cast(pair.layout[pivotFieldKey], listSpec("string"), null); + const val = Field.toString(pair.layout[pivotFieldKey] as Field); if (lval) { lval.forEach((val, i) => { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); - pivotColumnGroups.get(val)!.docs.push(doc); + pivotColumnGroups.get(val)!.docs.push(pair.layout); pivotColumnGroups.get(val)!.replicas.push(i.toString()); }); } else if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] }); - pivotColumnGroups.get(val)!.docs.push(doc); + pivotColumnGroups.get(val)!.docs.push(pair.layout); pivotColumnGroups.get(val)!.replicas.push(""); } else { - docMap.set(doc[Id], { - type: "doc", + docMap.set(pair.layout[Id], { x: 0, y: 0, zIndex: -99, width: 0, height: 0, - payload: undefined, - pair: { layout: doc }, + pair, replica: "" - }) + }); } - } + }); let nonNumbers = 0; childPairs.map(pair => { const num = toNumber(pair.layout[pivotFieldKey]); @@ -252,12 +244,10 @@ export function computePivotLayout( wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } docMap.set(doc[Id] + (val.replicas || ""), { - type: "doc", x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0), y: -y + (pivotAxisWidth - hgt) / 2, width: wid, height: hgt, - payload: undefined, pair: { layout: doc }, replica: val.replicas[i] }); @@ -273,7 +263,7 @@ export function computePivotLayout( const dividers = sortedPivotKeys.map((key, i) => ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters })); groupNames.push(...dividers); - return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childPairs.filter(c => !filterDocs.includes(c.layout))); + return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []); } function toNumber(val: FieldResult) { @@ -283,14 +273,13 @@ function toNumber(val: FieldResult) { export function computeTimelineLayout( poolData: Map, pivotDoc: Doc, - filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { const fieldKey = "data"; const pivotDateGroups = new Map(); - const docMap = new Map(); + const docMap = new Map(); const groupNames: ViewDefBounds[] = []; const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field); const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]); @@ -306,11 +295,11 @@ export function computeTimelineLayout( let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq; let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq; - filterDocs.map(doc => { - const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey]))); + childPairs.forEach(pair => { + const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey]))); if (!Number.isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) { !pivotDateGroups.get(num) && pivotDateGroups.set(num, []); - pivotDateGroups.get(num)!.push(doc); + pivotDateGroups.get(num)!.push(pair.layout); minTime = Math.min(num, minTime); maxTime = Math.max(num, maxTime); } @@ -369,7 +358,7 @@ export function computeTimelineLayout( } const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; - return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childPairs.filter(c => !filterDocs.includes(c.layout))); + return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { keyDocs.forEach(doc => { @@ -382,13 +371,11 @@ export function computeTimelineLayout( wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } docMap.set(doc[Id], { - type: "doc", x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2, zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt / (Math.max(stack, 1)), - payload: undefined, pair: { layout: doc }, replica: "" }); @@ -397,12 +384,19 @@ export function computeTimelineLayout( } } -function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, - poolData: Map, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[], - extraDocs: { data?: Doc, layout: Doc }[]): ViewDefResult[] { +function normalizeResults( + panelDim: number[], + fontHeight: number, + docMap: Map, + poolData: Map, + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], + groupNames: ViewDefBounds[], + minWidth: number, + extras: ViewDefBounds[] +): ViewDefResult[] { const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds); const docEles = Array.from(docMap.entries()).map(ele => ele[1]); - const aggBounds = aggregateBounds(docEles.concat(grpEles).filter(e => e.zIndex !== -99), 0, 0); + const aggBounds = aggregateBounds(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" }))).filter(e => e.zIndex !== -99), 0, 0); aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x); const wscale = panelDim[0] / (aggBounds.r - aggBounds.x); let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale; @@ -420,12 +414,11 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { zIndex: newPosRaw.zIndex, width: (newPosRaw.width || 0) * scale, height: newPosRaw.height! * scale, - pair: ele[1].pair! + pair: ele[1].pair }; poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "transform 1s", ...newPos }); } }); - extraDocs.map(ed => poolData.set(ed.layout[Id], { x: 0, y: 0, zIndex: -99, pair: ed })); return viewDefsToJSX(extras.concat(groupNames).map(gname => ({ type: gname.type, diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5555d9e84..af9d3c5be 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -902,7 +902,7 @@ export class CollectionFreeFormView extends CollectionSubView, pivotDoc: Doc, - filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[]) { - return engine(poolData, this.props.Document, this.childDocs, - this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); + viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[] + ) { + return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } doFreeformLayout(poolData: Map) { @@ -977,7 +976,7 @@ export class CollectionFreeFormView extends CollectionSubView(); - const engine = StrCast(this.layoutDoc._layoutEngine) || this.props.layoutEngine?.(); + const engine = this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); switch (engine) { case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) }; case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) }; @@ -1001,6 +1000,7 @@ export class CollectionFreeFormView extends CollectionSubView this._cachedPool.set(k[0], k[1])); const elements: ViewDefResult[] = computedElementData.slice(); + const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry => elements.push({ ele: , @@ -1082,7 +1083,7 @@ export class CollectionFreeFormView extends CollectionSubView this.props.Document.jitterRotation = (this.props.Document.jitterRotation ? 0 : 10)), icon: "paint-brush" }); + optionItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document._jitterRotation = (this.props.Document._jitterRotation ? 0 : 10)), icon: "paint-brush" }); optionItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => { const input = document.createElement("input"); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index ab8174ba1..cd8166309 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,12 +1,12 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DataSym, WidthSym, HeightSym } from "../../../../new_fields/Doc"; +import { Doc, DocListCast, DataSym, WidthSym, HeightSym, Opt } from "../../../../new_fields/Doc"; import { InkField, InkData } from "../../../../new_fields/InkField"; import { List } from "../../../../new_fields/List"; import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; import { Cast, NumCast, FieldValue, StrCast } from "../../../../new_fields/Types"; import { Utils } from "../../../../Utils"; -import { Docs, DocUtils } from "../../../documents/Documents"; +import { Docs, DocUtils, DocumentOptions } from "../../../documents/Documents"; import { SelectionManager } from "../../../util/SelectionManager"; import { Transform } from "../../../util/Transform"; import { undoBatch } from "../../../util/UndoManager"; @@ -310,11 +310,10 @@ export class MarqueeView extends React.Component { + getCollection = (selected: Doc[], creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => { const bounds = this.Bounds; // const inkData = this.ink ? this.ink.inkData : undefined; - const creator = asTemplate ? Docs.Create.StackingDocument : Docs.Create.FreeformDocument; - const newCollection = creator(selected, { + const newCollection = (creator || Docs.Create.FreeformDocument)(selected, { x: bounds.left, y: bounds.top, _panX: 0, @@ -338,8 +337,7 @@ export class MarqueeView extends React.Component this.props.removeDocument(d)); - const newCollection = this.getCollection(selected, false); - Doc.pileup(newCollection, selected); + const newCollection = Doc.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2); this.props.addDocument(newCollection); this.props.selectDocuments([newCollection], []); MarqueeOptionsMenu.Instance.fadeOut(true); @@ -359,7 +357,7 @@ export class MarqueeView extends React.Component { - const newCollection = this.getCollection([], false, true); + const newCollection = this.getCollection([], undefined, true); this.props.addDocument(newCollection); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 18ffc38f5..337274774 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -976,7 +976,8 @@ export namespace Doc { if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); } - export function pileup(newCollection: Doc, selected: Doc[]) { + export function pileup(selected: Doc[], x: number, y: number) { + const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true }); let w = 0, h = 0; selected.forEach((d, i) => { Doc.iconify(d); @@ -993,12 +994,10 @@ export namespace Doc { newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; newCollection._width = newCollection._height = 110; //newCollection.borderRounding = "40px"; - newCollection.jitterRotation = 10; + newCollection._jitterRotation = 10; newCollection._backgroundColor = "gray"; - - newCollection._layoutEngine = "pass"; newCollection.overflow = "visible"; - newCollection.onClick = ScriptField.MakeScript("toggleStarburst(self, this)", { self: Doc.name, this: Doc.name }); + return newCollection; } @@ -1055,28 +1054,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo return docs.length ? new List(docs) : prevValue; }); Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); -Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); -Scripting.addGlobal(function toggleStarburst(self: Doc, tLayout: Doc) { - if (self._layoutEngine === 'starburst') { - const defaultSize = 110; - tLayout.overflow = undefined; - self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - NumCast(tLayout._starburstPileWidth, defaultSize) / 2; - self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - NumCast(tLayout._starburstPileHeight, defaultSize) / 2; - tLayout._width = NumCast(tLayout._starburstPileWidth, defaultSize); - tLayout._height = NumCast(tLayout._starburstPileHeight, defaultSize); - self._layoutEngine = 'pass'; - } else { - const defaultSize = 25; - tLayout.overflow = 'visible'; - !tLayout._starburstRadius && (tLayout._starburstRadius = 500); - !tLayout._starburstDocScale && (tLayout._starburstDocScale = 2.5); - if (self._layoutEngine === 'pass') { - self.x = NumCast(self.x) + tLayout[WidthSym]() / 2 - defaultSize / 2; - self.y = NumCast(self.y) + tLayout[HeightSym]() / 2 - defaultSize / 2; - tLayout._starburstPileWidth = tLayout[WidthSym](); - tLayout._starburstPileHeight = tLayout[HeightSym](); - } - tLayout._width = tLayout._height = defaultSize; - self._layoutEngine = 'starburst'; - } -}); \ No newline at end of file +Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 308ae1521cbd58126d35239cfdd5a138f6fa1fe0 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Apr 2020 15:40:07 -0400 Subject: got rid of focus mechanism on click. fixed shift-click to multiselect. fixed pile view interactions. added FollowLink on click menu items. --- src/client/util/DragManager.ts | 9 +++- src/client/util/ProsemirrorExampleTransfer.ts | 18 +++++-- src/client/views/ContextMenu.tsx | 9 ++++ src/client/views/TemplateMenu.tsx | 5 +- .../views/collections/CollectionPileView.tsx | 38 +++++++++++-- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +++---- src/client/views/nodes/DocumentView.tsx | 63 +++++++++++++++------- src/client/views/nodes/FormattedTextBox.tsx | 4 +- src/client/views/nodes/FormattedTextBoxComment.tsx | 1 - src/client/views/nodes/LinkAnchorBox.tsx | 1 - src/client/views/pdf/Annotation.tsx | 1 - 12 files changed, 124 insertions(+), 47 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 42a78a4bf..6bb44f7d6 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -83,6 +83,7 @@ export namespace DragManager { } export let AbortDrag: () => void = emptyFunction; export type MoveFunction = (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean; + export type RemoveFunction = (document: Doc) => boolean; export interface DragDropDisposer { (): void; } export interface DragOptions { @@ -138,6 +139,7 @@ export namespace DragManager { userDropAction: dropActionType; embedDoc?: boolean; moveDocument?: MoveFunction; + removeDocument?: RemoveFunction; isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts } export class LinkDragData { @@ -351,12 +353,17 @@ export namespace DragManager { let lastX = downX; let lastY = downY; + let alias = "alias"; const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : undefined; } if (e.shiftKey && CollectionDockingView.Instance && dragData.droppedDocuments.length === 1) { + !dragData.dropAction && (dragData.dropAction = alias); + if (dragData.dropAction === "move") { + dragData.removeDocument?.(dragData.draggedDocuments[0]); + } AbortDrag(); finishDrag?.(new DragCompleteEvent(true, dragData)); CollectionDockingView.Instance.StartOtherDrag({ @@ -366,7 +373,7 @@ export namespace DragManager { button: 0 }, dragData.droppedDocuments); } - //TODO: Why can't we use e.movementX and e.movementY? + alias = "move"; const moveX = e.pageX - lastX; const moveY = e.pageY - lastY; lastX = e.pageX; diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 680f48f70..356f20ce6 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "./SelectionManager"; import { Docs } from "../documents/Documents"; -import { NumCast, BoolCast, Cast } from "../../new_fields/Types"; +import { NumCast, BoolCast, Cast, StrCast } from "../../new_fields/Types"; import { Doc } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { Id } from "../../new_fields/FieldSymbols"; @@ -153,10 +153,16 @@ export default function buildKeymap>(schema: S, props: any const layoutDoc = props.Document; const originalDoc = layoutDoc.rootDocument || layoutDoc; if (originalDoc instanceof Doc) { + const layoutKey = StrCast(originalDoc.layoutKey); const newDoc = Docs.Create.TextDocument("", { - layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), + layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, + layoutKey, + _singleLine: BoolCast(originalDoc._singleLine), x: NumCast(originalDoc.x), y: NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10, _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height) }); + if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { + newDoc[layoutKey] = originalDoc[layoutKey]; + } FormattedTextBox.SelectOnLoad = newDoc[Id]; props.addDocument(newDoc); } @@ -171,10 +177,16 @@ export default function buildKeymap>(schema: S, props: any const layoutDoc = props.Document; const originalDoc = layoutDoc.rootDocument || layoutDoc; if (force || props.Document._singleLine) { + const layoutKey = StrCast(originalDoc.layoutKey); const newDoc = Docs.Create.TextDocument("", { - layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, _singleLine: BoolCast(originalDoc._singleLine), + layout: Cast(originalDoc.layout, Doc, null) || FormattedTextBox.DefaultLayout, + layoutKey, + _singleLine: BoolCast(originalDoc._singleLine), x: NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10, y: NumCast(originalDoc.y), _width: NumCast(layoutDoc._width), _height: NumCast(layoutDoc._height) }); + if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { + newDoc[layoutKey] = originalDoc[layoutKey]; + } FormattedTextBox.SelectOnLoad = newDoc[Id]; props.addDocument(newDoc); return true; diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 4d04d4e89..5b66b63ed 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -99,6 +99,15 @@ export class ContextMenu extends React.Component { } } @action + moveAfter(item: ContextMenuProps, after: ContextMenuProps) { + if (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); + } + } + @action setDefaultItem(prefix: string, item: (name: string) => void) { this._defaultPrefix = prefix; this._defaultItem = item; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 4c84a22ad..665ab4e41 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -14,6 +14,7 @@ import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZer import { Transform } from "../util/Transform"; import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; import { Scripting } from "../util/Scripting"; +import { List } from "../../new_fields/List"; @observer class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent, template: Template) => void }> { @@ -106,8 +107,8 @@ export class TemplateMenu extends React.Component { return100 = () => 100; @computed get scriptField() { - return ScriptField.MakeScript("switchView(firstDoc, this)", { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name, firstDoc: Doc.name }, - { firstDoc: this.props.docViews[0].props.Document }); + return ScriptField.MakeScript("docs.map(d => switchView(d, this))", { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name, firstDoc: Doc.name }, + { docs: new List(this.props.docViews.map(dv => dv.props.Document)) }); } render() { const firstDoc = this.props.docViews[0].props.Document; diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 511efe0a7..410b16ec7 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -11,6 +11,7 @@ import "./CollectionPileView.scss"; import React = require("react"); import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; +import { UndoManager } from "../../util/UndoManager"; @observer export class CollectionPileView extends CollectionSubView(doc => doc) { @@ -34,7 +35,10 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { layoutEngine = () => this._layoutEngine; @computed get contents() { - return
+ return
; } @@ -71,9 +75,32 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { } }); + _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { + let dist = 0; // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header - setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false, false); // this sets _doubleTap + setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { + if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) { + dist += Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]); + if (dist > 100) { + if (!this._undoBatch) { + this._undoBatch = UndoManager.StartBatch("layout pile"); + } + const doc = this.childDocs[0]; + doc.x = e.clientX; + doc.y = e.clientY; + this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc); + dist = 0; + } + } + return false; + }, () => { + this._undoBatch?.end(); + this._undoBatch = undefined; + if (!this.childDocs.length) { + this.props.ContainingCollectionView?.removeDocument(this.props.Document); + } + }, emptyFunction, false, this.layoutEngine() === "pass" && this.props.isSelected(true)); // this sets _doubleTap } onClick = (e: React.MouseEvent) => { @@ -81,10 +108,11 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { SelectionManager.DeselectAll(); this.toggleStarburst(); e.stopPropagation(); - } else if (this.layoutEngine() === "pass") { - runInAction(() => this._contentsActive = false); - setTimeout(action(() => this._contentsActive = true), 300); } + // else if (this.layoutEngine() === "pass") { + // runInAction(() => this._contentsActive = false); + // setTimeout(action(() => this._contentsActive = true), 300); + // } } render() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f82c6d8a6..2bd37bda5 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -225,7 +225,7 @@ export class CollectionView extends Touchable { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 this.setupViewTypes("Change Perspective...", (vtype => { this.props.Document._viewType = vtype; return this.props.Document; }), true); - this.setupViewTypes("New Perspective...", vtype => { + this.setupViewTypes("Add a Perspective...", vtype => { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; this.props.addDocTab(newRendition, "onRight"); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index af9d3c5be..6a3764bb7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -821,18 +821,18 @@ export class CollectionFreeFormView extends CollectionSubView { if (afterFocus?.()) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 06bd40992..f9d9ca9a1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -234,6 +234,7 @@ export class DocumentView extends DocComponent(Docu const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; + dragData.removeDocument = this.props.removeDocument; dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument; dragData.dragDivName = this.props.dragDivName; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart }); @@ -309,19 +310,19 @@ export class DocumentView extends DocComponent(Docu } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"); //ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click"); - } else if (this.Document.isLinkButton) { + } else if (this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { if ((this.props.Document.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } else { - if (this.props.Document.type === DocumentType.RTF) { - DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0]; - DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)]; + // if (this.props.Document.type === DocumentType.RTF) { + // DocumentView._focusHack = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY) || [0, 0]; + // DocumentView._focusHack = [DocumentView._focusHack[0] + NumCast(this.props.Document.x), DocumentView._focusHack[1] + NumCast(this.props.Document.y)]; - this.props.focus(this.props.Document, false); - } - SelectionManager.SelectDoc(this, e.ctrlKey); + // this.props.focus(this.props.Document, false); + // } + SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey); } preventDefault = false; } @@ -329,7 +330,6 @@ export class DocumentView extends DocComponent(Docu preventDefault && e.preventDefault(); } } - static _focusHack: number[] = []; // bcz :this will get fixed... // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place @@ -344,11 +344,11 @@ export class DocumentView extends DocComponent(Docu finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout. return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false }; - this.props.addDocTab(doc, where) && this.props.focus(doc, true, undefined, hackToCallFinishAfterFocus); // add the target and focus on it. + this.props.addDocTab(doc, where) && this.props.focus(doc, BoolCast(this.Document.followLinkZoom, true), undefined, hackToCallFinishAfterFocus); // add the target and focus on it. return where !== "inPlace"; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target) }; // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target - this.props.focus(this.props.Document, true, 1, targetFocusAfterDocFocus); + this.props.focus(this.props.Document, BoolCast(this.Document.followLinkZoom, true), 1, targetFocusAfterDocFocus); }; await DocumentManager.Instance.FollowLink(undefined, this.props.Document, createViewFunc, shiftKey, this.props.ContainingCollectionDoc, batch.end, altKey ? true : undefined); } @@ -569,6 +569,7 @@ export class DocumentView extends DocComponent(Docu this.Document.onClick = undefined; } else { this.Document.isLinkButton = true; + this.Document.followLinkZoom = false; this.Document.followLinkLocation = undefined; } } @@ -579,10 +580,24 @@ export class DocumentView extends DocComponent(Docu this.Document.isLinkButton = false; } else { this.Document.isLinkButton = true; + this.Document.followLinkZoom = true; this.Document.followLinkLocation = "inPlace"; } } + @undoBatch + toggleFollowOnRight = (): void => { + if (this.Document.isLinkButton) { + this.Document.isLinkButton = false; + } else { + this.Document.isLinkButton = true; + this.Document.followLinkZoom = false; + const first = DocListCast(this.Document.links).find(d => d instanceof Doc); + first && (first.hidden = true); + this.Document.followLinkLocation = "onRight"; + } + } + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { @@ -679,20 +694,27 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" })); - const existing = cm.findByDescription("Options..."); - const layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; - layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); - layoutItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); - layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); - layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); - !existing && cm.addItem({ description: "Options...", subitems: layoutItems, icon: "compass" }); - - const open = cm.findByDescription("New Perspective..."); + let open = cm.findByDescription("Add a Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); templateDoc && openItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - !open && cm.addItem({ description: "New Perspective...", subitems: openItems, icon: "external-link-alt" }); + if (!open) { + open = { description: "Add a Perspective....", subitems: openItems, icon: "external-link-alt" }; + cm.addItem(open); + } + + let options = cm.findByDescription("Options..."); + const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; + optionItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); + optionItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); + optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); + optionItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); + if (!options) { + options = { description: "Options...", subitems: optionItems, icon: "compass" }; + cm.addItem(options); + } + cm.moveAfter(options, open); const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -700,6 +722,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.props.Document.layoutKey}")`), icon: "window-restore" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); + onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" }); onClicks.push({ description: this.Document.isLinkButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index a018f17e8..d7cfbae9f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -444,7 +444,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp changeItems.push({ description: "FreeForm", event: undoBatch(() => Doc.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), "change view"), icon: "eye" }); !change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" }); - const open = cm.findByDescription("New Perspective..."); + const open = cm.findByDescription("Add a Perspective..."); const openItems: ContextMenuProps[] = open && "subitems" in open ? open.subitems : []; openItems.push({ @@ -454,7 +454,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.props.addDocTab(alias, "onRight"); }), icon: "eye" }); - !open && cm.addItem({ description: "New Perspective...", subitems: openItems, icon: "external-link-alt" }); + !open && cm.addItem({ description: "Add a Perspective...", subitems: openItems, icon: "external-link-alt" }); } diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx index 41df5b3c1..a9f76ae8f 100644 --- a/src/client/views/nodes/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/FormattedTextBoxComment.tsx @@ -88,7 +88,6 @@ export class FormattedTextBoxComment { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); } else { - DocumentView._focusHack = []; DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); } diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index eb647d0e4..707b9d12a 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -75,7 +75,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - DocumentView._focusHack = []; DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "inTab")), false); this._editing = false; }), 300 - (Date.now() - this._lastTap)); diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index eaf80d252..672d3adb8 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -98,7 +98,6 @@ class RegionAnnotation extends React.Component { else if (e.button === 0) { const annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { - DocumentView._focusHack = []; DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined); e.stopPropagation(); } -- cgit v1.2.3-70-g09d2 From c6d8ac7eef3480a7e19687b066456d9d34519587 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Apr 2020 20:25:50 -0400 Subject: several fixes for performance and for working with isBackground objects. --- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 21 ++++++++++++++------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 21 ++++++++++++++------- src/client/views/nodes/ImageBox.scss | 9 +-------- src/client/views/nodes/ImageBox.tsx | 8 ++++---- src/new_fields/util.ts | 2 +- 9 files changed, 39 insertions(+), 32 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 5d926b7c7..8992de9d1 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -364,7 +364,7 @@ export class CollectionStackingViewFieldColumn extends React.Component diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2bd37bda5..801704673 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -493,7 +493,7 @@ export class CollectionView extends Touchable { {!this.props.isSelected() || this.props.PanelHeight() < 100 || this.props.Document.hideFilterView ? (null) :
} - {this.filterView} + {this.facetWidth() < 10 ? (null) : this.filterView}
); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 9ca747113..9a864078a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -39,7 +39,7 @@ export interface PoolData { color?: string; transition?: string; highlight?: boolean; - replica?: string; + replica: string; pair: { layout: Doc, data?: Doc }; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6a3764bb7..a13c81a15 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -508,7 +508,7 @@ export class CollectionFreeFormView extends CollectionSubView BoolCast(this.Document.useClusters); - + @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); } + parentActive = () => this.props.active() || this.backgroundActive ? true : false; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { ...this.props, @@ -879,7 +880,7 @@ export class CollectionFreeFormView extends CollectionSubView) { + childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) { return this._layoutPoolData.get(doc[Id] + (replica || "")); }.bind(this)); @@ -975,6 +976,9 @@ export class CollectionFreeFormView extends CollectionSubView(); const engine = this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); switch (engine) { @@ -1009,8 +1013,10 @@ export class CollectionFreeFormView extends CollectionSubView { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string, replica: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string, replica: string } | undefined; x?: number; y?: number; z?: number; @@ -23,7 +23,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { jitterRotation: number; transition?: string; fitToBox?: boolean; - replica?: string; + replica: string; } @observer diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f9d9ca9a1..7ae77fc65 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -281,12 +281,12 @@ export class DocumentView extends DocComponent(Docu } } - onClick = (e: React.MouseEvent | React.PointerEvent) => { + onClick = action((e: React.MouseEvent | React.PointerEvent) => { if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { let stopPropagate = true; let preventDefault = true; - this.props.Document.isBackground === undefined && this.props.bringToFront(this.props.Document); + !this.props.Document.isBackground && this.props.bringToFront(this.props.Document); if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (!(e.nativeEvent as any).formattedHandled) { const fullScreenAlias = Doc.MakeAlias(this.props.Document); @@ -329,7 +329,7 @@ export class DocumentView extends DocComponent(Docu stopPropagate && e.stopPropagation(); preventDefault && e.preventDefault(); } - } + }); // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place @@ -347,8 +347,12 @@ export class DocumentView extends DocComponent(Docu this.props.addDocTab(doc, where) && this.props.focus(doc, BoolCast(this.Document.followLinkZoom, true), undefined, hackToCallFinishAfterFocus); // add the target and focus on it. return where !== "inPlace"; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target) }; - // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target - this.props.focus(this.props.Document, BoolCast(this.Document.followLinkZoom, true), 1, targetFocusAfterDocFocus); + if (!this.Document.followLinkZoom) { + targetFocusAfterDocFocus(); + } else { + // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target + this.props.focus(this.props.Document, BoolCast(this.Document.followLinkZoom, true), 1, targetFocusAfterDocFocus); + } }; await DocumentManager.Instance.FollowLink(undefined, this.props.Document, createViewFunc, shiftKey, this.props.ContainingCollectionDoc, batch.end, altKey ? true : undefined); } @@ -642,6 +646,7 @@ export class DocumentView extends DocComponent(Docu const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" }); DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); } + this.Document.followLinkZoom = true; this.Document.isLinkButton = true; } @@ -1069,7 +1074,7 @@ export class DocumentView extends DocComponent(Docu const titleView = (!showTitle ? (null) :
(Docu
; } @computed get ignorePointerEvents() { - return this.props.pointerEvents === false || (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); + return this.props.pointerEvents === false || + (this.Document.isBackground && !this.isSelected() && !SelectionManager.GetIsDragging()) || + (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @undoBatch @action diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 49425c2c2..15148d01d 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -1,5 +1,4 @@ -.imageBox, -.imageBox-dragging { +.imageBox { border-radius: inherit; width: 100%; height: 100%; @@ -11,12 +10,6 @@ } } -.imageBox-dragging { - .imageBox-fader { - pointer-events: none; - } -} - #upload-icon { position: absolute; bottom: 0; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e189f5814..14b367868 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -360,7 +360,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url.href).filter(url => url); // access the primary layout data of the alternate documents + const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; return paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; } @@ -438,8 +439,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent [this.content]; render() { TraceMobx(); - const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging"; - return (
Date: Sat, 25 Apr 2020 20:59:28 -0400 Subject: added a sizeProvider computedFn for efficiency since size doesn't change as often as position --- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +++++++++++--- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 12 +++++++----- src/new_fields/util.ts | 2 +- 3 files changed, 19 insertions(+), 9 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 a13c81a15..6a6879fd4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -86,6 +86,7 @@ export class CollectionFreeFormView extends CollectionSubView(); + private _layoutSizeData = new ObservableMap(); private _cachedPool: Map = new Map(); @observable private _pullCoords: number[] = [0, 0]; @observable private _pullDirection: string = ""; @@ -903,13 +904,13 @@ export class CollectionFreeFormView extends CollectionSubView, engine: ( @@ -997,9 +1001,12 @@ export class CollectionFreeFormView extends CollectionSubView { const lastPos = this._cachedPool.get(entry[0]); // last computed pos const newPos = entry[1]; - if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex || newPos.width !== lastPos.width || newPos.height !== lastPos.height) { + if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) { this._layoutPoolData.set(entry[0], newPos); } + if (!lastPos || newPos.height !== lastPos.height) { + this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height }); + } })); this._cachedPool.clear(); Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1])); @@ -1012,6 +1019,7 @@ export class CollectionFreeFormView extends CollectionSubView { x: number, y: number, zIndex?: number, highlight?: boolean, width: number, height: number, z: number, transition?: string, replica: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, highlight?: boolean, z: number, transition?: string } | undefined; + sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined; x?: number; y?: number; z?: number; @@ -41,13 +42,14 @@ export class CollectionFreeFormDocumentView extends DocComponent this.nativeWidth > 0 && !this.props.fitToBox && !this.freezeDimensions ? this.width / this.nativeWidth : 1; - panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth?.()); - panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight?.()); + panelWidth = () => (this.sizeProvider?.width || this.props.PanelWidth?.()); + panelHeight = () => (this.sizeProvider?.height || this.props.PanelHeight?.()); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) .scale(1 / this.contentScaling()) diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 480a55da0..8c719ccd8 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -12,7 +12,7 @@ function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); } -const tracing = true; +const tracing = false; export function TraceMobx() { tracing && trace(); } -- cgit v1.2.3-70-g09d2 From 627adb92b604e117694e26af65afdc19920ac509 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Apr 2020 21:26:28 -0400 Subject: added internalPreDrop to set dropActions. made linksView turn off during dragging. --- src/client/util/DragManager.ts | 22 ++++++++++++++++++++-- .../views/collections/CollectionPileView.tsx | 2 ++ src/client/views/collections/CollectionSubView.tsx | 7 ++++++- .../CollectionFreeFormLinksView.tsx | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6bb44f7d6..6a55f4a00 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -179,7 +179,8 @@ export namespace DragManager { export function MakeDropTarget( element: HTMLElement, - dropFunc: (e: Event, de: DropEvent) => void + dropFunc: (e: Event, de: DropEvent) => void, + preDropFunc?: (e: Event, de: DropEvent) => void ): DragDropDisposer { if ("canDrop" in element.dataset) { throw new Error( @@ -187,10 +188,13 @@ export namespace DragManager { ); } element.dataset.canDrop = "true"; - const handler = (e: Event) => { dropFunc(e, (e as CustomEvent).detail); }; + const handler = (e: Event) => dropFunc(e, (e as CustomEvent).detail); + const preDropHandler = (e: Event) => preDropFunc?.(e, (e as CustomEvent).detail); element.addEventListener("dashOnDrop", handler); + preDropFunc && element.addEventListener("dashPreDrop", preDropHandler); return () => { element.removeEventListener("dashOnDrop", handler); + preDropFunc && element.removeEventListener("dashPreDrop", preDropHandler); delete element.dataset.canDrop; }; } @@ -425,6 +429,20 @@ export namespace DragManager { }); if (target) { const complete = new DragCompleteEvent(false, dragData); + target.dispatchEvent( + new CustomEvent("dashPreDrop", { + bubbles: true, + detail: { + x: e.x, + y: e.y, + complete: complete, + shiftKey: e.shiftKey, + altKey: e.altKey, + metaKey: e.metaKey, + ctrlKey: e.ctrlKey + } + }) + ); finishDrag?.(complete); target.dispatchEvent( new CustomEvent("dashOnDrop", { diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 410b16ec7..3bbfcc4d7 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -78,6 +78,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { _undoBatch: UndoManager.Batch | undefined; pointerDown = (e: React.PointerEvent) => { let dist = 0; + SelectionManager.SetIsDragging(true); // this._lastTap should be set to 0, and this._doubleTap should be set to false in the class header setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { if (this.layoutEngine() === "pass" && this.childDocs.length && this.props.isSelected(true)) { @@ -97,6 +98,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { }, () => { this._undoBatch?.end(); this._undoBatch = undefined; + SelectionManager.SetIsDragging(false); if (!this.childDocs.length) { this.props.ContainingCollectionView?.removeDocument(this.props.Document); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index c530b24fb..60cc1bd49 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -64,7 +64,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: this.multiTouchDisposer?.(); if (ele) { this._mainCont = ele; - this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); + this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.onInternalPreDrop.bind(this)); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } @@ -208,6 +208,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: protected onGesture(e: Event, ge: GestureUtils.GestureEvent) { } + protected onInternalPreDrop(e: Event, de: DragManager.DropEvent): boolean { + // de.complete.docDragData!.dropAction = "alias"; bcz: for example, you can convert all drops to aliases here + return true; + } + @undoBatch @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 702b02a20..4b5e977df 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -36,7 +36,7 @@ export class CollectionFreeFormLinksView extends React.Component { } render() { - return
+ return SelectionManager.GetIsDragging() ? (null) :
{this.uniqueConnections} -- cgit v1.2.3-70-g09d2 From f1ac48c19b4683668b0df91bd4ad68690a069029 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Apr 2020 21:43:21 -0400 Subject: added a targetDropAction field to specify the drop action from the target's side --- src/client/util/DragManager.ts | 15 ++++++++++----- src/client/views/collections/CollectionCarouselView.tsx | 2 +- src/client/views/collections/CollectionLinearView.tsx | 2 +- .../views/collections/CollectionSchemaMovableTableHOC.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 9 ++------- src/client/views/collections/CollectionTreeView.tsx | 4 ++-- src/client/views/collections/CollectionViewChromes.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/LabelBox.tsx | 2 +- src/new_fields/documentSchemas.ts | 3 ++- 12 files changed, 26 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6a55f4a00..577366cd4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,5 +1,5 @@ import { Doc, Field, DocListCast } from "../../new_fields/Doc"; -import { Cast, ScriptCast } from "../../new_fields/Types"; +import { Cast, ScriptCast, StrCast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; @@ -180,7 +180,7 @@ export namespace DragManager { export function MakeDropTarget( element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, - preDropFunc?: (e: Event, de: DropEvent) => void + doc?:Doc ): DragDropDisposer { if ("canDrop" in element.dataset) { throw new Error( @@ -189,12 +189,17 @@ export namespace DragManager { } element.dataset.canDrop = "true"; const handler = (e: Event) => dropFunc(e, (e as CustomEvent).detail); - const preDropHandler = (e: Event) => preDropFunc?.(e, (e as CustomEvent).detail); + const preDropHandler = (e:Event) => { + const de = (e as CustomEvent).detail; + if (de.complete.docDragData && doc?.targetDropAction) { + de.complete.docDragData!.dropAction = StrCast(doc.targetDropAction) as dropActionType; + } + } element.addEventListener("dashOnDrop", handler); - preDropFunc && element.addEventListener("dashPreDrop", preDropHandler); + doc && element.addEventListener("dashPreDrop", preDropHandler); return () => { element.removeEventListener("dashOnDrop", handler); - preDropFunc && element.removeEventListener("dashPreDrop", preDropHandler); + doc && element.removeEventListener("dashPreDrop", preDropHandler); delete element.dataset.canDrop; }; } diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 9e7248db2..2453acddf 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -27,7 +27,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer?.(); if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); } } diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index cb0206260..344dca23a 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -64,7 +64,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); } } diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 670d6dbb2..972714e34 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -54,7 +54,7 @@ export class MovableColumn extends React.Component { } createColDropTarget = (ele: HTMLDivElement) => { - this._colDropDisposer && this._colDropDisposer(); + this._colDropDisposer?.(); if (ele) { this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this)); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 60cc1bd49..49abc6ee6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -11,7 +11,7 @@ import { Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocumentType } from "../../documents/DocumentTypes"; import { Docs, DocumentOptions } from "../../documents/Documents"; -import { DragManager } from "../../util/DragManager"; +import { DragManager, dropActionType } from "../../util/DragManager"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; @@ -64,7 +64,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: this.multiTouchDisposer?.(); if (ele) { this._mainCont = ele; - this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.onInternalPreDrop.bind(this)); + this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } @@ -208,11 +208,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: protected onGesture(e: Event, ge: GestureUtils.GestureEvent) { } - protected onInternalPreDrop(e: Event, de: DragManager.DropEvent): boolean { - // de.complete.docDragData!.dropAction = "alias"; bcz: for example, you can convert all drops to aliases here - return true; - } - @undoBatch @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ca2004b79..8c5a4538e 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -143,7 +143,7 @@ class TreeView extends React.Component { protected createTreeDropTarget = (ele: HTMLDivElement) => { this._treedropDisposer && this._treedropDisposer(); - ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this))); + ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this)), this.props.document); } onPointerEnter = (e: React.PointerEvent): void => { @@ -663,7 +663,7 @@ export class CollectionTreeView extends CollectionSubView { this.treedropDisposer?.(); if (this._mainEle = ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); + this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.props.Document); } } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 7315d2c4e..d26e3a38b 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -215,9 +215,9 @@ export class CollectionViewBaseChrome extends React.Component { - this.dropDisposer && this.dropDisposer(); + this.dropDisposer?.(); if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7ae77fc65..2aee49ad3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -191,7 +191,7 @@ export class DocumentView extends DocComponent(Docu @action componentDidMount() { - this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); + this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document)); this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); // this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this))); @@ -208,7 +208,7 @@ export class DocumentView extends DocComponent(Docu this.multiTouchDisposer?.(); this.holdDisposer?.(); if (this._mainCont.current) { - this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this)); + this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document); this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)); this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d7cfbae9f..815b8b3ae 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -253,7 +253,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp protected createDropTarget = (ele: HTMLDivElement) => { this.ProseRef = ele; this.dropDisposer?.(); - ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); + ele && (this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); } @undoBatch diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 14b367868..08917d281 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -76,7 +76,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { this._dropDisposer && this._dropDisposer(); - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); } @undoBatch diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 5c2fc3ffe..a41e4fac4 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -32,7 +32,7 @@ export class LabelBox extends ViewBoxBaseComponent { this.dropDisposer?.(); if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document); } } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 61b185e5c..f24ad42b2 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -8,7 +8,8 @@ export const documentSchema = createSchema({ layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below layoutKey: "string", // holds the field key for the field that actually holds the current lyoat title: "string", // document title (can be on either data document or layout) - dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") + dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") + targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy") _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set -- cgit v1.2.3-70-g09d2 From 94ecd9c794ca27760e7885d2dc3bc33b928968e7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Apr 2020 21:53:40 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (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 6a6879fd4..852442fd4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1004,7 +1004,7 @@ export class CollectionFreeFormView extends CollectionSubView Date: Sun, 26 Apr 2020 01:56:41 -0400 Subject: fixed title layout for text. fixed documentBox for nested collections. fixed tree view for text boxes. changed labelbox formatting. --- src/client/util/DragManager.ts | 8 ++++---- src/client/views/collections/CollectionTreeView.tsx | 13 +++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- src/client/views/nodes/ColorBox.scss | 1 + src/client/views/nodes/DocumentBox.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 7 ++----- src/client/views/nodes/FormattedTextBox.scss | 1 - src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- src/client/views/nodes/LabelBox.scss | 2 -- src/client/views/nodes/LabelBox.tsx | 8 +++++++- src/server/authentication/models/current_user_utils.ts | 4 ++-- 11 files changed, 31 insertions(+), 24 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 577366cd4..35694a6bd 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -180,7 +180,7 @@ export namespace DragManager { export function MakeDropTarget( element: HTMLElement, dropFunc: (e: Event, de: DropEvent) => void, - doc?:Doc + doc?: Doc ): DragDropDisposer { if ("canDrop" in element.dataset) { throw new Error( @@ -189,12 +189,12 @@ export namespace DragManager { } element.dataset.canDrop = "true"; const handler = (e: Event) => dropFunc(e, (e as CustomEvent).detail); - const preDropHandler = (e:Event) => { + const preDropHandler = (e: Event) => { const de = (e as CustomEvent).detail; if (de.complete.docDragData && doc?.targetDropAction) { - de.complete.docDragData!.dropAction = StrCast(doc.targetDropAction) as dropActionType; + de.complete.docDragData.dropAction = StrCast(doc.targetDropAction) as dropActionType; } - } + }; element.addEventListener("dashOnDrop", handler); doc && element.addEventListener("dashPreDrop", preDropHandler); return () => { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8c5a4538e..ddd7474bd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -261,7 +261,7 @@ class TreeView extends React.Component { docHeight = () => { const layoutDoc = Doc.Layout(this.props.document); const bounds = this.boundsOfCollectionDocument; - return Math.min(this.MAX_EMBED_HEIGHT, (() => { + return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); @@ -269,7 +269,7 @@ class TreeView extends React.Component { Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, NumCast(this.props.containingCollection._height)))) : NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; - })()); + })())); } @computed get expandedField() { @@ -321,6 +321,9 @@ class TreeView extends React.Component { return rows; } + rtfWidth = () => Math.min(Doc.Layout(this.props.document)?.[WidthSym](), this.props.panelWidth() - 20); + rtfHeight = () => this.rtfWidth() < Doc.Layout(this.props.document)?.[WidthSym]() ? Math.min(Doc.Layout(this.props.document)?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + @computed get renderContent() { const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined; if (expandKey !== undefined) { @@ -355,8 +358,10 @@ class TreeView extends React.Component { backgroundColor={this.props.backgroundColor} fitToBox={this.boundsOfCollectionDocument !== undefined} FreezeDimensions={true} - PanelWidth={this.docWidth} - PanelHeight={this.docHeight} + NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined} + NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} + PanelWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth} + PanelHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight} getTransform={this.docTransform} CollectionDoc={this.props.containingCollection} CollectionView={undefined} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 852442fd4..28b461313 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1026,7 +1026,8 @@ export class CollectionFreeFormView extends CollectionSubView, bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) diff --git a/src/client/views/nodes/ColorBox.scss b/src/client/views/nodes/ColorBox.scss index bf334c939..da3266dc1 100644 --- a/src/client/views/nodes/ColorBox.scss +++ b/src/client/views/nodes/ColorBox.scss @@ -3,6 +3,7 @@ height:100%; position: relative; pointer-events: none; + transform-origin: top left; .sketch-picker { margin:auto; diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index 0111cadd5..0d18baaed 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -120,8 +120,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent(Docu layoutKey={this.finalLayoutKey} />
); const titleView = (!showTitle ? (null) : -
@@ -1086,10 +1086,7 @@ export class DocumentView extends DocComponent(Docu return !showTitle && !showCaption ? this.contents :
-
- {this.contents} -
- {titleView} + {this.Document.type !== DocumentType.RTF ? <> {this.contents} {titleView} : <> {titleView} {this.contents} } {captionView}
; } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 7a05ec3a3..3bedb7127 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -24,7 +24,6 @@ overflow-y: auto; overflow-x: hidden; color: initial; - height: 100%; max-height: 100%; display: flex; flex-direction: row; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 815b8b3ae..425af8ccf 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -194,7 +194,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const tsel = this._editorView.state.selection.$from; tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000))); - const curText = state.doc.textBetween(0, state.doc.content.size, "\n\n"); + const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); const curTemp = Cast(this.props.Document[this.props.fieldKey + "-textTemplate"], RichTextField); if (!this._applyingChange) { this._applyingChange = true; @@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return (
{StrCast(this.rootDoc.text, StrCast(this.rootDoc.title))} diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index afde524b6..25775edef 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -167,7 +167,7 @@ export class CurrentUserUtils { doc["template-icon-view"] = new PrefetchProxy(iconView); } if (doc["template-icon-view-rtf"] === undefined) { - const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }); + const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(self)") }); iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); } @@ -214,7 +214,7 @@ export class CurrentUserUtils { { title: "Drag a screenshot", label: "Grab", icon: "photo-video", ignoreClick: true, drag: 'Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot" })' }, { title: "Drag a webcam", label: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' }, { title: "Drag a audio recorder", label: "Audio", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, - { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, + { title: "Drag a clickable button", label: "Btn", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding:10, _yPadding: 10, title: "Button" })' }, { title: "Drag a presentation view", label: "Prezi", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory,true)`, dragFactory: doc.emptyPresentation as Doc }, { title: "Drag a search box", label: "Query", icon: "search", ignoreClick: true, drag: 'Docs.Create.QueryDocument({ _width: 200, title: "an image of a cat" })' }, { title: "Drag a scripting box", label: "Script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250 title: "untitled script" })' }, -- cgit v1.2.3-70-g09d2 From 63985ba64dc4a3a94bac75a9120cd8a5ca791fec Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 26 Apr 2020 11:28:03 -0700 Subject: buxton tweaks --- src/client/views/collections/CollectionTreeView.tsx | 7 ++++--- src/scraping/buxton/final/BuxtonImporter.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ddd7474bd..7f5dc98f1 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -727,7 +727,7 @@ export class CollectionTreeView extends CollectionSubView Date: Sun, 26 Apr 2020 21:22:51 -0400 Subject: fixed multi/col/row views to work with templates. fixed context menu subitems to appear on screen. fixed tree view display of text nodes. --- src/client/views/ContextMenu.scss | 47 +++++++++++++++++----- src/client/views/ContextMenuItem.tsx | 16 +++++--- .../views/collections/CollectionStackingView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 20 +++++---- .../CollectionMulticolumnView.tsx | 31 +++++++++----- .../CollectionMultirowView.tsx | 31 +++++++++----- src/client/views/nodes/RadialMenu.scss | 13 ------ src/client/views/nodes/RadialMenu.tsx | 9 ++--- .../authentication/models/current_user_utils.ts | 28 ++++++++++++- 9 files changed, 133 insertions(+), 66 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 8f112de0c..30938688d 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -61,6 +61,42 @@ letter-spacing: 2px; text-transform: uppercase; padding-right: 30px; + + .icon-background { + pointer-events: all; + background-color: transparent; + width: 35px; + text-align: center; + font-size: 20px; + margin-left: 5px; + margin-top: 5px; + margin-bottom: 5px; + height: 20px; + } +} +.contextMenu-description { + // width: 11vw; //10vw + background: whitesmoke; + display: flex; //comment out to allow search icon to be inline with search text + justify-content: left; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-style: none; + // padding: 10px 0px 10px 0px; + white-space: nowrap; + font-size: 13px; + color: grey; + letter-spacing: 2px; + text-transform: uppercase; + padding-right: 30px; + margin-top: 5px; + height: 20px; + margin-bottom: 5px; } .contextMenu-item:hover { @@ -121,15 +157,4 @@ padding-left: 10px; border: solid black 1px; border-radius: 5px; -} - -.icon-background { - pointer-events: all; - height:100%; - margin-top: 15px; - background-color: transparent; - width: 35px; - text-align: center; - font-size: 20px; - margin-left: 5px; } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index fef9e5f60..99840047f 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -51,7 +51,8 @@ export class ContextMenuItem extends React.Component { + _overPosY = 0; + onPointerEnter = (e: React.MouseEvent) => { if (this.currentTimeout) { clearTimeout(this.currentTimeout); this.currentTimeout = undefined; @@ -59,6 +60,7 @@ export class ContextMenuItem extends React.Component this.overItem = true), ContextMenuItem.timeout); } @@ -88,18 +90,22 @@ export class ContextMenuItem extends React.Component ); } else if ("subitems" in this.props) { + const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center"; + const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : ""; const submenu = !this.overItem ? (null) : -
+
{this._items.map(prop => )}
; return ( -
+
{this.props.icon ? ( - + ) : null} -
+
{this.props.description}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 24a3119cc..799bc7f67 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -189,8 +189,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { active={this.props.active} whenActiveChanged={this.props.whenActiveChanged} addDocTab={this.addDocTab} - pinToPres={this.props.pinToPres}> - ; + pinToPres={this.props.pinToPres} + />; } getDocWidth(d?: Doc) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7f5dc98f1..218c5705d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -348,7 +348,9 @@ class TreeView extends React.Component {
; } else { const layoutDoc = Doc.Layout(this.props.document); - return
+ const panelHeight = layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight; + const panelWidth = layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth; + return
{ FreezeDimensions={true} NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined} NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} - PanelWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth} - PanelHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight} + PanelWidth={panelWidth} + PanelHeight={panelHeight} getTransform={this.docTransform} CollectionDoc={this.props.containingCollection} CollectionView={undefined} @@ -727,7 +729,7 @@ export class CollectionTreeView extends CollectionSubView Transform, width: () => number, height: () => number) { return ; } /** diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 1eb486c4f..af0cc3b5c 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -13,7 +13,7 @@ import { Transform } from '../../../util/Transform'; import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; import { undoBatch } from '../../../util/UndoManager'; -import { DragManager } from '../../../util/DragManager'; +import { DragManager, dropActionType } from '../../../util/DragManager'; import { List } from '../../../../new_fields/List'; type MultirowDocument = makeInterface<[typeof documentSchema]>; @@ -214,21 +214,32 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) } getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return ; } /** diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss index ce0c263ef..daa620d12 100644 --- a/src/client/views/nodes/RadialMenu.scss +++ b/src/client/views/nodes/RadialMenu.scss @@ -67,17 +67,4 @@ s margin-left: 5px; text-align: left; display: inline; //need this? -} - - - -.icon-background { - pointer-events: all; - height:100%; - margin-top: 15px; - background-color: transparent; - width: 35px; - text-align: center; - font-size: 20px; - margin-left: 5px; } \ No newline at end of file diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx index 0ffed78de..ddfdb67b4 100644 --- a/src/client/views/nodes/RadialMenu.tsx +++ b/src/client/views/nodes/RadialMenu.tsx @@ -1,12 +1,9 @@ import React = require("react"); +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { action, observable, computed, IReactionDisposer, reaction, runInAction } from "mobx"; -import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import Measure from "react-measure"; -import "./RadialMenu.scss"; -import MobileInkOverlay from "../../../mobile/MobileInkOverlay"; import MobileInterface from "../../../mobile/MobileInterface"; +import "./RadialMenu.scss"; +import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem"; @observer export class RadialMenu extends React.Component { diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 25775edef..76aa51f63 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -82,8 +82,34 @@ export class CurrentUserUtils { }); } + if (doc["template-button-detail"] === undefined) { + const { TextDocument, ImageDocument, CarouselDocument, TreeDocument } = Docs.Create; + const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; + const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] } ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`; + + const textDoc = TextDocument("", { title: "details", _autoHeight: true }); + const detailView = Docs.Create.MultirowDocument([ + CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }), + TreeDocument([ + // textDoc, + TextDocument("", { title: "short description", _autoHeight: true }), + // TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true }), + TextDocument("", { title: "long description", _height: 350 }) + ], { title: "stuff", _height: 100 }) + ], { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" }); + textDoc.data = new RichTextField(detailedTemplate, "year company"); + detailView.isTemplateDoc = makeTemplate(detailView); + + doc["template-button-detail"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: new PrefetchProxy(detailView) as any as Doc, + removeDropProperties: new List(["dropAction"]), title: "detail view", icon: "window-maximize" + }); + } + if (doc["template-buttons"] === undefined) { - doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, doc["template-button-query"] as Doc], { + doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument([doc["template-button-slides"] as Doc, doc["template-button-description"] as Doc, + doc["template-button-query"] as Doc, doc["template-button-detail"] as Doc], { title: "Compound Item Creators", _xMargin: 0, _showTitle: "title", _autoHeight: true, _width: 500, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), -- cgit v1.2.3-70-g09d2 From c75ffd4900acea74c55b6bf275a5e8082c15d573 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 26 Apr 2020 19:29:50 -0700 Subject: formatted textbox disposers refactor, paragraph chunked rich text initialization and buxton importer updates --- src/client/documents/Documents.ts | 1 + .../views/collections/CollectionTreeView.tsx | 21 ++++++-- src/client/views/nodes/FormattedTextBox.tsx | 60 ++++++++-------------- src/scraping/buxton/final/BuxtonImporter.ts | 17 +++--- 4 files changed, 49 insertions(+), 50 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1268c97b0..6bd2eee3c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -407,6 +407,7 @@ export namespace Docs { const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); + deviceProto.fontFamily = "Arial"; Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } }); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7f5dc98f1..49eb90bc2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -730,18 +730,31 @@ export class CollectionTreeView extends CollectionSubView ({ + type: "paragraph", + content: [{ type: "dashField", attrs: { fieldKey } }] + })) + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + textDoc.data = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" ")); const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work? heroView.proto!.layout = ImageBox.LayoutString("hero"); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 425af8ccf..dd0df9f3f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -83,17 +83,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _lastX = 0; private _lastY = 0; private _undoTyping?: UndoManager.Batch; - private _searchReactionDisposer?: Lambda; - private _recordReactionDisposer: Opt; - private _scrollToRegionReactionDisposer: Opt; - private _reactionDisposer: Opt; - private _heightReactionDisposer: Opt; - private _proxyReactionDisposer: Opt; - private _pullReactionDisposer: Opt; - private _pushReactionDisposer: Opt; - private _buttonBarReactionDisposer: Opt; - private _linkMakerDisposer: Opt; - private _scrollDisposer: Opt; + private _disposers: { [name: string]: IReactionDisposer } = {}; private dropDisposer?: DragManager.DragDropDisposer; @computed get _recording() { return this.dataDoc.audioState === "recording"; } @@ -564,7 +554,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } componentDidMount() { - this._buttonBarReactionDisposer = reaction( + this._disposers.buttonBar = reaction( () => DocumentButtonBar.Instance, instance => { if (instance) { @@ -573,7 +563,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } ); - this._linkMakerDisposer = reaction( + this._disposers.linkMaker = reaction( () => this.props.makeLink?.(), (linkDoc: Opt) => { if (linkDoc) { @@ -584,8 +574,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, { fireImmediately: true } ); - - this._reactionDisposer = reaction( + this._disposers.editorState = reaction( () => { if (this.dataDoc[this.props.fieldKey + "-noTemplate"] || !this.props.Document[this.props.fieldKey + "-textTemplate"]) { return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data; @@ -600,8 +589,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } ); - - this._pullReactionDisposer = reaction( + this._disposers.pullDoc = reaction( () => this.props.Document[Pulls], () => { if (!DocumentButtonBar.hasPulledHack) { @@ -611,8 +599,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } ); - - this._pushReactionDisposer = reaction( + this._disposers.pushDoc = reaction( () => this.props.Document[Pushes], () => { if (!DocumentButtonBar.hasPushedHack) { @@ -621,19 +608,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } ); - - this._heightReactionDisposer = reaction( + this._disposers.height = reaction( () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight], () => this.tryUpdateHeight() ); this.setupEditor(this.config, this.props.fieldKey); - this._searchReactionDisposer = reaction(() => this.rootDoc.searchMatch, + this._disposers.search = reaction(() => this.rootDoc.searchMatch, search => search ? this.highlightSearchTerms([Doc.SearchQuery()]) : this.unhighlightSearchTerms(), { fireImmediately: true }); - this._recordReactionDisposer = reaction(() => this._recording, + this._disposers.record = reaction(() => this._recording, () => { if (this._recording) { setTimeout(action(() => { @@ -643,8 +629,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } else setTimeout(() => this.stopDictation(true), 0); } ); - - this._scrollToRegionReactionDisposer = reaction( + this._disposers.scrollToRegion = reaction( () => StrCast(this.layoutDoc.scrollToLinkID), async (scrollToLinkID) => { const findLinkFrag = (frag: Fragment, editor: EditorView) => { @@ -689,8 +674,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, { fireImmediately: true } ); - - this._scrollDisposer = reaction(() => NumCast(this.props.Document.scrollPos), + this._disposers.scroll = reaction(() => NumCast(this.props.Document.scrollPos), pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true } ); @@ -876,7 +860,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field); if (startupText) { - this._editorView.dispatch(this._editorView.state.tr.insertText(startupText)); + const paragraphSegments = startupText.split("\n\n"); + const { state: { tr }, dispatch } = this._editorView; + if (paragraphSegments.length) { + for (const paragraph of paragraphSegments) { + dispatch(tr.insertText(paragraph)); + } + } else { + dispatch(tr.insertText(startupText)); + } } } @@ -906,17 +898,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } componentWillUnmount() { - this._scrollDisposer?.(); - this._scrollToRegionReactionDisposer?.(); - this._reactionDisposer?.(); - this._proxyReactionDisposer?.(); - this._pushReactionDisposer?.(); - this._pullReactionDisposer?.(); - this._heightReactionDisposer?.(); - this._searchReactionDisposer?.(); - this._recordReactionDisposer?.(); - this._buttonBarReactionDisposer?.(); - this._linkMakerDisposer?.(); + Object.values(this._disposers).forEach(disposer => disposer?.()); this._editorView?.destroy(); } diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts index 122415460..64b988610 100644 --- a/src/scraping/buxton/final/BuxtonImporter.ts +++ b/src/scraping/buxton/final/BuxtonImporter.ts @@ -16,6 +16,7 @@ interface DocumentContents { hyperlinks: string[]; captions: string[]; embeddedFileNames: string[]; + longDescriptionParagraphs: string[]; } export interface DeviceDocument { @@ -186,10 +187,6 @@ const RegexMap = new Map>([ exp: /Short Description:\s+(.*)Bill Buxton[’']s Notes/, transformer: Utilities.correctSentences }], - ["longDescription", { - exp: /Bill Buxton[’']s Notes(.*)Device Details/, - transformer: Utilities.correctSentences - }], ]); const sourceDir = path.resolve(__dirname, "source"); @@ -267,7 +264,12 @@ async function extractFileContents(pathToDocument: string): Promise node.text()); + const captionTargets = document.find(tableCellXPath).map(node => node.text().trim()); + + const paragraphs = document.find('//*[name()="w:p"]').map(node => Utilities.correctSentences(node.text()).transformed!); + const start = paragraphs.indexOf(paragraphs.find(el => /Bill Buxton[’']s Notes/.test(el))!) + 1; + const end = paragraphs.indexOf("Device Details"); + const longDescriptionParagraphs = paragraphs.slice(start, end); const { length } = captionTargets; strictEqual(length > 3, true, "No captions written."); @@ -290,7 +292,7 @@ async function extractFileContents(pathToDocument: string): Promise { } function analyze(fileName: string, contents: DocumentContents): AnalysisResult { - const { body, imageData, captions, hyperlinks, embeddedFileNames } = contents; + const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescriptionParagraphs } = contents; const device: any = { hyperlinks, captions, @@ -376,6 +378,7 @@ function analyze(fileName: string, contents: DocumentContents): AnalysisResult { return { errors }; } + device.longDescription = longDescriptionParagraphs.join("\n\n"); return { device }; } -- cgit v1.2.3-70-g09d2 From fc470b25759e8f051dc527066f9bebcaf5e7707d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 26 Apr 2020 23:55:23 -0700 Subject: various buxton fixes --- src/client/documents/Documents.ts | 1 - .../views/collections/CollectionTreeView.tsx | 24 ++++++++-------- src/client/views/nodes/DocumentContentsView.tsx | 8 +++--- src/client/views/nodes/FormattedTextBox.tsx | 9 +----- src/scraping/buxton/final/BuxtonImporter.ts | 32 ++++++++++++---------- 5 files changed, 36 insertions(+), 38 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1651a6d55..5e0890e76 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -408,7 +408,6 @@ export namespace Docs { const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); const deviceProto = Doc.GetProto(doc); deviceProto.hero = new ImageField(constructed[0].url); - deviceProto.fontFamily = "Arial"; Docs.Get.FromJson({ data: device, appendToExisting: { targetDoc: deviceProto } }); Doc.AddDocToList(parentProto, "data", doc); } else if (errors) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 362d43ee7..dcb5e116c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -733,19 +733,21 @@ export class CollectionTreeView extends CollectionSubView { const style: { [key: string]: any } = {}; const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit; Object.keys(divKeys).map((prop: string) => { - let p = (this.props as any)[prop] as string; + const p = (this.props as any)[prop] as string; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || ""; }; @@ -178,9 +178,9 @@ export class DocumentContentsView extends React.Component 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 8d4b90c41..d98172823 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -860,15 +860,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field); if (startupText) { - const paragraphSegments = startupText.split("\n\n"); const { state: { tr }, dispatch } = this._editorView; - if (paragraphSegments.length) { - for (const paragraph of paragraphSegments) { - dispatch(tr.insertText(paragraph)); - } - } else { - dispatch(tr.insertText(startupText)); - } + dispatch(tr.insertText(startupText)); } } diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts index 64b988610..713207a07 100644 --- a/src/scraping/buxton/final/BuxtonImporter.ts +++ b/src/scraping/buxton/final/BuxtonImporter.ts @@ -16,7 +16,7 @@ interface DocumentContents { hyperlinks: string[]; captions: string[]; embeddedFileNames: string[]; - longDescriptionParagraphs: string[]; + longDescription: string; } export interface DeviceDocument { @@ -269,7 +269,7 @@ async function extractFileContents(pathToDocument: string): Promise Utilities.correctSentences(node.text()).transformed!); const start = paragraphs.indexOf(paragraphs.find(el => /Bill Buxton[’']s Notes/.test(el))!) + 1; const end = paragraphs.indexOf("Device Details"); - const longDescriptionParagraphs = paragraphs.slice(start, end); + const longDescription = paragraphs.slice(start, end).filter(paragraph => paragraph.length).join("\n\n"); const { length } = captionTargets; strictEqual(length > 3, true, "No captions written."); @@ -292,7 +292,7 @@ async function extractFileContents(pathToDocument: string): Promise { const imageEntries = allEntries.filter(name => imageEntry.test(name)); const imageUrls: ImageData[] = []; - for (const mediaPath of imageEntries) { - const getImageStream = () => new Promise((resolve, reject) => { - zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream)); - }); + const valid: any[] = []; + + const getImageStream = (mediaPath: string) => new Promise((resolve, reject) => { + zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream)); + }); + for (const mediaPath of imageEntries) { const { width, height, type } = await new Promise(async resolve => { const sizeStream = (createImageSizeStream() as PassThrough).on('size', (dimensions: Dimensions) => { readStream.destroy(); resolve(dimensions); }).on("error", () => readStream.destroy()); - const readStream = await getImageStream(); + const readStream = await getImageStream(mediaPath); readStream.pipe(sizeStream); }); - if (Math.abs(width - height) < 10) { - continue; + + if (Math.abs(width - height) > 10) { + valid.push({ width, height, type, mediaPath }); } + } + for (const { type, width, height, mediaPath } of valid) { const generatedFileName = `upload_${Utils.GenerateGuid()}.${type.toLowerCase()}`; - await DashUploadUtils.outputResizedImages(getImageStream, generatedFileName, imageDir); - + await DashUploadUtils.outputResizedImages(() => getImageStream(mediaPath), generatedFileName, imageDir); imageUrls.push({ url: `/files/images/buxton/${generatedFileName}`, nativeWidth: width, @@ -339,11 +343,12 @@ async function writeImages(zip: any): Promise { } function analyze(fileName: string, contents: DocumentContents): AnalysisResult { - const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescriptionParagraphs } = contents; + const { body, imageData, captions, hyperlinks, embeddedFileNames, longDescription } = contents; const device: any = { hyperlinks, captions, embeddedFileNames, + longDescription, __images: imageData }; const errors: { [key: string]: string } = { fileName }; @@ -378,7 +383,6 @@ function analyze(fileName: string, contents: DocumentContents): AnalysisResult { return { errors }; } - device.longDescription = longDescriptionParagraphs.join("\n\n"); return { device }; } -- cgit v1.2.3-70-g09d2 From 7f6bc06f72e070bd8b45eba8b4c0669ee398387b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 27 Apr 2020 02:55:59 -0400 Subject: fixed fontsize inheritance. fixed masonry view with a columnWidth of -1 to fit width. cleaned up masonry and editableViews --- src/client/documents/Documents.ts | 3 +- src/client/util/RichTextRules.ts | 2 +- src/client/views/EditableView.tsx | 2 - .../collections/CollectionMasonryViewFieldRow.tsx | 53 ++++++++-------------- .../views/collections/CollectionStackingView.tsx | 4 +- .../CollectionStackingViewFieldColumn.tsx | 5 -- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 3 +- src/client/views/nodes/SliderBox.tsx | 2 +- src/new_fields/documentSchemas.ts | 2 + 11 files changed, 35 insertions(+), 49 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5e0890e76..c64916897 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -110,7 +110,8 @@ export interface DocumentOptions { isBackground?: boolean; isLinkButton?: boolean; columnWidth?: number; - fontSize?: number; + _fontSize?: number; + _fontFamily?: string; curPage?: number; currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 3746199ba..8b11be6fb 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -143,7 +143,7 @@ export class RichTextRules { textDoc.inlineTextCount = numInlines + 1; const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: 9, title: "inline comment" }); textDocInline.title = inlineFieldKey; // give the annotation its own title textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 2219966e5..c51173ad3 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -46,7 +46,6 @@ export interface EditableProps { menuCallback?: (x: number, y: number) => void; showMenuOnLoad?: boolean; HeadingObject?: SchemaHeaderField | undefined; - HeadingsHack?: number; toggle?: () => void; color?: string | undefined; } @@ -60,7 +59,6 @@ export interface EditableProps { export class EditableView extends React.Component { public static loadId = ""; @observable _editing: boolean = false; - @observable _headingsHack: number = 1; constructor(props: EditableProps) { super(props); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index b272151c1..42f0c4311 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -4,7 +4,6 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import Measure from "react-measure"; import { Doc } from "../../../new_fields/Doc"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { ScriptField } from "../../../new_fields/ScriptField"; @@ -46,7 +45,6 @@ export class CollectionMasonryViewFieldRow extends React.Component = React.createRef(); private _sensitivity: number = 16; - private _counter: number = 0; private _ele: any; createRowDropRef = (ele: HTMLDivElement | null) => { @@ -167,7 +164,6 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; if (this.props.headingObject) { - this._headingsHack++; this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed); this.toggleVisibility(); } @@ -197,6 +193,10 @@ export class CollectionMasonryViewFieldRow extends React.Component); } - handleResize = (size: any) => { - if (++this._counter !== 1) { - this.getTrueHeight(); - } - } - @computed get contentLayout() { const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap)))); const style = this.props.parent; @@ -277,7 +271,6 @@ export class CollectionMasonryViewFieldRow extends React.Component : !this.props.headingObject ? (null) :
-
- {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) : + {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
} - - {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) : + } + {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :