diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTimeView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx | 87 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 15 | ||||
-rw-r--r-- | src/client/views/nodes/PresBox.tsx | 2 | ||||
-rw-r--r-- | src/new_fields/Doc.ts | 31 | ||||
-rw-r--r-- | src/new_fields/util.ts | 2 |
7 files changed, 87 insertions, 70 deletions
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<T>(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) { </>} </div>; } -}
\ 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<FieldResult<Field>, Doc[]>(); + const pivotColumnGroups = new Map<FieldResult<Field>, { 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<Doc, ViewDefBounds>(); - 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<Field>) { + return val === undefined ? undefined : NumCast(val, Number(StrCast(val))); +} export function computeTimelineLayout( poolData: Map<string, PoolData>, @@ -190,9 +191,9 @@ export function computeTimelineLayout( const fieldKey = "data"; const pivotDateGroups = new Map<number, Doc[]>(); const docMap = new Map<Doc, ViewDefBounds>(); - 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<Doc, ViewDefBounds>, - poolData: Map<string, PoolData>, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: PivotData[], minWidth: number, extras: PivotData[]) { + poolData: Map<string, PoolData>, 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<ViewDefResult> { + onViewDefDivClick = (e: React.MouseEvent, payload: any) => { + (this.props.Document.onViewDefDivClick as ScriptField)?.script.run({ this: this.props.Document, payload }); + } + private viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> { 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} </div>, - 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: <div className="collectionFreeform-customDiv" key={"div" + x + y + z} + ele: <div className="collectionFreeform-customDiv" key={"div" + x + y + z} onClick={e => 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<FieldViewProps> { 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<string>(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<string>(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" && |