aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBob Zeleznik <zzzman@gmail.com>2020-02-03 23:48:06 -0500
committerBob Zeleznik <zzzman@gmail.com>2020-02-03 23:48:06 -0500
commit6247bfe9aee9ea7f1a04b1aa154d22629a927042 (patch)
tree89b336b6f41e66aad1965b14b3727eb609e4f811 /src
parente42c6b732decff0d8903c8613b9021dbd8ddf3e0 (diff)
parent11b4645ce52df01f071a9a4a2582cae10a0140ba (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r--src/client/util/RichTextRules.ts30
-rw-r--r--src/client/util/RichTextSchema.tsx10
-rw-r--r--src/client/views/collections/CollectionSubView.tsx7
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx86
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx52
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx32
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx32
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx20
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx12
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx12
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx20
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx1
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx11
-rw-r--r--src/client/views/nodes/RadialMenu.tsx4
-rw-r--r--src/new_fields/Doc.ts17
-rw-r--r--src/new_fields/util.ts2
-rw-r--r--src/scraping/buxton/scraper.py18
-rw-r--r--src/server/ApiManagers/DownloadManager.ts6
-rw-r--r--src/server/ApiManagers/GooglePhotosManager.ts9
-rw-r--r--src/server/ApiManagers/UploadManager.ts3
-rw-r--r--src/server/ApiManagers/UtilManager.ts7
-rw-r--r--src/server/DashUploadUtils.ts26
23 files changed, 234 insertions, 187 deletions
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 8411cc6ee..77b111412 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -72,36 +72,38 @@ export const inpRules = {
return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size }));
}),
- // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+ // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document [[ <fieldKey> : <Doc>]] // [[:Doc]] => hyperlink [[fieldKey]] => show field [[fieldKey:Doc]] => show field of doc
new InputRule(
new RegExp(/\[\[([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\]\]$/),
(state, match, start, end) => {
- if (!match[2]) {
- const docId = match[1];
- DocServer.GetRefField(docId).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true, }, docId);
- DocUtils.Publish(target, docId, returnFalse, returnFalse);
+ const fieldKey = match[1];
+ const docid = match[2]?.substring(1);
+ if (!fieldKey) {
+ DocServer.GetRefField(docid).then(docx => {
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true, }, docid);
+ DocUtils.Publish(target, docid, returnFalse, returnFalse);
DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", "");
});
- const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId });
+ const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid });
return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey: match[2]?.substring(1), docid: match[1] });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
return state.tr.deleteRange(start, end).insert(start, fieldView);
}),
- // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
+ // create an inline view of a document {{ <layoutKey> : <Doc> }} // {{:Doc}} => show default view of document {{<layout>}} => show layout for this doc {{<layout> : Doc}} => show layout for another doc
new InputRule(
new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\}\}$/),
(state, match, start, end) => {
- const docId = match[1];
- DocServer.GetRefField(docId).then(docx => {
+ const fieldKey = match[1];
+ const docid = match[2]?.substring(1);
+ DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true }, docId);
- DocUtils.Publish(docx, docId, returnFalse, returnFalse);
+ const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, _LODdisable: true }, docid);
+ DocUtils.Publish(docx, docid, returnFalse, returnFalse);
}
});
const node = (state.doc.resolve(start) as any).nodeAfter;
- const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: "dashDoc", docid: docId, float: "right", fieldKey: match[2]?.substring(1), alias: Utils.GenerateGuid() });
+ const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: "dashDoc", docid, fieldKey, float: "right", alias: Utils.GenerateGuid() });
const sm = state.storedMarks || undefined;
return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr;
}),
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3957a1f26..b98e422cb 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -767,8 +767,8 @@ export class DashDocView {
if (!(dashDoc instanceof Doc)) {
alias && DocServer.GetRefField(docid).then(async dashDocBase => {
if (dashDocBase instanceof Doc) {
- const aliasedDoc = Doc.MakeDelegate(dashDocBase, docid + alias);
- aliasedDoc.layoutKey = "layout_" + node.attrs.fieldKey;
+ const aliasedDoc = Doc.MakeAlias(dashDocBase, docid + alias);
+ aliasedDoc.layoutKey = "layout" + (node.attrs.fieldKey ? "_" + node.attrs.fieldKey : "");
self.doRender(aliasedDoc, removeDoc, node, view, getPos);
}
});
@@ -810,10 +810,10 @@ export class DashDocView {
finalLayout._textTemplate = ComputedField.MakeFunction(`copyField(this.${finalKey})`, { this: Doc.name });
}
}
- this._reactionDisposer && this._reactionDisposer();
+ this._reactionDisposer?.();
this._reactionDisposer = reaction(() => [finalLayout[WidthSym](), finalLayout[HeightSym]()], (dim) => {
- this._dashSpan.style.width = this._outer.style.width = dim[0] + "px";
- this._dashSpan.style.height = this._outer.style.height = dim[1] + "px";
+ this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px";
+ this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px";
}, { fireImmediately: true });
ReactDOM.render(<DocumentView
Document={finalLayout}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 0eeb1c83d..9cdd48089 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -254,7 +254,12 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
if (img) {
const split = img.split("src=\"")[1].split("\"")[0];
- const doc = Docs.Create.ImageDocument(split, { ...options, _width: 300 });
+ let source = split;
+ if (split.startsWith("data:image") && split.includes("base64")) {
+ const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+ source = Utils.prepend(clientAccessPath);
+ }
+ const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
ImageUtils.ExtractExif(doc);
this.props.addDocument(doc);
return;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index d5d62159b..bdd908807 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -133,7 +133,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
@action.bound
addDocument(doc: Doc): boolean {
- const targetDataDoc = Doc.GetProto(this.props.Document);
+ const targetDataDoc = Doc.GetProto(this.props.DataDoc || this.props.Document);
Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc);
targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
Doc.GetProto(doc).lastOpened = new DateField;
@@ -144,7 +144,7 @@ export class CollectionView extends Touchable<FieldViewProps> {
removeDocument(doc: Doc): boolean {
const docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView);
docView && SelectionManager.DeselectDoc(docView);
- const value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []);
+ const value = Cast((this.props.DataDoc || this.props.Document)[this.props.fieldKey], listSpec(Doc), []);
let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1);
index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index f08c2506e..63bcc68e5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -60,32 +60,36 @@ function toLabel(target: FieldResult<Field>) {
}
export function computePivotLayout(
- poolData: ObservableMap<string, PoolData>,
+ poolData: Map<string, PoolData>,
pivotDoc: Doc,
childDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
panelDim: number[],
viewDefsToJSX: (views: any) => ViewDefResult[]
) {
+ console.log("PIVOT " + pivotDoc[HeightSym]());
const fieldKey = "data";
- const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 1000);
const pivotColumnGroups = new Map<FieldResult<Field>, 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);
}
}
- const minSize = Array.from(pivotColumnGroups.entries()).reduce((min, pair) => Math.min(min, pair[1].length), Infinity);
- let numCols = NumCast(pivotDoc.pivotNumColumns, Math.ceil(Math.sqrt(minSize)));
+ 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);
+
const docMap = new Map<Doc, ViewDefBounds>();
- const groupNames: PivotData[] = [];
- numCols = Math.min(Math.max(1, panelDim[0] / pivotAxisWidth), numCols);
+ const groupNames: PivotData[] = [];;
const expander = 1.05;
const gap = .15;
@@ -133,7 +137,7 @@ export function computePivotLayout(
export function computeTimelineLayout(
- poolData: ObservableMap<string, PoolData>,
+ poolData: Map<string, PoolData>,
pivotDoc: Doc,
childDocs: Doc[],
childPairs: { layout: Doc, data?: Doc }[],
@@ -204,7 +208,21 @@ export function computeTimelineLayout(
x += scaling * (key - prevKey);
const stack = findStack(x, stacking);
prevKey = key;
- !stack && Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth && groupNames.push({ type: "text", text: key.toString(), x: x, y: stack * 25, height: fontHeight, fontSize });
+ !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 });
+ layoutDocsAtTime(keyDocs, key);
+ });
+ if (sortedKeys.length && 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 });
+ }
+ 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 });
+ }
+
+ const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any;
+ return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
+
+ function layoutDocsAtTime(keyDocs: Doc[], key: number) {
keyDocs.forEach(doc => {
const stack = findStack(x, stacking);
const layoutDoc = Doc.Layout(doc);
@@ -215,55 +233,38 @@ export function computeTimelineLayout(
wid = layoutDoc._nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth;
}
docMap.set(doc, {
- x: x, y: - Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+ 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
});
stacking[stack] = x + pivotAxisWidth;
});
- });
- if (sortedKeys.length && 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 });
}
- 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 });
- }
-
- const divider = { type: "div", color: "black", x: 0, y: 0, width: panelDim[0], height: 1 } as any;
- return normalizeResults(panelDim, fontHeight, childPairs, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
}
function normalizeResults(panelDim: number[], fontHeight: number, childPairs: { data?: Doc, layout: Doc }[], docMap: Map<Doc, ViewDefBounds>,
- poolData: ObservableMap<string, PoolData>, viewDefsToJSX: (views: any) => ViewDefResult[], groupNames: PivotData[], minWidth: number, extras: PivotData[]) {
+ poolData: Map<string, PoolData>, 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) || { x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), width: pair.layout[WidthSym](), height: pair.layout[HeightSym]() } as PivotData // new pos is computed pos, or pos written to the document's fields
- );
+ const docEles = childPairs.filter(d => !d.layout.isMinimized).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.map(pair => {
- const fallbackPos = {
- x: NumCast(pair.layout.x),
- y: NumCast(pair.layout.y),
- z: NumCast(pair.layout.z),
- highlight: undefined,
- zIndex: NumCast(pair.layout.zIndex),
- width: NumCast(pair.layout._width),
- height: NumCast(pair.layout._height)
- };
- const newPosRaw = docMap.get(pair.layout) || fallbackPos; // new pos is computed pos, or pos written to the document's fields
- const newPos = {
- x: newPosRaw.x * scale, y: newPosRaw.y * scale, z: newPosRaw.z, zIndex: newPosRaw.zIndex, highlight: newPosRaw.highlight,
- width: (newPosRaw.width || 0) * scale, height: newPosRaw.height! * scale
- };
- const lastPos = poolData.get(pair.layout[Id]); // last computed pos
- 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(() => poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos }));
+ childPairs.filter(d => !d.layout.isMinimized).map(pair => {
+ const newPosRaw = docMap.get(pair.layout);
+ if (newPosRaw) {
+ const newPos = {
+ x: newPosRaw.x * scale,
+ y: newPosRaw.y * scale,
+ z: newPosRaw.z,
+ highlight: newPosRaw.highlight,
+ zIndex: newPosRaw.zIndex,
+ width: (newPosRaw.width || 0) * scale,
+ height: newPosRaw.height! * scale
+ };
+ poolData.set(pair.layout[Id], { transition: "transform 1s", ...newPos });
}
});
@@ -275,8 +276,7 @@ function normalizeResults(panelDim: number[], fontHeight: number, childPairs: {
y: gname.y * scale,
color: gname.color,
width: gname.width === undefined ? undefined : gname.width * scale,
- height: Math.max(fontHeight, gname.height! * scale),
- // height: gname.height === undefined ? undefined : gname.height * scale,
+ height: Math.max(fontHeight, (gname.height || 0) * scale),
fontSize: gname.fontSize
}))))
};
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f1a239050..6453cfe17 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,7 +1,7 @@
import { library } from "@fortawesome/fontawesome-svg-core";
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons";
-import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx";
+import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer, trace } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync, Field } from "../../../../new_fields/Doc";
import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas";
@@ -72,7 +72,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
private _clusterDistance: number = 75;
private _hitCluster = false;
private _layoutComputeReaction: IReactionDisposer | undefined;
- private _layoutPoolData = new ObservableMap<string, any>();
+ private _layoutPoolData = observable.map<string, any>();
public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@@ -789,47 +789,56 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
return this._layoutPoolData.get(doc[Id]);
}.bind(this));
- doTimelineLayout(poolData: ObservableMap<string, any>) {
+ doTimelineLayout(poolData: Map<string, any>) {
return computeTimelineLayout(poolData, this.props.Document, this.childDocs,
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+ this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
- doPivotLayout(poolData: ObservableMap<string, any>) {
+ doPivotLayout(poolData: Map<string, any>) {
return computePivotLayout(poolData, this.props.Document, this.childDocs,
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
+ this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX);
}
- doFreeformLayout(poolData: ObservableMap<string, any>) {
+ _cachedPool: Map<string, any> = new Map();
+ doFreeformLayout(poolData: Map<string, any>) {
const layoutDocs = this.childLayoutPairs.map(pair => pair.layout);
const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log);
let state = initResult && initResult.success ? initResult.result.scriptState : undefined;
const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
- const data = poolData.get(pair.layout[Id]);
const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state });
state = pos.state === undefined ? state : pos.state;
- if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.zIndex !== data.zIndex ||
- pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) {
- runInAction(() => poolData.set(pair.layout[Id], pos));
- }
+ poolData.set(pair.layout[Id], pos);
});
return { elements: elements };
}
- get doLayoutComputation() {
- let computedElementData: { elements: ViewDefResult[] };
+ @computed get doInternalLayoutComputation() {
+ const newPool = new Map<string, any>();
switch (this.Document._freeformLayoutEngine) {
- case "timeline": computedElementData = this.doTimelineLayout(this._layoutPoolData); break;
- case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break;
- default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break;
+ case "timeline": return { newPool, computedElementData: this.doTimelineLayout(newPool) };
+ case "pivot": return { newPool, computedElementData: this.doPivotLayout(newPool) };
}
+ return { newPool, computedElementData: this.doFreeformLayout(newPool) };
+ }
+ 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 }));
+ }
+ });
+ 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 =>
computedElementData.elements.push({
ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} {...this.getChildDocumentViewProps(pair.layout, pair.data)}
dataProvider={this.childDataProvider}
jitterRotation={NumCast(this.props.Document.jitterRotation)}
- fitToBox={this.props.fitToBox || this.Document._freeformLayoutEngine === "pivot" || this.Document._freeformLayoutEngine === "timeline"} />,
+ fitToBox={this.props.fitToBox || this.Document._freeformLayoutEngine !== undefined} />,
bounds: this.childDataProvider(pair.layout)
}));
@@ -838,12 +847,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
componentDidMount() {
super.componentDidMount();
- this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation; },
- action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)),
+ this._layoutComputeReaction = reaction(
+ () => (this.doLayoutComputation),
+ (computation) => this._layoutElements = computation?.elements.slice() || [],
{ fireImmediately: true, name: "doLayout" });
}
componentWillUnmount() {
- this._layoutComputeReaction && this._layoutComputeReaction();
+ this._layoutComputeReaction?.();
}
@computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
elementFunc = () => this._layoutElements;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 041eb69da..65862f34f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -28,12 +28,12 @@ interface LayoutData {
starSum: number;
}
-export const WidthUnit = {
+export const DimUnit = {
Pixel: "px",
Ratio: "*"
};
-const resolvedUnits = Object.values(WidthUnit);
+const resolvedUnits = Object.values(DimUnit);
const resizerWidth = 4;
@observer
@@ -45,12 +45,12 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
*/
@computed
private get ratioDefinedDocs() {
- return this.childLayoutPairs.map(({ layout }) => layout).filter(({ widthUnit }) => StrCast(widthUnit) === WidthUnit.Ratio);
+ return this.childLayoutPairs.map(({ layout }) => layout).filter(({ dimUnit }) => StrCast(dimUnit) === DimUnit.Ratio);
}
/**
- * This loops through all childLayoutPairs and extracts the values for widthUnit
- * and widthMagnitude, ignoring any that are malformed. Additionally, it then
+ * This loops through all childLayoutPairs and extracts the values for dimUnit
+ * and dimMagnitude, ignoring any that are malformed. Additionally, it then
* normalizes the ratio values so that one * value is always 1, with the remaining
* values proportionate to that easily readable metric.
* @returns the list of the resolved width specifiers (unit and magnitude pairs)
@@ -60,11 +60,11 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
private get resolvedLayoutInformation(): LayoutData {
let starSum = 0;
const widthSpecifiers: WidthSpecifier[] = [];
- this.childLayoutPairs.map(({ layout: { widthUnit, widthMagnitude } }) => {
- const unit = StrCast(widthUnit);
- const magnitude = NumCast(widthMagnitude);
+ this.childLayoutPairs.map(({ layout: { dimUnit, dimMagnitude } }) => {
+ const unit = StrCast(dimUnit);
+ const magnitude = NumCast(dimMagnitude);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
- (unit === WidthUnit.Ratio) && (starSum += magnitude);
+ (unit === DimUnit.Ratio) && (starSum += magnitude);
widthSpecifiers.push({ magnitude, unit });
}
/**
@@ -82,9 +82,9 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
setTimeout(() => {
const { ratioDefinedDocs } = this;
if (this.childLayoutPairs.length) {
- const minimum = Math.min(...ratioDefinedDocs.map(({ widthMagnitude }) => NumCast(widthMagnitude)));
+ const minimum = Math.min(...ratioDefinedDocs.map(({ dimMagnitude }) => NumCast(dimMagnitude)));
if (minimum !== 0) {
- ratioDefinedDocs.forEach(layout => layout.widthMagnitude = NumCast(layout.widthMagnitude) / minimum);
+ ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude) / minimum);
}
}
});
@@ -103,7 +103,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
@computed
private get totalFixedAllocation(): number | undefined {
return this.resolvedLayoutInformation?.widthSpecifiers.reduce(
- (sum, { magnitude, unit }) => sum + (unit === WidthUnit.Pixel ? magnitude : 0), 0);
+ (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
}
/**
@@ -160,8 +160,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
if (columnUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
- let width = NumCast(layout.widthMagnitude);
- if (StrCast(layout.widthUnit) === WidthUnit.Ratio) {
+ let width = NumCast(layout.dimMagnitude);
+ if (StrCast(layout.dimUnit) === DimUnit.Ratio) {
width *= columnUnitLength;
}
return width;
@@ -193,8 +193,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
drop = (e: Event, de: DragManager.DropEvent) => {
if (super.drop(e, de)) {
de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
- d.widthUnit = "*";
- d.widthMagnitude = 1;
+ d.dimUnit = "*";
+ d.dimMagnitude = 1;
}));
}
return false;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index e07985bb4..aa440b677 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -28,12 +28,12 @@ interface LayoutData {
starSum: number;
}
-export const HeightUnit = {
+export const DimUnit = {
Pixel: "px",
Ratio: "*"
};
-const resolvedUnits = Object.values(HeightUnit);
+const resolvedUnits = Object.values(DimUnit);
const resizerHeight = 4;
@observer
@@ -45,12 +45,12 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
*/
@computed
private get ratioDefinedDocs() {
- return this.childLayoutPairs.map(({ layout }) => layout).filter(({ widthUnit }) => StrCast(widthUnit) === HeightUnit.Ratio);
+ return this.childLayoutPairs.map(({ layout }) => layout).filter(({ dimUnit }) => StrCast(dimUnit) === DimUnit.Ratio);
}
/**
- * This loops through all childLayoutPairs and extracts the values for widthUnit
- * and widthMagnitude, ignoring any that are malformed. Additionally, it then
+ * This loops through all childLayoutPairs and extracts the values for dimUnit
+ * and dimUnit, ignoring any that are malformed. Additionally, it then
* normalizes the ratio values so that one * value is always 1, with the remaining
* values proportionate to that easily readable metric.
* @returns the list of the resolved width specifiers (unit and magnitude pairs)
@@ -60,11 +60,11 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
private get resolvedLayoutInformation(): LayoutData {
let starSum = 0;
const heightSpecifiers: HeightSpecifier[] = [];
- this.childLayoutPairs.map(({ layout: { heightUnit, heightMagnitude } }) => {
- const unit = StrCast(heightUnit);
- const magnitude = NumCast(heightMagnitude);
+ this.childLayoutPairs.map(({ layout: { dimUnit, dimMagnitude } }) => {
+ const unit = StrCast(dimUnit);
+ const magnitude = NumCast(dimMagnitude);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
- (unit === HeightUnit.Ratio) && (starSum += magnitude);
+ (unit === DimUnit.Ratio) && (starSum += magnitude);
heightSpecifiers.push({ magnitude, unit });
}
/**
@@ -82,9 +82,9 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
setTimeout(() => {
const { ratioDefinedDocs } = this;
if (this.childLayoutPairs.length) {
- const minimum = Math.min(...ratioDefinedDocs.map(({ heightMagnitude }) => NumCast(heightMagnitude)));
+ const minimum = Math.min(...ratioDefinedDocs.map(({ dimMagnitude }) => NumCast(dimMagnitude)));
if (minimum !== 0) {
- ratioDefinedDocs.forEach(layout => layout.heightMagnitude = NumCast(layout.heightMagnitude) / minimum);
+ ratioDefinedDocs.forEach(layout => layout.dimMagnitude = NumCast(layout.dimMagnitude) / minimum);
}
}
});
@@ -103,7 +103,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
@computed
private get totalFixedAllocation(): number | undefined {
return this.resolvedLayoutInformation?.heightSpecifiers.reduce(
- (sum, { magnitude, unit }) => sum + (unit === HeightUnit.Pixel ? magnitude : 0), 0);
+ (sum, { magnitude, unit }) => sum + (unit === DimUnit.Pixel ? magnitude : 0), 0);
}
/**
@@ -160,8 +160,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
if (rowUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
- let height = NumCast(layout.heightMagnitude);
- if (StrCast(layout.heightUnit) === HeightUnit.Ratio) {
+ let height = NumCast(layout.dimMagnitude);
+ if (StrCast(layout.dimUnit) === DimUnit.Ratio) {
height *= rowUnitLength;
}
return height;
@@ -193,8 +193,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
drop = (e: Event, de: DragManager.DropEvent) => {
if (super.drop(e, de)) {
de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => {
- d.heightUnit = "*";
- d.heightMagnitude = 1;
+ d.dimUnit = "*";
+ d.dimMagnitude = 1;
}));
}
return false;
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 11e210958..46c39d817 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import { observable, action } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast } from "../../../../new_fields/Types";
-import { WidthUnit } from "./CollectionMulticolumnView";
+import { DimUnit } from "./CollectionMulticolumnView";
interface ResizerProps {
width: number;
@@ -46,14 +46,14 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const unitLength = columnUnitLength();
if (unitLength) {
if (toNarrow) {
- const { widthUnit, widthMagnitude } = toNarrow;
- const scale = widthUnit === WidthUnit.Ratio ? unitLength : 1;
- toNarrow.widthMagnitude = NumCast(widthMagnitude) - Math.abs(movementX) / scale;
+ const { dimUnit, dimMagnitude } = toNarrow;
+ const scale = dimUnit === DimUnit.Ratio ? unitLength : 1;
+ toNarrow.dimMagnitude = NumCast(dimMagnitude) - Math.abs(movementX) / scale;
}
if (this.resizeMode === ResizeMode.Pinned && toWiden) {
- const { widthUnit, widthMagnitude } = toWiden;
- const scale = widthUnit === WidthUnit.Ratio ? unitLength : 1;
- toWiden.widthMagnitude = NumCast(widthMagnitude) + Math.abs(movementX) / scale;
+ const { dimUnit, dimMagnitude } = toWiden;
+ const scale = dimUnit === DimUnit.Ratio ? unitLength : 1;
+ toWiden.dimMagnitude = NumCast(dimMagnitude) + Math.abs(movementX) / scale;
}
}
}
@@ -61,17 +61,17 @@ export default class ResizeBar extends React.Component<ResizerProps> {
private get isActivated() {
const { toLeft, toRight } = this.props;
if (toLeft && toRight) {
- if (StrCast(toLeft.widthUnit) === WidthUnit.Pixel && StrCast(toRight.widthUnit) === WidthUnit.Pixel) {
+ if (StrCast(toLeft.dimUnit) === DimUnit.Pixel && StrCast(toRight.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
} else if (toLeft) {
- if (StrCast(toLeft.widthUnit) === WidthUnit.Pixel) {
+ if (StrCast(toLeft.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
} else if (toRight) {
- if (StrCast(toRight.widthUnit) === WidthUnit.Pixel) {
+ if (StrCast(toRight.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
index b394fed62..5b2054428 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -4,7 +4,7 @@ import { computed } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
import { EditableView } from "../../EditableView";
-import { WidthUnit } from "./CollectionMulticolumnView";
+import { DimUnit } from "./CollectionMulticolumnView";
interface WidthLabelProps {
layout: Doc;
@@ -18,8 +18,8 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
@computed
private get contents() {
const { layout, decimals } = this.props;
- const getUnit = () => StrCast(layout.widthUnit);
- const getMagnitude = () => String(+NumCast(layout.widthMagnitude).toFixed(decimals ?? 3));
+ const getUnit = () => StrCast(layout.dimUnit);
+ const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
return (
<div className={"label-wrapper"}>
<EditableView
@@ -27,7 +27,7 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
SetValue={value => {
const converted = Number(value);
if (!isNaN(converted) && converted > 0) {
- layout.widthMagnitude = converted;
+ layout.dimMagnitude = converted;
return true;
}
return false;
@@ -37,8 +37,8 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
<EditableView
GetValue={getUnit}
SetValue={value => {
- if (Object.values(WidthUnit).includes(value)) {
- layout.widthUnit = value;
+ if (Object.values(DimUnit).includes(value)) {
+ layout.dimUnit = value;
return true;
}
return false;
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index 56a2e868d..899577fd5 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -4,7 +4,7 @@ import { computed } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types";
import { EditableView } from "../../EditableView";
-import { HeightUnit } from "./CollectionMultirowView";
+import { DimUnit } from "./CollectionMultirowView";
interface HeightLabelProps {
layout: Doc;
@@ -18,8 +18,8 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
@computed
private get contents() {
const { layout, decimals } = this.props;
- const getUnit = () => StrCast(layout.heightUnit);
- const getMagnitude = () => String(+NumCast(layout.heightMagnitude).toFixed(decimals ?? 3));
+ const getUnit = () => StrCast(layout.dimUnit);
+ const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
return (
<div className={"label-wrapper"}>
<EditableView
@@ -27,7 +27,7 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
SetValue={value => {
const converted = Number(value);
if (!isNaN(converted) && converted > 0) {
- layout.heightMagnitude = converted;
+ layout.dimMagnitude = converted;
return true;
}
return false;
@@ -37,8 +37,8 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
<EditableView
GetValue={getUnit}
SetValue={value => {
- if (Object.values(HeightUnit).includes(value)) {
- layout.heightUnit = value;
+ if (Object.values(DimUnit).includes(value)) {
+ layout.dimUnit = value;
return true;
}
return false;
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 20c6cd3df..4f58f3fa8 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import { observable, action } from "mobx";
import { Doc } from "../../../../new_fields/Doc";
import { NumCast, StrCast } from "../../../../new_fields/Types";
-import { HeightUnit } from "./CollectionMultirowView";
+import { DimUnit } from "./CollectionMultirowView";
interface ResizerProps {
height: number;
@@ -46,14 +46,14 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const unitLength = columnUnitLength();
if (unitLength) {
if (toNarrow) {
- const { heightUnit, heightMagnitude } = toNarrow;
- const scale = heightUnit === HeightUnit.Ratio ? unitLength : 1;
- toNarrow.heightMagnitude = NumCast(heightMagnitude) - Math.abs(movementY) / scale;
+ const { dimUnit, dimMagnitude } = toNarrow;
+ const scale = dimUnit === DimUnit.Ratio ? unitLength : 1;
+ toNarrow.dimMagnitude = NumCast(dimMagnitude) - Math.abs(movementY) / scale;
}
if (this.resizeMode === ResizeMode.Pinned && toWiden) {
- const { heightUnit, heightMagnitude } = toWiden;
- const scale = heightUnit === HeightUnit.Ratio ? unitLength : 1;
- toWiden.heightMagnitude = NumCast(heightMagnitude) + Math.abs(movementY) / scale;
+ const { dimUnit, dimMagnitude } = toWiden;
+ const scale = dimUnit === DimUnit.Ratio ? unitLength : 1;
+ toWiden.dimMagnitude = NumCast(dimMagnitude) + Math.abs(movementY) / scale;
}
}
}
@@ -61,17 +61,17 @@ export default class ResizeBar extends React.Component<ResizerProps> {
private get isActivated() {
const { toTop, toBottom } = this.props;
if (toTop && toBottom) {
- if (StrCast(toTop.heightUnit) === HeightUnit.Pixel && StrCast(toBottom.heightUnit) === HeightUnit.Pixel) {
+ if (StrCast(toTop.dimUnit) === DimUnit.Pixel && StrCast(toBottom.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
} else if (toTop) {
- if (StrCast(toTop.heightUnit) === HeightUnit.Pixel) {
+ if (StrCast(toTop.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
} else if (toBottom) {
- if (StrCast(toBottom.heightUnit) === HeightUnit.Pixel) {
+ if (StrCast(toBottom.dimUnit) === DimUnit.Pixel) {
return false;
}
return true;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ea7ed1d54..2a11267d4 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -2,7 +2,6 @@ import anime from "animejs";
import { computed, IReactionDisposer, observable, reaction, trace } from "mobx";
import { observer } from "mobx-react";
import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc";
-import { listSpec } from "../../../new_fields/Schema";
import { Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 51c8e00da..bd1b6166f 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -45,13 +45,14 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); }
private get nativeWidth() { return NumCast(this.layoutDoc?._nativeWidth, this.props.PanelWidth()); }
private get nativeHeight() { return NumCast(this.layoutDoc?._nativeHeight, this.props.PanelHeight()); }
- private contentScaling = () => {
+ @computed get scaling() {
const wscale = this.props.PanelWidth() / (this.nativeWidth || this.props.PanelWidth() || 1);
if (wscale * this.nativeHeight > this.props.PanelHeight()) {
return (this.props.PanelHeight() / (this.nativeHeight || this.props.PanelHeight() || 1)) || 1;
}
return wscale || 1;
}
+ private contentScaling = () => this.scaling;
@undoBatch
@action
@@ -67,8 +68,12 @@ export class ContentFittingDocumentView extends React.Component<ContentFittingDo
}
return true;
}
- private PanelWidth = () => this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth();
- private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight();
+ private PanelWidth = () => this.panelWidth;
+ private PanelHeight = () => this.panelHeight;;
+
+ @computed get panelWidth() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); }
+ @computed get panelHeight() { return this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); }
+
private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling());
private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; }
private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight * this.contentScaling()) / 2 : 0; }
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 74c5f53bd..9314a3899 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -70,8 +70,8 @@ export class RadialMenu extends React.Component {
}
this._shouldDisplay && (this._display = true);
document.removeEventListener("pointermove", this.onPointerMove);
- if (this._closest !== -1) {
- this._items[this._closest]?.event();
+ if (this._closest !== -1 && this._items?.length > this._closest) {
+ this._items[this._closest].event();
}
}
componentWillUnmount() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 4f6f80775..4dcdc6581 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -421,8 +421,8 @@ export namespace Doc {
doc.title = title;
return doc;
}
- export function MakeAlias(doc: Doc) {
- const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc) : Doc.MakeDelegate(doc);
+ export function MakeAlias(doc: Doc, id?: string) {
+ const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id);
const layout = Doc.LayoutField(alias);
if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) {
Doc.SetLayout(alias, Doc.MakeAlias(layout));
@@ -574,7 +574,7 @@ export namespace Doc {
}
return undefined;
}
- export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined = undefined) {
+ export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) {
if (!templateDoc) {
target.layout = undefined;
target._nativeWidth = undefined;
@@ -584,14 +584,9 @@ export namespace Doc {
return;
}
- if ((target[targetKey] as Doc)?.proto !== templateDoc) {
- const layoutCustomLayout = Doc.MakeDelegate(templateDoc);
-
+ if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) {
titleTarget && (Doc.GetProto(target).title = titleTarget);
- Doc.GetProto(target).type = DocumentType.TEMPLATE;
- target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
-
- Doc.GetProto(target)[targetKey] = new PrefetchProxy(layoutCustomLayout);
+ Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
}
target.layoutKey = targetKey;
return target;
@@ -777,7 +772,7 @@ export namespace Doc {
}
export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
- const fieldVal = doc[key] ? doc[key] : doc[key + "_ext"];
+ const fieldVal = doc[key];
if (Cast(fieldVal, listSpec("string"), []).length) {
const vals = Cast(fieldVal, listSpec("string"), []);
return vals.some(v => v === value);
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index 2768f1213..3495a934d 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -136,7 +136,7 @@ export function getter(target: any, in_prop: string | symbol | number, receiver:
let x = resolvedLayout[Id];
let layout = (resolvedLayout.layout as string).split("'")[1];
let expanded = getFieldImpl(target, layout + "-layout[" + x + "]", receiver);
- return expanded?.[prop];
+ return (expanded || resolvedLayout)?.[prop];
//return resolvedLayout[prop];
}
}
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index f7a38112d..1441a8621 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -115,8 +115,8 @@ def write_collection(parse_results, display_fields, storage_key, viewType):
target_collection.insert_one(view_doc)
data_doc_guid = data_doc["_id"]
- print(f"inserted view document ({view_doc_guid})")
- print(f"inserted data document ({data_doc_guid})\n")
+ # print(f"inserted view document ({view_doc_guid})")
+ # print(f"inserted data document ({data_doc_guid})\n")
return view_doc_guid
@@ -188,8 +188,8 @@ def write_image(folder, name):
"y": 10,
"_width": min(800, native_width),
"zIndex": 2,
- "widthUnit": "*",
- "widthMagnitude": 1
+ "dimUnit": "*",
+ "dimMagnitude": 1
},
"__type": "Doc"
}
@@ -233,7 +233,7 @@ def parse_document(file_name: str):
result = {}
dir_path = image_dist + "/" + pure_name
- print(dir_path)
+ # print(dir_path)
mkdir_if_absent(dir_path)
raw = str(docx2txt.process(source + "/" + file_name, dir_path))
@@ -252,7 +252,7 @@ def parse_document(file_name: str):
medium = dir_path + "/" + image.replace(".", "_m.", 1)
copyfile(resolved, original)
copyfile(resolved, medium)
- print(f"extracted {count} images...")
+ # print(f"extracted {count} images...")
def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace(
u"\u2013", "-").replace(u"\u201c", '''"''').replace(u"\u201d", '''"''').strip()
@@ -360,7 +360,7 @@ def parse_document(file_name: str):
if len(notes) > 0:
result["notes"] = listify(notes)
- print("writing child schema...")
+ # print("writing child schema...")
return {
"schema": {
@@ -392,7 +392,7 @@ def write_common_proto():
if os.path.exists(image_dist):
- shutil.rmtree(image_dist)
+ shutil.rmtree(image_dist, True)
while os.path.exists(image_dist):
pass
os.mkdir(image_dist)
@@ -415,7 +415,7 @@ parent_guid = write_collection({
"__type": "Doc"
},
"child_guids": schema_guids
-}, ["title", "short_description", "original_price"], "data", 2)
+}, ["title", "short_description", "original_price"], "data", 4)
print("appending parent schema to main workspace...\n")
target_collection.update_one(
diff --git a/src/server/ApiManagers/DownloadManager.ts b/src/server/ApiManagers/DownloadManager.ts
index 1bb84f374..fad5e6789 100644
--- a/src/server/ApiManagers/DownloadManager.ts
+++ b/src/server/ApiManagers/DownloadManager.ts
@@ -254,11 +254,13 @@ async function writeHierarchyRecursive(file: Archiver.Archiver, hierarchy: Hiera
// and dropped in the browser and thus hosted remotely) so we upload it
// to our server and point the zip file to it, so it can bundle up the bytes
const information = await DashUploadUtils.UploadImage(result);
- path = information.serverAccessPaths[SizeSuffix.Original];
+ path = information instanceof Error ? "" : information.serverAccessPaths[SizeSuffix.Original];
}
// write the file specified by the path to the directory in the
// zip file given by the prefix.
- file.file(path, { name: documentTitle, prefix });
+ if (path) {
+ file.file(path, { name: documentTitle, prefix });
+ }
} else {
// we've hit a collection, so we have to recurse
await writeHierarchyRecursive(file, result, `${prefix}/${documentTitle}`);
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 107542ce2..1727cc5a6 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -88,8 +88,13 @@ export default class GooglePhotosManager extends ApiManager {
if (contents) {
const completed: Opt<DashUploadUtils.ImageUploadInformation>[] = [];
for (const item of contents.mediaItems) {
- const { contentSize, ...attributes } = await DashUploadUtils.InspectImage(item.baseUrl);
- const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize!);
+ const results = await DashUploadUtils.InspectImage(item.baseUrl);
+ if (results instanceof Error) {
+ failed++;
+ continue;
+ }
+ const { contentSize, ...attributes } = results;
+ const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize);
if (!found) {
const upload = await DashUploadUtils.UploadInspectedImage({ contentSize, ...attributes }, item.filename, prefix).catch(error => _error(res, downloadError, error));
if (upload) {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index a92b613b7..4d09528f4 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -65,7 +65,8 @@ export default class UploadManager extends ApiManager {
secureHandler: async ({ req, res }) => {
const { sources } = req.body;
if (Array.isArray(sources)) {
- return res.send(await Promise.all(sources.map(url => DashUploadUtils.UploadImage(url))));
+ const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
+ return res.send(results);
}
res.send();
}
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index a0d0d0f4b..d7b085a30 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -47,7 +47,12 @@ export default class UtilManager extends ApiManager {
const onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
const onRejected = (err: any) => { console.error(err.message); res.send(err); };
- const tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+ const tryPython3 = (reason: any) => {
+ console.log("Initial scraper failed for the following reason:");
+ console.log(red(reason.Error));
+ console.log("Falling back to python3...");
+ command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+ };
return command_line('python scraper.py', cwd).then(onResolved, tryPython3);
},
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index cb7104757..27c4bf854 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -1,4 +1,4 @@
-import { unlinkSync, createWriteStream, readFileSync, rename } from 'fs';
+import { unlinkSync, createWriteStream, readFileSync, rename, writeFile } from 'fs';
import { Utils } from '../Utils';
import * as path from 'path';
import * as sharp from 'sharp';
@@ -127,9 +127,12 @@ export namespace DashUploadUtils {
* 3) the size of the image, in bytes (4432130)
* 4) the content type of the image, i.e. image/(jpeg | png | ...)
*/
- export const UploadImage = async (source: string, filename?: string, format?: string, prefix: string = ""): Promise<ImageUploadInformation> => {
+ export const UploadImage = async (source: string, filename?: string, format?: string, prefix: string = ""): Promise<ImageUploadInformation | Error> => {
const metadata = await InspectImage(source);
- return UploadInspectedImage(metadata, filename, format, prefix);
+ if (metadata instanceof Error) {
+ return metadata;
+ }
+ return UploadInspectedImage(metadata, filename || metadata.filename, format, prefix);
};
export interface InspectionResults {
@@ -140,6 +143,7 @@ export namespace DashUploadUtils {
contentType: string;
nativeWidth: number;
nativeHeight: number;
+ filename?: string;
}
export interface EnrichedExifData {
@@ -164,7 +168,20 @@ export namespace DashUploadUtils {
*
* @param source is the path or url to the image in question
*/
- export const InspectImage = async (source: string): Promise<InspectionResults> => {
+ export const InspectImage = async (source: string): Promise<InspectionResults | Error> => {
+ let rawMatches: RegExpExecArray | null;
+ let filename: string | undefined;
+ if ((rawMatches = /^data:image\/([a-z]+);base64,(.*)/.exec(source)) !== null) {
+ const [ext, data] = rawMatches.slice(1, 3);
+ const resolved = filename = `upload_${Utils.GenerateGuid()}.${ext}`;
+ const error = await new Promise<Error | null>(resolve => {
+ writeFile(serverPathToFile(Directory.images, resolved), data, "base64", resolve);
+ });
+ if (error !== null) {
+ return error;
+ }
+ source = `http://localhost:1050${clientPathToFile(Directory.images, resolved)}`;
+ }
let resolvedUrl: string;
const matches = isLocal().exec(source);
if (matches === null) {
@@ -187,6 +204,7 @@ export namespace DashUploadUtils {
contentType: headers[type],
nativeWidth,
nativeHeight,
+ filename,
...results
};
};