From 471dce0623a751aed34f1cf69e022049a45e56bd Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 4 Feb 2020 11:37:06 -0500 Subject: added defaults for mulitirow/col --- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6453cfe17..63544a637 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -824,13 +824,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; - Array.from(newPool.keys()).map(key => { - const lastPos = this._cachedPool.get(key); // last computed pos - const newPos = newPool.get(key); - 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) { - runInAction(() => this._layoutPoolData.set(key, { transition: "transform 1s", ...newPos })); - } - }); + runInAction(() => + Array.from(newPool.keys()).map(key => { + const lastPos = this._cachedPool.get(key); // last computed pos + const newPos = newPool.get(key); + 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._cachedPool.clear(); Array.from(newPool.keys()).forEach(k => this._cachedPool.set(k, newPool.get(k))); this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair => -- cgit v1.2.3-70-g09d2 From 7eb6f1bcc35d852308e2c2ea466d9727d3ba10ef Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 4 Feb 2020 15:56:38 -0500 Subject: pivot view/time view working --- src/client/views/collections/CollectionSubView.tsx | 1 + .../views/collections/CollectionTimeView.scss | 3 + .../views/collections/CollectionTimeView.tsx | 61 ++++++++++---------- .../CollectionFreeFormLayoutEngines.tsx | 65 ++++++++++++++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- 5 files changed, 85 insertions(+), 49 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 9cdd48089..a2700e75a 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -43,6 +43,7 @@ export interface SubCollectionViewProps extends CollectionViewProps { children?: never | (() => JSX.Element[]) | React.ReactNode; isAnnotationOverlay?: boolean; annotationsKey: string; + layoutEngine?: () => string; } export function CollectionSubView(schemaCtor: (doc: Doc) => T) { diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss index 3aac2bc75..df5057b5b 100644 --- a/src/client/views/collections/CollectionTimeView.scss +++ b/src/client/views/collections/CollectionTimeView.scss @@ -8,6 +8,9 @@ .collectionFreeform-customText { text-align: left; } + .collectionFreeform-customDiv { + position: absolute; + } .collectionTimeView-thumb { position: absolute; width: 30px; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index c92ffdeea..f999067d3 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,6 +1,6 @@ import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, observable } from "mobx"; +import { action, computed, IReactionDisposer, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Set } from "typescript-collections"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -22,8 +22,10 @@ import { RichTextField } from "../../../new_fields/RichTextField"; @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { + _changing = false; + @observable _layoutEngine = "pivot"; + componentDidMount() { - this.props.Document._freeformLayoutEngine = "timeline"; const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked; if (!this.props.Document._facetCollection) { const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true }); @@ -190,14 +192,14 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { document.removeEventListener("pointermove", this.onMidUp); } + layoutEngine = () => this._layoutEngine; @computed get contents() { return
- +
; } - - _changing = false; - render() { + @computed get filterView() { + trace(); const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null); const flyout = (
@@ -208,6 +210,22 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { )}
); + return
+
e.stopPropagation()}> + +
+ Facet Filters + +
+
+
+
+ +
+
; + } + + render() { const newEditableViewProps = { GetValue: () => "", SetValue: (value: any) => { @@ -230,23 +248,18 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { nonNumbers++; } }); - const doTimeline = nonNumbers / this.childDocs.length < 0.1; - if (doTimeline !== (this.props.Document._freeformLayoutEngine === "timeline")) { + const doTimeline = nonNumbers / this.childDocs.length < 0.1 && this.props.PanelWidth() / this.props.PanelHeight() > 6; + if (doTimeline !== (this._layoutEngine === "timeline")) { if (!this._changing) { this._changing = true; - setTimeout(() => { - if (nonNumbers / this.childDocs.length > 0.1) { - this.childDocs.map(child => child.isMinimized = false); - this.props.Document._freeformLayoutEngine = "pivot"; - } else { - this.props.Document._freeformLayoutEngine = "timeline"; - } + setTimeout(action(() => { + this._layoutEngine = doTimeline ? "timeline" : "pivot"; this._changing = false; - }, 0); + }), 0); } - return (null); } + const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null); return !facetCollection ? (null) :
@@ -257,19 +270,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
} -
-
e.stopPropagation()}> - -
- Facet Filters - -
-
-
-
- -
-
+ {this.filterView} {this.contents} {!this.props.isSelected() || !doTimeline ? (null) : <>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 63bcc68e5..f35662dab 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -67,35 +67,59 @@ export function computePivotLayout( panelDim: number[], viewDefsToJSX: (views: any) => ViewDefResult[] ) { - console.log("PIVOT " + pivotDoc[HeightSym]()); const fieldKey = "data"; const pivotColumnGroups = new Map, Doc[]>(); const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3)); - let maxInColumn = 1; const pivotFieldKey = toLabel(pivotDoc.pivotField); for (const doc of childDocs) { const val = Field.toString(doc[pivotFieldKey] as Field); if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, []); pivotColumnGroups.get(val)!.push(doc); - maxInColumn = Math.max(maxInColumn, pivotColumnGroups.get(val)?.length || 0); } } + if (pivotColumnGroups.size > 10) { + const arrayofKeys = Array.from(pivotColumnGroups.keys()); + const sortedKeys = arrayofKeys.sort(); + const clusterSize = Math.ceil(pivotColumnGroups.size / 10); + const numClusters = Math.ceil(sortedKeys.length / clusterSize); + for (let i = 0; i < numClusters; i++) { + for (let j = i * clusterSize + 1; j < Math.min(sortedKeys.length, (i + 1) * clusterSize); j++) { + pivotColumnGroups.get(sortedKeys[i * clusterSize])!.push(...pivotColumnGroups.get(sortedKeys[j])!); + pivotColumnGroups.delete(sortedKeys[j]); + } + } + } + let maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.length), 1); const colWidth = panelDim[0] / pivotColumnGroups.size; const colHeight = panelDim[1]; - const pivotAxisWidth = Math.sqrt(colWidth * colHeight / maxInColumn); - const numCols = Math.max(Math.round(colWidth / pivotAxisWidth), 1); + let numCols = 0; + let bestArea = 0; + let pivotAxisWidth = 0; + for (let i = 1; i < 10; i++) { + const numInCol = Math.ceil(maxInColumn / i); + const hd = colHeight / numInCol; + const wd = colWidth / i; + const dim = Math.min(hd, wd); + const area = dim * dim * i * numInCol; + if (area > bestArea) { + bestArea = area; + numCols = i; + pivotAxisWidth = dim; + } + } const docMap = new Map(); - const groupNames: PivotData[] = [];; + const groupNames: PivotData[] = []; const expander = 1.05; const gap = .15; let x = 0; let max_text = 60; - pivotColumnGroups.forEach((val, key) => { + Array.from(pivotColumnGroups.keys()).sort().forEach(key => { + const val = pivotColumnGroups.get(key)!; let y = 0; let xCount = 0; const text = toLabel(key); @@ -132,6 +156,10 @@ export function computePivotLayout( x += pivotAxisWidth * (numCols * expander + gap); }); + const maxColHeight = pivotAxisWidth * Math.ceil(maxInColumn / numCols) + pivotAxisWidth; + const dividers = Array.from(pivotColumnGroups.values()).map((pg, i) => + ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight } as any)); + groupNames.push(...dividers); return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, []); } @@ -161,12 +189,10 @@ export function computeTimelineLayout( } let minTime = Number.MAX_VALUE; - let maxTime = Number.MIN_VALUE; + let maxTime = -Number.MAX_VALUE; childDocs.map(doc => { const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey]))); - if (Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq)) { - doc.isMinimized = true; - } else { + if (!(Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq))) { !pivotDateGroups.get(num) && pivotDateGroups.set(num, []); pivotDateGroups.get(num)!.push(doc); minTime = Math.min(num, minTime); @@ -180,8 +206,14 @@ export function computeTimelineLayout( minTime = curTime - (maxTime - curTime); } } - pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime; - pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime; + setTimeout(() => { + pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime; + pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime; + }, 0); + + if (maxTime === minTime) { + maxTime = minTime + 1; + } const arrayofKeys = Array.from(pivotDateGroups.keys()); const sortedKeys = arrayofKeys.sort((n1, n2) => n1 - n2); @@ -204,7 +236,6 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize }); } const keyDocs = pivotDateGroups.get(key)!; - keyDocs.forEach(d => d.isMinimized = false); x += scaling * (key - prevKey); const stack = findStack(x, stacking); prevKey = key; @@ -244,15 +275,15 @@ export function computeTimelineLayout( function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, poolData: Map, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: PivotData[], minWidth: number, extras: PivotData[]) { - const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, height: gn.height }) as PivotData); - const docEles = childPairs.filter(d => !d.layout.isMinimized).map(pair => docMap.get(pair.layout) as PivotData); + const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as PivotData); + const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as PivotData); const aggBounds = aggregateBounds(docEles.concat(grpEles), 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 => !d.layout.isMinimized).map(pair => { + childPairs.filter(d => docMap.get(d.layout)).map(pair => { const newPosRaw = docMap.get(pair.layout); if (newPosRaw) { const newPos = { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 63544a637..6b81eba7d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -816,7 +816,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get doInternalLayoutComputation() { const newPool = new Map(); - switch (this.Document._freeformLayoutEngine) { + switch (this.props.layoutEngine?.()) { case "timeline": return { newPool, computedElementData: this.doTimelineLayout(newPool) }; case "pivot": return { newPool, computedElementData: this.doPivotLayout(newPool) }; } @@ -839,7 +839,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ele: , + fitToBox={this.props.fitToBox || this.props.layoutEngine !== undefined} />, bounds: this.childDataProvider(pair.layout) })); -- cgit v1.2.3-70-g09d2 From ab95c12485c26e5e0e6dc48cef8abd63a9c1fb56 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 5 Feb 2020 15:05:32 -0500 Subject: several layout fixes for row/col stuff. fixes to icons. added buttons for buxton app. made PresBox not search for its own contents when following a link --- src/client/util/DocumentManager.ts | 4 +- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 4 +- src/client/views/collections/CollectionSubView.tsx | 6 ++- .../views/collections/CollectionTimeView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 47 +++++++++++++++------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../CollectionMulticolumnView.tsx | 5 +-- .../CollectionMultirowView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 21 +++++----- .../authentication/models/current_user_utils.ts | 9 ++--- 11 files changed, 67 insertions(+), 41 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 9fff8faa7..60bb25272 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -93,9 +93,9 @@ export class DocumentManager { const toReturn: DocumentView[] = []; DocumentManager.Instance.DocumentViews.map(view => - view.props.Document === toFind && toReturn.push(view)); + view.props.Document.presBox === undefined && view.props.Document === toFind && toReturn.push(view)); DocumentManager.Instance.DocumentViews.map(view => - view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); + view.props.Document.presBox === undefined && view.props.Document !== toFind && Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view)); return toReturn; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0cc374cee..a839f9fd2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { - faStickyNote, faArrowDown, faBullseye, faFilter, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, + faFileAlt, faStickyNote, faArrowDown, faBullseye, faFilter, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp, faClipboard } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -101,6 +101,8 @@ export class MainView extends React.Component { } } + library.add(faFileAlt); + library.add(faStickyNote); library.add(faFont); library.add(faExclamation); library.add(faPortrait); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 861a63503..9c67d8041 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -468,7 +468,9 @@ export class CollectionDockingView extends React.Component { e.preventDefault(); e.stopPropagation(); - DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY); + const dragData = new DragManager.DocumentDragData([doc]); + dragData.dropAction = doc._dropAction; + DragManager.StartDocumentDrag([dragSpan], dragData, e.clientX, e.clientY); }}> , dragSpan); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a2700e75a..731b5b01d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -128,11 +128,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const filteredDocs = docFilters.length ? viewedDocs.filter(d => { for (const key of Object.keys(clusters)) { const cluster = clusters[key]; + const satisfiesFacetx = !Object.keys(cluster).some((inner) => { + const modifier = cluster[inner]; + return ((modifier === "x") === Doc.matchFieldValue(d, key, inner)); + }); const satisfiesFacet = Object.keys(cluster).some(inner => { const modifier = cluster[inner]; return (modifier === "x") !== Doc.matchFieldValue(d, key, inner); }); - if (!satisfiesFacet) { + if (!satisfiesFacet || !satisfiesFacetx) { return false; } } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 264423d72..896fb74f0 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -33,7 +33,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { this.props.Document.excludeFields = new List(["_facetCollection", "_docFilter"]); const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; - const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailed'); useRightSplit(alias, shiftKey); "; + const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailView'); alias._dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; facetCollection.onCheckedClick = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name }); this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "boolean", containingCollection: Doc.name, shiftKey: "boolean" }); this.props.Document._facetCollection = facetCollection; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 9e3fb1d67..19900c48b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -640,27 +640,46 @@ export class CollectionTreeView extends CollectionSubView(Document) { d.captions = undefined; }); }); - const { TextDocument, ImageDocument, CarouselDocument } = Docs.Create; + const { TextDocument, ImageDocument, CarouselDocument, TreeDocument } = Docs.Create; const { Document } = this.props; const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; - const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "short_description" } } ] }, { "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 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 detailedLayout = Docs.Create.StackingDocument([ + const detailView = Docs.Create.StackingDocument([ CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }), textDoc, - ], { _chromeStatus: "disabled", title: "detailed layout stack" }); - textDoc.data = new RichTextField(detailedTemplate, "short_description year company"); - detailedLayout.isTemplateDoc = makeTemplate(detailedLayout); - - const cardLayout = ImageDocument(fallbackImg, { title: "cardLayout", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work? - cardLayout.proto!.layout = ImageBox.LayoutString("hero"); - cardLayout.showTitle = "title"; - cardLayout.showTitleHover = "titlehover"; - - Document.childLayout = cardLayout; - Document.childDetailed = detailedLayout; + TextDocument("", { title: "short_description", _autoHeight: true }), + TreeDocument([], { title: "narratives", _height: 75, treeViewHideTitle: true }) + ], { _chromeStatus: "disabled", _width: 300, _height: 300, _autoHeight: true, title: "detailView" }); + textDoc.data = new RichTextField(detailedTemplate, "year company"); + detailView.isTemplateDoc = makeTemplate(detailView); + + + 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"); + heroView.showTitle = "title"; + heroView.showTitleHover = "titlehover"; + heroView._dropAction = "alias"; + heroView.removeDropProperties = new List(["dropAction"]); + + Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", + Docs.Create.FontIconDocument({ + _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, _dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: heroView, removeDropProperties: new List(["dropAction"]), title: "hero view", icon: "portrait" + })); + + Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", + Docs.Create.FontIconDocument({ + _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, _dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), + dragFactory: detailView, removeDropProperties: new List(["dropAction"]), title: "detail view", icon: "file-alt" + })); + + + Document.childLayout = heroView; + Document.childDetailed = detailView; Document._viewType = CollectionViewType.Time; + Document._forceActive = true; Document.pivotField = "company"; } }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6b81eba7d..54c45e43a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -434,7 +434,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let y = this.Document._panY || 0; const docs = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.isMinimized).map(pair => pair.layout); const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - if (!this.isAnnotationOverlay && docs.length) { + if (!this.isAnnotationOverlay && docs.length && this.childDataProvider(docs[0])) { PDFMenu.Instance.fadeOut(true); const minx = this.childDataProvider(docs[0]).x;//docs.length ? NumCast(docs[0].x) : 0; const miny = this.childDataProvider(docs[0]).y;//docs.length ? NumCast(docs[0].y) : 0; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 92b908ab6..7d8de0db4 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -5,7 +5,6 @@ import { Doc } from '../../../../new_fields/Doc'; import { documentSchema } from '../../../../new_fields/documentSchemas'; import { makeInterface } from '../../../../new_fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../new_fields/Types'; -import { Utils } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -119,7 +118,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu private get totalRatioAllocation(): number | undefined { const layoutInfoLen = this.resolvedLayoutInformation.widthSpecifiers.length; if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) { - return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)); + return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._xMargin); } } @@ -228,7 +227,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu for (let i = 0; i < childLayoutPairs.length; i++) { const { layout } = childLayoutPairs[i]; const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)); - const width = () => this.lookupPixels(layout) - 2 * NumCast(Document._xMargin); + const width = () => this.lookupPixels(layout); const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); collector.push(
0 && this.totalFixedAllocation !== undefined) { - return this.props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)); + return this.props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._yMargin); } } @@ -228,7 +228,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) for (let i = 0; i < childLayoutPairs.length; i++) { const { layout } = childLayoutPairs[i]; const dxf = () => this.lookupIndividualTransform(layout).translate(-NumCast(Document._xMargin), -NumCast(Document._yMargin)); - const height = () => this.lookupPixels(layout) - 2 * NumCast(Document._yMargin); + const height = () => this.lookupPixels(layout); const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); collector.push(
(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.onClickHandler) && !this.Document.lockedPosition && !this.Document.inOverlay) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { + if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this.startDragging(this._downX, this._downY, this.Document._dropAction ? this.Document._dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); @@ -518,7 +518,7 @@ export class DocumentView extends DocComponent(Docu } @undoBatch - deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } + deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument?.(this.props.Document); } static makeNativeViewClicked = (doc: Doc) => { undoBatch(() => Doc.setNativeView(doc))(); @@ -597,7 +597,7 @@ export class DocumentView extends DocComponent(Docu `Link from ${StrCast(de.complete.annoDragData.annotationDocument.title)}`); } if (de.complete.docDragData && de.complete.docDragData.applyAsTemplate) { - Doc.ApplyTemplateTo(de.complete.docDragData.draggedDocuments[0], this.props.Document, "layout_custom"); + Doc.ApplyTemplateTo(de.complete.docDragData.draggedDocuments[0], this.props.Document, "layout_custom", undefined); e.stopPropagation(); } if (de.complete.linkDragData) { @@ -656,12 +656,13 @@ export class DocumentView extends DocComponent(Docu if (custom) { DocumentView.makeNativeViewClicked(this.props.Document); - let foundLayout: Opt = Cast(Doc.UserDoc().iconView, Doc, null); - !foundLayout && DocListCast(Cast(CurrentUserUtils.UserDocument.expandingButtons, Doc, null)?.data)?.map(btnDoc => { - if (StrCast(Cast(btnDoc?.dragFactory, Doc, null)?.title) === layout) { - foundLayout = btnDoc.dragFactory as Doc; - } - }) + let foundLayout: Opt; + DocListCast(Cast(Doc.UserDoc().expandingButtons, Doc, null)?.data)?.concat([Cast(Doc.UserDoc().iconView, Doc, null)]). + map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc).forEach(tempDoc => { + if (StrCast(tempDoc.title) === layout) { + foundLayout = tempDoc; + } + }) DocumentView. makeCustomViewClicked(this.props.Document, this.props.DataDoc, Docs.Create.StackingDocument, layout, foundLayout); } else { diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index a4af46a63..74db511df 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -53,14 +53,14 @@ export class CurrentUserUtils { doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", _height: 75 }); doc.activePen = doc; const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ - { title: "collection", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection }, + { title: "collection", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection }, { title: "preview", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,true,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' }, { title: "todo item", icon: "check", ignoreClick: true, drag: 'getCopy(this.dragFactory, true)', dragFactory: notes[notes.length - 1] }, { title: "web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", {_width: 300, _height: 300, title: "New Webpage" })' }, { title: "cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 200, title: "an image of a cat" })' }, { title: "record", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` }, { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' }, - { title: "presentation", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation }, + { title: "presentation", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation }, { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' }, { title: "mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, @@ -240,7 +240,7 @@ export class CurrentUserUtils { { _width: 400, _height: 300, title: "slide", _chromeStatus: "disabled", _autoHeight: true }); slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, onClick: ScriptField.MakeScript("setNativeView(this)") }); + const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("setNativeView(this)") }); Doc.GetProto(iconDoc).data = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); doc.isTemplateDoc = makeTemplate(iconDoc); doc.iconView = new PrefetchProxy(iconDoc); @@ -250,8 +250,7 @@ export class CurrentUserUtils { doc.redoBtn = Docs.Create.FontIconDocument( { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, _dropAction: "alias", onClick: ScriptField.MakeScript("redo()"), removeDropProperties: new List(["dropAction"]), title: "redo button", icon: "redo-alt" }); doc.slidesBtn = Docs.Create.FontIconDocument( - { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, _dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, removeDropProperties: new List(["dropAction"]), title: "slide button", icon: "sticky-note" }); - + { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, _dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, removeDropProperties: new List(["dropAction"]), title: "presentation slide", icon: "sticky-note" }); doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc, doc.slidesBtn as Doc], { title: "expanding buttons", _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true, -- cgit v1.2.3-70-g09d2 From 045233f2a8b8e79e6a3255ed594218929db1b042 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 6 Feb 2020 15:14:56 -0500 Subject: added pivoting into a pivot column --- src/client/views/collections/CollectionSubView.tsx | 6 +- .../views/collections/CollectionTimeView.tsx | 14 +++- .../CollectionFreeFormLayoutEngines.tsx | 87 ++++++++++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++-- src/client/views/nodes/PresBox.tsx | 2 +- src/new_fields/Doc.ts | 31 ++++---- src/new_fields/util.ts | 2 +- 7 files changed, 87 insertions(+), 70 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 731b5b01d..a2700e75a 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -128,15 +128,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const filteredDocs = docFilters.length ? viewedDocs.filter(d => { for (const key of Object.keys(clusters)) { const cluster = clusters[key]; - const satisfiesFacetx = !Object.keys(cluster).some((inner) => { - const modifier = cluster[inner]; - return ((modifier === "x") === Doc.matchFieldValue(d, key, inner)); - }); const satisfiesFacet = Object.keys(cluster).some(inner => { const modifier = cluster[inner]; return (modifier === "x") !== Doc.matchFieldValue(d, key, inner); }); - if (!satisfiesFacet || !satisfiesFacetx) { + if (!satisfiesFacet) { return false; } } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index a9114026f..c0e18714e 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -20,6 +20,8 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { RichTextField } from "../../../new_fields/RichTextField"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; +import { Scripting } from "../../util/Scripting"; +import { ViewDefResult, ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines"; @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { @@ -40,7 +42,11 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { this.props.Document._facetCollection = facetCollection; this.props.Document._fitToBox = true; } + if (!this.props.Document.onViewDefClick) { + this.props.Document.onViewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }) + } } + bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth; getTransform = () => this.props.ScreenToLocalTransform().translate(-this._facetWidth, 0); @@ -299,4 +305,10 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { }
; } -} \ No newline at end of file +} + +Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) { + console.log("filter down to key: " + pivotDoc._pivotField + " val:" + bounds.payload); + (bounds.payload as string[]).map(filterVal => + Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check")); +}); \ 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 d58925aab..884f3b353 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -1,29 +1,18 @@ -import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc"; -import { NumCast, StrCast, Cast, DateCast, BoolCast } from "../../../../new_fields/Types"; +import { Doc, Field, FieldResult } from "../../../../new_fields/Doc"; +import { NumCast, StrCast, Cast } from "../../../../new_fields/Types"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; import { ScriptField } from "../../../../new_fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { emptyFunction, aggregateBounds } from "../../../../Utils"; import React = require("react"); -import { ObservableMap, runInAction } from "mobx"; import { Id, ToString } from "../../../../new_fields/FieldSymbols"; import { ObjectField } from "../../../../new_fields/ObjectField"; import { RefField } from "../../../../new_fields/RefField"; -interface PivotData { - type: string; - text: string; - x: number; - y: number; - zIndex?: number; - width?: number; - height?: number; - fontSize: number; - color?: string; -} - export interface ViewDefBounds { + type: string; + text?: string; x: number; y: number; z?: number; @@ -31,7 +20,10 @@ export interface ViewDefBounds { width?: number; height?: number; transition?: string; + fontSize?: number; highlight?: boolean; + color?: string; + payload: any; } export interface PoolData { @@ -83,14 +75,14 @@ export function computePivotLayout( viewDefsToJSX: (views: any) => ViewDefResult[] ) { const fieldKey = "data"; - const pivotColumnGroups = new Map, Doc[]>(); + const pivotColumnGroups = new Map, { docs: Doc[], filters: string[] }>(); const pivotFieldKey = toLabel(pivotDoc._pivotField); for (const doc of childDocs) { const val = Field.toString(doc[pivotFieldKey] as Field); if (val) { - !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, []); - pivotColumnGroups.get(val)!.push(doc); + !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val] }); + pivotColumnGroups.get(val)!.docs.push(doc); } } if (pivotColumnGroups.size > 10) { @@ -100,7 +92,10 @@ export function computePivotLayout( const numClusters = Math.ceil(sortedKeys.length / clusterSize); for (let i = 0; i < numClusters; i++) { for (let j = i * clusterSize + 1; j < Math.min(sortedKeys.length, (i + 1) * clusterSize); j++) { - pivotColumnGroups.get(sortedKeys[i * clusterSize])!.push(...pivotColumnGroups.get(sortedKeys[j])!); + const curgrp = pivotColumnGroups.get(sortedKeys[i * clusterSize])!; + const newgrp = pivotColumnGroups.get(sortedKeys[j])!; + curgrp.docs.push(...newgrp.docs); + curgrp.filters.push(...newgrp.filters); pivotColumnGroups.delete(sortedKeys[j]); } } @@ -109,7 +104,7 @@ export function computePivotLayout( const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`; const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number); const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2); - let maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.length), 1); + let maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1); const colWidth = panelDim[0] / pivotColumnGroups.size; const colHeight = panelDim[1] - max_text; @@ -129,7 +124,7 @@ export function computePivotLayout( } const docMap = new Map(); - const groupNames: PivotData[] = []; + const groupNames: ViewDefBounds[] = []; const expander = 1.05; const gap = .15; @@ -146,9 +141,10 @@ export function computePivotLayout( y: pivotAxisWidth, width: pivotAxisWidth * expander * numCols, height: max_text, - fontSize + fontSize, + payload: val }); - for (const doc of val) { + for (const doc of val.docs) { const layoutDoc = Doc.Layout(doc); let wid = pivotAxisWidth; let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth; @@ -157,10 +153,12 @@ export function computePivotLayout( wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } docMap.set(doc, { - x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.length < numCols ? (numCols - val.length) * pivotAxisWidth / 2 : 0), + 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 + height: hgt, + payload: undefined }); xCount++; if (xCount >= numCols) { @@ -172,12 +170,15 @@ export function computePivotLayout( }); const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols); - const dividers = Array.from(pivotColumnGroups.values()).map((pg, i) => - ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight } as any)); + const dividers = Array.from(pivotColumnGroups.values()).map((pkey, i) => + ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pkey.filters })); groupNames.push(...dividers); return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, []); } +function toNumber(val: FieldResult) { + return val === undefined ? undefined : NumCast(val, Number(StrCast(val))); +} export function computeTimelineLayout( poolData: Map, @@ -190,9 +191,9 @@ export function computeTimelineLayout( const fieldKey = "data"; const pivotDateGroups = new Map(); const docMap = new Map(); - const groupNames: PivotData[] = []; + const groupNames: ViewDefBounds[] = []; const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field); - const curTime = Cast(pivotDoc[fieldKey + "-timelineCur"], "number", null); + const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]); const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null); const minTimeReq = curTime === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTimeSpan && (curTime - curTimeSpan); const maxTimeReq = curTime === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTimeSpan && (curTime + curTimeSpan); @@ -237,10 +238,10 @@ export function computeTimelineLayout( let prevKey = Math.floor(minTime); if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) { - groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize }); + groupNames.push({ type: "text", text: prevKey.toString(), x: x, y: 0, height: fontHeight, fontSize, payload: undefined }); } if (!sortedKeys.length && curTime !== undefined) { - groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize }); + groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined }); } const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5); @@ -248,21 +249,23 @@ export function computeTimelineLayout( let zind = 0; sortedKeys.forEach(key => { if (curTime !== undefined && curTime > prevKey && curTime <= key) { - groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize }); + groupNames.push({ type: "text", text: curTime.toString(), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key }); } const keyDocs = pivotDateGroups.get(key)!; x += scaling * (key - prevKey); const stack = findStack(x, stacking); prevKey = key; - !stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth) && groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize }); + if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) { + groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined }); + } layoutDocsAtTime(keyDocs, key); }); - if (sortedKeys.length && curTime > sortedKeys[sortedKeys.length - 1]) { + if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) { x = (curTime - minTime) * scaling; - groupNames.push({ type: "text", text: curTime.toString(), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize }); + groupNames.push({ type: "text", text: curTime.toString(), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined }); } if (Math.ceil(maxTime - minTime) * scaling > x + 25) { - groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize }); + groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any; @@ -279,8 +282,9 @@ export function computeTimelineLayout( wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } docMap.set(doc, { + 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 + zIndex: (curTime === key ? 1000 : zind++), highlight: curTime === key, width: wid / (Math.max(stack, 1)), height: hgt, payload: undefined }); stacking[stack] = x + pivotAxisWidth; }); @@ -288,10 +292,10 @@ export function computeTimelineLayout( } function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, - poolData: Map, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: PivotData[], minWidth: number, extras: PivotData[]) { + poolData: Map, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[]) { - const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as PivotData); - const docEles = childPairs.filter(d => docMap.get(d.layout)).map(pair => docMap.get(pair.layout) as PivotData); + 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); aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x); const wscale = panelDim[0] / (aggBounds.r - aggBounds.x); @@ -323,7 +327,8 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { color: gname.color, width: gname.width === undefined ? undefined : gname.width * scale, height: Math.max(fontHeight, (gname.height || 0) * scale), - fontSize: gname.fontSize + fontSize: gname.fontSize, + payload: gname.payload })))) }; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 54c45e43a..a34779046 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -32,7 +32,7 @@ import { FormattedTextBox } from "../../nodes/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import PDFMenu from "../../pdf/PDFMenu"; import { CollectionSubView } from "../CollectionSubView"; -import { computePivotLayout, ViewDefResult, computeTimelineLayout, PoolData } from "./CollectionFreeFormLayoutEngines"; +import { computePivotLayout, ViewDefResult, computeTimelineLayout, PoolData, ViewDefBounds } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; @@ -747,11 +747,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } - viewDefsToJSX = (views: any[]) => { + viewDefsToJSX = (views: ViewDefBounds[]) => { return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!); } - private viewDefToJSX(viewDef: any): Opt { + onViewDefDivClick = (e: React.MouseEvent, payload: any) => { + (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload }); + } + private viewDefToJSX(viewDef: ViewDefBounds): Opt { const x = Cast(viewDef.x, "number"); const y = Cast(viewDef.y, "number"); const z = Cast(viewDef.z, "number"); @@ -769,15 +772,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { style={{ width, height, color, fontSize, transform: `translate(${x}px, ${y}px)` }}> {text}
, - bounds: { x: x!, y: y!, z, zIndex, width, height } + bounds: viewDef }; } else if (viewDef.type === "div") { const backgroundColor = Cast(viewDef.color, "string"); return [x, y].some(val => val === undefined) ? undefined : { - ele:
this.onViewDefDivClick(e, viewDef)} style={{ width, height, backgroundColor, transform: `translate(${x}px, ${y}px)` }} />, - bounds: { x: x!, y: y!, z, zIndex, width, height } + bounds: viewDef }; } } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index be5d414a5..6c4cbba12 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -360,7 +360,7 @@ export class PresBox extends React.Component { const funcs: ContextMenuProps[] = []; funcs.push({ description: "Show as Slideshow", event: action(() => this.props.Document._slideshow = "slideshow"), icon: "asterisk" }); funcs.push({ description: "Show as Timeline", event: action(() => this.props.Document._slideshow = "timeline"), icon: "asterisk" }); - funcs.push({ description: "Show as List", event: action(() => this.props.Document._slideshow = "list"), icon: "asterisk" }); + funcs.push({ description: "Show as List", event: action(() => this.props.Document._slideshow = undefined), icon: "asterisk" }); ContextMenu.Instance.addItem({ description: "Presentation Funcs...", subitems: funcs, icon: "asterisk" }); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 6ae02720c..92b1fb090 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -791,6 +791,21 @@ export namespace Doc { if (StrCast(doc.title).endsWith("_" + prevLayout)) doc.title = StrCast(doc.title).replace("_" + prevLayout, ""); doc.layoutKey = deiconify || "layout"; } + export function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { + const docFilters = Cast(container._docFilter, listSpec("string"), []); + for (let i = 0; i < docFilters.length; i += 3) { + if (docFilters[i] === key && docFilters[i + 1] === value) { + docFilters.splice(i, 3); + break; + } + } + if (modifiers !== undefined) { + docFilters.push(key); + docFilters.push(value); + docFilters.push(modifiers); + container._docFilter = new List(docFilters); + } + } } Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); @@ -815,18 +830,4 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo const docs = DocListCast(Doc.UserDoc().SelectedDocs).filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCUMENT && d.type !== DocumentType.KVP && (!excludeCollections || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { - const docFilters = Cast(container._docFilter, listSpec("string"), []); - for (let i = 0; i < docFilters.length; i += 3) { - if (docFilters[i] === key && docFilters[i + 1] === value) { - docFilters.splice(i, 3); - break; - } - } - if (modifiers !== undefined) { - docFilters.push(key); - docFilters.push(value); - docFilters.push(modifiers); - container._docFilter = new List(docFilters); - } -}); \ No newline at end of file +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { Doc.setDocFilter(container, key, value, modifiers); }); \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 3495a934d..45ec676b2 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -101,7 +101,7 @@ export function makeEditable() { } let layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", - "LODdisable", "dropAction", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; + "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && -- cgit v1.2.3-70-g09d2 From 696d8c769424e4c5ff85147ecd60571a254527fd Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 6 Feb 2020 15:37:58 -0500 Subject: fixed indexing bug in pivot drill down --- src/client/views/collections/CollectionTimeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 14 ++++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index c0e18714e..fb36a1b0c 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -308,7 +308,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) { - console.log("filter down to key: " + pivotDoc._pivotField + " val:" + bounds.payload); + pivotDoc._docFilter = new List(); (bounds.payload as string[]).map(filterVal => Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check")); }); \ 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 884f3b353..4ef579f27 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -66,6 +66,11 @@ function getTextWidth(text: string, font: string): number { return metrics.width; } +interface pivotColumn { + docs: Doc[], + filters: string[] +} + export function computePivotLayout( poolData: Map, pivotDoc: Doc, @@ -75,7 +80,7 @@ export function computePivotLayout( viewDefsToJSX: (views: any) => ViewDefResult[] ) { const fieldKey = "data"; - const pivotColumnGroups = new Map, { docs: Doc[], filters: string[] }>(); + const pivotColumnGroups = new Map, pivotColumn>(); const pivotFieldKey = toLabel(pivotDoc._pivotField); for (const doc of childDocs) { @@ -129,7 +134,8 @@ export function computePivotLayout( const expander = 1.05; const gap = .15; let x = 0; - Array.from(pivotColumnGroups.keys()).sort().forEach(key => { + const sortedPivotKeys = Array.from(pivotColumnGroups.keys()).sort(); + sortedPivotKeys.forEach(key => { const val = pivotColumnGroups.get(key)!; let y = 0; let xCount = 0; @@ -170,8 +176,8 @@ export function computePivotLayout( }); const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols); - const dividers = Array.from(pivotColumnGroups.values()).map((pkey, i) => - ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pkey.filters })); + const dividers = sortedPivotKeys.map((key, i) => + ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), 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, []); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a34779046..1ac0d28f5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -778,7 +778,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const backgroundColor = Cast(viewDef.color, "string"); return [x, y].some(val => val === undefined) ? undefined : { - ele:
this.onViewDefDivClick(e, viewDef)} + ele:
this.onViewDefDivClick(e, viewDef)} style={{ width, height, backgroundColor, transform: `translate(${x}px, ${y}px)` }} />, bounds: viewDef }; -- cgit v1.2.3-70-g09d2 From 8597134b6ada1e57ae08d49e24c00d11f728ba90 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 6 Feb 2020 16:04:04 -0500 Subject: restored isBackground for documents that don't want to respond to anything. --- src/client/util/DragManager.ts | 4 ++-- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/client/views/nodes/ImageBox.scss | 5 ++--- src/client/views/nodes/ImageBox.tsx | 3 ++- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index b0dd90947..e572f0fcb 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,7 +15,7 @@ import { listSpec } from "../../new_fields/Schema"; import { Scripting } from "./Scripting"; import { convertDropDataToButtons } from "./DropConverter"; -export type dropActionType = "alias" | "copy" | "move" | undefined; +export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( _reference: React.RefObject, docFunc: () => Doc | Promise | undefined, @@ -198,7 +198,7 @@ export namespace DragManager { ); e.docDragData?.droppedDocuments.forEach((drop: Doc, i: number) => Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => { - drop[prop] = "move"; + drop[prop] = undefined; }) ); }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1ac0d28f5..f2c4cda34 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -126,6 +126,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { + if (this.props.Document.isBackground) return false; const xf = this.getTransform(); const xfo = this.getTransformOverlay(); const [xp, yp] = xf.transformPoint(de.x, de.y); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 2a11267d4..f484b6115 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -88,6 +88,7 @@ export class CollectionFreeFormDocumentView extends DocComponent {!this.props.fitToBox ? Date: Thu, 6 Feb 2020 18:33:07 -0500 Subject: quick fixes for pivot layout & pivoting down --- .../collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 6 +++--- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/server/authentication/models/current_user_utils.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 4ef579f27..e354ad0af 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -192,7 +192,7 @@ export function computeTimelineLayout( childDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: any) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds) => ViewDefResult[] ) { const fieldKey = "data"; const pivotDateGroups = new Map(); @@ -274,7 +274,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: Math.ceil(maxTime).toString(), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any; + const divider = { type: "div", color: "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]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { @@ -298,7 +298,7 @@ export function computeTimelineLayout( } function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map, - poolData: Map, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[]) { + poolData: Map, viewDefsToJSX: (views: ViewDefBounds) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[]) { 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); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f2c4cda34..ea3805b65 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -441,7 +441,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const miny = this.childDataProvider(docs[0]).y;//docs.length ? NumCast(docs[0].y) : 0; const maxx = this.childDataProvider(docs[0]).width + minx;//docs.length ? NumCast(docs[0].width) + minx : minx; const maxy = this.childDataProvider(docs[0]).height + miny;//docs.length ? NumCast(docs[0].height) + miny : miny; - const ranges = docs.filter(doc => doc).reduce((range, doc) => { + const ranges = docs.filter(doc => doc).filter(doc => this.childDataProvider(doc)).reduce((range, doc) => { const x = this.childDataProvider(doc).x;//NumCast(doc.x); const y = this.childDataProvider(doc).y;//NumCast(doc.y); const xe = this.childDataProvider(doc).width + x;//x + NumCast(layoutDoc.width); @@ -779,7 +779,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const backgroundColor = Cast(viewDef.color, "string"); return [x, y].some(val => val === undefined) ? undefined : { - ele:
this.onViewDefDivClick(e, viewDef)} + ele:
this.onViewDefDivClick(e, viewDef)} style={{ width, height, backgroundColor, transform: `translate(${x}px, ${y}px)` }} />, bounds: viewDef }; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b23ab5e49..8b760db00 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -237,7 +237,7 @@ export class CurrentUserUtils { Docs.Create.MulticolumnDocument([], { title: "images", _height: 200, _xMargin: 10, _yMargin: 10 }), Docs.Create.TextDocument("", { title: "contents", _height: 100 }) ], - { _width: 400, _height: 300, title: "slide", _chromeStatus: "disabled", _autoHeight: true }); + { _width: 400, _height: 300, title: "slide", _chromeStatus: "disabled", backgroundColor: "lightGray", _autoHeight: true }); slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("setNativeView(this)") }); -- cgit v1.2.3-70-g09d2 From 9f82ece7c763ba4a054d86a715311e0280fcb79f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 7 Feb 2020 01:22:48 -0500 Subject: sped up pivot viewer by not recreating docs when pivot changes. --- .../views/collections/CollectionStackingView.tsx | 6 ++-- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 29 +--------------- .../views/collections/CollectionTimeView.scss | 5 +++ .../views/collections/CollectionTimeView.tsx | 39 +++++++++++++++------- .../CollectionFreeFormLayoutEngines.tsx | 20 ++++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 37 +++++++++++++++++--- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + 8 files changed, 83 insertions(+), 56 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 01a2f1042..e04f4e8a6 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -52,11 +52,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; } - children(docs: Doc[]) { + children(docs: Doc[], columns?: number) { this._docXfs.length = 0; return docs.map((d, i) => { const height = () => this.getDocHeight(d); - const width = () => (this.widthScale ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + const width = () => (this.widthScale && !columns ? this.widthScale : 1) * Math.min(d._nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); const dref = React.createRef(); const dxf = () => this.getDocTransform(d, dref.current!); this._docXfs.push({ dxf: dxf, width: width, height: height }); @@ -381,7 +381,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return Math.min(this.props.Document[WidthSym]() / this.props.PanelWidth(), this.props.Document[HeightSym]() / this.props.PanelHeight()); } @computed get widthScale() { - return StrCast(this.props.Document.title).includes("slide") ? this.heightScale : undefined; + return StrCast(this.props.Document.title).includes("slide") || true ? this.heightScale : undefined; } render() { TraceMobx(); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index b81b1f31d..21982f1ca 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -407,7 +407,7 @@ export class CollectionStackingViewFieldColumn extends React.Component - {this.props.parent.children(this.props.docList)} + {this.props.parent.children(this.props.docList, uniqueHeadings.length)} {singleColumn ? (null) : this.props.parent.columnDragger}
{(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a2700e75a..e0e99d635 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -111,34 +111,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { get childDocs() { const docs = DocListCast(this.dataField); const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); - const viewedDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; - const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []); - const clusters: { [key: string]: { [value: string]: string } } = {}; - for (let i = 0; i < docFilters.length; i += 3) { - const [key, value, modifiers] = docFilters.slice(i, i + 3); - const cluster = clusters[key]; - if (!cluster) { - const child: { [value: string]: string } = {}; - child[value] = modifiers; - clusters[key] = child; - } else { - cluster[value] = modifiers; - } - } - const filteredDocs = docFilters.length ? viewedDocs.filter(d => { - for (const key of Object.keys(clusters)) { - const cluster = clusters[key]; - const satisfiesFacet = Object.keys(cluster).some(inner => { - const modifier = cluster[inner]; - return (modifier === "x") !== Doc.matchFieldValue(d, key, inner); - }); - if (!satisfiesFacet) { - return false; - } - } - return true; - }) : viewedDocs; - return filteredDocs; + return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; } @action diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss index df5057b5b..a5ce73a92 100644 --- a/src/client/views/collections/CollectionTimeView.scss +++ b/src/client/views/collections/CollectionTimeView.scss @@ -5,6 +5,11 @@ height: 100%; width: 100%; overflow: hidden; + .collectionTimeView-backBtn { + background: green; + display: inline; + margin-right: 20px; + } .collectionFreeform-customText { text-align: left; } diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index fb36a1b0c..0c1f93829 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,27 +1,27 @@ import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, observable, trace } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Set } from "typescript-collections"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Field } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; +import { RichTextField } from "../../../new_fields/RichTextField"; import { listSpec } from "../../../new_fields/Schema"; import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; +import { Scripting } from "../../util/Scripting"; +import { ContextMenu } from "../ContextMenu"; +import { ContextMenuProps } from "../ContextMenuItem"; import { EditableView } from "../EditableView"; import { anchorPoints, Flyout } from "../TemplateMenu"; +import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import "./CollectionTimeView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { CollectionTreeView } from "./CollectionTreeView"; +import "./CollectionTimeView.scss"; import React = require("react"); -import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from "../ContextMenuItem"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; -import { Scripting } from "../../util/Scripting"; -import { ViewDefResult, ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines"; +import { CollectionTreeView } from "./CollectionTreeView"; +import { ObjectField } from "../../../new_fields/ObjectField"; @observer export class CollectionTimeView extends CollectionSubView(doc => doc) { @@ -289,7 +289,19 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
- + +
{!this.props.isSelected() || this.props.PanelHeight() < 100 ? (null) :
@@ -308,6 +320,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) { + let pfilterIndex = NumCast(pivotDoc._pfilterIndex); + pivotDoc["_pfilter" + pfilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilter as ObjectField); + pivotDoc._pfilterIndex = ++pfilterIndex; pivotDoc._docFilter = new List(); (bounds.payload as string[]).map(filterVal => Doc.setDocFilter(pivotDoc, StrCast(pivotDoc._pivotField), filterVal, "check")); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index e354ad0af..95f7794bb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -36,7 +36,6 @@ export interface PoolData { color?: string, transition?: string, highlight?: boolean, - state?: any } export interface ViewDefResult { @@ -71,19 +70,21 @@ interface pivotColumn { filters: string[] } + export function computePivotLayout( poolData: Map, pivotDoc: Doc, childDocs: Doc[], + filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: any) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { const fieldKey = "data"; const pivotColumnGroups = new Map, pivotColumn>(); const pivotFieldKey = toLabel(pivotDoc._pivotField); - for (const doc of childDocs) { + for (const doc of filterDocs) { const val = Field.toString(doc[pivotFieldKey] as Field); if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val] }); @@ -179,7 +180,7 @@ export function computePivotLayout( const dividers = sortedPivotKeys.map((key, i) => ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap), 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, []); + return normalizeResults(panelDim, max_text, childPairs, docMap, poolData, viewDefsToJSX, groupNames, 0, [], childDocs.filter(c => !filterDocs.includes(c))); } function toNumber(val: FieldResult) { @@ -190,9 +191,10 @@ export function computeTimelineLayout( poolData: Map, pivotDoc: Doc, childDocs: Doc[], + filterDocs: Doc[], childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: ViewDefBounds) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] ) { const fieldKey = "data"; const pivotDateGroups = new Map(); @@ -212,7 +214,7 @@ export function computeTimelineLayout( let minTime = Number.MAX_VALUE; let maxTime = -Number.MAX_VALUE; - childDocs.map(doc => { + filterDocs.map(doc => { const num = NumCast(doc[timelineFieldKey], Number(StrCast(doc[timelineFieldKey]))); if (!(Number.isNaN(num) || (minTimeReq && num < minTimeReq) || (maxTimeReq && num > maxTimeReq))) { !pivotDateGroups.get(num) && pivotDateGroups.set(num, []); @@ -275,7 +277,7 @@ export function computeTimelineLayout( } const divider = { type: "div", color: "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]); + return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider], childDocs.filter(c => !filterDocs.includes(c))); function layoutDocsAtTime(keyDocs: Doc[], key: number) { keyDocs.forEach(doc => { @@ -298,7 +300,8 @@ 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[]) { + poolData: Map, viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], groupNames: ViewDefBounds[], minWidth: number, extras: ViewDefBounds[], + extraDocs: Doc[]) { 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); @@ -323,6 +326,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }); } }); + extraDocs.map(ed => poolData.set(ed[Id], { x: 0, y: 0, zIndex: -99 })); return { elements: viewDefsToJSX(extras.concat(groupNames.map(gname => ({ diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ea3805b65..2518a4a55 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } f import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkTool, InkField, InkData } from "../../../../new_fields/InkField"; -import { createSchema, makeInterface } from "../../../../new_fields/Schema"; +import { createSchema, makeInterface, listSpec } from "../../../../new_fields/Schema"; import { ScriptField } from "../../../../new_fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; @@ -794,12 +794,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }.bind(this)); doTimelineLayout(poolData: Map) { - return computeTimelineLayout(poolData, this.props.Document, this.childDocs, + return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.filterDocs, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } doPivotLayout(poolData: Map) { - return computePivotLayout(poolData, this.props.Document, this.childDocs, + return computePivotLayout(poolData, this.props.Document, this.childDocs, this.filterDocs, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } @@ -812,7 +812,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => { const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state }); - state = pos.state === undefined ? state : pos.state; poolData.set(pair.layout[Id], pos); }); return { elements: elements }; @@ -826,6 +825,36 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return { newPool, computedElementData: this.doFreeformLayout(newPool) }; } + + @computed get filterDocs() { + const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []); + const clusters: { [key: string]: { [value: string]: string } } = {}; + for (let i = 0; i < docFilters.length; i += 3) { + const [key, value, modifiers] = docFilters.slice(i, i + 3); + const cluster = clusters[key]; + if (!cluster) { + const child: { [value: string]: string } = {}; + child[value] = modifiers; + clusters[key] = child; + } else { + cluster[value] = modifiers; + } + } + const filteredDocs = docFilters.length ? this.childDocs.filter(d => { + for (const key of Object.keys(clusters)) { + const cluster = clusters[key]; + const satisfiesFacet = Object.keys(cluster).some(inner => { + const modifier = cluster[inner]; + return (modifier === "x") !== Doc.matchFieldValue(d, key, inner); + }); + if (!satisfiesFacet) { + return false; + } + } + return true; + }) : this.childDocs; + return filteredDocs; + } get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; runInAction(() => diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index f484b6115..3bceec45f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -88,6 +88,7 @@ export class CollectionFreeFormDocumentView extends DocComponent -- cgit v1.2.3-70-g09d2