aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2022-07-08 00:17:26 -0400
committerbobzel <zzzman@gmail.com>2022-07-08 00:17:26 -0400
commit146f8622d5bac2edc6b09f57c173bd057dfbcfad (patch)
treef871089c438a476543ca96bac163c0532b9557c7 /src/client/views/collections/collectionFreeForm
parentb7e66da6b23cdb41c127000dfe13843d35f7d0cc (diff)
restructured currentUserUtils to avoid having import cycles.
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx248
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx201
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx105
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx28
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx517
5 files changed, 572 insertions, 527 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9de2cfcf9..a0ebe4cdc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,13 +1,12 @@
-import { Doc, Field, FieldResult, HeightSym, WidthSym } from "../../../../fields/Doc";
-import { Id, ToString } from "../../../../fields/FieldSymbols";
-import { ObjectField } from "../../../../fields/ObjectField";
-import { RefField } from "../../../../fields/RefField";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { aggregateBounds } from "../../../../Utils";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import React = require("react");
-import { ColorScheme } from "../../../util/SettingsManager";
+import { Doc, Field, FieldResult, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Id, ToString } from '../../../../fields/FieldSymbols';
+import { ObjectField } from '../../../../fields/ObjectField';
+import { RefField } from '../../../../fields/RefField';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { aggregateBounds } from '../../../../Utils';
+import { ColorScheme } from '../../../util/SettingsManager';
+import React = require('react');
export interface ViewDefBounds {
type: string;
@@ -25,7 +24,7 @@ export interface ViewDefBounds {
color?: string;
opacity?: number;
replica?: string;
- pair?: { layout: Doc, data?: Doc };
+ pair?: { layout: Doc; data?: Doc };
}
export interface PoolData {
@@ -40,7 +39,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
replica: string;
- pair: { layout: Doc, data?: Doc };
+ pair: { layout: Doc; data?: Doc };
}
export interface ViewDefResult {
@@ -48,7 +47,7 @@ export interface ViewDefResult {
bounds?: ViewDefBounds;
}
function toLabel(target: FieldResult<Field>) {
- if (typeof target === "number" || Number(target)) {
+ if (typeof target === 'number' || Number(target)) {
const truncated = Number(Number(target).toFixed(0));
const precise = Number(Number(target).toFixed(2));
return truncated === precise ? Number(target).toFixed(0) : Number(target).toFixed(2);
@@ -60,16 +59,16 @@ function toLabel(target: FieldResult<Field>) {
}
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- *
+ *
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- *
+ *
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
function getTextWidth(text: string, font: string): number {
// re-use canvas object for better performance
- const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
- const context = canvas.getContext("2d");
+ const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas'));
+ const context = canvas.getContext('2d');
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
@@ -81,14 +80,7 @@ interface PivotColumn {
filters: string[];
}
-export function computerPassLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
childPairs.forEach(({ layout, data }, i) => {
docMap.set(layout[Id], {
@@ -97,60 +89,49 @@ export function computerPassLayout(
width: layout[WidthSym](),
height: layout[HeightSym](),
pair: { layout, data },
- replica: ""
+ replica: '',
});
});
return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
-export function computerStarburstLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computerStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
const docMap = new Map<string, PoolData>();
- const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+ const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
childPairs.forEach(({ layout, data }, i) => {
- const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
- const deg = i / childPairs.length * Math.PI * 2;
+ const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75
+ const deg = (i / childPairs.length) * Math.PI * 2;
docMap.set(layout[Id], {
x: Math.cos(deg) * burstRadius[0] - docSize / 2,
- y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
- width: docSize,//layout[WidthSym](),
- height: docSize * layout[HeightSym]() / layout[WidthSym](),
+ y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2,
+ width: docSize, //layout[WidthSym](),
+ height: (docSize * layout[HeightSym]()) / layout[WidthSym](),
zIndex: NumCast(layout.zIndex),
pair: { layout, data },
- replica: ""
+ replica: '',
});
});
- const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
+ const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
}
-
-export function computePivotLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
- const fieldKey = "data";
+ const fieldKey = 'data';
const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
let nonNumbers = 0;
- const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || "author";
+ const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
childPairs.map(pair => {
- const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) :
- Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+ const lval =
+ pivotFieldKey === '#' || pivotFieldKey === 'tags'
+ ? Array.from(Object.keys(Doc.GetProto(pair.layout)))
+ .filter(k => k.startsWith('#'))
+ .map(k => k.substring(1))
+ : Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
const num = toNumber(pair.layout[pivotFieldKey]);
if (num === undefined || Number.isNaN(num)) {
@@ -166,7 +147,7 @@ export function computePivotLayout(
} else if (val) {
!pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
pivotColumnGroups.get(val)!.docs.push(pair.layout);
- pivotColumnGroups.get(val)!.replicas.push("");
+ pivotColumnGroups.get(val)!.replicas.push('');
} else {
docMap.set(pair.layout[Id], {
x: 0,
@@ -175,11 +156,11 @@ export function computePivotLayout(
width: 0,
height: 0,
pair,
- replica: ""
+ replica: '',
});
}
});
- const pivotNumbers = nonNumbers / childPairs.length < .1;
+ const pivotNumbers = nonNumbers / childPairs.length < 0.1;
if (pivotColumnGroups.size > 10) {
const arrayofKeys = Array.from(pivotColumnGroups.keys());
const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
@@ -196,9 +177,11 @@ export function computePivotLayout(
}
}
}
- const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+ const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
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 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);
const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
@@ -222,7 +205,7 @@ export function computePivotLayout(
const groupNames: ViewDefBounds[] = [];
const expander = 1.05;
- const gap = .15;
+ const gap = 0.15;
const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
let x = 0;
const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
@@ -232,14 +215,14 @@ export function computePivotLayout(
let xCount = 0;
const text = toLabel(key);
groupNames.push({
- type: "text",
+ type: 'text',
text,
x,
y: pivotAxisWidth,
width: pivotAxisWidth * expander * numCols,
height: max_text,
fontSize,
- payload: val
+ payload: val,
});
val.docs.forEach((doc, i) => {
const layoutDoc = Doc.Layout(doc);
@@ -249,13 +232,13 @@ export function computePivotLayout(
hgt = pivotAxisWidth;
wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
}
- docMap.set(doc[Id] + (val.replicas || ""), {
- x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
+ docMap.set(doc[Id] + (val.replicas || ''), {
+ 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,
pair: { layout: doc },
- replica: val.replicas[i]
+ replica: val.replicas[i],
});
xCount++;
if (xCount >= numCols) {
@@ -266,13 +249,14 @@ export function computePivotLayout(
x += pivotAxisWidth * (numCols * expander + gap);
});
- const dividers = sortedPivotKeys.map((key, i) =>
- ({
- type: "div", color: "lightGray",
- x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
- y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+ const dividers = sortedPivotKeys.map((key, i) => ({
+ type: 'div',
+ color: 'lightGray',
+ x: i * pivotAxisWidth * (numCols * expander + gap) - (pivotAxisWidth * (expander - 1)) / 2,
+ y: -maxColHeight + pivotAxisWidth,
+ width: pivotAxisWidth * numCols * expander,
height: maxColHeight,
- payload: pivotColumnGroups.get(key)!.filters
+ payload: pivotColumnGroups.get(key)!.filters,
}));
groupNames.push(...dividers);
return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
@@ -282,24 +266,17 @@ function toNumber(val: FieldResult<Field>) {
return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
}
-export function computeTimelineLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps?: any
-) {
- const fieldKey = "data";
+export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps?: any) {
+ const fieldKey = 'data';
const pivotDateGroups = new Map<number, Doc[]>();
const docMap = new Map<string, PoolData>();
const groupNames: ViewDefBounds[] = [];
const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
- const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
- const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null);
- const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTime && (curTime - curTimeSpan);
- const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTime && (curTime + curTimeSpan);
- const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+ const curTime = toNumber(pivotDoc[fieldKey + '-timelineCur']);
+ const curTimeSpan = Cast(pivotDoc[fieldKey + '-timelineSpan'], 'number', null);
+ const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMinReq'], 'number', null) : curTime && curTime - curTimeSpan;
+ const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMaxReq'], 'number', null) : curTime && curTime + curTimeSpan;
+ const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
const fontHeight = panelDim[1] > 58 ? 30 : panelDim[1] / 2;
const findStack = (time: number, stack: number[]) => {
const index = stack.findIndex(val => val === undefined || val < x);
@@ -325,8 +302,8 @@ export function computeTimelineLayout(
}
}
setTimeout(() => {
- pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
- pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
+ pivotDoc[fieldKey + '-timelineMin'] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
+ pivotDoc[fieldKey + '-timelineMax'] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
}, 0);
if (maxTime === minTime) {
@@ -340,10 +317,10 @@ export function computeTimelineLayout(
let prevKey = Math.floor(minTime);
if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
- groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
}
if (!sortedKeys.length && curTime !== undefined) {
- groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: 'orange', y: 0, height: fontHeight, fontSize, payload: undefined });
}
const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -351,26 +328,26 @@ export function computeTimelineLayout(
let zind = 0;
sortedKeys.forEach(key => {
if (curTime !== undefined && curTime > prevKey && curTime <= key) {
- groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+ groupNames.push({ type: 'text', text: toLabel(curTime), 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;
if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
- groupNames.push({ type: "text", text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
}
layoutDocsAtTime(keyDocs, key);
});
if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
x = (curTime - minTime) * scaling;
- groupNames.push({ type: "text", text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(curTime), 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: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -384,13 +361,14 @@ export function computeTimelineLayout(
wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
}
docMap.set(doc[Id], {
- x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
- zIndex: (curTime === key ? 1000 : zind++),
+ x: x,
+ y: (-Math.sqrt(stack) * pivotAxisWidth) / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+ zIndex: curTime === key ? 1000 : zind++,
highlight: curTime === key,
- width: wid / (Math.max(stack, 1)),
- height: hgt / (Math.max(stack, 1)),
+ width: wid / Math.max(stack, 1),
+ height: hgt / Math.max(stack, 1),
pair: { layout: doc },
- replica: ""
+ replica: '',
});
stacking[stack] = x + pivotAxisWidth;
});
@@ -407,41 +385,49 @@ function normalizeResults(
minWidth: number,
extras: ViewDefBounds[]
): ViewDefResult[] {
- const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
+ const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height } as ViewDefBounds));
const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
- const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
+ const aggBounds = aggregateBounds(
+ extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: 'doc', payload: '' })))).filter(e => e.zIndex !== -99),
+ 0,
+ 0
+ );
aggBounds.r = aggBounds.x + 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;
+ let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale;
if (Number.isNaN(scale)) scale = 1;
- Array.from(docMap.entries()).filter(ele => ele[1].pair).map(ele => {
- const newPosRaw = ele[1];
- if (newPosRaw) {
- const newPos = {
- x: newPosRaw.x * scale,
- y: newPosRaw.y * scale,
- z: newPosRaw.z,
- replica: newPosRaw.replica,
- highlight: newPosRaw.highlight,
- zIndex: newPosRaw.zIndex,
- width: (newPosRaw.width || 0) * scale,
- height: newPosRaw.height! * scale,
- pair: ele[1].pair
- };
- poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "all 1s", ...newPos });
- }
- });
+ Array.from(docMap.entries())
+ .filter(ele => ele[1].pair)
+ .map(ele => {
+ const newPosRaw = ele[1];
+ if (newPosRaw) {
+ const newPos = {
+ x: newPosRaw.x * scale,
+ y: newPosRaw.y * scale,
+ z: newPosRaw.z,
+ replica: newPosRaw.replica,
+ highlight: newPosRaw.highlight,
+ zIndex: newPosRaw.zIndex,
+ width: (newPosRaw.width || 0) * scale,
+ height: newPosRaw.height! * scale,
+ pair: ele[1].pair,
+ };
+ poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
+ }
+ });
- return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
- type: gname.type,
- text: gname.text,
- x: gname.x * scale,
- y: gname.y * scale,
- color: gname.color,
- width: gname.width === undefined ? undefined : gname.width * scale,
- height: gname.height === -1 ? 1 : gname.type === "text" ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
- fontSize: gname.fontSize,
- payload: gname.payload
- })));
+ return viewDefsToJSX(
+ extras.concat(groupNames).map(gname => ({
+ type: gname.type,
+ text: gname.text,
+ x: gname.x * scale,
+ y: gname.y * scale,
+ color: gname.color,
+ width: gname.width === undefined ? undefined : gname.width * scale,
+ height: gname.height === -1 ? 1 : gname.type === 'text' ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
+ fontSize: gname.fontSize,
+ payload: gname.payload,
+ }))
+ );
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 5f890c810..d979ef961 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,19 +1,18 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Field } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, NumCast } from '../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { LinkManager } from "../../../util/LinkManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { DocumentView } from "../../nodes/DocumentView";
-import "./CollectionFreeFormLinkView.scss";
-import React = require("react");
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { Colors } from "../../global/globalEnums";
-
+import { LinkManager } from '../../../util/LinkManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
+import './CollectionFreeFormLinkView.scss';
+import React = require('react');
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -27,31 +26,41 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
@observable _start = 0;
_anchorDisposer: IReactionDisposer | undefined;
_timeout: NodeJS.Timeout | undefined;
- componentWillUnmount() { this._anchorDisposer?.(); }
- @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25)));
+ componentWillUnmount() {
+ this._anchorDisposer?.();
+ }
+ @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
componentDidMount() {
- this._anchorDisposer = reaction(() => [
- this.props.A.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
- this.props.B.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
- ],
+ this._anchorDisposer = reaction(
+ () => [
+ this.props.A.props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
+ this.props.B.props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
+ ],
action(() => {
this._start = Date.now();
this._timeout && clearTimeout(this._timeout);
this._timeout = setTimeout(this.timeout, 25);
setTimeout(this.placeAnchors);
- })
- , { fireImmediately: true });
+ }),
+ { fireImmediately: true }
+ );
}
placeAnchors = () => {
const { A, B, LinkDocs } = this.props;
const linkDoc = LinkDocs[0];
if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
- setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
- setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
+ setTimeout(
+ action(() => (this._opacity = 0.75)),
+ 0
+ ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+ setTimeout(
+ action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+ 750
+ ); // this will unhighlight the link line.
const a = A.ContentDiv.getBoundingClientRect();
const b = B.ContentDiv.getBoundingClientRect();
const { left: aleft, top: atop, width: awidth, height: aheight } = A.ContentDiv.parentElement!.getBoundingClientRect();
@@ -60,7 +69,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
// really hacky stuff to make the LinkAnchorBox display where we want it to:
- // if there's an element in the DOM with a classname containing a link anchor's id,
+ // if there's an element in the DOM with a classname containing a link anchor's id,
// then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
// otherwise, we just use the computed nearest point on the document boundary to the target Document
const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
@@ -68,8 +77,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
if (!targetAhyperlink) {
if (linkDoc.linkAutoMove) {
- linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100;
- linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100;
+ linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
+ linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
}
} else {
const m = targetAhyperlink.getBoundingClientRect();
@@ -78,13 +87,13 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const mpy = mp[1] / A.props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
- if (getComputedStyle(targetAhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+ if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
else linkDoc.opacity = 1;
}
if (!targetBhyperlink) {
if (linkDoc.linkAutoMove) {
- linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100;
- linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100;
+ linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+ linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
}
} else {
const m = targetBhyperlink.getBoundingClientRect();
@@ -93,80 +102,86 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const mpy = mp[1] / B.props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
- if (getComputedStyle(targetBhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+ if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
else linkDoc.opacity = 1;
}
- }
-
+ };
pointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e, down, delta) => {
- this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
- this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
- return false;
- }, emptyFunction, () => {
- // OverlayView.Instance.addElement(
- // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
- // showLinks={action(() => { })}
- // />, { x: 300, y: 300 });
- });
-
-
- }
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
+ this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+ return false;
+ },
+ emptyFunction,
+ () => {
+ // OverlayView.Instance.addElement(
+ // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+ // showLinks={action(() => { })}
+ // />, { x: 300, y: 300 });
+ }
+ );
+ };
visibleY = (el: any) => {
let rect = el.getBoundingClientRect();
- const top = rect.top, height = rect.height;
+ const top = rect.top,
+ height = rect.height;
var el = el.parentNode;
while (el && el !== document.body) {
rect = el.getBoundingClientRect?.();
if (rect?.width) {
- if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom;
+ if (top <= rect.bottom === false && getComputedStyle(el).overflow === 'hidden') return rect.bottom;
// Check if the element is out of view due to a container scrolling
- if ((top + height) <= rect.top && getComputedStyle(el).overflow === "hidden") return rect.top;
+ if (top + height <= rect.top && getComputedStyle(el).overflow === 'hidden') return rect.top;
}
el = el.parentNode;
}
// Check its within the document viewport
return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- }
+ };
visibleX = (el: any) => {
let rect = el.getBoundingClientRect();
- const left = rect.left, width = rect.width;
+ const left = rect.left,
+ width = rect.width;
var el = el.parentNode;
while (el && el !== document.body) {
rect = el?.getBoundingClientRect();
if (rect?.width) {
- if (left <= rect.right === false && getComputedStyle(el).overflow === "hidden") return rect.right;
+ if (left <= rect.right === false && getComputedStyle(el).overflow === 'hidden') return rect.right;
// Check if the element is out of view due to a container scrolling
- if ((left + width) <= rect.left && getComputedStyle(el).overflow === "hidden") return rect.left;
+ if (left + width <= rect.left && getComputedStyle(el).overflow === 'hidden') return rect.left;
}
el = el.parentNode;
}
// Check its within the document viewport
return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- }
+ };
@action
toggleProperties = () => {
- if (CurrentUserUtils.propertiesWidth > 0) {
- CurrentUserUtils.propertiesWidth = 0;
+ if (SettingsManager.propertiesWidth > 0) {
+ SettingsManager.propertiesWidth = 0;
} else {
- CurrentUserUtils.propertiesWidth = 250;
+ SettingsManager.propertiesWidth = 250;
}
- }
+ };
onClickLine = () => {
SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
this.toggleProperties();
- }
+ };
@computed.struct get renderData() {
- this._start; SnappingManager.GetIsDragging();
+ this._start;
+ SnappingManager.GetIsDragging();
const { A, B, LinkDocs } = this.props;
if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
- const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
- const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+ const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+ const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
const adiv = acont.length ? acont[0] : A.ContentDiv;
const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
@@ -185,11 +200,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2 = [bleft + b.width / 2, btop + b.width / 2];
const pt1vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt1[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt1[1]];
const pt2vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt2[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt2[1]];
- const pt1len = Math.sqrt((pt1vec[0] * pt1vec[0]) + (pt1vec[1] * pt1vec[1]));
- const pt2len = Math.sqrt((pt2vec[0] * pt2vec[0]) + (pt2vec[1] * pt2vec[1]));
+ const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
+ const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
- const pt1norm = clipped ? [0, 0] : [pt1vec[0] / pt1len * ptlen, pt1vec[1] / pt1len * ptlen];
- const pt2norm = clipped ? [0, 0] : [pt2vec[0] / pt2len * ptlen, pt2vec[1] / pt2len * ptlen];
+ const pt1norm = clipped ? [0, 0] : [(pt1vec[0] / pt1len) * ptlen, (pt1vec[1] / pt1len) * ptlen];
+ const pt2norm = clipped ? [0, 0] : [(pt2vec[0] / pt2len) * ptlen, (pt2vec[1] / pt2len) * ptlen];
const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
@@ -203,7 +218,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
render() {
- if (!this.renderData) return (null);
+ if (!this.renderData) return null;
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
LinkManager.currentLink = this.props.LinkDocs[0];
@@ -216,31 +231,37 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
//access stroke color using index of the relationship in the color list (default black)
- const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex];
+ const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
// const hexStroke = this.rgbToHex(stroke)
//calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
//thickness varies linearly from 3px to 12px for increasing link count
- const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px";
+ const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
if (this.props.LinkDocs[0].displayArrow === undefined) {
this.props.LinkDocs[0].displayArrow = false;
}
- return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
- <defs>
- <marker id="arrowhead" markerWidth="4" markerHeight="3"
- refX="0" refY="1.5" orient="auto">
- <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
- </marker>
- </defs>
- <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
- onClick={this.onClickLine}
- d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
- markerEnd={this.props.LinkDocs[0].displayArrow ? "url(#arrowhead)" : ""} />
- {textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
- {Field.toString(this.props.LinkDocs[0].description as any as Field)}
- </text>}
- </>);
+ return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+ <>
+ <defs>
+ <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+ <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+ </marker>
+ </defs>
+ <path
+ className="collectionfreeformlinkview-linkLine"
+ style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+ onClick={this.onClickLine}
+ d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
+ markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+ />
+ {textX === undefined ? null : (
+ <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+ {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+ </text>
+ )}
+ </>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9f6938e67..9e8d92d7d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,79 +1,82 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../fields/CursorField";
-import { FieldResult } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast } from "../../../../fields/Types";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { CollectionViewProps } from "../CollectionView";
-import "./CollectionFreeFormView.scss";
-import React = require("react");
-import v5 = require("uuid/v5");
+import CursorField from '../../../../fields/CursorField';
+import { Doc, FieldResult } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
+import { CollectionViewProps } from '../CollectionView';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+import v5 = require('uuid/v5');
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
-
@computed protected get cursors(): CursorField[] {
const doc = this.props.Document;
let cursors: FieldResult<List<CursorField>>;
- const { id } = CurrentUserUtils;
+ const id = Doc.UserDoc()[Id];
if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
return [];
}
const now = mobxUtils.now();
- return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && (now - metadata.timestamp) < 1000);
+ return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000);
}
@computed get renderedCursors() {
- return this.cursors.map(({ data: { metadata, position: { x, y } } }) => {
- return (
- <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont"
- style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}
- >
- <canvas className="collectionFreeFormRemoteCursors-canvas"
- ref={(el) => {
- if (el) {
- const ctx = el.getContext('2d');
- if (ctx) {
- ctx.fillStyle = "#" + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + "22";
- ctx.fillRect(0, 0, 20, 20);
+ return this.cursors.map(
+ ({
+ data: {
+ metadata,
+ position: { x, y },
+ },
+ }) => {
+ return (
+ <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
+ <canvas
+ className="collectionFreeFormRemoteCursors-canvas"
+ ref={el => {
+ if (el) {
+ const ctx = el.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillRect(0, 0, 20, 20);
- ctx.fillStyle = "black";
- ctx.lineWidth = 0.5;
+ ctx.fillStyle = 'black';
+ ctx.lineWidth = 0.5;
- ctx.beginPath();
+ ctx.beginPath();
- ctx.moveTo(10, 0);
- ctx.lineTo(10, 8);
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
- ctx.moveTo(10, 20);
- ctx.lineTo(10, 12);
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
- ctx.moveTo(0, 10);
- ctx.lineTo(8, 10);
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
- ctx.moveTo(20, 10);
- ctx.lineTo(12, 10);
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
- ctx.stroke();
+ ctx.stroke();
+ }
}
- }
- }}
- width={20}
- height={20}
- />
- <p className="collectionFreeFormRemoteCursors-symbol">
- {metadata.identifier[0].toUpperCase()}
- </p>
- </div>
- );
- });
+ }}
+ width={20}
+ height={20}
+ />
+ <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
+ </div>
+ );
+ }
+ );
}
render() {
return this.renderedCursors;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8444c9119..07ea26346 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -18,13 +18,13 @@ import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { HistoryUtil } from '../../../util/History';
import { InteractionUtils } from '../../../util/InteractionUtils';
import { RecordingApi } from '../../../util/RecordingApi';
+import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
import { ColorScheme } from '../../../util/SettingsManager';
@@ -48,7 +48,6 @@ import { StyleProp } from '../../StyleProvider';
import { CollectionDockingView } from '../CollectionDockingView';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
-import { CollectionViewType } from '../CollectionView';
import { TabDocView } from '../TabDocView';
import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
@@ -56,8 +55,6 @@ import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
import e = require('connect-flash');
-import { ReplayMovements } from '../../../util/ReplayMovements';
-
export type collectionFreeformViewProps = {
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -517,7 +514,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
) {
- switch (CurrentUserUtils.ActiveTool) {
+ switch (Doc.ActiveTool) {
case InkTool.Highlighter:
break;
// TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -556,7 +553,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.addMoveListeners();
this.removeEndListeners();
this.addEndListeners();
- if (CurrentUserUtils.ActiveTool === InkTool.None) {
+ if (Doc.ActiveTool === InkTool.None) {
this._lastX = pt.pageX;
this._lastY = pt.pageY;
e.preventDefault();
@@ -577,14 +574,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
+ const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
title: 'ink stroke',
x: B.x - ActiveInkWidth() / 2,
y: B.y - ActiveInkWidth() / 2,
_width: B.width + ActiveInkWidth(),
_height: B.height + ActiveInkWidth(),
});
- if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+ if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
}
@@ -775,7 +772,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onPointerMove = (e: PointerEvent): void => {
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- CurrentUserUtils.ActiveTool = InkTool.None;
+ Doc.ActiveTool = InkTool.None;
if (this.props.isContentActive(true)) e.stopPropagation();
} else if (!e.cancelBubble) {
if (this.tryDragCluster(e, this._hitCluster)) {
@@ -907,7 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
if (myTouches[0]) {
- if (CurrentUserUtils.ActiveTool === InkTool.None) {
+ if (Doc.ActiveTool === InkTool.None) {
if (this.tryDragCluster(e, this._hitCluster)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
@@ -1061,8 +1058,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline)
- return;
+ if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
// things that can scroll vertically should do that instead of zooming
e.stopPropagation();
@@ -1079,7 +1075,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Doc.UserDoc()?.presentationMode === 'recording' && RecordingApi.Instance.setRecordingFFView(this);
// TODO: make this based off the specific recording FFView
Doc.UserDoc()?.presentationMode === 'none' && RecordingApi.Instance.setPlayFFView(this);
-
+
// TODO: zzz + michael to figure out this merge in case of strange behaviour
// if (Doc.UserDoc()?.presentationMode === 'watching') {
// RecordingApi.Instance.pauseVideoAndMovements();
@@ -1118,7 +1114,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
- if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
+ if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
this._viewTransition = panTime;
const scale = this.getLocalTransform().inverse().Scale;
const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -2218,7 +2214,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
const renderGridSpace = gridSpace * this.props.zoomScaling();
const w = this.props.PanelWidth() + 2 * renderGridSpace;
const h = this.props.PanelHeight() + 2 * renderGridSpace;
- const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+ const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
return (
<canvas
className="collectionFreeFormView-grid"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8a34d5a..4513ffb39 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,36 +1,33 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCastAsync, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { intersectRect, returnFalse, Utils } from "../../../../Utils";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
-import { PresMovement } from "../../nodes/trails/PresEnums";
-import { VideoBox } from "../../nodes/VideoBox";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
-import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { SubCollectionViewProps } from "../CollectionSubView";
-import { TreeView } from "../TreeView";
-import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
-import "./MarqueeView.scss";
-import React = require("react");
-import { TabDocView } from "../TabDocView";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { intersectRect, returnFalse, Utils } from '../../../../Utils';
+import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PinViewProps, PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { PreviewCursor } from '../../PreviewCursor';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { TabDocView } from '../TabDocView';
+import { TreeView } from '../TreeView';
+import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import './MarqueeView.scss';
+import React = require('react');
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -46,15 +43,14 @@ interface MarqueeViewProps {
}
export interface MarqueeViewBounds {
- left: number;
- top: number;
- width: number;
- height: number;
+ left: number;
+ top: number;
+ width: number;
+ height: number;
}
@observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
-{
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
private _commandExecuted = false;
@observable _lastX: number = 0;
@observable _lastY: number = 0;
@@ -64,18 +60,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@observable _lassoPts: [number, number][] = [];
@observable _lassoFreehand: boolean = false;
- @computed get Transform() { return this.props.getTransform(); }
+ @computed get Transform() {
+ return this.props.getTransform();
+ }
@computed get Bounds() {
// nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
// nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+ const bounds: MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
return bounds;
}
- get inkDoc() { return this.props.Document; }
- get ink() { return Cast(this.props.Document.ink, InkField); }
- set ink(value: Opt<InkField>) { this.props.Document.ink = value; }
+ get inkDoc() {
+ return this.props.Document;
+ }
+ get ink() {
+ return Cast(this.props.Document.ink, InkField);
+ }
+ set ink(value: Opt<InkField>) {
+ this.props.Document.ink = value;
+ }
componentDidMount() {
this.props.setPreviewCursor?.(this.setPreviewCursor);
@@ -84,14 +88,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
cleanupInteractions = (all: boolean = false, hideMarquee: boolean = true) => {
if (all) {
- document.removeEventListener("pointerup", this.onPointerUp, true);
- document.removeEventListener("pointermove", this.onPointerMove, true);
+ document.removeEventListener('pointerup', this.onPointerUp, true);
+ document.removeEventListener('pointermove', this.onPointerMove, true);
}
- document.removeEventListener("keydown", this.marqueeCommand, true);
+ document.removeEventListener('keydown', this.marqueeCommand, true);
hideMarquee && this.hideMarquee();
this._lassoPts = [];
- }
+ };
@undoBatch
@action
@@ -100,76 +104,75 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// tslint:disable-next-line:prefer-const
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
- if (e.key === "?") {
- cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
- Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: "bing", useCors: true }), "add:right"));
+ if (e.key === '?') {
+ cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), 'add:right'));
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
- } else
- if (e.key === "u" && this.props.ungroup) {
- e.stopPropagation();
- this.props.ungroup();
- }
- else if (e.key === ":") {
- DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+ } else if (e.key === 'u' && this.props.ungroup) {
+ e.stopPropagation();
+ this.props.ungroup();
+ } else if (e.key === ':') {
+ DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
- cm.displayMenu(this._downX, this._downY, undefined, true);
- e.stopPropagation();
- } else if (e.key === "a" && (e.ctrlKey || e.metaKey)) {
- e.preventDefault();
- this.props.selectDocuments(this.props.activeDocuments());
- e.stopPropagation();
- } else if (e.key === "q" && e.ctrlKey) {
- e.preventDefault();
- (async () => {
- const text: string = await navigator.clipboard.readText();
- const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
- for (let i = 0; i < ns.length - 1; i++) {
- while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
- ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
- ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
- ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
- const sub = ns[i].endsWith("\r") ? 1 : 0;
- const br = ns[i + 1].trim() === "";
- ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
- if (br) break;
- }
+ cm.displayMenu(this._downX, this._downY, undefined, true);
+ e.stopPropagation();
+ } else if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+ e.preventDefault();
+ this.props.selectDocuments(this.props.activeDocuments());
+ e.stopPropagation();
+ } else if (e.key === 'q' && e.ctrlKey) {
+ e.preventDefault();
+ (async () => {
+ const text: string = await navigator.clipboard.readText();
+ const ns = text.split('\n').filter(t => t.trim() !== '\r' && t.trim() !== '');
+ for (let i = 0; i < ns.length - 1; i++) {
+ while (
+ !(ns[i].trim() === '' || ns[i].endsWith('-\r') || ns[i].endsWith('-') || ns[i].endsWith(';\r') || ns[i].endsWith(';') || ns[i].endsWith('.\r') || ns[i].endsWith('.') || ns[i].endsWith(':\r') || ns[i].endsWith(':')) &&
+ i < ns.length - 1
+ ) {
+ const sub = ns[i].endsWith('\r') ? 1 : 0;
+ const br = ns[i + 1].trim() === '';
+ ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
+ if (br) break;
}
- let ypos = y;
- ns.map(line => {
- const indent = line.search(/\S|$/);
- const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line });
- this.props.addDocument?.(newBox);
- ypos += 40 * this.Transform.Scale;
- });
- })();
- e.stopPropagation();
- } else if (e.key === "b" && e.ctrlKey) {
- document.body.focus(); // so that we can access the clipboard without an error
- setTimeout(() =>
- pasteImageBitmap((data: any, error: any) => {
- error && console.log(error);
- data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
- this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+ }
+ let ypos = y;
+ ns.map(line => {
+ const indent = line.search(/\S|$/);
+ const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + (indent / 3) * 10, y: ypos, title: line });
+ this.props.addDocument?.(newBox);
+ ypos += 40 * this.Transform.Scale;
+ });
+ })();
+ e.stopPropagation();
+ } else if (e.key === 'b' && e.ctrlKey) {
+ document.body.focus(); // so that we can access the clipboard without an error
+ setTimeout(() =>
+ pasteImageBitmap((data: any, error: any) => {
+ error && console.log(error);
+ data &&
+ VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+ this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
});
- }));
- } else if (e.key === "s" && e.ctrlKey) {
- e.preventDefault();
- const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
- slide.x = x;
- slide.y = y;
- FormattedTextBox.SelectOnLoad = slide[Id];
- TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
- this.props.addDocument?.(slide);
- e.stopPropagation();
- } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
- FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : "";
- FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
- this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0));
- e.stopPropagation();
- }
- }
+ })
+ );
+ } else if (e.key === 's' && e.ctrlKey) {
+ e.preventDefault();
+ const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
+ slide.x = x;
+ slide.y = y;
+ FormattedTextBox.SelectOnLoad = slide[Id];
+ TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
+ this.props.addDocument?.(slide);
+ e.stopPropagation();
+ } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
+ FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+ FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('live text batch');
+ this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
+ e.stopPropagation();
+ }
+ };
//heuristically converts pasted text into a table.
// assumes each entry is separated by a tab
// skips all rows until it gets to a row with more than one entry
@@ -178,26 +181,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
// assumes each cell is a string or a number
pasteTable(ns: string[], x: number, y: number) {
- while (ns.length > 0 && ns[0].split("\t").length < 2) {
+ while (ns.length > 0 && ns[0].split('\t').length < 2) {
ns.splice(0, 1);
}
if (ns.length > 0) {
- const columns = ns[0].split("\t");
+ const columns = ns[0].split('\t');
const docList: Doc[] = [];
- let groupAttr: string | number = "";
+ let groupAttr: string | number = '';
const rowProto = new Doc();
rowProto.title = rowProto.Id;
rowProto._width = 200;
rowProto.isPrototype = true;
for (let i = 1; i < ns.length - 1; i++) {
- const values = ns[i].split("\t");
+ const values = ns[i].split('\t');
if (values.length === 1 && columns.length > 1) {
groupAttr = values[0];
continue;
}
const docDataProto = Doc.MakeDelegate(rowProto);
docDataProto.isPrototype = true;
- columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
+ columns.forEach((col, i) => (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined));
if (groupAttr) {
docDataProto._group = groupAttr;
}
@@ -206,7 +209,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
doc._width = 200;
docList.push(doc);
}
- const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 });
+ const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField('_group', '#f1efeb')] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, '#f1efeb'))], docList, {
+ x: x,
+ y: y,
+ title: 'droppedTable',
+ _width: 300,
+ _height: 100,
+ });
this.props.addDocument?.(newCol);
}
@@ -227,10 +236,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
// bcz: do we need this? it kills the context menu on the main collection if !altKey
// e.stopPropagation();
- }
- else PreviewCursor.Visible = false;
+ } else PreviewCursor.Visible = false;
}
- }
+ };
@action
onPointerMove = (e: PointerEvent): void => {
@@ -238,8 +246,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._lastY = e.pageY;
this._lassoPts.push([e.clientX, e.clientY]);
if (!e.cancelBubble) {
- if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD ||
- Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
+ if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
if (!this._commandExecuted) {
this.showMarquee();
}
@@ -253,7 +260,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (PresBox.startMarquee) {
e.stopPropagation();
}
- }
+ };
@action
onPointerUp = (e: PointerEvent): void => {
@@ -270,14 +277,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const hideMarquee = () => {
this.hideMarquee();
MarqueeOptionsMenu.Instance.fadeOut(true);
- document.removeEventListener("pointerdown", hideMarquee);
- document.removeEventListener("wheel", hideMarquee);
+ document.removeEventListener('pointerdown', hideMarquee);
+ document.removeEventListener('wheel', hideMarquee);
};
if (PresBox.startMarquee) {
this.pinWithView();
PresBox.startMarquee = false;
}
- if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
+ if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100 && !PresBox.startMarquee) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
@@ -285,19 +292,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
- document.addEventListener("pointerdown", hideMarquee);
- document.addEventListener("wheel", hideMarquee);
+ document.addEventListener('pointerdown', hideMarquee);
+ document.addEventListener('wheel', hideMarquee);
} else {
this.hideMarquee();
}
this.cleanupInteractions(true, this._commandExecuted);
e.altKey && e.preventDefault();
- }
+ };
clearSelection() {
- if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
- else if (document.getSelection()) { document.getSelection()?.empty(); }
+ if (window.getSelection) {
+ window.getSelection()?.removeAllRanges();
+ } else if (document.getSelection()) {
+ document.getSelection()?.empty();
+ }
}
setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
@@ -312,9 +322,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._commandExecuted = false;
PreviewCursor.Visible = false;
this.cleanupInteractions(true);
- document.addEventListener("pointermove", this.onPointerMove, true);
- document.addEventListener("pointerup", this.onPointerUp, true);
- document.addEventListener("keydown", this.marqueeCommand, true);
+ document.addEventListener('pointermove', this.onPointerMove, true);
+ document.addEventListener('pointerup', this.onPointerUp, true);
+ document.addEventListener('keydown', this.marqueeCommand, true);
} else {
this._downX = x;
this._downY = y;
@@ -328,9 +338,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onClick = (e: React.MouseEvent): void => {
- if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
- Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- if (CurrentUserUtils.ActiveTool === InkTool.None) {
+ if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+ if (Doc.ActiveTool === InkTool.None) {
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -339,17 +348,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
}
// let the DocumentView stopPropagation of this event when it selects this document
- } else { // why do we get a click event when the cursor have moved a big distance?
+ } else {
+ // why do we get a click event when the cursor have moved a big distance?
// let's cut it off here so no one else has to deal with it.
e.stopPropagation();
}
- }
+ };
@action
- showMarquee = () => { this._visible = true; }
+ showMarquee = () => {
+ this._visible = true;
+ };
@action
- hideMarquee = () => { this._visible = false; }
+ hideMarquee = () => {
+ this._visible = false;
+ };
@undoBatch
@action
@@ -361,16 +375,18 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.cleanupInteractions(false);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
+ };
getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
- const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
- Doc.GetProto(doc).data = new List<Doc>(selected);
- Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
- !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
- doc._panX = doc._panY = 0;
- return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ const newCollection = creator
+ ? creator(selected, { title: 'nested stack' })
+ : ((doc: Doc) => {
+ Doc.GetProto(doc).data = new List<Doc>(selected);
+ Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
+ !this.props.isAnnotationOverlay && Doc.AddDocToList(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+ doc._panX = doc._panY = 0;
+ return doc;
+ })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.system = undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
@@ -378,7 +394,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
newCollection.forceActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
- selected.forEach(d => d.context = newCollection);
+ selected.forEach(d => (d.context = newCollection));
this.hideMarquee();
return newCollection;
});
@@ -393,74 +409,76 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
-
- /**
- * This triggers the TabDocView.PinDoc method which is the universal method
- * used to pin documents to the currently active presentation trail.
- *
- * This one is unique in that it includes the bounds associated with marquee view.
- */
+ };
+
+ /**
+ * This triggers the TabDocView.PinDoc method which is the universal method
+ * used to pin documents to the currently active presentation trail.
+ *
+ * This one is unique in that it includes the bounds associated with marquee view.
+ */
@undoBatch
@action
pinWithView = async () => {
const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
- const doc = this.props.Document;
- const viewOptions:PinViewProps = {
- bounds: this.Bounds,
- scale: scale
- };
- TabDocView.PinDoc(doc, {pinWithView: viewOptions});
- MarqueeOptionsMenu.Instance.fadeOut(true);
+ const doc = this.props.Document;
+ const viewOptions: PinViewProps = {
+ bounds: this.Bounds,
+ scale: scale,
+ };
+ TabDocView.PinDoc(doc, { pinWithView: viewOptions });
+ MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
+ };
@undoBatch
@action
collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
const selected = this.marqueeSelect(false);
- if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) {
- selected.map(action(d => {
- const dx = NumCast(d.x);
- const dy = NumCast(d.y);
- delete d.x;
- delete d.y;
- delete d.activeFrame;
- delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- d.x = dx - this.Bounds.left - this.Bounds.width / 2;
- d.y = dy - this.Bounds.top - this.Bounds.height / 2;
- return d;
- }));
+ if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
+ selected.map(
+ action(d => {
+ const dx = NumCast(d.x);
+ const dy = NumCast(d.y);
+ delete d.x;
+ delete d.y;
+ delete d.activeFrame;
+ delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ d.x = dx - this.Bounds.left - this.Bounds.width / 2;
+ d.y = dy - this.Bounds.top - this.Bounds.height / 2;
+ return d;
+ })
+ );
this.props.removeDocument?.(selected);
}
// TODO: nda - this is the code to actually get a new grouped collection
- const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
+ const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
this.props.addDocument?.(newCollection);
this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
+ };
@undoBatch
@action
syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
- if (e instanceof KeyboardEvent ? e.key === "i" : true) {
+ if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
const inks = selected.filter(s => s.type === DocumentType.INK);
const setDocs = selected.filter(s => s.type === DocumentType.RTF && s.color);
- const sets = setDocs.map((sd) => Cast(sd.data, RichTextField)?.Text as string);
+ const sets = setDocs.map(sd => Cast(sd.data, RichTextField)?.Text as string);
const colors = setDocs.map(sd => FieldValue(sd.color) as string);
const wordToColor = new Map<string, string>();
- sets.forEach((st: string, i: number) => st.split(",").forEach(word => wordToColor.set(word, colors[i])));
+ sets.forEach((st: string, i: number) => st.split(',').forEach(word => wordToColor.set(word, colors[i])));
const strokes: InkData[] = [];
inks.filter(i => Cast(i.data, InkField)).forEach(i => {
const d = Cast(i.data, InkField, null);
- const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
- const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+ const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
});
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {
// const wordResults = results.filter((r: any) => r.category === "inkWord");
// for (const word of wordResults) {
// const indices: number[] = word.strokeIds;
@@ -501,12 +519,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
// });
// }
- const lines = results.filter((r: any) => r.category === "line");
- const text = lines.map((l: any) => l.recognizedText).join("\r\n");
+ const lines = results.filter((r: any) => r.category === 'line');
+ const text = lines.map((l: any) => l.recognizedText).join('\r\n');
this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
});
}
- }
+ };
@undoBatch
@action
@@ -517,15 +535,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.y = NumCast(d.y) - this.Bounds.top;
return d;
});
- const summary = Docs.Create.TextDocument("", { backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
- const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
- DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
+ const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+ const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+ DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', '');
portal.hidden = true;
this.props.addDocument?.(portal);
this.props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
- }
+ };
@action
background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
@@ -534,33 +552,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
setTimeout(() => this.props.selectDocuments([newCollection]));
- }
+ };
@undoBatch
marqueeCommand = action((e: KeyboardEvent) => {
if (this._commandExecuted || (e as any).propagationIsStopped) {
return;
}
- if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
+ if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd') {
this._commandExecuted = true;
e.stopPropagation();
(e as any).propagationIsStopped = true;
this.delete();
e.stopPropagation();
}
- if ("cbtsSpg".indexOf(e.key) !== -1) {
+ if ('cbtsSpg'.indexOf(e.key) !== -1) {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
(e as any).propagationIsStopped = true;
- if (e.key === "g") this.collection(e, true);
- if (e.key === "c" || e.key === "t") this.collection(e);
- if (e.key === "s" || e.key === "S") this.summary(e);
- if (e.key === "b") this.background(e);
- if (e.key === "p") this.pileup(e);
+ if (e.key === 'g') this.collection(e, true);
+ if (e.key === 'c' || e.key === 't') this.collection(e);
+ if (e.key === 's' || e.key === 'S') this.summary(e);
+ if (e.key === 'b') this.background(e);
+ if (e.key === 'p') this.pileup(e);
this.cleanupInteractions(false);
}
- if (e.key === "r" || e.key === " ") {
+ if (e.key === 'r' || e.key === ' ') {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
@@ -568,18 +586,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
});
- touchesLine(r1: { left: number, top: number, width: number, height: number }) {
+ touchesLine(r1: { left: number; top: number; width: number; height: number }) {
for (const lassoPt of this._lassoPts) {
const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
- if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width &&
- r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
+ if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
return true;
}
}
return false;
}
- boundingShape(r1: { left: number, top: number, width: number, height: number }) {
+ boundingShape(r1: { left: number; top: number; width: number; height: number }) {
const xs = this._lassoPts.map(pair => pair[0]);
const ys = this._lassoPts.map(pair => pair[1]);
const tl = this.Transform.transformPoint(Math.min(...xs), Math.min(...ys));
@@ -592,10 +609,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
let hasRight = false;
for (const lassoPt of this._lassoPts) {
const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
- hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
- hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
- hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
- hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+ hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+ hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+ hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+ hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
if (hasTop && hasLeft && hasBottom && hasRight) {
return true;
}
@@ -615,9 +632,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
(this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
}
};
- this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
- if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
- if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
+ this.props
+ .activeDocuments()
+ .filter(doc => !doc.z && !doc._lockedPosition)
+ .map(selectFunc);
+ if (!selection.length && selectBackgrounds)
+ this.props
+ .activeDocuments()
+ .filter(doc => doc.z === undefined)
+ .map(selectFunc);
+ if (!selection.length)
+ this.props
+ .activeDocuments()
+ .filter(doc => doc.z !== undefined)
+ .map(selectFunc);
return selection;
}
@@ -625,31 +653,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
const p = this.props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
const v = this._lassoFreehand ? [0, 0] : this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- return <div className="marquee" style={{
- transform: `translate(${p[0]}px, ${p[1]}px)`,
- width: Math.abs(v[0]),
- height: Math.abs(v[1]),
- zIndex: 2000
- }}> {this._lassoFreehand ?
- <svg height={2000} width={2000}>
- <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + "," + pt[1] + " ", "")} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
- </svg>
- :
- <span className="marquee-legend" />}
- </div>;
+ return (
+ <div
+ className="marquee"
+ style={{
+ transform: `translate(${p[0]}px, ${p[1]}px)`,
+ width: Math.abs(v[0]),
+ height: Math.abs(v[1]),
+ zIndex: 2000,
+ }}>
+ {' '}
+ {this._lassoFreehand ? (
+ <svg height={2000} width={2000}>
+ <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
+ </svg>
+ ) : (
+ <span className="marquee-legend" />
+ )}
+ </div>
+ );
}
render() {
- return <div className="marqueeView"
- style={{
- overflow: StrCast(this.props.Document._overflow),
- cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
- }}
-
- onDragOver={e => e.preventDefault()}
- onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
- {this._visible ? this.marqueeDiv : null}
- {this.props.children}
- </div>;
+ return (
+ <div
+ className="marqueeView"
+ style={{
+ overflow: StrCast(this.props.Document._overflow),
+ cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible || PresBox.startMarquee ? 'crosshair' : 'pointer',
+ }}
+ onDragOver={e => e.preventDefault()}
+ onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
+ </div>
+ );
}
-} \ No newline at end of file
+}