aboutsummaryrefslogtreecommitdiff
path: root/src/client/documents/DocUtils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/documents/DocUtils.ts')
-rw-r--r--src/client/documents/DocUtils.ts170
1 files changed, 138 insertions, 32 deletions
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index e3cb5e72c..1c7ccadd1 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -3,14 +3,14 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { saveAs } from 'file-saver';
import * as JSZip from 'jszip';
import { action, runInAction } from 'mobx';
-import { ClientUtils } from '../../ClientUtils';
+import { ClientUtils, DashColor } from '../../ClientUtils';
import * as JSZipUtils from '../../JSZipUtils';
import { decycle } from '../../decycler/decycler';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Field, FieldResult, FieldType, LinkedTo, Opt, StrListCast } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
-import { InkDataFieldName, InkField } from '../../fields/InkField';
+import { InkData, InkDataFieldName, InkField } from '../../fields/InkField';
import { List, ListFieldName } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
import { RichTextField } from '../../fields/RichTextField';
@@ -33,14 +33,22 @@ import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
import { DocumentType } from './DocumentTypes';
import { Docs, DocumentOptions } from './Documents';
import { DocumentView } from '../views/nodes/DocumentView';
-import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
+import { INode, parse } from 'svgson';
+import { SVGToBezier, SVGType } from '../util/bezierFit';
+import { SmartDrawHandler } from '../views/smartdraw/SmartDrawHandler';
+import { PointData } from '../../pen-gestures/GestureTypes';
export namespace DocUtils {
+ function HasFunctionFilter(val: string) {
+ if (val.includes(ClientUtils.isTransparentFunctionHack)) return (d: Doc, color: string) => !d.disableMixBlend && color !== '' && DashColor(color).alpha() !== 1;
+ // add other function filters here...
+ return undefined;
+ }
function matchFieldValue(doc: Doc, key: string, valueIn: unknown): boolean {
let value = valueIn;
- const hasFunctionFilter = ClientUtils.HasFunctionFilter(value as string);
+ const hasFunctionFilter = HasFunctionFilter(value as string);
if (hasFunctionFilter) {
- return hasFunctionFilter(StrCast(doc[key]));
+ return hasFunctionFilter(doc, StrCast(doc[key]));
}
if (key === LinkedTo) {
// links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("..."))
@@ -67,9 +75,9 @@ export namespace DocUtils {
}
const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings
if (vals.length) {
- return vals.some(v => typeof v === 'string' && v.includes(value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ return vals.some(v => typeof v === 'string' && v === (value as string)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
- return Field.toString(fieldVal as FieldType).includes(value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ return Field.toString(fieldVal as FieldType) === (value as string); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
/**
* @param docs
@@ -163,7 +171,7 @@ export namespace DocUtils {
return rangeFilteredDocs;
}
- export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string }, id?: string, showPopup?: number[]) {
+ export function MakeLink(source: Doc, target: Doc, linkSettings: { layout_isSvg?: boolean; link_relationship?: string; link_description?: string }, id?: string, showPopup?: number[]) {
if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
if (target.doc === Doc.UserDoc()) return undefined;
@@ -215,6 +223,7 @@ export namespace DocUtils {
link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined,
link_relationship: linkSettings.link_relationship,
link_description: linkSettings.link_description,
+ layout_isSvg: linkSettings.layout_isSvg,
x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as unknown as number, // x can accept functions even though type says it can't
y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as unknown as number, // y can accept functions even though type says it can't
link_autoMoveAnchors: true,
@@ -352,8 +361,20 @@ export namespace DocUtils {
return ctor ? ctor(path, overwriteDoc ? { ...options, title: StrCast(overwriteDoc.title, path) } : options, overwriteDoc) : undefined;
}
+ /**
+ * Adds items to the doc creator (':') context menu for creating each document type
+ * @param docTextAdder
+ * @param docAdder
+ * @param x
+ * @param y
+ * @param simpleMenu
+ * @param pivotField
+ * @param pivotValue
+ */
export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string | number | boolean): void {
- const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data)
+ const foo = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data).concat(...DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data));
+
+ const documentList: ContextMenuProps[] = foo
.filter(btnDoc => !btnDoc.hidden)
.map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
.filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title)
@@ -365,7 +386,8 @@ export namespace DocUtils {
newDoc.author = ClientUtils.CurrentUserEmail();
newDoc.x = x;
newDoc.y = y;
- Doc.SetSelectOnLoad(newDoc);
+ newDoc[DocData].backgroundColor = Doc.UserDoc().textBackgroundColor;
+ DocumentView.SetSelectOnLoad(newDoc);
if (pivotField) {
newDoc[pivotField] = pivotValue;
}
@@ -376,9 +398,13 @@ export namespace DocUtils {
})) as ContextMenuProps[];
documentList.push({
description: ':Smart Drawing',
- event: e => (DocumentView.Selected().lastElement().ComponentView as CollectionFreeFormView)?.showSmartDraw(e?.x || 0, e?.y || 0),
+ event: e =>
+ DocumentView.Selected()
+ .lastElement()
+ .ComponentView?.showSmartDraw?.(e?.x || 0, e?.y || 0),
icon: 'file',
});
+
ContextMenu.Instance.addItem({
description: 'Create document',
subitems: documentList,
@@ -420,7 +446,7 @@ export namespace DocUtils {
newDoc.author = ClientUtils.CurrentUserEmail();
newDoc.x = x;
newDoc.y = y;
- Doc.SetSelectOnLoad(newDoc);
+ DocumentView.SetSelectOnLoad(newDoc);
if (pivotField) {
newDoc[pivotField] = pivotValue;
}
@@ -665,29 +691,56 @@ export namespace DocUtils {
}
generatedDocuments.push(doc);
}
+ return doc;
}
export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) {
const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout);
- const tbox = Docs.Create.TextDocument('', {
- annotationOn,
- backgroundColor,
- x,
- y,
- title,
- ...(defaultTextTemplate
- ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
- : {
- _width: width || 200,
- _height: BoolCast(Doc.UserDoc().fitBox) ? 70 : 35,
- _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
- _layout_fitWidth: true,
- _layout_autoHeight: true,
- text_fitBox: BoolCast(Doc.UserDoc().fitBox),
- text_align: StrCast(Doc.UserDoc().textAlign),
+ const tbox =
+ StrCast(Doc.UserDoc().fontFamily) === 'Math'
+ ? Docs.Create.EquationDocument('', {
+ //
+ annotationOn,
+ backgroundColor: backgroundColor ?? StrCast(Doc.UserDoc().textBackgroundColor),
+ borderColor: Doc.UserDoc().borderColor as string,
+ borderWidth: Doc.UserDoc().borderWidth as number,
+ x,
+ y,
+ title,
text_fontColor: StrCast(Doc.UserDoc().fontColor),
- }),
- });
+ _width: 50,
+ _height: 50,
+ _yMargin: 10,
+ _xMargin: 10,
+ nativeWidth: 40,
+ nativeHeight: 40,
+ })
+ : Docs.Create.TextDocument('', {
+ annotationOn,
+ backgroundColor,
+ x,
+ y,
+ title,
+ ...(defaultTextTemplate
+ ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+ : {
+ _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
+ _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
+ _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
+ _layout_fitWidth: true,
+ _layout_autoHeight: true,
+ backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
+ borderColor: Doc.UserDoc().borderColor as string,
+ borderWidth: Doc.UserDoc().borderWidth as number,
+ text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+ text_align: StrCast(Doc.UserDoc().textAlign),
+ text_fontColor: StrCast(Doc.UserDoc().fontColor),
+ text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+ text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
+ text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
+ text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
+ }),
+ });
if (defaultTextTemplate) {
tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
@@ -735,10 +788,61 @@ export namespace DocUtils {
return generatedDocuments;
}
+ export async function openSVGfile(file: File, options: DocumentOptions) {
+ const reader = new FileReader();
+ const scale = 1;
+ const startPoint = { X: (options.x as number) ?? 0, Y: (options.y as number) ?? 0 };
+ const buffer = await new Promise<string>((res, rej) => {
+ reader.onload = event => {
+ const fileContent = event.target?.result;
+ // Process the file content here
+ console.log(fileContent);
+ typeof fileContent === 'string' ? res(fileContent) : rej();
+ };
+
+ reader.readAsText(file);
+ });
+ const svg = buffer.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
+ if (svg) {
+ const svgObject = await parse(svg[0]);
+ const strokeData: [InkData, string, string][] = [];
+ const tl = { X: Number.MAX_SAFE_INTEGER, Y: Number.MAX_SAFE_INTEGER };
+ let last: PointData = { X: 0, Y: 0 };
+ const processStroke = (child: INode) => {
+ child.attributes.d
+ .split(/[\n]?M/)
+ .slice(1)
+ .map((d, ind) => {
+ const convertedBezier: InkData = SVGToBezier(child.name as SVGType, { ...child, d: '\nM' + d } as unknown as Record<string, string>, last);
+ last = convertedBezier.lastElement();
+ convertedBezier.forEach(point => {
+ if (point.X < tl.X) tl.X = point.X;
+ if (point.Y < tl.Y) tl.Y = point.Y;
+ });
+ strokeData.push([convertedBezier, child.attributes.stroke || 'black', ind === 0 ? child.attributes.fill : child.attributes.fill === 'none' ? child.attributes.fill : DashColor(child.attributes.fill).negate().toString()]);
+ });
+ };
+ const processNode = (parent: INode) => {
+ if (parent.children.length) parent.children.forEach(processNode);
+ else if (parent.type !== 'text') processStroke(parent);
+ };
+ processNode(svgObject);
+
+ const mapStroke = (pd: PointData): PointData => ({ X: startPoint.X + (pd.X - tl.X) * scale, Y: startPoint.Y + (pd.Y - tl.Y) * scale });
+
+ return SmartDrawHandler.CreateDrawingDoc(
+ strokeData.map(sdata => [sdata[0].map(mapStroke), sdata[1], sdata[2]] as [PointData[], string, string]),
+ { autoColor: true },
+ '',
+ undefined
+ );
+ }
+ }
+
export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) {
const generatedDocuments: Doc[] = [];
// Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid.
- Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => {
+ return Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => {
const {
source: { newFilename, mimetype },
result,
@@ -748,7 +852,9 @@ export namespace DocUtils {
overwriteDoc.loadingError = result.message;
Doc.removeCurrentlyLoading(overwriteDoc);
}
- } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc);
+ return undefined;
+ }
+ return newFilename && mimetype ? processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc) : undefined;
});
}