aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/DocUtils.ts7
-rw-r--r--src/client/util/CalendarManager.tsx11
-rw-r--r--src/client/util/CurrentUserUtils.ts13
-rw-r--r--src/client/util/DictationManager.ts6
-rw-r--r--src/client/util/DocumentManager.ts11
-rw-r--r--src/client/util/DragManager.ts5
-rw-r--r--src/client/util/History.ts2
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts14
-rw-r--r--src/client/util/LinkManager.ts6
-rw-r--r--src/client/util/RTFMarkup.tsx4
-rw-r--r--src/client/util/SharingManager.tsx2
-rw-r--r--src/client/views/DashboardView.tsx26
-rw-r--r--src/client/views/DocComponent.tsx88
-rw-r--r--src/client/views/DocumentDecorations.tsx6
-rw-r--r--src/client/views/FieldsDropdown.tsx4
-rw-r--r--src/client/views/FilterPanel.tsx7
-rw-r--r--src/client/views/GlobalKeyHandler.ts14
-rw-r--r--src/client/views/InkStrokeProperties.ts3
-rw-r--r--src/client/views/InkTranscription.tsx23
-rw-r--r--src/client/views/LightboxView.tsx2
-rw-r--r--src/client/views/MainView.tsx3
-rw-r--r--src/client/views/MarqueeAnnotator.tsx22
-rw-r--r--src/client/views/PinFuncs.ts5
-rw-r--r--src/client/views/PropertiesButtons.tsx8
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx50
-rw-r--r--src/client/views/ScriptBox.tsx6
-rw-r--r--src/client/views/SidebarAnnos.tsx31
-rw-r--r--src/client/views/StyleProviderQuiz.tsx91
-rw-r--r--src/client/views/TagsView.tsx33
-rw-r--r--src/client/views/TemplateMenu.tsx1
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx56
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx19
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx11
-rw-r--r--src/client/views/collections/CollectionMenu.tsx9
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx19
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx20
-rw-r--r--src/client/views/collections/CollectionPivotView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx20
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx28
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx28
-rw-r--r--src/client/views/collections/CollectionSubView.tsx10
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx4
-rw-r--r--src/client/views/collections/TabDocView.tsx30
-rw-r--r--src/client/views/collections/TreeView.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx77
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx21
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss12
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx6
-rw-r--r--src/client/views/collections/collectionSchema/SchemaCellField.tsx62
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx63
-rw-r--r--src/client/views/global/globalScripts.ts49
-rw-r--r--src/client/views/nodes/AudioBox.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx2
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx26
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx30
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx471
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx8
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx16
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx15
-rw-r--r--src/client/views/nodes/DiagramBox.tsx5
-rw-r--r--src/client/views/nodes/DocumentView.tsx44
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/ImageBox.tsx50
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx36
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx18
-rw-r--r--src/client/views/nodes/LabelBox.tsx10
-rw-r--r--src/client/views/nodes/LinkBox.tsx6
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.tsx12
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts2
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx45
-rw-r--r--src/client/views/nodes/MapBox/MapBox.scss8
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx178
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx5
-rw-r--r--src/client/views/nodes/PDFBox.tsx22
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx3
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.tsx48
-rw-r--r--src/client/views/nodes/WebBox.tsx2
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.tsx15
-rw-r--r--src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx6
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss9
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx71
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx118
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx24
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts208
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts42
-rw-r--r--src/client/views/nodes/formattedText/SummaryView.tsx46
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts4
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts5
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx9
-rw-r--r--src/client/views/nodes/imageEditor/imageMeshTool/ImageMeshTool.ts0
-rw-r--r--src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.scss24
-rw-r--r--src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.tsx109
-rw-r--r--src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.scss21
-rw-r--r--src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.tsx90
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx20
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx11
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx77
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.scss16
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx160
-rw-r--r--src/client/views/pdf/PDFViewer.scss4
-rw-r--r--src/client/views/pdf/PDFViewer.tsx65
-rw-r--r--src/client/views/search/FaceRecognitionHandler.tsx21
-rw-r--r--src/client/views/search/SearchBox.tsx29
-rw-r--r--src/client/views/smartdraw/DrawingFillHandler.tsx6
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx21
-rw-r--r--src/client/views/smartdraw/StickerPalette.tsx40
-rw-r--r--src/fields/DateField.ts4
-rw-r--r--src/fields/Doc.ts37
-rw-r--r--src/fields/RichTextField.ts2
-rw-r--r--src/fields/RichTextUtils.ts3
-rw-r--r--src/fields/ScriptField.ts3
-rw-r--r--src/fields/Types.ts5
-rw-r--r--src/fields/util.ts10
-rw-r--r--src/server/ApiManagers/FlashcardManager.ts161
-rw-r--r--src/server/DashSession/DashSessionAgent.ts4
-rw-r--r--src/server/index.ts2
-rw-r--r--src/typings/index.d.ts1
128 files changed, 1956 insertions, 1739 deletions
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 84e2db989..0bb6c0ce9 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -8,7 +8,6 @@ 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 { InkData, InkDataFieldName, InkField } from '../../fields/InkField';
import { List, ListFieldName } from '../../fields/List';
@@ -383,10 +382,10 @@ export namespace DocUtils {
event: undoable(() => {
const newDoc = DocUtils.copyDragFactory(dragDoc);
if (newDoc) {
- newDoc.author = ClientUtils.CurrentUserEmail();
+ newDoc._author = ClientUtils.CurrentUserEmail();
newDoc.x = x;
newDoc.y = y;
- newDoc[DocData].backgroundColor = Doc.UserDoc().textBackgroundColor;
+ newDoc.$backgroundColor = Doc.UserDoc().textBackgroundColor;
DocumentView.SetSelectOnLoad(newDoc);
if (pivotField) {
newDoc[pivotField] = pivotValue;
@@ -772,7 +771,7 @@ export namespace DocUtils {
if (defaultTextTemplate) {
tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template
- tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
+ tbox.$proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
}
return tbox;
}
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index d28b3a2c9..1a5a5f889 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -8,7 +8,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import Select from 'react-select';
import { Doc, DocListCast } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { StrCast } from '../../fields/Types';
import { Docs } from '../documents/Documents';
import { MainViewModal } from '../views/MainViewModal';
@@ -51,8 +50,6 @@ export class CalendarManager extends ObservableReactComponent<object> {
@observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the target doc
@observable private dialogueBoxOpacity = 1; // for the modal
- @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-
@observable private creationType: CreationType = 'new-calendar';
@observable private existingCalendars: Doc[] = DocListCast(Doc.MyCalendars?.data);
@@ -97,7 +94,6 @@ export class CalendarManager extends ObservableReactComponent<object> {
}),
500
);
- this.layoutDocAcls = false;
});
constructor(props: object) {
@@ -122,9 +118,8 @@ export class CalendarManager extends ObservableReactComponent<object> {
// TODO: Make undoable
private addToCalendar = () => {
const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document);
- const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; // doc to add to calendar
+ const targetDoc = docs[0];
- console.log(targetDoc);
if (targetDoc) {
let calendar: Doc;
if (this.creationType === 'new-calendar') {
@@ -234,7 +229,7 @@ export class CalendarManager extends ObservableReactComponent<object> {
@computed
get calendarInterface() {
const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document);
- const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+ const targetDoc = docs[0];
return (
<div
@@ -324,7 +319,7 @@ export class CalendarManager extends ObservableReactComponent<object> {
<div className="date-range-picker-container">
<div>Select a date range: </div>
<Provider theme={defaultTheme}>
- <DateRangePicker aria-label="Select a date range" value={this.selectedDateRange} onChange={v => this.setSelectedDateRange(v)} />
+ <DateRangePicker aria-label="Select a date range" value={this.selectedDateRange} onChange={v => v && this.setSelectedDateRange(v)} />
</Provider>
</div>
{this.createButtonActive && (
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 21e1d3e12..537c703b4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -3,7 +3,6 @@ import { reaction, runInAction } from "mobx";
import * as rp from 'request-promise';
import { ClientUtils, OmitKeys } from "../../ClientUtils";
import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc";
-import { DocData } from "../../fields/DocSymbols";
import { InkEraserTool, InkInkTool, InkProperty, InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
@@ -323,9 +322,9 @@ export class CurrentUserUtils {
type: 'scatter'
}`);
const slide = Docs.Create.TextDocument("", opts);
- slide[DocData].text = rtfield;
- slide[DocData].layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
- slide[DocData]._type_collection = CollectionViewType.Freeform;
+ slide.$text = rtfield;
+ slide.$layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
+ slide.$type_collection = CollectionViewType.Freeform;
slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"});
return slide;
}
@@ -374,9 +373,9 @@ pie title Minerals in my tap water
"Potassium" : 50
"Magnesium" : 10.01`);
const slide = Docs.Create.TextDocument("", opts);
- slide[DocData].text = rtfield;
- slide[DocData].layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
- slide[DocData]._type_collection = CollectionViewType.Freeform;
+ slide.$text = rtfield;
+ slide.$layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
+ slide.$_type_collection = CollectionViewType.Freeform;
slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"});
return slide;
}
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 897366757..44fbda319 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -2,7 +2,6 @@
import * as interpreter from 'words-to-numbers';
import { ClientUtils } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
import { listSpec } from '../../fields/Schema';
@@ -339,13 +338,12 @@ export namespace DictationManager {
{
action: (target: DocumentView) => {
const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true });
- const proto = newBox[DocData];
const prompt = 'Press alt + r to start dictating here...';
const head = 3;
const anchor = head + prompt.length;
const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
- proto.data = new RichTextField(proseMirrorState, prompt);
- proto.backgroundColor = '#eeffff';
+ newBox.$data = new RichTextField(proseMirrorState, prompt);
+ newBox.$backgroundColor = '#eeffff';
target.props.addDocTab(newBox, OpenWhere.addRight);
},
},
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index e33449782..5ce005811 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -2,7 +2,6 @@ import { Howl } from 'howler';
import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx';
import { Doc, Opt } from '../../fields/Doc';
import { Animation, DocData } from '../../fields/DocSymbols';
-import { Id } from '../../fields/FieldSymbols';
import { listSpec } from '../../fields/Schema';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { AudioField } from '../../fields/URLField';
@@ -108,16 +107,16 @@ export class DocumentManager {
});
// gets all views
- public getDocumentViewsById(id: string) {
+ public getAllDocumentViews(doc: Doc) {
const toReturn: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.forEach(view => {
- if (view.Document[Id] === id) {
+ if (view.Document === doc) {
toReturn.push(view);
}
});
if (toReturn.length === 0) {
DocumentManager.Instance.DocumentViews.forEach(view => {
- if (view.Document[DocData]?.[Id] === id) {
+ if (view.Document[DocData] === doc) {
toReturn.push(view);
}
});
@@ -125,10 +124,6 @@ export class DocumentManager {
return toReturn;
}
- public getAllDocumentViews(doc: Doc) {
- return this.getDocumentViewsById(doc[Id]);
- }
-
public getDocumentView(target: Doc | undefined, preferredCollection?: DocumentView): DocumentView | undefined {
const docViewArray = DocumentManager.Instance.DocumentViews;
const passes = !target ? [] : preferredCollection ? [preferredCollection, undefined] : [undefined];
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2a7859f09..e2e4c0fe4 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -280,11 +280,12 @@ export namespace DragManager {
export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: FieldType }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) {
const finishDrag = (e: DragCompleteEvent) => {
const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) });
+ const bdData = bd[DocData];
params.forEach(p => {
- Object.keys(vars).indexOf(p) !== -1 && (bd[DocData][p] = new PrefetchProxy(vars[p] as Doc));
+ Object.keys(vars).indexOf(p) !== -1 && (bdData[p] = new PrefetchProxy(vars[p] as Doc));
}); // copy all "captured" arguments into document parameterfields
initialize?.(bd);
- bd[DocData]['onClick-paramFieldKeys'] = new List<string>(params);
+ bd.$onClick_paramFieldKeys = new List<string>(params);
e.docDragData && (e.docDragData.droppedDocuments = [bd]);
return e;
};
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 9728e3177..0df0ec337 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -94,7 +94,7 @@ export namespace HistoryUtil {
}
if (Array.isArray(value)) {
} else if (parser === true || parser === 'json') {
- value = JSON.parse(value);
+ value = value === 'undefined' ? undefined : JSON.parse(value);
} else if (parser === 'none') {
} else {
value = parser(value);
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 43807397f..9c32ca25a 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,6 +1,5 @@
import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
@@ -16,13 +15,12 @@ export namespace ImageUtils {
export const AssignImgInfo = (document: Doc, data?: Upload.InspectionResults) => {
if (data) {
data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth);
- const proto = document[DocData];
- const field = Doc.LayoutFieldKey(document);
- proto[`${field}_nativeWidth`] = data.nativeWidth;
- proto[`${field}_nativeHeight`] = data.nativeHeight;
- proto[`${field}_path`] = data.source;
- proto[`${field}_exif`] = JSON.stringify(data.exifData.data);
- proto[`${field}_contentSize`] = data.contentSize ? data.contentSize : undefined;
+ const field = '$' + Doc.LayoutFieldKey(document);
+ document[`${field}_nativeWidth`] = data.nativeWidth;
+ document[`${field}_nativeHeight`] = data.nativeHeight;
+ document[`${field}_path`] = data.source;
+ document[`${field}_exif`] = JSON.stringify(data.exifData.data);
+ document[`${field}_contentSize`] = data.contentSize ? data.contentSize : undefined;
}
return document;
};
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index d04d41968..3f98ab3c4 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -156,7 +156,7 @@ export class LinkManager {
}
public deleteLink(linkDoc: Doc) {
const ret = Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
- linkDoc[DocData].link_anchor_1 = linkDoc[DocData].link_anchor_2 = undefined;
+ linkDoc.$link_anchor_1 = linkDoc.$link_anchor_2 = undefined;
return ret;
}
public deleteAllLinksOnAnchor(anchor: Doc) {
@@ -258,8 +258,8 @@ export function UPDATE_SERVER_CACHE() {
// print out cached docs
//Doc.MyDockedBtns.linearView_IsOpen && console.log('Set cached docs = ');
- const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
- const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
+ // const isFiltered = filtered.filter(doc => !Doc.IsSystem(doc));
+ // const strings = isFiltered.map(doc => StrCast(doc.title) + ' ' + (Doc.IsDataProto(doc) ? '(data)' : '(embedding)'));
//Doc.MyDockedBtns.linearView_IsOpen && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
rp.post(ClientUtils.prepend('/setCacheDocumentIds'), {
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index a01b64eda..23ef9bba0 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -115,11 +115,11 @@ export class RTFMarkup extends React.Component<object> {
{` display value of fieldname of text document (unless (doctitle.) is used to indicate another document by it's title)`}
</p>
<p>
- <b style={{ fontSize: 'larger' }}>{`[@fieldname:value] `}</b>
+ <b style={{ fontSize: 'larger' }}>{`@fieldname:value `}</b>
{` assign value to fieldname to data document and display it (if '=' is used instead of ':' the value is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the response will replace the value)`}
</p>
<p>
- <b style={{ fontSize: 'larger' }}>{`[@fieldname:=expression] `}</b>
+ <b style={{ fontSize: 'larger' }}>{`@fieldname:=expression `}</b>
{` assign a computed expression to fieldname to data document and display it (if '=:=' is used instead of ':=' the expression is set on the layout Doc. if value is wrapped in (()) then it will be sent to ChatGPT and the prompt/response will replace the value)`}
</p>
</div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3a248400b..962f51cd4 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -633,7 +633,7 @@ export class SharingManager extends React.Component<object> {
private focusOn = (contents: string) => {
const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
- const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.props.Document) : [this.targetDoc];
+ const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.Document) : [this.targetDoc];
return (
<span
className="focus-span"
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 7f0118ed3..f61f6db18 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -6,7 +6,7 @@ import * as React from 'react';
import { FaPlus } from 'react-icons/fa';
import { ClientUtils } from '../../ClientUtils';
import { Doc, DocListCast } from '../../fields/Doc';
-import { AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclPrivate, DocAcl } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
@@ -113,7 +113,7 @@ export class DashboardView extends ObservableReactComponent<object> {
getDashboards = (whichGroup: DashboardGroup) => {
if (whichGroup === DashboardGroup.MyDashboards) {
- return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard[DocData].author === ClientUtils.CurrentUserEmail());
+ return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard.$author === ClientUtils.CurrentUserEmail());
}
return DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc.dockingConfig);
};
@@ -226,7 +226,7 @@ export class DashboardView extends ObservableReactComponent<object> {
color={color}
val={StrCast(dashboard.title)}
setVal={val => {
- dashboard[DocData].title = val;
+ dashboard.$title = val;
}}
/>
{this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div />}
@@ -403,8 +403,8 @@ export class DashboardView extends ObservableReactComponent<object> {
},
],
};
- if (dashboard.dockingConfig && dashboard.dockingConfig !== dashboard[DocData].dockingConfig) dashboard.dockingConfig = JSON.stringify(reset);
- else Doc.SetInPlace(dashboard, 'dockingConfig', JSON.stringify(reset), true);
+ const dockingOnLayout = dashboard._dockingConfig && dashboard._dockingConfig !== dashboard.$dockingConfig;
+ dashboard[`${dockingOnLayout ? '_' : '$'}dockingConfig`] = JSON.stringify(reset);
return reset;
};
@@ -427,14 +427,14 @@ export class DashboardView extends ObservableReactComponent<object> {
Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true);
Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
- dashboardDoc.pane_count = 1;
- freeformDoc.embedContainer = dashboardDoc;
- dashboardDoc.myOverlayDocs = new List<Doc>();
- dashboardDoc[DocData].myPublishedDocs = new List<Doc>();
- dashboardDoc[DocData].myTagCollections = new List<Doc>();
- dashboardDoc[DocData].myUniqueFaces = new List<Doc>();
- dashboardDoc[DocData].myTrails = DashboardView.SetupDashboardTrails();
- dashboardDoc[DocData].myCalendars = DashboardView.SetupDashboardCalendars();
+ freeformDoc._embedContainer = dashboardDoc;
+ dashboardDoc.$myPaneCount = 1;
+ dashboardDoc.$myOverlayDocs = new List<Doc>();
+ dashboardDoc.$myPublishedDocs = new List<Doc>();
+ dashboardDoc.$myTagCollections = new List<Doc>();
+ dashboardDoc.$myUniqueFaces = new List<Doc>();
+ dashboardDoc.$myTrails = DashboardView.SetupDashboardTrails();
+ dashboardDoc.$myCalendars = DashboardView.SetupDashboardCalendars();
// open this new dashboard
Doc.ActiveDashboard = dashboardDoc;
Doc.ActivePage = 'dashboard';
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index e351e2dec..8f254ddcd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -5,7 +5,7 @@ import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { toList } from '../../fields/Types';
+import { DocCast, toList } from '../../fields/Types';
import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
import { DocumentType } from '../documents/DocumentTypes';
import { ObservableReactComponent } from './ObservableReactComponent';
@@ -30,26 +30,40 @@ export function DocComponent<P extends DocComponentProps>() {
}
/**
- * This is the document being rendered. In the case of a compound template, it
- * may not be the actual document rendered and it also may not be the 'real' root document.
- * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+ * This is the doc that is being rendered. It will be either:
+ * 1) the same as Document if the root of a regular or compound Doc is rendered
+ * 2) the same as the layoutDoc if a component of a compound Doc is rendered.
+ * NOTE: it is very unlikely that you really want to use this method. Instead
+ * consider: Document, layoutDoc, dataDoc
*/
- get Document() {
+ get _renderDoc() {
return this._props.Document;
}
/**
- * This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
+ * This is the "root" Doc being rendered. In the case of a compound template Doc,
+ * this is the outermost Doc that represents the entire compound Doc. It is not
+ * necessarily the Doc being rendered in the current React component.
+ * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+ */
+ get Document() {
+ return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+ }
+ /**
+ * This is the document being rendered by the React component. In the
+ * case of a compound template, this will be the expanded template Doc
+ * that represents the component of the compound Doc being rendered.
+ * This may or may not inherit from the data doc.
*/
@computed get layoutDoc() {
- return this._props.LayoutTemplateString ? this.Document : Doc.Layout(this.Document, this._props.LayoutTemplate?.());
+ return this._props.LayoutTemplateString ? this._renderDoc : Doc.Layout(this._renderDoc, this._props.LayoutTemplate?.());
}
/**
- * This is the unique data repository for a dcoument that stores the intrinsic document data
+ * This is the unique data repository for a document that stores the intrinsic document data.
*/
@computed get dataDoc() {
- return this.Document[DocData];
+ return this._renderDoc[DocData];
}
}
return Component;
@@ -75,25 +89,40 @@ export function ViewBoxBaseComponent<P extends FieldViewProps>() {
}
/**
- * This is the document being rendered. In the case of a compound template, it
- * may not be the actual document rendered and it also may not be the 'real' root document.
- * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+ * This is the doc that is being rendered. It will be either:
+ * 1) the same as Document if the root of a regular or compound Doc is rendered
+ * 2) the same as the layoutDoc if a component of a compound Doc is rendered.
+ * NOTE: it is very unlikely that you really want to use this method. Instead
+ * consider: Document, layoutDoc, dataDoc
*/
- get Document() {
+ get _renderDoc() {
return this._props.Document;
}
+
/**
- * This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
+ * This is the "root" Doc being rendered. In the case of a compound template Doc,
+ * this is the outermost Doc that represents the entire compound Doc. It is not
+ * necessarily the Doc being rendered in the current React component.
+ * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+ */
+ get Document() {
+ return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+ }
+ /**
+ * This is the document being rendered by the React component. In the
+ * case of a compound template, this will be the expanded template Doc
+ * that represents the component of the compound Doc being rendered.
+ * This may or may not inherit from the data doc.
*/
@computed get layoutDoc() {
- return Doc.Layout(this.Document);
+ return Doc.Layout(this._renderDoc);
}
/**
* This is the unique data repository for a dcoument that stores the intrinsic document data
*/
@computed get dataDoc() {
- return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData];
+ return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : this._renderDoc[DocData];
}
/**
@@ -133,25 +162,37 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
}
/**
- * This is the document being rendered. In the case of a compound template, it
- * may not be the actual document rendered and it also may not be the 'real' root document.
- * Rather, it specifies the shared properties of all layouts of the document (eg, x,y,)
+ * This is the doc that is being rendered. It will be either:
+ * 1) the same as Document if the root of a regular or compound Doc is rendered
+ * 2) the same as the layoutDoc if a component of a compound Doc is rendered.
+ * NOTE: it would unlikely that you really want to use this instead of the
+ * other Doc options (Document, layoutDoc, dataDoc)
*/
- @computed get Document() {
+ get _renderDoc() {
return this._props.Document;
}
+
+ /**
+ * This is the "root" Doc being rendered. In the case of a compound template Doc,
+ * this is the outermost Doc that represents the entire compound Doc. It is not
+ * necessarily the Doc being rendered in the current React component.
+ * This Doc inherits from the dataDoc, and may or may not inherit (or be) the layoutDoc.
+ */
+ @computed get Document() {
+ return DocCast(this._renderDoc.rootDocument, this._renderDoc);
+ }
/**
* This is the document being rendered. It may be a template so it may or may no inherit from the data doc.
*/
@computed get layoutDoc() {
- return Doc.Layout(this.Document);
+ return Doc.Layout(this._renderDoc);
}
/**
* This is the unique data repository for a dcoument that stores the intrinsic document data
*/
@computed get dataDoc() {
- return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData];
+ return this._renderDoc.isTemplateForField || this._renderDoc.isTemplateDoc ? (this._props.TemplateDataDocument ?? this._renderDoc[DocData]) : this._props.Document[DocData];
}
/**
@@ -225,8 +266,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
added.forEach(adoc => {
adoc._dragOnlyWithinContainer = undefined;
- if (annotationKey ?? this._annotationKeySuffix()) adoc[DocData].annotationOn = this.Document;
- else adoc[DocData].annotationOn = undefined;
+ adoc.$annotationOn = (annotationKey ?? this._annotationKeySuffix()) ? this.Document : undefined;
Doc.SetContainer(adoc, this.Document);
inheritParentAcls(targetDataDoc, adoc, true);
});
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 54e050f9f..19b987cb5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -127,7 +127,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
} else if (this._titleControlString.startsWith('$')) {
if (this._accumulatedTitle.startsWith('-->#')) {
DocumentView.SelectedDocs().forEach(doc => {
- doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`);
+ doc.$onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`);
});
}
const titleFieldKey = this._titleControlString.substring(1);
@@ -621,7 +621,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
this._inkDragDocs
.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
.forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
- doc[DocData].data = new InkField(inkPts.map(
+ doc.$data = new InkField(inkPts.map(
(ipt) => ({// (new x — oldx) + newWidth * (oldxpoint /oldWidth)
X: NumCast(doc.x) - x + (NumCast(doc.width) * ipt.X) / width,
Y: NumCast(doc.y) - y + (NumCast(doc.height) * ipt.Y) / height,
@@ -635,7 +635,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
if (DocumentView.Selected().length === 1) {
const selected = DocumentView.Selected()[0];
if (this._titleControlString.startsWith('$')) {
- return Field.toJavascriptString(selected.Document[this._titleControlString.substring(1)] as FieldType) || '-unset-';
+ return Field.toJavascriptString(selected._renderDoc[this._titleControlString.substring(1)] as FieldType) || '-unset-';
}
return this._accumulatedTitle;
}
diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx
index 407031b40..e7ab6a180 100644
--- a/src/client/views/FieldsDropdown.tsx
+++ b/src/client/views/FieldsDropdown.tsx
@@ -18,7 +18,7 @@ import './FilterPanel.scss';
import { ObservableReactComponent } from './ObservableReactComponent';
interface fieldsDropdownProps {
- Document: Doc; // show fields for this Doc if set, otherwise for all docs in dashboard
+ Doc: Doc; // show fields for this Doc if set, otherwise for all docs in dashboard
selectFunc: (value: string) => void;
menuClose?: () => void;
placeholder?: string | (() => string);
@@ -36,7 +36,7 @@ export class FieldsDropdown extends ObservableReactComponent<fieldsDropdownProps
@computed get allDescendantDocs() {
const allDocs = new Set<Doc>();
- SearchUtil.foreachRecursiveDoc([this._props.Document], (depth, doc) => allDocs.add(doc));
+ SearchUtil.foreachRecursiveDoc([this._props.Doc], (depth, doc) => allDocs.add(doc));
return Array.from(allDocs);
}
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 99738052d..c3b3f9d0c 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -9,7 +9,6 @@ import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai';
import { CiCircleRemove } from 'react-icons/ci';
import { Doc, DocListCast, Field, FieldType, LinkedTo, StrListCast } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
@@ -89,7 +88,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
key={icon.toString()}
onClick={undoable(e => {
e.stopPropagation;
- hotKey[DocData].icon = icon.toString();
+ hotKey.$icon = icon.toString();
}, '')}
className="icon-panel-button">
<FontAwesomeIcon icon={icon} color={SnappingManager.userColor} />
@@ -380,7 +379,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
<div className="filterBox-treeView">
<div className="filterBox-select">
<div style={{ width: '100%' }}>
- <FieldsDropdown Document={this.Document} selectFunc={this.facetClick} showPlaceholder placeholder="add a filter" addedFields={['acl_Guest', LinkedTo, 'Star', 'Heart', 'Bolt', 'Cloud']} />
+ <FieldsDropdown Doc={this.Document} selectFunc={this.facetClick} showPlaceholder placeholder="add a filter" addedFields={['acl_Guest', LinkedTo, 'Star', 'Heart', 'Bolt', 'Cloud']} />
</div>
{/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
{/* <div className="filterBox-select-bool">
@@ -444,7 +443,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
<div>
<div className="filterBox-select">
<div style={{ width: '100%' }}>
- <FieldsDropdown Document={this.Document} selectFunc={this._props.addHotKey} showPlaceholder placeholder="add a hotkey" addedFields={['acl_Guest', LinkedTo]} />
+ <FieldsDropdown Doc={this.Document} selectFunc={this._props.addHotKey} showPlaceholder placeholder="add a hotkey" addedFields={['acl_Guest', LinkedTo]} />
</div>
</div>
</div>
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 2d342d1b1..37060d20c 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -109,20 +109,6 @@ export class KeyManager {
preventDefault: false,
};
switch (keyname) {
- case 'u':
- if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- const ungroupings = DocumentView.Selected();
- undoable(() => () => ungroupings.forEach(dv => { dv.layoutDoc.group = undefined; }), 'ungroup');
- DocumentView.DeselectAll();
- }
- break;
- case 'g':
- if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- const selected = DocumentView.Selected();
- const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView);
- cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, DocumentView.SelectedDocs()), 'grouping');
- }
- break;
case ' ':
// MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
break;
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 358274f0e..6854476e2 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -3,7 +3,6 @@ import * as fitCurve from 'fit-curve';
import * as _ from 'lodash';
import { action, makeObservable, observable, reaction, runInAction } from 'mobx';
import { Doc, NumListCast, Opt } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { InkData, InkField, InkTool } from '../../fields/InkField';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
@@ -509,7 +508,7 @@ export class InkStrokeProperties {
const inkStroke = inkView?.ComponentView as InkingStroke;
const polylinePoints = this.sampleBezier(inkStroke?.inkScaledData().inkData ?? [])?.map(pt => [pt.x, pt.y]);
if (polylinePoints) {
- inkDoc[DocData].stroke = new InkField(
+ inkDoc.$stroke = new InkField(
fitCurve.default(polylinePoints, tolerance)
.reduce((cpts, bez) =>
({n: cpts.push(...bez.map(cpt => ({X:cpt[0], Y:cpt[1]}))), cpts}).cpts,
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index e800e0ae3..f91f98807 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -48,7 +48,7 @@ export class InkTranscription extends React.Component {
const options = {
configuration: {
server: {
- scheme: 'https',
+ scheme: 'https' as iink.TScheme,
host: 'cloud.myscript.com',
applicationKey: 'c0901093-5ac5-4454-8e64-0def0f13f2ca',
hmacKey: 'f6465cca-1856-4492-a6a4-e2395841be2f',
@@ -56,13 +56,14 @@ export class InkTranscription extends React.Component {
},
recognition: {
type: 'TEXT',
+ lang: 'en_US',
+ text: {
+ mimeTypes: ['application/vnd.myscript.jiix'] as 'application/vnd.myscript.jiix'[],
+ },
},
},
};
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const editor = new iink.Editor(r, options as any);
-
- await editor.initialize();
+ await iink.Editor.load(r, 'INKV2', options);
this._textRegister = r;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -78,7 +79,7 @@ export class InkTranscription extends React.Component {
const options = {
configuration: {
server: {
- scheme: 'https',
+ scheme: 'https' as iink.TScheme,
host: 'cloud.myscript.com',
applicationKey: 'c0901093-5ac5-4454-8e64-0def0f13f2ca',
hmacKey: 'f6465cca-1856-4492-a6a4-e2395841be2f',
@@ -86,14 +87,14 @@ export class InkTranscription extends React.Component {
},
recognition: {
type: 'TEXT',
+ lang: 'en_US',
+ text: {
+ mimeTypes: ['application/vnd.myscript.jiix'] as 'application/vnd.myscript.jiix'[],
+ },
},
},
};
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const editor = new iink.Editor(r, options as any);
-
- await editor.initialize();
- this.iinkEditor = editor;
+ this.iinkEditor = await iink.Editor.load(r, 'INKV2', options);
this._textRegister = r;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
r?.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 5698da785..de2c7cd09 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -317,7 +317,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
</GestureOverlay>
</div>
- {this._showPalette && <StickerPalette ref={r => (this._annoPaletteView = r)} Document={DocCast(Doc.UserDoc().myLightboxDrawings)} />}
+ {this._showPalette && <StickerPalette ref={r => (this._annoPaletteView = r)} Doc={DocCast(Doc.UserDoc().myLightboxDrawings)} />}
{this.renderNavBtn(0, undefined, this._props.PanelHeight / 2 - 12.5, 'chevron-left', this._doc && this._history.length ? true : false, this.previous)}
{this.renderNavBtn(
this._props.PanelWidth - Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]),
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ef8d0c197..e70f9e5ed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -11,7 +11,6 @@ import '@dash/components/src/global/globalCssVariables.scss';
import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { DocCast, StrCast, toList } from '../../fields/Types';
import { DocServer } from '../DocServer';
@@ -849,7 +848,7 @@ export class MainView extends ObservableReactComponent<object> {
@action
selectMenu = (button: Doc) => {
- const title = StrCast(button[DocData].title);
+ const title = StrCast(button.$title);
const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
this.closeFlyout();
if (willOpen) {
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 3f4200dce..e4811a902 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -97,7 +97,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
presentation_zoomText: true,
title: '>' + this.props.Document.title,
});
- const textRegionAnnoProto = textRegionAnno[DocData];
let minX = Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = Number.MAX_VALUE;
@@ -118,15 +117,15 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
})
);
- textRegionAnnoProto.y = Math.max(minY, 0);
- textRegionAnnoProto.x = Math.max(minX, 0);
- textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- textRegionAnnoProto.backgroundColor = color;
+ textRegionAnno.$y = Math.max(minY, 0);
+ textRegionAnno.$x = Math.max(minX, 0);
+ textRegionAnno.$height = Math.max(maxY, 0) - Math.max(minY, 0);
+ textRegionAnno.$width = Math.max(maxX, 0) - Math.max(minX, 0);
+ textRegionAnno.$backgroundColor = color;
// mainAnnoDocProto.text = this._selectionText;
- textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- textRegionAnnoProto.opacity = 0;
- textRegionAnnoProto.layout_unrendered = true;
+ textRegionAnno.$text_inlineAnnotations = new List<string>(annoRects);
+ textRegionAnno.$opacity = 0;
+ textRegionAnno.$layout_unrendered = true;
savedAnnoMap.clear();
return textRegionAnno;
};
@@ -226,9 +225,8 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
dragComplete: dragEx => {
if (!dragEx.aborted && dragEx.linkDocument) {
- const linkDocData = dragEx.linkDocument[DocData];
- linkDocData.link_relationship = 'cropped image';
- linkDocData.title = 'crop: ' + this.props.Document.title;
+ dragEx.linkDocument.$link_relationship = 'cropped image';
+ dragEx.linkDocument.$title = 'crop: ' + this.props.Document.title;
}
},
});
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index ab02c2d07..d756830da 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -1,5 +1,4 @@
import { Doc, DocListCast, Field } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { Copy, Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { ObjectField } from '../../fields/ObjectField';
@@ -77,8 +76,8 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
pinDoc.config_data = Field.Copy(targetDoc[fkey]);
}
if (pinProps.pinData.dataannos) {
- const fieldKey = Doc.LayoutFieldKey(targetDoc);
- pinDoc.config_annotations = new List<Doc>(DocListCast(targetDoc[DocData][fieldKey + '_annotations']).filter(doc => !doc.layout_unrendered));
+ const fieldKey = '$' + Doc.LayoutFieldKey(targetDoc) + +'_annotations';
+ pinDoc.config_annotations = new List<Doc>(DocListCast(targetDoc[fieldKey]).filter(doc => !doc.layout_unrendered));
}
if (pinProps.pinData.inkable) {
pinDoc.config_fillColor = targetDoc.fillColor;
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 606fb17ed..a1e8fe7ba 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/no-unused-class-component-methods */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from '@dash/components';
import { action, computed, observable } from 'mobx';
@@ -14,7 +13,6 @@ import { RxWidth } from 'react-icons/rx';
import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb';
import { TfiBarChart } from 'react-icons/tfi';
import { Doc, Opt } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { ScriptField } from '../../fields/ScriptField';
import { BoolCast, ScriptCast, StrCast } from '../../fields/Types';
import { ImageField } from '../../fields/URLField';
@@ -133,9 +131,9 @@ export class PropertiesButtons extends React.Component {
on => `${on ? 'Flashcard enabled' : 'Flashcard disabled'} `,
() => <MdTouchApp />,
(dv, doc) => {
- const on = !!doc.onPaint;
- doc[DocData].onPaint = on ? undefined : ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, { documentView: 'any' });
- doc[DocData].layout_textPainted = on ? undefined : `<ComparisonBox {...props} fieldKey={'${dv?.LayoutFieldKey ?? 'text'}'}/>`;
+ const on = !!doc.$onPaint;
+ doc.$onPaint = on ? undefined : ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, { documentView: 'any' });
+ doc.$layout_textPainted = on ? undefined : `<ComparisonBox {...props} fieldKey={'${dv?.LayoutFieldKey ?? 'text'}'}/>`;
}
);
}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index f494ff16a..ee6486a9c 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -26,7 +26,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
@computed get _docs() {
if (!this._props.DocView) return [];
- const target = this._props.DocView._props.Document;
+ const target = this._props.DocView.Document;
const targetContext = this._props.DocView.containerViewPath?.().lastElement()?.Document;
const embeddings = Doc.GetEmbeddings(target);
const containerProtos = embeddings.filter(embedding => embedding.embedContainer && embedding.embedContainer instanceof Doc).reduce((set, embedding) => set.add(Cast(embedding.embedContainer, Doc, null)), new Set<Doc>());
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 11adf7435..7e9cd002b 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -181,7 +181,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
@action
docHeight = () => {
const layoutDoc = this.selectedLayoutDoc;
- if (layoutDoc && this.dataDoc) {
+ if (layoutDoc) {
return Math.max(
70,
Math.min(
@@ -282,7 +282,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const tags = StrListCast(doc.tags);
if (!tags.includes(value)) {
tags.push(value);
- doc[DocData].tags = tags.length ? new List<string>(tags) : undefined;
+ doc.$tags = tags.length ? new List<string>(tags) : undefined;
}
return true;
}
@@ -308,11 +308,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
}
@computed get contextCount() {
- if (this.selectedDocumentView) {
- const target = this.selectedDocumentView.Document;
- return Doc.GetEmbeddings(target).length - 1;
- }
- return 0;
+ return this.selectedDocumentView ? Doc.GetEmbeddings(this.selectedDocumentView.Document).length - 1 : 0;
}
@computed get links() {
@@ -498,7 +494,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const individualTableEntries = [];
const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
- const seldoc = this.layoutDocAcls ? this.selectedLayoutDoc : this.selectedDoc?.[DocData];
+ const seldoc = this.layoutDocAcls ? this.selectedLayoutDoc : this.dataDoc;
// adds each user to usersAdded
SharingManager.Instance.users.forEach(eachUser => {
let userOnDoc = true;
@@ -777,7 +773,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
switch (field) {
case 'Xps': selDoc.x = NumCast(this.selectedDoc?.x) + (dirs === 'up' ? 10 : -10); break;
case 'Yps': selDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10); break;
- case 'stk': selDoc.stroke_width = NumCast(this.selectedDoc?.[DocData].stroke_width) + (dirs === 'up' ? 0.1 : -0.1); break;
+ case 'stk': selDoc.stroke_width = NumCast(this.selectedDoc?.$stroke_width) + (dirs === 'up' ? 0.1 : -0.1); break;
case 'wid': {
const oldWidth = NumCast(selDoc._width);
const oldHeight = NumCast(selDoc._height);
@@ -825,11 +821,11 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
};
getField(key: string) {
- return Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
+ return Field.toString(this.selectedDoc?.['$' + key] as FieldType);
}
@computed get selectedStrokes() {
- return this.containsInkDoc ? DocListCast(this.selectedDoc[DocData].data) : DocumentView.SelectedSchemaDoc() ? [DocumentView.SelectedSchemaDoc()!] : DocumentView.SelectedDocs().filter(doc => doc.layout_isSvg);
+ return this.containsInkDoc ? DocListCast(this.selectedDoc.$data) : DocumentView.SelectedSchemaDoc() ? [DocumentView.SelectedSchemaDoc()!] : DocumentView.SelectedDocs().filter(doc => doc.layout_isSvg);
}
@computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore
set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Math.round(value * 100) / 100); } // prettier-ignore
@@ -839,10 +835,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
set shapeWid(value) { this.selectedDoc && (this.selectedDoc._width = Math.round(value * 100) / 100); } // prettier-ignore
@computed get shapeHgt() { return NumCast(this.selectedDoc?._height); } // prettier-ignore
set shapeHgt(value) { this.selectedDoc && (this.selectedDoc._height = Math.round(value * 100) / 100); } // prettier-ignore
- @computed get strokeThk(){ return NumCast(this.selectedStrokes.lastElement()?.[DocData].stroke_width); } // prettier-ignore
+ @computed get strokeThk(){ return NumCast(this.selectedStrokes.lastElement()?.$stroke_width); } // prettier-ignore
set strokeThk(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_width = Math.round(value * 100) / 100;
+ doc.$stroke_width = Math.round(value * 100) / 100;
});
}
@@ -881,20 +877,20 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
private _lastDash: string = '2';
- @computed get colorFil() { return StrCast(this.selectedStrokes.lastElement()?.[DocData].fillColor); } // prettier-ignore
+ @computed get colorFil() { return StrCast(this.selectedStrokes.lastElement()?.$fillColor); } // prettier-ignore
set colorFil(value) {
this.selectedStrokes.forEach(doc => {
const inkStroke = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke;
const { inkData } = inkStroke.inkScaledData();
if (InkingStroke.IsClosed(inkData)) {
- doc[DocData].fillColor = value || undefined;
+ doc.$fillColor = value || undefined;
}
});
}
- @computed get colorStk() { return StrCast(this.selectedStrokes.lastElement()?.[DocData].color); } // prettier-ignore
+ @computed get colorStk() { return StrCast(this.selectedStrokes.lastElement()?.$color); } // prettier-ignore
set colorStk(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].color = value || undefined;
+ doc.$color = value || undefined;
});
}
@computed get borderColor() {
@@ -902,7 +898,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const layoutDoc = doc ? Doc.Layout(doc) : doc;
return StrCast(layoutDoc.color);
}
- set borderColor(value) { this.selectedDoc && (this.selectedDoc[DocData].color = value || undefined); } // prettier-ignore
+ set borderColor(value) { this.selectedDoc && (this.selectedDoc.$color = value || undefined); } // prettier-ignore
colorButton(value: string, type: string, setter: () => void) {
return (
@@ -1034,41 +1030,41 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
set dashdStk(value) {
value && (this._lastDash = value as string);
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_dash = value ? this._lastDash : undefined;
+ doc.$stroke_dash = value ? this._lastDash : undefined;
});
}
@computed get widthStk() { return this.getField('stroke_width') || '1'; } // prettier-ignore
set widthStk(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_width = Number(value);
+ doc.$stroke_width = Number(value);
});
}
@computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore
set markScal(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_markerScale = Number(value);
+ doc.$stroke_markerScale = Number(value);
});
}
@computed get refStrength() { return Number(this.getField('drawing_refStrength') || '50'); } // prettier-ignore
set refStrength(value) {
- this.selectedDoc[DocData].drawing_refStrength = Number(value);
+ this.selectedDoc.$drawing_refStrength = Number(value);
}
@computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '5'); } // prettier-ignore
set smoothAmt(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_smoothAmount = Number(value);
+ doc.$stroke_smoothAmount = Number(value);
});
}
@computed get markHead() { return this.getField('stroke_startMarker') || ''; } // prettier-ignore
set markHead(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_startMarker = value;
+ doc.$stroke_startMarker = value;
});
}
@computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore
set markTail(value) {
this.selectedStrokes.forEach(doc => {
- doc[DocData].stroke_endMarker = value;
+ doc.$stroke_endMarker = value;
});
}
@@ -1356,7 +1352,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
* of ink strokes in the properties menu.
*/
containsInk = (selectedDoc: Doc) => {
- const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data);
+ const childDocs: Doc[] = DocListCast(selectedDoc.$data);
for (let i = 0; i < childDocs.length; i++) {
if (DocumentView.getDocumentView(childDocs[i])?.layoutDoc?.layout_isSvg) {
return true;
@@ -1454,7 +1450,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
@undoBatch
setDescripValue = action((value: string) => {
if (this.selectedLink) {
- this.selectedLink[DocData].link_description = value;
+ this.selectedLink.$link_description = value;
}
});
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index d05b0a6b6..52c0227d8 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { emptyFunction } from '../../Utils';
import { Doc, Opt } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { ScriptField } from '../../fields/ScriptField';
import { ScriptCast } from '../../fields/Types';
import { DragManager } from '../util/DragManager';
@@ -101,7 +100,6 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
);
}
// let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0; this.target[0].index = ind; this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
- // eslint-disable-next-line react/sort-comp
public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }, defaultScript?: ScriptField) {
let overlayDisposer: () => void = emptyFunction;
const script = ScriptCast(doc[fieldKey]) || defaultScript;
@@ -119,7 +117,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
onCancel={overlayDisposer}
onSave={(text, onError) => {
if (!text) {
- doc[DocData][fieldKey] = undefined;
+ doc['$' + fieldKey] = undefined;
} else {
const compScript = CompileScript(text, {
params: { this: Doc.name, ...contextParams },
@@ -142,7 +140,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
div.innerHTML = 'button';
params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, () => {}, clientX, clientY);
- doc[DocData][fieldKey] = new ScriptField(compScript);
+ doc['$' + fieldKey] = new ScriptField(compScript);
overlayDisposer();
}
}}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 816fc8ed3..3c0611f03 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -4,7 +4,6 @@ import * as React from 'react';
import { ClientUtils, returnAll, returnFalse, returnOne, returnZero } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
import { Doc, DocListCast, Field, FieldResult, FieldType, StrListCast } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
@@ -23,7 +22,7 @@ import { FieldViewProps } from './nodes/FieldView';
interface ExtraProps {
fieldKey: string;
- Document: Doc;
+ Doc: Doc;
layoutDoc: Doc;
dataDoc: Doc;
// usePanelWidth: boolean;
@@ -46,7 +45,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
_stackRef = React.createRef<CollectionStackingView>();
@computed get allMetadata() {
const keys = new Map<string, FieldResult<FieldType>>();
- DocListCast(this._props.Document[this.sidebarKey]).forEach(doc =>
+ DocListCast(this._props.Doc[this.sidebarKey]).forEach(doc =>
SearchUtil.documentKeys(doc)
.filter(key => key[0] && key[0] !== '_' && key[0] === key[0].toUpperCase())
.map(key => keys.set(key, doc[key]))
@@ -55,7 +54,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
}
@computed get allHashtags() {
const keys = new Set<string>();
- DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
+ DocListCast(this._props.Doc[this.sidebarKey]).forEach(doc => StrListCast(doc.tags).forEach(tag => keys.add(tag)));
return Array.from(keys.keys())
.filter(key => key[0])
.filter(key => !key.startsWith('_') && (key[0] === '#' || key[0] === key[0].toUpperCase()))
@@ -63,7 +62,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
}
@computed get allUsers() {
const keys = new Set<string>();
- DocListCast(this._props.Document[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
+ DocListCast(this._props.Doc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
return Array.from(keys.keys()).sort();
}
@@ -73,7 +72,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
.join(' ');
const target = Docs.Create.TextDocument(startup, {
title: '-note-',
- annotationOn: this._props.Document,
+ annotationOn: this._props.Doc,
_width: 200,
_height: 50,
_layout_fitWidth: true,
@@ -88,9 +87,9 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
.filter(data => data.split(':')[0])
.filter(data => !filterExlusions?.includes(data.split(':')[0]))
.map(data => {
- const key = data.split(':')[0];
+ const key = '$' + data.split(':')[0];
const val = Field.Copy(this.allMetadata.get(key));
- target[DocData][key] = val;
+ target[key] = val;
return {
type: 'dashField',
attrs: { fieldKey: key, docId: '', hideKey: false, hideValue: false, editable: true },
@@ -98,7 +97,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
};
});
- if (!anchor.text) anchor[DocData].text = '-selection-';
+ if (!anchor.text) anchor.$text = '-selection-';
const textLines: { type: string; attrs: object; content?: unknown[] }[] = [
{
type: 'paragraph',
@@ -133,7 +132,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
};
if (taggedContent.length) textLines.push(metadatatext);
if (textLines.length) {
- target[DocData].text = new RichTextField(
+ target.$text = new RichTextField(
JSON.stringify({
doc: {
type: 'doc',
@@ -149,7 +148,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
return target;
};
makeDocUnfiltered = (doc: Doc) => {
- if (DocListCast(this._props.Document[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ if (DocListCast(this._props.Doc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
if (this.childFilters()) {
// if any child filters exist, get rid of them
this._props.layoutDoc._childFilters = new List<string>();
@@ -190,7 +189,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
const renderTag = (tag: string) => {
const active = this.childFilters().includes(`tags${Doc.FilterSep}${tag}${Doc.FilterSep}check`);
return (
- <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
+ <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Doc, 'tags', tag, 'check', true, undefined, e.shiftKey)}>
{tag}
</div>
);
@@ -198,7 +197,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
const renderMeta = (tag: string) => {
const active = this.childFilters().includes(`${tag}${Doc.FilterSep}${Doc.FilterAny}${Doc.FilterSep}exists`);
return (
- <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
+ <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Doc, tag, Doc.FilterAny, 'exists', true, undefined, e.shiftKey)}>
{tag}
</div>
);
@@ -206,7 +205,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
const renderUsers = (user: string) => {
const active = this.childFilters().includes(`author:${user}:check`);
return (
- <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Document, 'author', user, 'check', true, undefined, e.shiftKey)}>
+ <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this._props.Doc, 'author', user, 'check', true, undefined, e.shiftKey)}>
{user}
</div>
);
@@ -217,9 +216,9 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
style={{
position: 'absolute',
pointerEvents: this._props.isContentActive() ? 'all' : undefined,
- top: this._props.Document.type !== DocumentType.RTF && StrCast(this._props.Document._layout_showTitle) === 'title' ? 15 : 0,
+ top: this._props.Doc.type !== DocumentType.RTF && StrCast(this._props.Doc._layout_showTitle) === 'title' ? 15 : 0,
right: 0,
- background: this._props.styleProvider?.(this._props.Document, this._props, StyleProp.WidgetColor) as string,
+ background: this._props.styleProvider?.(this._props.Doc, this._props, StyleProp.WidgetColor) as string,
width: `100%`,
height: '100%',
}}>
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index db9ab831a..b4a1f53c5 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -1,24 +1,25 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import axios from 'axios';
+import { runInAction } from 'mobx';
import * as React from 'react';
import { returnFalse, setupMoveUpEvents } from '../../ClientUtils';
-import { emptyFunction } from '../../Utils';
+import { emptyFunction, unimplementedFunction } from '../../Utils';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { NumCast, StrCast } from '../../fields/Types';
-import { GPTCallType, gptAPICall, gptImageLabel } from '../apis/gpt/GPT';
+import { Networking } from '../Network';
+import { GPTCallType, gptAPICall } from '../apis/gpt/GPT';
import { Docs } from '../documents/Documents';
import { ContextMenu } from './ContextMenu';
import { ContextMenuProps } from './ContextMenuItem';
import { StyleProp } from './StyleProp';
-import { AnchorMenu } from './pdf/AnchorMenu';
+import './StyleProviderQuiz.scss';
import { DocumentViewProps } from './nodes/DocumentView';
import { FieldViewProps } from './nodes/FieldView';
import { ImageBox } from './nodes/ImageBox';
import { ImageUtility } from './nodes/imageEditor/imageEditorUtils/ImageHandler';
-import './StyleProviderQuiz.scss';
+import { AnchorMenu } from './pdf/AnchorMenu';
export namespace styleProviderQuiz {
enum quizMode {
@@ -45,8 +46,8 @@ export namespace styleProviderQuiz {
* @param boxes
* @param texts
*/
- async function createBoxes(img: ImageBox, boxes: [[[number, number]]], texts: [string]) {
- img.Document._quizBoxes = new List<Doc>([]);
+ async function createBoxes(img: ImageBox, boxes: number[][][], texts: string[]) {
+ img.Document.quizBoxes = new List<Doc>([]);
for (let i = 0; i < boxes.length; i++) {
const coords = boxes[i] ? boxes[i] : [];
const width = coords[1][0] - coords[0][0];
@@ -66,8 +67,8 @@ export namespace styleProviderQuiz {
newCol.zIndex = 1000;
newCol.forceActive = true;
newCol.quiz = text;
- newCol[DocData][Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
- Doc.AddDocToList(img.Document, '_quizBoxes', newCol);
+ newCol['$' + Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
+ Doc.AddDocToList(img.Document, 'quizBoxes', newCol);
img.addDocument(newCol);
// img._loading = false;
}
@@ -83,23 +84,15 @@ export namespace styleProviderQuiz {
imgBox.Document._quizMode = quiz;
const quizBoxes = DocListCast(imgBox.Document.quizBoxes);
if (!quizBoxes.length) {
- imgBox.Loading = true;
+ runInAction(() => (imgBox.Loading = true));
- const img = {
- file: i ? i : imgBox.paths[0],
- drag: i ? 'drag' : 'full',
- smart: quiz,
- };
- const response = await axios.post('http://localhost:105/labels/', img, {
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- if (response.data['boxes'].length != 0) {
- createBoxes(imgBox, response.data['boxes'], response.data['text']);
- } else {
- imgBox.Loading = false;
+ const response = (await Networking.PostToServer('/labels', { file: i ? i : imgBox.paths[0], drag: i ? 'drag' : 'full', smart: quiz })) as { result: string };
+ const replacedResponse = response.result.replace(/ '/g, '"').replace(/',/g, '",').replace(/\{'/g, '{"').replace(/':/g, '":').replace(/'\]/g, '"]').replace(/\['/g, '["');
+ const parsedResponse = JSON.parse(replacedResponse) as { boxes: number[][][]; text: string[] };
+ if (parsedResponse.boxes.length != 0) {
+ createBoxes(imgBox, parsedResponse.boxes, parsedResponse.text);
}
+ runInAction(() => (imgBox.Loading = false));
} else quizBoxes.forEach(box => (box.hidden = false));
}
@@ -117,20 +110,21 @@ export namespace styleProviderQuiz {
const blob = await ImageUtility.canvasToBlob(canvas);
return selectUrlToBase64(blob);
}
- /**
- * Create flashcards from an image.
- */
- async function getImageDesc(img: ImageBox) {
- img.Loading = true;
- try {
- const hrefBase64 = await createCanvas(img);
- const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
- AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc.x), NumCast(img.layoutDoc.y));
- } catch (error) {
- console.log('Error', error);
- }
- img.Loading = false;
- }
+
+ // /**
+ // * Create flashcards from an image.
+ // */
+ // async function makeFlashcardsForImage(img: ImageBox) {
+ // img.Loading = true;
+ // try {
+ // const hrefBase64 = await createCanvas(img);
+ // const response = await gptImageLabel(hrefBase64, 'Make flashcards out of this image with each question and answer labeled as "question" and "answer". Do not label each flashcard and do not include asterisks: ');
+ // AnchorMenu.Instance.transferToFlashcard(response, NumCast(img.layoutDoc.x), NumCast(img.layoutDoc.y));
+ // } catch (error) {
+ // console.log('Error', error);
+ // }
+ // img.Loading = false;
+ // }
/**
* Calls the createCanvas and pushInfo methods to convert the
@@ -230,8 +224,8 @@ export namespace styleProviderQuiz {
*/
function extractHexAndSentences(inputString: string) {
// Regular expression to match a hexadecimal number at the beginning followed by a period and sentences
- const regex = /^#([0-9A-Fa-f]+)\.\s*(.+)$/s;
- const match = inputString.match(regex);
+ const regex = /^#([0-9A-Fa-f]+)\.\s*(.+)$/;
+ const match = inputString.replace('\n', ' ').match(regex);
if (match) {
const hexNumber = match[1];
@@ -254,7 +248,7 @@ export namespace styleProviderQuiz {
function check(img: ImageBox) {
//this._loading = true;
imgQuizBoxes(img).forEach(async doc => {
- const input = StrCast(doc[DocData].title);
+ const input = StrCast(doc.$title);
if (imgQuizMode(img) == quizMode.SMART && input) {
const questionText = 'Question: What was labeled in this image?';
const rubricText = ' Rubric: ' + StrCast(doc.quiz);
@@ -271,7 +265,9 @@ export namespace styleProviderQuiz {
doc.backgroundColor = '#' + hexSent.hexNumber;
} else {
const match = compareWords(input, StrCast(doc.quiz).trim());
- doc.backgroundColor = match ? '#11c249' : '#eb2d2d';
+ if (input) {
+ doc.backgroundColor = match ? '#11c249' : '#eb2d2d';
+ }
}
});
//this._loading = false;
@@ -279,8 +275,8 @@ export namespace styleProviderQuiz {
function redo(img: ImageBox) {
imgQuizBoxes(img).forEach(doc => {
- doc[DocData].title = '';
- doc.backgroundColor = '#e4e4e4';
+ doc.$title = '';
+ doc.$backgroundColor = '#e4e4e4';
});
}
@@ -289,7 +285,7 @@ export namespace styleProviderQuiz {
*/
function exitQuizMode(img: ImageBox) {
img.Document._quizMode = quizMode.NONE;
- DocListCast(img.Document._quizBoxes).forEach(box => {
+ DocListCast(img.Document.quizBoxes).forEach(box => {
box.hidden = true;
});
}
@@ -388,10 +384,7 @@ export namespace styleProviderQuiz {
}
break;
case StyleProp.AnchorMenuItems:
- if (imgBox) {
- AnchorMenu.Instance.gptFlashcards = () => getImageDesc(imgBox);
- AnchorMenu.Instance.makeLabels = () => makeLabels(props?.DocumentView?.().ComponentView as ImageBox);
- }
+ AnchorMenu.Instance.makeLabels = imgBox ? () => makeLabels(props?.DocumentView?.().ComponentView as ImageBox) : unimplementedFunction;
}
return undefined;
}
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 93d6fb684..7d2a6be5e 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -72,7 +72,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
const newTagCol = new Doc();
newTagCol.title = tag;
newTagCol.collections = new List<Doc>();
- newTagCol[DocData].docs = new List<Doc>();
+ newTagCol.$docs = new List<Doc>();
Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard, 'myTagCollections', newTagCol);
return newTagCol;
@@ -82,7 +82,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
* @param tag tag string
* @returns An array of documents that contain the tag.
*/
- public static allDocsWithTag = (tag: string) => DocListCast(TagItem.findTagCollectionDoc(tag)?.[DocData].docs);
+ public static allDocsWithTag = (tag: string) => DocListCast(TagItem.findTagCollectionDoc(tag)?.$docs);
public static docHasTag = (doc: Doc, tag: string) => StrListCast(doc?.tags).includes(tag);
/**
@@ -95,11 +95,11 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
// If the document is of type COLLECTION, make it a smart collection, otherwise, add the tag to the document.
if (doc.type === DocumentType.COL && !doc.annotationOn) {
- Doc.AddDocToList(tagCollection[DocData], 'collections', doc);
+ Doc.AddDocToList(tagCollection, 'collections', doc);
// Iterate through the tag Doc collections and add a copy of the document to each collection
- for (const cdoc of DocListCast(tagCollection[DocData].docs)) {
- if (!DocListCast(doc[DocData].data).find(d => Doc.AreProtosEqual(d, cdoc))) {
+ for (const cdoc of DocListCast(tagCollection.$docs)) {
+ if (!DocListCast(doc.$data).find(d => Doc.AreProtosEqual(d, cdoc))) {
const newEmbedding = Doc.MakeEmbedding(cdoc);
Doc.AddDocToList(doc[DocData], 'data', newEmbedding);
Doc.SetContainer(newEmbedding, doc);
@@ -111,7 +111,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
// Iterate through the tag document's collections and add a copy of the document to each collection
for (const collection of DocListCast(tagCollection.collections)) {
- if (!DocListCast(collection[DocData].data).find(d => Doc.AreProtosEqual(d, doc))) {
+ if (!DocListCast(collection.$data).find(d => Doc.AreProtosEqual(d, doc))) {
const newEmbedding = Doc.MakeEmbedding(doc);
Doc.AddDocToList(collection[DocData], 'data', newEmbedding);
Doc.SetContainer(newEmbedding, collection);
@@ -119,8 +119,8 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
}
}
- if (!doc[DocData].tags) doc[DocData].tags = new List<string>();
- const tagList = doc[DocData].tags as List<string>;
+ if (!doc.$tags) doc.$tags = new List<string>();
+ const tagList = doc.$tags as List<string>;
if (!tagList.includes(tag)) tagList.push(tag);
};
@@ -131,22 +131,22 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
* @param tagDoc doc that collections the Docs with the tag
*/
public static removeTagFromDoc = (doc: Doc, tag: string, tagDoc?: Doc) => {
- if (doc[DocData].tags) {
+ if (doc.$tags) {
if (doc.type === DocumentType.COL) {
tagDoc && Doc.RemoveDocFromList(tagDoc[DocData], 'collections', doc);
for (const cur_doc of TagItem.allDocsWithTag(tag)) {
- doc[DocData].data = new List<Doc>(DocListCast(doc[DocData].data).filter(d => !Doc.AreProtosEqual(cur_doc, d)));
+ doc.$data = new List<Doc>(DocListCast(doc.$data).filter(d => !Doc.AreProtosEqual(cur_doc, d)));
}
} else {
tagDoc && Doc.RemoveDocFromList(tagDoc[DocData], 'docs', doc);
for (const collection of DocListCast(tagDoc?.collections)) {
- collection[DocData].data = new List<Doc>(DocListCast(collection[DocData].data).filter(d => !Doc.AreProtosEqual(doc, d)));
+ collection.$data = new List<Doc>(DocListCast(collection.$data).filter(d => !Doc.AreProtosEqual(doc, d)));
}
}
}
- doc[DocData].tags = new List<string>(StrListCast(doc[DocData].tags).filter(label => label !== tag));
+ doc.$tags = new List<string>(StrListCast(doc.$tags).filter(label => label !== tag));
};
private _ref: React.RefObject<HTMLDivElement>;
@@ -171,11 +171,10 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
// Create a new collection and set up configurations.
const newCollection = ((doc: Doc) => {
- const docData = doc[DocData];
- docData.data = new List<Doc>(newEmbeddings);
- docData.title = this._props.tag;
- docData.tags = new List<string>([this._props.tag]);
- docData.freeform_fitContentsToBox = true;
+ doc.$data = new List<Doc>(newEmbeddings);
+ doc.$title = this._props.tag;
+ doc.$tags = new List<string>([this._props.tag]);
+ doc.$freeform_fitContentsToBox = true;
doc._freeform_panX = doc._freeform_panY = 0;
doc._width = 900;
doc._height = 900;
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 680c8ed0e..1266a11c1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -80,7 +80,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
const addedTypes = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data);
const templateMenu: Array<JSX.Element> = [];
templateMenu.push(<OtherToggle key="default" name={firstDoc.layout instanceof Doc ? StrCast(firstDoc.layout.title) : 'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
- // eslint-disable-next-line no-return-assign
addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
this._addedKeys &&
Array.from(this._addedKeys)
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 15683ebf2..cd2c7df1b 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -46,7 +46,7 @@ import { Id } from '../../../fields/FieldSymbols';
*/
@observer
-export class Timeline extends ObservableReactComponent<FieldViewProps> {
+export class Timeline extends ObservableReactComponent<FieldViewProps & { Doc: Doc }> {
// readonly constants
private readonly DEFAULT_TICK_SPACING: number = 50;
private readonly MAX_TITLE_HEIGHT = 75;
@@ -57,7 +57,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
private DEFAULT_CONTAINER_HEIGHT: number = 330;
private MIN_CONTAINER_HEIGHT: number = 205;
- constructor(props: FieldViewProps) {
+ constructor(props: FieldViewProps & { Doc: Doc }) {
super(props);
makeObservable(this);
}
@@ -90,11 +90,11 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
*/
@computed
private get children(): Doc[] {
- const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Document.type) as unknown as DocumentType);
+ const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this._props.Doc.type) as unknown as DocumentType);
if (annotatedDoc) {
- return DocListCast(this._props.Document[Doc.LayoutFieldKey(this._props.Document) + '_annotations']);
+ return DocListCast(this._props.Doc[Doc.LayoutFieldKey(this._props.Doc) + '_annotations']);
}
- return DocListCast(this._props.Document[this._props.fieldKey]);
+ return DocListCast(this._props.Doc[this._props.fieldKey]);
}
/// //////lifecycle functions////////////
@@ -104,21 +104,21 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; // check if relHeight is less than Maxheight. Else, just set relheight to max
this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; // offset
this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; // twice the titleheight + offset
- if (!this._props.Document.AnimationLength) {
+ if (!this._props.Doc.AnimationLength) {
// if animation length did not exist
- this._props.Document.AnimationLength = this._time; // set it to default time
+ this._props.Doc.AnimationLength = this._time; // set it to default time
} else {
- this._time = NumCast(this._props.Document.AnimationLength); // else, set time to animationlength stored from before
+ this._time = NumCast(this._props.Doc.AnimationLength); // else, set time to animationlength stored from before
}
this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); // the entire length of the timeline div (actual div part itself)
this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; // the visible length of the timeline (the length that you current see)
this._visibleStart = this._infoContainer.current!.scrollLeft; // where the div starts
- this._props.Document.isATOn = !this._props.Document.isATOn; // turns the boolean on, saying AT (animation timeline) is on
+ this._props.Doc.isATOn = !this._props.Doc.isATOn; // turns the boolean on, saying AT (animation timeline) is on
this.toggleHandle();
}
componentWillUnmount() {
- this._props.Document.AnimationLength = this._time; // save animation length
+ this._props.Doc.AnimationLength = this._time; // save animation length
}
/// //////////////////////////////////////////////
@@ -224,7 +224,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
*/
@action
onPanDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, e => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
+ setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, movEv => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + movEv.clientX - this._trackbox.current!.getBoundingClientRect().left));
};
/**
@@ -241,7 +241,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
this._visibleStart -= e.movementX;
this._totalLength -= e.movementX;
this._time -= RegionHelpers.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
- this._props.Document.AnimationLength = this._time;
+ this._props.Doc.AnimationLength = this._time;
}
return false;
};
@@ -259,8 +259,8 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
setupMoveUpEvents(
this,
e,
- action(e => {
- const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
+ action(movEv => {
+ const offset = movEv.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
return false;
}),
@@ -358,7 +358,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
const size = 40 * scale; // 50 is default
const iconSize = 25;
const width: number = this._props.PanelWidth();
- const modeType = this._props.Document.isATOn ? 'Author' : 'Play';
+ const modeType = this._props.Doc.isATOn ? 'Author' : 'Play';
// decides if information should be omitted because the timeline is very small
// if its less than 950 pixels then it's going to be overlapping
@@ -397,7 +397,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
tickIncrement={this._tickIncrement}
time={this._time}
parent={this}
- isAuthoring={BoolCast(this._props.Document.isATOn)}
+ isAuthoring={BoolCast(this._props.Doc.isATOn)}
currentBarX={this._currentBarX}
totalLength={this._totalLength}
visibleLength={this._visibleLength}
@@ -418,10 +418,10 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
</div>
<div className="time-box overview-tool" style={{ display: 'flex' }}>
{this.timeIndicator(lengthString, totalTime)}
- <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this._props.Document)}>
+ <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this._props.Doc)}>
<FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
</div>
- <div className="resetView-tool" style={{ display: this._props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this._props.Document)}>
+ <div className="resetView-tool" style={{ display: this._props.Doc.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this._props.Doc)}>
<FontAwesomeIcon icon="expand-arrows-alt" size="lg" />
</div>
</div>
@@ -431,17 +431,17 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
};
timeIndicator(lengthString: string, totalTime: number) {
- if (this._props.Document.isATOn) {
- return <div key="time-text" className="animation-text" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', display: this._props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
+ if (this._props.Doc.isATOn) {
+ return <div key="time-text" className="animation-text" style={{ visibility: this._props.Doc.isATOn ? 'visible' : 'hidden', display: this._props.Doc.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
} else {
const ctime = `Current: ${this.getCurrentTime()}`;
const ttime = `Total: ${this.toReadTime(this._time)}`;
return (
<div style={{ flexDirection: 'column' }}>
- <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}>
+ <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Doc.isATOn ? 'block' : 'none' }}>
{ctime}
</div>
- <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Document.isATOn ? 'block' : 'none' }}>
+ <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this._props.Doc.isATOn ? 'block' : 'none' }}>
{ttime}
</div>
</div>
@@ -467,8 +467,8 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
const roundToggleContainer = this._roundToggleContainerRef.current!;
const timelineContainer = this._timelineContainer.current!;
- this._props.Document.isATOn = !this._props.Document.isATOn;
- if (!BoolCast(this._props.Document.isATOn)) {
+ this._props.Doc.isATOn = !this._props.Doc.isATOn;
+ if (!BoolCast(this._props.Doc.isATOn)) {
// turning on playmode...
roundToggle.style.transform = 'translate(0px, 0px)';
roundToggle.style.animationName = 'turnoff';
@@ -543,7 +543,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
// change visible and total width
return (
<div style={{ visibility: 'visible' }}>
- <div key="timeline_wrapper" style={{ visibility: this._props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
+ <div key="timeline_wrapper" style={{ visibility: this._props.Doc.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
<div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}>
<div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}>
{this.drawTicks()}
@@ -551,7 +551,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
<div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown} />
</div>
<div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
- {[...this.children, this._props.Document].map(doc => (
+ {[...this.children, this._props.Doc].map(doc => (
<Track
key={doc[Id]}
ref={ref => this.mapOfTracks.push(ref)}
@@ -563,7 +563,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
time={this._time}
tickSpacing={this._tickSpacing}
tickIncrement={this._tickIncrement}
- collection={this._props.Document}
+ collection={this._props.Doc}
timelineVisible={true}
/>
))}
@@ -571,7 +571,7 @@ export class Timeline extends ObservableReactComponent<FieldViewProps> {
</div>
<div className="currentTime">Current: {this.getCurrentTime()}</div>
<div key="timeline_title" className="title-container" ref={this._titleContainer}>
- {[...this.children, this._props.Document].map(doc => (
+ {[...this.children, this._props.Doc].map(doc => (
<div key={doc[Id]} style={{ height: `${this._titleHeight}px` }} className="datapane" onPointerOver={() => Doc.BrushDoc(doc)} onPointerOut={() => Doc.UnBrushDoc(doc)}>
<p>{StrCast(doc.title)}</p>
</div>
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e51bc18ef..12a131deb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -5,7 +5,7 @@ import * as ReactDOM from 'react-dom/client';
import ResizeObserver from 'resize-observer-polyfill';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, incrementTitleCopy, returnTrue, UpdateIcon } from '../../../ClientUtils';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
-import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -481,7 +481,7 @@ export class CollectionDockingView extends CollectionSubView() {
Array.from(cloned.map.entries()).forEach(entry => {
json = json.replace(entry[0], entry[1][Id]);
});
- cloned.clone[DocData].dockingConfig = json;
+ cloned.clone.$dockingConfig = json;
return DashboardView.openDashboard(cloned.clone);
}
const matches = json.match(/"documentId":"[a-z0-9-]+"/g);
@@ -495,7 +495,7 @@ export class CollectionDockingView extends CollectionSubView() {
const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
if (newtabdocs.length) {
- newtab[DocData].data = new List<Doc>(newtabdocs);
+ newtab.$data = new List<Doc>(newtabdocs);
newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
}
json = json.replace(origtab[Id], newtab[Id]);
@@ -503,9 +503,8 @@ export class CollectionDockingView extends CollectionSubView() {
});
const dashboardDoc = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
- dashboardDoc.pane_count = 1;
- dashboardDoc.myOverlayDocs = new List<Doc>();
- dashboardDoc.myPublishedDocs = new List<Doc>();
+ dashboardDoc.$myOverlayDocs = new List<Doc>();
+ dashboardDoc.$myPublishedDocs = new List<Doc>();
DashboardView.SetupDashboardTrails();
DashboardView.SetupDashboardCalendars(); // Zaul TODO: needed?
@@ -555,13 +554,13 @@ export class CollectionDockingView extends CollectionSubView() {
stack.header?.element.on('mousedown', (e: MouseEvent) => {
const dashboard = Doc.ActiveDashboard;
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
- dashboard.pane_count = NumCast(dashboard.pane_count) + 1;
+ dashboard.$myPaneCount = NumCast(dashboard.$myPaneCount) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
_width: this._props.PanelWidth(),
_height: this._props.PanelHeight(),
_freeform_backgroundGrid: true,
_layout_fitWidth: true,
- title: `Untitled Tab ${NumCast(dashboard.pane_count)}`,
+ title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.Document, docToAdd, false);
@@ -572,13 +571,13 @@ export class CollectionDockingView extends CollectionSubView() {
const addNewDoc = undoable(() => {
const dashboard = Doc.ActiveDashboard;
if (dashboard) {
- dashboard.pane_count = NumCast(dashboard.pane_count) + 1;
+ dashboard.$myPaneCount = NumCast(dashboard.$myPaneCount) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
_width: this._props.PanelWidth(),
_height: this._props.PanelHeight(),
_layout_fitWidth: true,
_freeform_backgroundGrid: true,
- title: `Untitled Tab ${NumCast(dashboard.pane_count)}`,
+ title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.dataDoc, docToAdd, false);
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 996626575..89ccf5a0f 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -5,7 +5,6 @@ import * as React from 'react';
import { returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction, numberRange } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
import { Docs } from '../../documents/Documents';
@@ -23,7 +22,7 @@ import './CollectionStackingView.scss';
interface CMVFieldRowProps {
rows: () => number;
headings: () => object[];
- Document: Doc;
+ Doc: Doc;
chromeHidden?: boolean;
heading: string;
headingObject: SchemaHeaderField | undefined;
@@ -74,7 +73,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
createRowDropRef = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
- if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
+ if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Doc);
else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -164,7 +163,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const { pivotField } = this._props;
const newDoc = Docs.Create.TextDocument(value, { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
DocumentView.SetSelectOnLoad(newDoc);
- pivotField && (newDoc[DocData][pivotField] = this.getValue(this._props.heading));
+ pivotField && (newDoc['$' + pivotField] = this.getValue(this._props.heading));
const docs = this._props.parent.childDocList;
return docs ? !!docs.splice(0, 0, newDoc) : this._props.parent._props.addDocument?.(newDoc) || false; // should really extend addDocument to specify insertion point (at beginning of list)
};
@@ -190,7 +189,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
};
headerMove = (e: PointerEvent) => {
- const embedding = Doc.MakeEmbedding(this._props.Document);
+ const embedding = Doc.MakeEmbedding(this._props.Doc);
const key = this._props.pivotField;
let value = this.getValue(this.heading);
value = typeof value === 'string' ? `"${value}"` : value;
@@ -290,7 +289,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const key = this._props.pivotField;
const evContents = this.heading ? this.heading : this._props.type && this._props.type === 'number' ? '0' : `NO ${key.toUpperCase()} VALUE`;
const editableHeaderView = <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine />;
- return this._props.Document.miniHeaders ? (
+ return this._props.Doc.miniHeaders ? (
<div className="collectionStackingView-miniHeader">{editableHeaderView}</div>
) : !this._props.headingObject ? null : (
<div className="collectionStackingView-sectionHeader" ref={this._headerRef}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index de999c91a..c79610595 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-unused-class-component-methods */
-/* eslint-disable react/sort-comp */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from '@dash/components';
@@ -9,7 +7,6 @@ import * as React from 'react';
import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { RichTextField } from '../../../fields/RichTextField';
@@ -158,9 +155,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
}
interface CollectionViewMenuProps {
- // eslint-disable-next-line react/no-unused-prop-types
type: CollectionViewType;
- // eslint-disable-next-line react/no-unused-prop-types
fieldKey: string;
docView: DocumentView;
}
@@ -185,7 +180,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
try {
formatStr && JSON.parse(formatStr);
- } catch (e) {
+ } catch {
formatStr = '';
}
if (source.length === 1 && formatStr) {
@@ -213,7 +208,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
title: 'set content',
script: 'getProto(this.target).data = copyField(this.source);',
immediate: undoable((source: Doc[]) => {
- this.target[DocData].data = new List<Doc>(source);
+ this.target.$data = new List<Doc>(source);
}, ''),
initialize: emptyFunction,
};
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index c499bd288..01695dbaf 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -32,6 +32,7 @@ import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn
import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { Property } from 'csstype';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
/**
* CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1)
@@ -270,7 +271,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
containerViewPath={this.childContainerViewPath}
fitWidth={this._props.childLayoutFitWidth}
isContentActive={emptyFunction}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
// TODO: change this from a prop to a parameter passed into a function
dontHideOnDrag
isDocumentActive={this.isContentActive}
@@ -437,11 +438,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
@undoBatch
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
+ if ((e.ctrlKey || textBox.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
e.stopPropagation?.();
- const newDoc = Doc.MakeCopy(fieldProps.Document, true);
- newDoc[DocData].text = undefined;
+ const newDoc = Doc.MakeCopy(textBox.Document, true);
+ newDoc.$text = undefined;
DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
@@ -543,8 +544,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.Document}
- TemplateDataDocument={this._props.TemplateDataDocument}
+ Doc={this.Document}
+ TemplateDataDoc={this._props.TemplateDataDocument}
resizeColumns={this.resizeColumns}
renderChildren={this.children}
numGroupColumns={this.numGroupColumns}
@@ -567,7 +568,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
@undoBatch
remColumn = (value: SchemaHeaderField) => {
- const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
if (value) {
const index = colHdrData.indexOf(value);
index !== -1 && colHdrData.splice(index, 1);
@@ -701,7 +702,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
{this.renderedSections}
<div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={undoable(fieldKey => {
this.layoutDoc._pivotField = fieldKey;
this.removeEmptyColumns();
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 40b3f9ef2..f283b0abe 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -21,8 +21,8 @@ import './CollectionNoteTakingView.scss';
import { DocumentView } from '../nodes/DocumentView';
interface CSVFieldColumnProps {
- Document: Doc;
- TemplateDataDocument: Opt<Doc>;
+ Doc: Doc;
+ TemplateDataDoc: Opt<Doc>;
backgroundColor?: () => string | undefined;
docList: Doc[];
heading: string;
@@ -65,7 +65,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
// columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- if (this._props.Document._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
+ if (this._props.Doc._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
if (!this._props.colHeaderData || !this._props.headingObject || this._props.colHeaderData.length === 1) return `${(this._props.availableWidth / this._props.PanelWidth()) * 100}%`;
const i = this._props.colHeaderData.findIndex(hd => hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
@@ -81,7 +81,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
- if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
+ if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc);
else if (this._ele) this.props.refList.slice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -155,9 +155,9 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
// all docs are added to the column directly to the left.
@undoBatch
deleteColumn = () => {
- const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const colHdrData = Array.from(Cast(this._props.Doc[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
if (this._props.headingObject) {
- // this._props.docList.forEach(d => (d[DocData][this._props.pivotField] = undefined));
+ // this._props.docList.forEach(d => (d['$'+this._props.pivotField] = undefined));
colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
this._props.resizeColumns(colHdrData);
}
@@ -184,11 +184,11 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
);
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this._props.Document)[name] = '';
+ Doc.GetProto(this._props.Doc)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
this._props.addDocument?.(created);
}
@@ -267,7 +267,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
style={{
width: this.columnWidth,
background: this._hover && SnappingManager.IsDragging ? '#b4b4b4' : 'inherit',
- marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Document.xMargin) : 0,
+ marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Doc.xMargin) : 0,
}}>
<div className="collectionNoteTakingViewFieldColumn" key={this._heading} ref={this.createColumnDropRef}>
{this.innards}
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 2600c0f57..4736070c3 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -103,7 +103,7 @@ export class CollectionPivotView extends CollectionSubView() {
{this.contents}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={fieldKey => {
this.layoutDoc._pivotField = fieldKey;
}}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index c3047e5fb..fd4bdf364 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -5,7 +5,6 @@ import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../ClientUtils';
import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -187,13 +186,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@computed get rangeClick() {
// prettier-ignore
return ScriptField.MakeFunction('stackedTimeline.clickAnchor(this, clientX)',
- { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: 'string' /* should be CollectionStackedTimeline */ }
- )!;
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as unknown as string })!; // NOTE: scripts can't serialize a run-time React component as captured variable BUT this script will not be serialized so we can "stuff" anything we want in the capture variable
}
@computed get rangePlay() {
// prettier-ignore
return ScriptField.MakeFunction('stackedTimeline.playOnClick(this, clientX)',
- { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: 'string' /* should be CollectionStackedTimeline */})!;
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as unknown as string })!; // NOTE: scripts can't serialize a run-time React component as captured variable BUT this script will not be serialized so we can "stuff" anything we want in the capture variable
}
rangeClickScript = () => this.rangeClick;
rangePlayScript = () => this.rangePlay;
@@ -268,13 +266,13 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setupMoveUpEvents(
this,
e,
- action(() => {
+ action(movEv => {
if (!wasSelecting) {
this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
wasSelecting = true;
this._timelineWrapper && (this._timelineWrapper.style.cursor = 'ew-resize');
}
- this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
+ this._markerEnd = this.toTimeline(movEv.clientX - rect.x, rect.width);
return false;
}),
action((upEvent, movement, isClick) => {
@@ -433,8 +431,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
_isTimelineLabel: true,
layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
});
- anchor[DocData][startTag] = anchorStartTime;
- anchor[DocData][endTag] = anchorEndTime;
+ anchor['$' + startTag] = anchorStartTime;
+ anchor['$' + endTag] = anchorEndTime;
if (addAsAnnotation) {
if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
@@ -805,9 +803,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
};
resetTitle = () => {
- this._props.mark[DocData].title = ComputedField.MakeFunction(
- `["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`
- );
+ this._props.mark.$title = ComputedField.MakeFunction(`["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`);
};
// context menu
contextMenuItems = () => {
@@ -844,7 +840,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
styleProvider={this._props.styleProvider}
renderDepth={this._props.renderDepth + 1}
LayoutTemplate={undefined}
- LayoutTemplateString={LabelBox.LayoutString('data')}
+ LayoutTemplateString={LabelBox.LayoutString('title')}
isDocumentActive={this._props.isDocumentActive}
PanelWidth={width}
PanelHeight={height}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6bbd43b1b..fd48a9dc1 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -5,7 +5,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -35,6 +34,7 @@ import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { computedFn } from 'mobx-utils';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
export type collectionStackingViewProps = {
sortFunc?: (a: Doc, b: Doc) => number;
@@ -310,17 +310,17 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return this._props.styleProvider?.(doc, props, property);
};
@undoBatch
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
if (['Enter'].includes(e.key) && e.ctrlKey) {
e.stopPropagation?.();
- const layoutFieldKey = StrCast(fieldProps.fieldKey);
- const newDoc = Doc.MakeCopy(fieldProps.Document, true);
- const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)];
- newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
- if (layoutFieldKey !== 'layout' && fieldProps.Document[layoutFieldKey] instanceof Doc) {
- newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey];
+ const layoutFieldKey = StrCast(textBox.fieldKey);
+ const newDoc = Doc.MakeCopy(textBox.Document, true);
+ const dataField = textBox.Document[Doc.LayoutFieldKey(newDoc)];
+ newDoc['$' + Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ if (layoutFieldKey !== 'layout' && textBox.Document[layoutFieldKey] instanceof Doc) {
+ newDoc[layoutFieldKey] = textBox.Document[layoutFieldKey];
}
- newDoc[DocData].text = undefined;
+ newDoc.$text = undefined;
DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
@@ -359,7 +359,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
containerViewPath={this.childContainerViewPath}
fitWidth={this.childFitWidth}
isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
DataTransition={trans}
isDocumentActive={this.isContentActive}
LayoutTemplate={this._props.childLayoutTemplate}
@@ -404,7 +404,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
return new Transform(-translateX + (dref?.centeringX || 0) * scale,
-translateY + (dref?.centeringY || 0) * scale, 1)
- .scale(1 / scale); // prettier-ignore
+ .scale(1 / (scale||1)); // prettier-ignore
});
getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
@@ -575,8 +575,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.Document}
- TemplateDataDocument={this._props.TemplateDataDocument}
+ Doc={this.Document}
+ TemplateDataDoc={this._props.TemplateDataDocument}
renderChildren={this.children}
columnWidth={this.columnWidth}
numGroupColumns={this.numGroupColumns}
@@ -611,7 +611,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
<div style={{ top: this.yMargin }}>
<CollectionMasonryViewFieldRow
showHandle={first}
- Document={this.Document}
+ Doc={this.Document}
chromeHidden={this.chromeHidden}
pivotField={this.pivotField}
refList={this._refList}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6f32dd2e0..3b9d167c6 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -29,8 +29,8 @@ import './CollectionStackingView.scss';
// So this is how we are storing a column
interface CSVFieldColumnProps {
- Document: Doc;
- TemplateDataDocument: Opt<Doc>;
+ Doc: Doc;
+ TemplateDataDoc: Opt<Doc>;
docList: Doc[];
heading: string;
pivotField: string;
@@ -90,7 +90,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
// is that the only way to have drop targets?
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
- if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document, this.onInternalPreDrop.bind(this));
+ if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -183,7 +183,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
// TODO: I think this is where I'm supposed to edit stuff
startDrag = (e: PointerEvent) => {
// is MakeEmbedding a way to make a copy of a doc without rendering it?
- const embedding = Doc.MakeEmbedding(this._props.Document);
+ const embedding = Doc.MakeEmbedding(this._props.Doc);
embedding._width = this._props.columnWidth / (this._props.colHeaderData?.length || 1);
embedding._pivotField = undefined;
let value = this.getValue(this._heading);
@@ -230,7 +230,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
- const dataDoc = this._props.TemplateDataDocument || this._props.Document;
+ const dataDoc = this._props.TemplateDataDoc || this._props.Doc;
const width = this._ele ? DivWidth(this._ele) : 0;
const height = this._ele ? DivHeight(this._ele) : 0;
DocUtils.addDocumentCreatorMenuItems(
@@ -250,10 +250,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
+ const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Doc));
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
return this._props.addDocument?.(created);
}
@@ -270,7 +270,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
event: () => {
const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
if (created) {
- const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
+ const container = this._props.Doc.resolvedDataDoc ? Doc.GetProto(this._props.Doc) : this._props.Doc;
if (container.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, container);
return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
@@ -285,11 +285,11 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this._props.Document)[name] = '';
+ Doc.GetProto(this._props.Doc)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
this._props.addDocument?.(created);
}
@@ -350,10 +350,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
</div>
) : null;
const templatecols = `${this._props.columnWidth / this._props.numGroupColumns}px `;
- const { type } = this._props.Document;
+ const { type } = this._props.Doc;
return (
<>
- {this._props.Document._columnsHideIfEmpty ? null : headingView}
+ {this._props.Doc._columnsHideIfEmpty ? null : headingView}
{this.collapsed ? null : (
<div
style={{
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 655894e40..a43cf0755 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -108,7 +108,11 @@ export function CollectionSubView<X>() {
}
get dataDoc() {
- return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField ? Doc.GetProto(this._props.TemplateDataDocument) : this.Document.resolvedDataDoc ? this.Document : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+ return this._props.TemplateDataDocument instanceof Doc && this.layoutDoc.isTemplateForField //
+ ? this._props.TemplateDataDocument[DocData]
+ : this.layoutDoc.resolvedDataDoc
+ ? this._props.Document
+ : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
}
get childContainerViewPath() {
@@ -127,7 +131,7 @@ export function CollectionSubView<X>() {
hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
@computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
- const { Document, TemplateDataDocument } = this._props;
+ const { Document: Document, TemplateDataDocument } = this._props;
const validPairs = this.childDocs
.map(doc => Doc.GetLayoutDataDocPair(Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
.filter(
@@ -295,7 +299,7 @@ export function CollectionSubView<X>() {
const dragData = de.complete.docDragData;
if (dragData) {
const sourceDragAction = dragData.dropAction;
- const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._props.Document);
+ const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._renderDoc);
dragData.dropAction = !sameCollection // if doc from another tree
? sourceDragAction || targetDropAction // then use the source's dragAction otherwise the target's
: sourceDragAction === dropActionType.inPlace // if source drag is inPlace
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 98bd06221..fd562e64f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -121,7 +121,7 @@ export class CollectionTimeView extends CollectionSubView() {
{this.contents}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={fieldKey => {
this.layoutDoc._pivotField = fieldKey;
}}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e93724dd4..1960e12bd 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -241,7 +241,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
);
}
- onKey = (e: React.KeyboardEvent /* , fieldProps: FieldViewProps */) => {
+ onKey = (e: KeyboardEvent /* , textBox: FormattedTextBox */) => {
if (this.outlineMode && e.key === 'Enter') {
e.stopPropagation();
this.makeTextCollection(this.treeChildren);
@@ -252,7 +252,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
get documentTitle() {
return (
<FormattedTextBox
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
fieldKey="text"
renderDepth={this._props.renderDepth + 1}
@@ -471,7 +470,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
<div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
{!(this.Document instanceof Doc) || !this.treeChildren ? null : this.Document.treeView_HasOverlay ? (
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
setContentViewBox={emptyFunction}
NativeWidth={returnZero}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cc56a8ff9..620be2726 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -43,7 +43,7 @@ import './TabDocView.scss';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
interface TabMinimapViewProps {
- document: Doc;
+ doc: Doc;
tabView: () => DocumentView | undefined;
addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
PanelWidth: () => number;
@@ -100,15 +100,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
return bounds === undefined ? bounds : { l: bounds.x + width / 2 - dim / 2, t: bounds.y + height / 2 - dim / 2, cx, cy, dim };
}
@computed get xPadding() {
- return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
+ return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
}
@computed get yPadding() {
- return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
+ return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
}
- childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
- returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
+ childLayoutTemplate = () => Cast(this._props.doc.childLayoutTemplate, Doc, null);
+ returnMiniSize = () => NumCast(this._props.doc._miniMapSize, 150);
miniDown = (e: React.PointerEvent) => {
- const doc = this._props.document;
+ const doc = this._props.doc;
const miniSize = this.returnMiniSize();
doc &&
setupMoveUpEvents(
@@ -127,15 +127,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
popup = () => {
const { renderBounds } = this;
if (!renderBounds) return <div />;
- const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
- const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
- const miniLeft = () => 50 + ((NumCast(this._props.document._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
- const miniTop = () => 50 + ((NumCast(this._props.document._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
+ const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniLeft = () => 50 + ((NumCast(this._props.doc._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
+ const miniTop = () => 50 + ((NumCast(this._props.doc._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
const miniSize = this.returnMiniSize();
return (
<div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
<CollectionFreeFormView
- Document={this._props.document}
+ Document={this._props.doc}
docViewPath={returnEmptyDocViewList}
childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
noOverlay // don't render overlay Docs since they won't scale
@@ -144,7 +144,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
select={emptyFunction}
isSelected={returnFalse}
dontRegisterView
- fieldKey={Doc.LayoutFieldKey(this._props.document)}
+ fieldKey={Doc.LayoutFieldKey(this._props.doc)}
addDocument={returnFalse}
moveDocument={returnFalse}
removeDocument={returnFalse}
@@ -172,7 +172,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
);
};
render() {
- return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._type_collection !== CollectionViewType.Freeform ? null : (
+ return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
<div className="miniMap-hidden">
<Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SnappingManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
</div>
@@ -347,7 +347,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
undoable(() => {
const target = e.currentTarget as unknown as { value: string };
titleEle.size = target?.value.length + 3;
- doc[DocData].title = target?.value ?? '';
+ doc.$title = target?.value ?? '';
}, 'edit tab title')();
};
@@ -631,7 +631,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
}}>
{!this._activated || !this._document ? null : this.renderDocView(this._document)}
{this.disableMinimap() || !this._document ? null : (
- <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
+ <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} doc={this._document} tabView={this.tabView} />
)}
</div>
);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6208905fe..cb7da8c84 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -372,9 +372,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
_width: 1000,
_height: 10,
});
- const bulletData = bullet[DocData];
- bulletData.title = ComputedField.MakeFunction('this.text?.Text');
- bulletData.data = new List<Doc>([]);
+ bullet.$title = ComputedField.MakeFunction('this.text?.Text');
+ bullet.$data = new List<Doc>([]);
DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
return bullet;
@@ -835,7 +834,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
@observable headerEleWidth = 0;
@computed get titleButtons() {
- const customHeaderButtons = this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.Decorations);
+ const customHeaderButtons = this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.Decorations) as JSX.Element;
const color = SettingsManager.userColor;
return this._props.treeViewHideHeaderFields() || this.Document.treeView_HideHeaderFields ? null : (
<>
@@ -940,7 +939,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
if (property.startsWith(StyleProp.Decorations)) return null;
return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
};
- onKeyDown = (e: React.KeyboardEvent) => {
+ onKey = (e: KeyboardEvent) => {
if (this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode) {
switch (e.key) {
case 'Tab':
@@ -1139,7 +1138,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
ScreenToLocalTransform={this.docTransform}
renderDepth={this._props.renderDepth + 1}
onClickScript={this.onChildClick}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
containerViewPath={this.treeView.childContainerViewPath}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 8b9a3e0ec..89d2bf2c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,20 +1,19 @@
import { makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc';
+import { Doc, DocListCast, FieldResult, FieldType } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { StrCast } from '../../../../fields/Types';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { TopBar } from '../../topbar/TopBar';
import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
-import './CollectionFreeFormView.scss';
-import { DocData } from '../../../../fields/DocSymbols';
import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
export interface CollectionFreeFormInfoUIProps {
- Document: Doc;
- LayoutDoc: Doc;
+ Doc: Doc;
+ layoutDoc: Doc;
childDocs: () => Doc[];
close: () => void;
}
@@ -24,7 +23,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
public static Init() {
CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => (
//
- <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} />
+ <CollectionFreeFormInfoUI Doc={doc} layoutDoc={layout} childDocs={childDocs} close={close} />
));
}
_firstDocPos = { x: 0, y: 0 };
@@ -41,7 +40,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
set currState(val) { runInAction(() => {this._currState = val;}); } // prettier-ignore
componentWillUnmount(): void {
- this._props.Document[DocData].backgroundColor = this._originalbackground;
+ this._props.Doc.$backgroundColor = this._originalbackground;
}
setCurrState = (state: infoState) => {
@@ -52,10 +51,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
};
setupStates = () => {
- this._originalbackground = StrCast(this._props.Document[DocData].backgroundColor);
+ this._originalbackground = StrCast(this._props.Doc.$backgroundColor);
// state entry functions
- // const setBackground = (colour: string) => () => {this._props.Document[DocData].backgroundColor = colour;} // prettier-ignore
- // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore
+ // const setBackground = (colour: string) => () => {this._props.Doc.$backgroundColor = colour;} // prettier-ignore
+ // const setOpacity = (opacity: number) => () => {this._props.layoutDoc.opacity = opacity;} // prettier-ignore
// arc transition trigger conditions
const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined);
const numDocs = () => this._props.childDocs().length;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 241a56a88..4ea1de680 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -107,7 +107,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
}
function toNumber(val: FieldResult<FieldType>) {
- return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getMilliseconds() : NumCast(val, Number(StrCast(val)));
+ return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getTime() : NumCast(val, Number(StrCast(val)));
}
export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index bc9dd022c..2683d9439 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -7,7 +7,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
export interface CollectionFreeFormPannableContentsProps {
- Document: Doc;
+ Doc: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
transition: () => string;
@@ -33,7 +33,7 @@ export class CollectionFreeFormPannableContents extends ObservableReactComponent
makeObservable(this);
}
@computed get presPaths() {
- return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null;
+ return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Doc) : null;
}
// rectangle highlight used when following trail/link to a region of a collection that isn't a document
showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index f64c6715b..86310dca3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,7 +13,7 @@ import './CollectionFreeFormView.scss';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@computed protected get cursors(): CursorField[] {
- const { Document } = this.props;
+ const { Document: Document } = this.props;
const cursors = Cast(Document.cursors, listSpec(CursorField));
if (!cursors) {
return [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 89aa53c35..7fd5d71fd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,10 +1,13 @@
-import { Bezier } from 'bezier-js';
import { Button, Colors, Type } from '@dash/components';
+import { Slider } from '@mui/material';
+import { Bezier } from 'bezier-js';
import { Property } from 'csstype';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
+import { AiOutlineSend } from 'react-icons/ai';
+import ReactLoading from 'react-loading';
import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
@@ -28,6 +31,7 @@ import { DragManager } from '../../../util/DragManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SettingsManager } from '../../../util/SettingsManager';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -37,26 +41,26 @@ import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
import {
+ ActiveEraserWidth,
ActiveInkArrowEnd,
ActiveInkArrowStart,
- ActiveInkDash,
- ActiveEraserWidth,
- ActiveInkFillColor,
ActiveInkBezierApprox,
ActiveInkColor,
+ ActiveInkDash,
+ ActiveInkFillColor,
ActiveInkWidth,
ActiveIsInkMask,
DocumentView,
SetActiveInkColor,
SetActiveInkWidth,
} from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { OpenWhere } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
-import { StickerPalette } from '../../smartdraw/StickerPalette';
+import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
import { StyleProp } from '../../StyleProp';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeViewType';
@@ -67,11 +71,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import ReactLoading from 'react-loading';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { Slider } from '@mui/material';
-import { AiOutlineSend } from 'react-icons/ai';
-import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -180,10 +179,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get nativeWidth() {
- return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document);
}
@computed get nativeHeight() {
- return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document);
}
@computed get centeringShiftX(): number {
return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / this.nativeDimScaling; // shift so pan position is at center of window for non-overlay collections
@@ -285,8 +284,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
// freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
// this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
- panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
- panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
+ panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
+ panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
@@ -561,7 +560,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = this.createInkDoc(points, B);
- if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent';
+ if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc.$backgroundColor = 'transparent';
if (Doc.ActiveInk === InkInkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -639,7 +638,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
const inkDoc = this.createInkDoc(points, B);
['color', 'fillColor', 'stroke_width', 'stroke_dash', 'stroke_bezier'].forEach(field => {
- inkDoc[DocData][field] = stroke.dataDoc[field];
+ inkDoc['$' + field] = stroke.dataDoc[field];
});
this.addDocument(inkDoc);
});
@@ -1260,15 +1259,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
- const docData = doc[DocData];
- docData.title = opts.text;
- docData._width = opts.size;
- docData.ai_drawing_input = opts.text;
- docData.ai_drawing_complexity = opts.complexity;
- docData.ai_drawing_colored = opts.autoColor;
- docData.ai_drawing_size = opts.size;
- docData.ai_drawing_data = gptRes;
- docData.ai = 'gpt';
+ doc.$title = opts.text;
+ doc.$width = opts.size;
+ doc.$ai_drawing_input = opts.text;
+ doc.$ai_drawing_complexity = opts.complexity;
+ doc.$ai_drawing_colored = opts.autoColor;
+ doc.$ai_drawing_size = opts.size;
+ doc.$ai_drawing_data = gptRes;
+ doc.$ai = 'gpt';
this._drawingContainer = doc;
if (x !== undefined && y !== undefined) {
[doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
@@ -1490,20 +1488,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @param below whether to place the new text Doc below or to the right of the one being typed into.
* @returns whether the new text doc was created and added successfully
*/
- createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
- const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
+ createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
+ const textDoc = DocCast(textBox.Document.rootDocument, textBox.Document);
const newDoc = Doc.MakeCopy(textDoc, true);
- newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
+ newDoc['$' + Doc.LayoutFieldKey(newDoc, textBox._props.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}, 'copied text note');
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
+ if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
- return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
+ return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab');
}
return undefined;
};
@@ -1543,7 +1541,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
rootSelected={childData ? this.rootSelected : returnFalse}
waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
bringToFront={this.bringToFront}
ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
@@ -1841,15 +1839,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- updateIcon = (usePanelDimensions?: boolean) => {
+ updateIcon = (/*usePanelDimensions?: boolean*/) => {
const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
- usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
- usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
this._props.PanelWidth(),
this._props.PanelHeight(),
0,
@@ -2116,7 +2114,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
return (
<CollectionFreeFormPannableContents
- Document={this.Document}
+ Doc={this.Document}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.PanZoomCenterXf}
@@ -2134,6 +2132,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
<MarqueeView
{...this._props}
ref={this._marqueeViewRef}
+ Doc={this.Document}
ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
@@ -2150,7 +2149,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
isAnnotationOverlay={this.isAnnotationOverlay}>
{this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
{this.pannableContents}
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} Doc={this._props.Document} /> : null}
</MarqueeView>
);
}
@@ -2315,7 +2314,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
NativeWidth={returnZero}
NativeHeight={returnZero}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index b9f8b13a7..b40189d76 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -108,7 +108,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (faceAnno) {
faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face));
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
}
}
});
@@ -118,7 +118,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
const imgDoc = faceAnno;
faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face));
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
});
e.stopPropagation();
return true;
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index a3d9641da..c983d7c26 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -9,7 +9,6 @@ import React from 'react';
import { imageUrlToBase64 } from '../../../../ClientUtils';
import { Utils, numberRange } from '../../../../Utils';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
-import { DocData } from '../../../../fields/DocSymbols';
import { List } from '../../../../fields/List';
import { ImageCast } from '../../../../fields/Types';
import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT';
@@ -165,7 +164,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Converts the images into a Base64 format, afterwhich the information is sent to GPT to label them.
const imageInfos = this._selectedImages.map(async doc => {
- if (!doc[DocData].tags_chat) {
+ if (!doc.$tags_chat) {
const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 =>
!hrefBase64 ? undefined :
@@ -176,7 +175,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
(await Promise.all(imageInfos)).forEach(imageInfo => {
if (imageInfo) {
- imageInfo.doc[DocData].tags_chat = (imageInfo.doc[DocData].tags_chat as List<string>) ?? new List<string>();
+ imageInfo.doc.$tags_chat = (imageInfo.doc.$tags_chat as List<string>) ?? new List<string>();
const labels = imageInfo.labels.split('\n');
labels.forEach(label => {
@@ -186,7 +185,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
.replace(/^\d+\.\s*|-|f\*/, '')
.replace(/^#/, '')
.trim();
- (imageInfo.doc[DocData].tags_chat as List<string>).push(hashLabel);
+ (imageInfo.doc.$tags_chat as List<string>).push(hashLabel);
});
}
});
@@ -201,10 +200,10 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.startLoading();
for (const doc of this._selectedImages) {
- for (let index = 0; index < (doc[DocData].tags_chat as List<string>).length; index++) {
- const label = (doc[DocData].tags_chat as List<string>)[index];
+ for (let index = 0; index < (doc.$tags_chat as List<string>).length; index++) {
+ const label = (doc.$tags_chat as List<string>)[index];
const embedding = await gptGetEmbedding(label);
- doc[DocData][`tags_embedding_${index + 1}`] = new List<number>(embedding);
+ doc[`$tags_embedding_${index + 1}`] = new List<number>(embedding);
}
}
@@ -215,13 +214,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// For each image, loop through the labels, and calculate similarity. Associate it with the
// most similar one.
this._selectedImages.forEach(doc => {
- const embedLists = numberRange((doc[DocData].tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`])));
+ const embedLists = numberRange((doc.$tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[`$tags_embedding_${n + 1}`])));
const bestEmbedScore = (embedding: Opt<number[]>) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0));
const {label: mostSimilarLabelCollect} =
this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) }))
.reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur,
{ label: '', similarityScore: 0, }); // prettier-ignore
- doc[DocData].data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
+ doc.$data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
});
this.endLoading();
@@ -322,7 +321,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
await DocumentView.showDocument(doc, { willZoomCentered: true });
}}></img>
<div className="image-information-labels" onClick={() => this._props.addDocTab(doc, OpenWhere.addRightKeyvalue)}>
- {(doc[DocData].tags_chat as List<string>).map(label => {
+ {(doc.$tags_chat as List<string>).map(label => {
return (
<div key={Utils.GenerateGuid()} className="image-label" style={{ backgroundColor: SettingsManager.userVariantColor, borderColor: SettingsManager.userColor }}>
{label}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 00d7ea451..eaa8826ed 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -32,6 +32,7 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
interface MarqueeViewProps {
+ Doc: Doc;
getContainerTransform: () => Transform;
getTransform: () => Transform;
activeDocuments: () => Doc[];
@@ -113,6 +114,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
// 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://wikipedia.org/wiki/${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: `wiki:${str}`, data_useCors: true }), OpenWhere.addRight)
@@ -164,7 +166,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
error && console.log(error);
data &&
ClientUtils.convertDataUri(data, this._props.Document[Id] + '_icon_' + new Date().getTime()).then(returnedfilename => {
- this._props.Document[DocData].icon = new ImageField(returnedfilename);
+ this._props.Document.$icon = new ImageField(returnedfilename);
});
})
);
@@ -370,10 +372,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
- const docData = doc[DocData];
- docData.data = new List<Doc>(selected);
- docData.isGroup = makeGroup;
- docData.title = makeGroup ? 'grouping' : 'nested freeform';
+ doc.$data = new List<Doc>(selected);
+ doc.$isGroup = makeGroup;
+ doc.$title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -461,9 +462,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newColDim = 900;
for (const label of labelGroups) {
const newCollection = MarqueeView.getCollection([], undefined, false, this.Bounds);
- newCollection[DocData].title = label + ' Collection';
- newCollection._x = this.Bounds.left + x_offset;
- newCollection._y = this.Bounds.top + y_offset;
+ newCollection.$title = label + ' Collection';
+ newCollection.x = this.Bounds.left + x_offset;
+ newCollection.y = this.Bounds.top + y_offset;
newCollection._width = newColDim;
newCollection._height = newColDim;
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
@@ -480,8 +481,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}
for (const doc of selectedImages) {
- if (doc[DocData].data_label) {
- Doc.AddDocToList(labelToCollection.get(doc[DocData].data_label as string)!, undefined, doc);
+ if (doc.$data_label) {
+ Doc.AddDocToList(labelToCollection.get(doc.$data_label as string)!, undefined, doc);
this._props.removeDocument?.(doc);
}
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 0bf78f57c..53c0823ea 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -224,6 +224,7 @@
display: none;
}
+.schema-table-cell-selected,
.schema-table-cell,
.row-menu {
border: 1px solid global.$medium-gray;
@@ -232,6 +233,14 @@
display: inline-flex;
padding: 0;
align-items: center;
+ input[type='text'] {
+ border: unset;
+ }
+}
+.schema-table-cell-selected {
+ input[type='text'] {
+ background: lightgray;
+ }
}
.schemaRTFCell {
@@ -310,4 +319,7 @@
.schemaField-editing {
outline: none;
height: 100%;
+ cursor: text;
+ outline: none;
+ overflow: auto;
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 8e9e8e1cc..05670562e 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -252,7 +252,8 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
onKeyDown = (e: KeyboardEvent) => {
if (this._selectedDocs.length > 0) {
- switch (e.key) {
+ switch (e.key + (e.shiftKey ? 'Shift' : '')) {
+ case 'Enter':
case 'ArrowDown':
{
const lastDoc = this._selectedDocs.lastElement();
@@ -272,6 +273,7 @@ export class CollectionSchemaView extends CollectionSubView() {
e.preventDefault();
}
break;
+ case 'EnterShift':
case 'ArrowUp':
{
const firstDoc = this._selectedDocs.lastElement();
@@ -291,6 +293,7 @@ export class CollectionSchemaView extends CollectionSubView() {
e.preventDefault();
}
break;
+ case 'Tab':
case 'ArrowRight':
if (this._selectedCells) {
this._selectedCol = Math.min(this._colEles.length - 1, this._selectedCol + 1);
@@ -298,6 +301,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.selectCell(this._selectedDocs[0], 0, false, false);
}
break;
+ case 'TabShift':
case 'ArrowLeft':
if (this._selectedCells) {
this._selectedCol = Math.max(0, this._selectedCol - 1);
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index e6acff061..daffdf1f5 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -21,11 +21,11 @@ import DOMPurify from 'dompurify';
*/
export interface SchemaCellFieldProps {
+ Doc: Doc;
contents: FieldType | undefined;
fieldContents?: FieldViewProps;
editing?: boolean;
oneLine?: boolean;
- Document: Doc;
fieldKey: string;
// eslint-disable-next-line no-use-before-define
refSelectModeInfo: { enabled: boolean; currEditing: SchemaCellField | undefined };
@@ -55,7 +55,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
}); //must be moved to end of batch or else other docs aren't loaded, so render as d-1 in function
}
- get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+ get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
get selfRefPattern() {
return `d${this.docIndex}.${this._props.fieldKey}`;
@@ -86,15 +86,6 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
editing => {
if (editing) {
this.setupRefSelect(this.refSelectConditionMet);
- setTimeout(() => {
- if (this._inputref?.innerText.startsWith('=') || this._inputref?.innerText.startsWith(':=')) {
- this._overlayDisposer?.();
- this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
- this._props.highlightCells?.(this._unrenderedContent);
- this.setContent(this._unrenderedContent);
- setTimeout(() => this.setCursorPosition(this._unrenderedContent.length));
- }
- });
} else {
this._overlayDisposer?.();
this._overlayDisposer = undefined;
@@ -192,6 +183,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
setIsFocused = (value: boolean) => {
const wasFocused = this._editing;
this._editing = value;
+ this._editing && setTimeout(() => this._inputref?.focus());
return wasFocused !== this._editing;
};
@@ -272,8 +264,6 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.nativeEvent.defaultPrevented) return; // hack .. DashFieldView grabs native events, but react ignores stoppedPropagation and preventDefault, so we need to check it here
-
switch (e.key) {
case 'Tab':
e.stopPropagation();
@@ -284,9 +274,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
break;
case 'Enter':
e.stopPropagation();
- if (!e.ctrlKey) {
- this.finalizeEdit(e.shiftKey, false, true);
- }
+ !e.ctrlKey && this.finalizeEdit(e.shiftKey, false, true);
break;
case 'Escape':
e.stopPropagation();
@@ -297,7 +285,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
case 'ArrowLeft':
case 'ArrowRight': // prettier-ignore
e.stopPropagation();
- setTimeout(() => this.setupRefSelect(this.refSelectConditionMet), 0);
+ setTimeout(() => this.setupRefSelect(this.refSelectConditionMet));
break;
case ' ':
{
@@ -306,18 +294,14 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
setTimeout(() => {
this.setContent(this._unrenderedContent);
setTimeout(() => this.setCursorPosition(cursorPos));
- }, 0);
+ });
}
break;
- case 'u': // for some reason 'u' otherwise exits the editor
- e.stopPropagation();
- break;
case 'Shift':
case 'Alt':
case 'Meta':
case 'Control':
- case ':': // prettier-ignore
- break;
+ case ':':
default:
break;
}
@@ -361,12 +345,9 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
<div
contentEditable
className="schemaField-editing"
- ref={r => {
- this._inputref = r;
- }}
- style={{ cursor: 'text', outline: 'none', overflow: 'auto', minHeight: `min(100%, ${(this._props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20 }}
+ ref={r => (this._inputref = r)}
+ style={{ minHeight: `min(100%, ${(this._props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20 }}
onBlur={() => (this._props.refSelectModeInfo.enabled ? setTimeout(() => this.setIsFocused(true), 1000) : this.finalizeEdit(false, true, false))}
- autoFocus
onInput={this.onChange}
onKeyDown={this.onKeyDown}
onPointerDown={e => {
@@ -383,14 +364,37 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
);
};
+ onFocus = () => {
+ if (this._inputref?.innerText.startsWith('=') || this._inputref?.innerText.startsWith(':=')) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ this._props.highlightCells?.(this._unrenderedContent);
+ this.setContent(this._unrenderedContent);
+ setTimeout(() => this.setCursorPosition(this._unrenderedContent.length));
+ }
+ };
+
+ onBlur = action(() => {
+ this._editing = false;
+ });
+
render() {
const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
if (this._editing && gval !== undefined) {
- return <div className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}>{this.renderEditor()}</div>;
+ return (
+ <div
+ className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`} //
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}>
+ {this.renderEditor()}
+ </div>
+ );
} else
return this._props.contents instanceof ObjectField ? null : (
<div
className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
style={{
minHeight: '10px',
whiteSpace: this._props.oneLine ? 'nowrap' : 'pre-line',
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index da203abfa..c9853fab0 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -171,7 +171,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
isolatedSelection={this.isolatedSelection}
key={key}
rowSelected={this._props.isSelected}
- Document={this.Document}
+ Doc={this.Document}
col={index}
fieldKey={key}
allowCRs={false} // to enter text with new lines, must use \n
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index d404378eb..e6fe46638 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -40,7 +40,7 @@ import { SchemaCellField } from './SchemaCellField';
*/
export interface SchemaTableCellProps {
- Document: Doc;
+ Doc: Doc;
col: number;
deselectCell: () => void;
selectCell: (doc: Doc, col: number, shift: boolean, ctrl: boolean) => void;
@@ -71,7 +71,7 @@ export interface SchemaTableCellProps {
}
function selectedCell(props: SchemaTableCellProps) {
- return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Document)?.length;
+ return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Doc)?.length;
}
@observer
@@ -84,11 +84,11 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
makeObservable(this);
}
- get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+ get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
get isDefault(){return SchemaColumnHeader.isDefaultField(this._props.fieldKey);} // prettier-ignore
- get lockedInteraction(){return (this.isDefault || this._props.Document._lockedSchemaEditing);} // prettier-ignore
+ get lockedInteraction(){return (this.isDefault || this._props.Doc._lockedSchemaEditing);} // prettier-ignore
get backgroundColor() {
if (this.lockedInteraction) {
@@ -102,7 +102,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return true;
};
public static renderProps(props: SchemaTableCellProps) {
- const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props;
+ const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
let protoCount = 0;
let doc: Doc | undefined = Document;
while (doc) {
@@ -186,7 +186,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return (
<div
className="schemacell-edit-wrapper"
- // onContextMenu={}
+ tabIndex={1}
style={{
color,
textDecoration,
@@ -196,7 +196,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
<SchemaCellField
fieldKey={this._props.fieldKey}
refSelectModeInfo={this._props.refSelectModeInfo}
- Document={this._props.Document}
+ Doc={this._props.Doc}
highlightCells={(text: string) => this._props.highlightCells(this.adjustSelfReference(text))}
getCells={(text: string) => this._props.eqHighlightFunc(this.adjustSelfReference(text))}
ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
@@ -224,7 +224,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
get getCellType() {
const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
- const cellValue = this._props.Document[this._props.fieldKey];
+ const cellValue = this._props.Doc[this._props.fieldKey];
if (cellValue instanceof ImageField) return ColumnType.Image;
if (cellValue instanceof DateField) return ColumnType.Date;
@@ -252,15 +252,15 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
const sides: Array<string | undefined> = [];
sides[0] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // left
sides[1] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // right
- sides[2] = !this._props.isolatedSelection(this._props.Document)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
- sides[3] = !this._props.isolatedSelection(this._props.Document)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
+ sides[2] = !this._props.isolatedSelection(this._props.Doc)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
+ sides[3] = !this._props.isolatedSelection(this._props.Doc)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
return sides;
}
render() {
return (
<div
- className="schema-table-cell"
+ className={`schema-table-cell${selectedCell(this._props) ? '-selected' : ''}`}
onContextMenu={e => StopEvent(e)}
onPointerDown={action(e => {
if (this.lockedInteraction) {
@@ -272,7 +272,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
if (this._props.refSelectModeInfo.enabled && !selectedCell(this._props)) {
e.stopPropagation();
e.preventDefault();
- this._props.selectReference(this._props.Document, this._props.col);
+ this._props.selectReference(this._props.Doc, this._props.col);
return;
}
@@ -280,9 +280,9 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
const ctrl: boolean = e.ctrlKey;
if (this._props.isRowActive?.()) {
if (selectedCell(this._props) && ctrl) {
- this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
e.stopPropagation();
- } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ } else !selectedCell(this._props) && this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
}
})}
style={{
@@ -321,8 +321,8 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
}
get url() {
- const field = Cast(this._props.Document[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this._props.Document[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
+ const field = Cast(this._props.Doc[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(this._props.Doc[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
const altpaths = alts
.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
.filter(url => url)
@@ -359,7 +359,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
};
render() {
- const aspect = Doc.NativeAspect(this._props.Document); // aspect ratio
+ const aspect = Doc.NativeAspect(this._props.Doc); // aspect ratio
// let width = Math.max(75, this._props.columnWidth); // get a with that is no smaller than 75px
// const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined;
@@ -379,7 +379,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
@observable _pickingDate: boolean = false;
@computed get date(): DateField {
// if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
- return DateCast(this._props.Document[this._props.fieldKey]);
+ return DateCast(this._props.Doc[this._props.fieldKey]);
}
handleChange = undoable((date: Date | null) => {
@@ -388,7 +388,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
// this.applyToDoc(this._document, this._props.row, this._props.col, script.run);
// } else {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
- date && (this._props.Document[this._props.fieldKey] = new DateField(date));
+ date && (this._props.Doc[this._props.fieldKey] = new DateField(date));
// }
}, 'date change');
@@ -396,10 +396,10 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
const { pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<>
- <div style={{ pointerEvents: 'none' }}>
+ <div style={{ pointerEvents: 'none' }} tabIndex={1}>
<DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} />
</div>
- {pointerEvents === 'none' ? null : (
+ {pointerEvents === 'none' || !selectedCell(this._props) ? null : (
<Popup
icon={<FontAwesomeIcon size="xs" icon="caret-down" />}
size={Size.XSMALL}
@@ -451,11 +451,11 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
onPointerDown={e => e.stopPropagation()}
style={{ marginRight: 4 }}
type="checkbox"
- checked={BoolCast(this._props.Document[this._props.fieldKey])}
+ checked={BoolCast(this._props.Doc[this._props.fieldKey])}
onChange={undoable((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
if ((value?.nativeEvent as MouseEvent | PointerEvent).shiftKey) {
this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
- } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
+ } else Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
}, 'set bool cell')}
/>
@@ -463,14 +463,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
contents=""
fieldContents={fieldProps}
editing={selectedCell(this._props) ? undefined : false}
- GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
+ GetValue={() => Field.toKeyValueString(this._props.Doc, this._props.fieldKey)}
SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
this._props.finishEdit?.();
return true;
}
- const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+ const set = Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Doc) ? true : undefined);
this._props.finishEdit?.();
return set;
}, 'set bool cell')}
@@ -494,14 +494,22 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
<div style={{ width: '100%' }}>
<Select
styles={{
+ dropdownIndicator: base => ({
+ ...base,
+ display: selectedCell(this._props) ? 'unset' : 'none',
+ }),
container: base => ({
...base,
height: 20,
+ border: 'unset !important',
+ pointerEvents: selectedCell(this._props) ? 'all' : 'none',
}),
control: base => ({
...base,
height: 20,
minHeight: 20,
+ border: 'unset !important',
+ background: selectedCell(this._props) ? 'lightgray' : undefined,
}),
placeholder: base => ({
...base,
@@ -517,6 +525,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
...base,
height: 20,
transform: 'scale(0.75)',
+ border: 'unset !important',
}),
menuPortal: base => ({
...base,
@@ -529,10 +538,10 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
}}
menuPortalTarget={this._props.menuTarget}
menuPosition="absolute"
- placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')}
+ placeholder={StrCast(this._props.Doc[this._props.fieldKey], 'select...')}
options={options}
isMulti={false}
- onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+ onChange={val => Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
/>
</div>
</div>
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 835c28daa..79d0ee88f 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,15 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Colors } from '@dash/components';
import { runInAction } from 'mobx';
-import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { Doc, Opt, StrListCast } from '../../../fields/Doc';
import { InkEraserTool, InkInkTool, InkProperty, InkTool } from '../../../fields/InkField';
-import { List } from '../../../fields/List';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { Gestures } from '../../../pen-gestures/GestureTypes';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
@@ -17,30 +14,30 @@ import { UndoManager, undoable } from '../../util/UndoManager';
import { GestureOverlay } from '../GestureOverlay';
import { InkTranscription } from '../InkTranscription';
import { PropertiesView } from '../PropertiesView';
+import { docSortings } from '../collections/CollectionSubView';
import { CollectionFreeFormView } from '../collections/collectionFreeForm';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import {
ActiveEraserWidth,
- ActiveInkFillColor,
- ActiveInkColor,
ActiveHideTextLabels,
+ ActiveInkColor,
+ ActiveInkFillColor,
ActiveInkWidth,
ActiveIsInkMask,
DocumentView,
- SetActiveInkFillColor,
SetActiveInkColor,
- SetactiveHideTextLabels,
+ SetActiveInkFillColor,
SetActiveInkWidth,
SetActiveIsInkMask,
SetEraserWidth,
+ SetactiveHideTextLabels,
} from '../nodes/DocumentView';
import { ImageBox } from '../nodes/ImageBox';
+import { OpenWhere } from '../nodes/OpenWhere';
import { VideoBox } from '../nodes/VideoBox';
import { WebBox } from '../nodes/WebBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
-import { OpenWhere } from '../nodes/OpenWhere';
-import { docSortings } from '../collections/CollectionSubView';
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function IsNoneSelected() {
@@ -101,10 +98,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
return (selected.lastElement() ?? Doc.UserDoc()).borderColor ?? defaultBorder();
}
if (!selected.length) setDefaultBorder(color ?? 'transparent');
- else
- selected.forEach(doc => {
- doc[DocData].borderColor = color;
- });
+ else selected.forEach(doc => (doc.$borderColor = color));
}
return '';
});
@@ -114,7 +108,7 @@ ScriptingGlobals.add(function setBorderColor(color?: string, checkResult?: boole
ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
const selectedViews = DocumentView.Selected();
const selectedDoc = selectedViews.lastElement()?.Document;
- const defaultFill = selectedDoc?._layout_isSvg ? () => StrCast(selectedDoc[DocData].fillColor) : !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? () => StrCast(Doc.UserDoc().textBackgroundColor, 'transparent') : () => ActiveInkFillColor();
+ const defaultFill = selectedDoc?._layout_isSvg ? () => StrCast(selectedDoc.$fillColor) : !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? () => StrCast(Doc.UserDoc().textBackgroundColor, 'transparent') : () => ActiveInkFillColor();
const setDefaultFill = !Doc.ActiveTool || Doc.ActiveTool === InkTool.None ? (c: string) => { Doc.UserDoc().textBackgroundColor = c; }: SetActiveInkFillColor; // prettier-ignore
if (Doc.ActiveTool !== InkTool.None && !selectedViews.lastElement()?.Document._layout_isSvg) {
if (checkResult) return defaultFill();
@@ -149,10 +143,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
return selected.lastElement()?._backgroundColor ?? defaultFill();
}
if (!selected.length) setDefaultFill(color ?? 'transparent');
- else
- selected.forEach(doc => {
- doc[DocData][doc._layout_isSvg ? 'fillColor' : 'backgroundColor'] = color;
- });
+ else selected.forEach(doc => (doc[doc._layout_isSvg ? '$fillColor' : '$backgroundColor'] = color));
}
return '';
});
@@ -170,7 +161,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
}
if (DocumentView.Selected().length) {
DocumentView.SelectedDocs().forEach(doc => {
- doc[DocData].layout_headingColor = color === 'transparent' ? undefined : color;
+ doc.$layout_headingColor = color === 'transparent' ? undefined : color;
doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title');
});
} else {
@@ -470,23 +461,23 @@ ScriptingGlobals.add(function setInkProperty(option: InkProperty, value: string
// prettier-ignore
const map: Map<InkProperty, { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
[InkProperty.Mask, {
- checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())),
- setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; },
+ checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected.$stroke_isInkMask) : ActiveIsInkMask())),
+ setInk: (doc: Doc) => { doc.$stroke_isInkMask = !doc.stroke_isInkMask; },
setMode: () => SetActiveIsInkMask(value ? true : false)
}],
[InkProperty.Labels, {
- checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_showLabel) : !ActiveHideTextLabels())),
- setInk: (doc: Doc) => { doc[DocData].stroke_showLabel = value; },
+ checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected.$stroke_showLabel) : !ActiveHideTextLabels())),
+ setInk: (doc: Doc) => { doc.$stroke_showLabel = value; },
setMode: () => SetactiveHideTextLabels(value? false : true),
}],
[ InkProperty.StrokeWidth, {
- checkResult: () => (selected?._layout_isSvg ? NumCast(selected[DocData].stroke_width, 1) : ActiveInkWidth()),
- setInk: (doc: Doc) => { doc[DocData].stroke_width = NumCast(value); },
+ checkResult: () => (selected?._layout_isSvg ? NumCast(selected.$stroke_width, 1) : ActiveInkWidth()),
+ setInk: (doc: Doc) => { doc.$stroke_width = NumCast(value); },
setMode: () => SetActiveInkWidth(value.toString()),
}],
[InkProperty.StrokeColor, {
- checkResult: () => (selected?._layout_isSvg? StrCast(selected[DocData].color) : ActiveInkColor()),
- setInk: (doc: Doc) => { doc[DocData].color = String(value); },
+ checkResult: () => (selected?._layout_isSvg? StrCast(selected.$color) : ActiveInkColor()),
+ setInk: (doc: Doc) => { doc.$color = String(value); },
setMode: () => SetActiveInkColor(StrCast(value))
}],
[ InkProperty.EraserWidth, {
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 25e76e2a6..e0d59cc9d 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -296,7 +296,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
'Content-Type': 'application/json',
},
});
- this.Document[DocData].phoneticTranscription = response.data['transcription'];
+ this.Document.$phoneticTranscription = response.data['transcription'];
};
// context menu
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index ce1e9280a..6f86383c2 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -304,7 +304,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
) : (
<DocumentView
{...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore
- Document={this._props.Document}
+ Document={this._renderDoc}
renderDepth={this._props.renderDepth}
isContentActive={this._props.isContentActive}
childFilters={this._props.childFilters}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 5315612e1..992fbba66 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -80,8 +80,8 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
const front = Docs.Create.CenteredTextCreator('question', question, {}, img);
const back = Docs.Create.CenteredTextCreator('answer', answer, {});
if (useDoc) {
- useDoc[DocData][frontKey] = front;
- useDoc[DocData][backKey] = back;
+ useDoc['$' + frontKey] = front;
+ useDoc['$' + backKey] = back;
return useDoc;
}
return Docs.Create.FlashcardDocument(title, front, back, { _width: 300, _height: 300 });
@@ -475,7 +475,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
axios
.post(
'http://localhost:105/recognize/', //
- { file: DocCast(this.Document.audio)[DocData].url },
+ { file: DocCast(this.Document.audio).$url },
{ headers: { 'Content-Type': 'application/json' } }
)
.then(response => {
@@ -520,7 +520,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
action(resp => {
switch (resp && callType) {
case GPTCallType.CHATCARD:
- DocCast(this.dataDoc[this.backKey])[DocData].text = resp;
+ DocCast(this.dataDoc[this.backKey]).$text = resp;
break;
case GPTCallType.QUIZDOC:
this._renderSide = this.backKey;
@@ -618,7 +618,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
const hrefBase64 = await imageUrlToBase64(u);
const response = await gptImageLabel(hrefBase64, 'Answer the following question as a short flashcard response. Do not include a label.' + (this.dataDoc.text as RichTextField)?.Text);
- DocCast(this.dataDoc[this.backKey])[DocData].text = response;
+ DocCast(this.dataDoc[this.backKey]).$text = response;
} catch (error) {
console.log('Error', error);
}
@@ -745,18 +745,18 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
</div>
<div>
<div className="submit-button">
- <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, false)}>
+ {/* <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, false)}>
<FontAwesomeIcon color="white" icon="caret-down" />
- </div>
- <button className="submit-buttonrecord" onClick={this._listening ? this.stopListening : this.startListening} style={{ background: this._listening ? 'lightgray' : '' }}>
+ </div> */}
+ {/* <button className="submit-buttonrecord" onClick={this._listening ? this.stopListening : this.startListening} style={{ background: this._listening ? 'lightgray' : '' }}>
{<FontAwesomeIcon icon="microphone" size="lg" />}
- </button>
- <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, true)} style={{ left: '50px', zIndex: '100' }}>
+ </button> */}
+ {/* <div className="submit-buttonschema-header-button" onPointerDown={e => this.openContextMenu(e.clientX, e.clientY, true)} style={{ left: '50px', zIndex: '100' }}>
<FontAwesomeIcon color="white" icon="caret-down" />
- </div>
- <button className="submit-buttonpronunciation" onClick={this.evaluatePronunciation}>
+ </div> */}
+ {/* <button className="submit-buttonpronunciation" onClick={this.evaluatePronunciation}>
Evaluate Pronunciation
- </button>
+ </button> */}
<button className="submit-buttonsubmit" type="button" onClick={this._renderSide === this.backKey ? () => this.animateFlipping(this.frontKey) : this.handleRenderGPTClick}>
{this._renderSide === this.backKey ? 'Redo the Question' : 'Submit'}
</button>
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index d5e37b3b5..117eb05f8 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,43 +1,39 @@
+import { Colors, Toggle, ToggleType, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Checkbox } from '@mui/material';
-import { Colors, Toggle, ToggleType, Type } from '@dash/components';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ClientUtils, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
+import { returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols';
+import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { PrefetchProxy } from '../../../../fields/Proxy';
import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { CsvField } from '../../../../fields/URLField';
-import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
+import { TraceMobx } from '../../../../fields/util';
import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT';
import { DocUtils } from '../../../documents/DocUtils';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
-import { LinkManager } from '../../../util/LinkManager';
import { UndoManager, undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { PinProps } from '../../PinFuncs';
import { SidebarAnnos } from '../../SidebarAnnos';
-import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { FocusViewOptions } from '../FocusViewOptions';
import './DataVizBox.scss';
-import { Col, DataVizTemplateInfo, DocCreatorMenu, LayoutType} from './DocCreatorMenu/DocCreatorMenu';
+import { Col, DocCreatorMenu } from './DocCreatorMenu/DocCreatorMenu';
+import { TemplateFieldSize, TemplateFieldType } from './DocCreatorMenu/TemplateBackend';
import { Histogram } from './components/Histogram';
import { LineChart } from './components/LineChart';
import { PieChart } from './components/PieChart';
import { TableBox } from './components/TableBox';
-import { TemplateFieldSize, TemplateFieldType } from './DocCreatorMenu/TemplateBackend';
export enum DataVizView {
TABLE = 'table',
@@ -440,10 +436,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
if (!this.records.length) return 'no data/visualization';
switch (this.dataVizView) {
- case DataVizView.TABLE: return <TableBox {...sharedProps} specHighlightedRow={this._specialHighlightedRow} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>;
- case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} vizBox={this} />;
- case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} />;
- case DataVizView.PIECHART: return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}}
+ case DataVizView.TABLE: return <TableBox {...sharedProps} Doc={this.Document} specHighlightedRow={this._specialHighlightedRow} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>;
+ case DataVizView.LINECHART: return <LineChart {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} vizBox={this} />;
+ case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} />;
+ case DataVizView.PIECHART: return <PieChart {...sharedProps} Doc={this.Document} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}}
margin={{ top: 10, right: 15, bottom: 15, left: 15 }} />;
default:
} // prettier-ignore
@@ -574,9 +570,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined);
- cols.forEach(col => {
- this.setColumnDefault(col, `${this.records[rowToCheck][col]}`);
- });
+ cols.forEach(col => this.setColumnDefault(col, `${this.records[rowToCheck][col]}`));
};
updateGPTSummary = async () => {
@@ -706,7 +700,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={this._sidebarRef}
{...this._props}
fieldKey={this.fieldKey}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index 5a9442d2f..5450d03b1 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,27 +1,29 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ColorPicker, EditableText, IconButton, Size, Type } from '@dash/components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, makeObservable, observable } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { FaFillDrip } from 'react-icons/fa';
-import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc';
+import { Doc, NumListCast } from '../../../../../fields/Doc';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
import { undoable } from '../../../../util/UndoManager';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
-import { PinProps, PinDocView } from '../../../PinFuncs';
+import { PinDocView, PinProps } from '../../../PinFuncs';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
import './Chart.scss';
+export interface HistogramData {
+ [key: string]: string | number;
+}
export interface HistogramProps {
- Document: Doc;
+ Doc: Doc;
layoutDoc: Doc;
axes: string[];
- titleCol: string;
- records: { [key: string]: any }[];
+ records: HistogramData[];
width: number;
height: number;
dataDoc: Doc;
@@ -39,64 +41,85 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _histogramRef: HTMLDivElement | null = null;
private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
- private numericalXData: boolean = false; // whether the data is organized by numbers rather than categoreis
- private numericalYData: boolean = false; // whether the y axis is controlled by provided data rather than frequency
- private maxBins = 15; // maximum number of bins that is readable on a normal sized doc
- @observable _currSelected: any | undefined = undefined; // Object of selected bar
- private curBarSelected: any = undefined; // histogram bin of selected bar for when just one bar is selected
- private selectedData: any[] = []; // array of selected bars
- private hoverOverData: any = undefined; // Selection of bar being hovered over
-
- constructor(props: any) {
+ private _numericalXData: boolean = false; // whether the data is organized by numbers rather than categoreis
+ private _numericalYData: boolean = false; // whether the y axis is controlled by provided data rather than frequency
+ private _maxBins = 15; // maximum number of bins that is readable on a normal sized doc
+ private _selectedBars: HistogramData[] = [];
+ @observable private _currSelected: { [key: string]: string | number; frequency: number } | undefined = undefined;
+
+ constructor(props: HistogramProps) {
super(props);
makeObservable(this);
}
+ @computed get xAxis() {
+ return this._props.axes[0];
+ }
+
+ @computed get yAxis() {
+ return this._props.axes[1];
+ }
+
+ @computed get Doc() {
+ return this._props.Doc;
+ }
+ @computed get layoutDoc() {
+ return this._props.layoutDoc;
+ }
+
@computed get _tableDataIds() {
return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
- @computed get _tableData() {
+
+ @computed get _tableData(): Record<string, string | number>[] {
return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
- // filters all data to just display selected data if brushed (created from an incoming link)
- @computed get _histogramData() {
+
+ @computed get _histogramData(): HistogramData[] {
if (this._props.axes.length < 1) return [];
if (this._props.axes.length < 2) {
- const ax0 = this._props.axes[0];
- if (!/[A-Za-z-:]/.test(this._props.records[0][ax0])) {
- this.numericalXData = true;
+ if (!/[A-Za-z-:]/.test(this._props.records[0][this.xAxis] as string)) {
+ this._numericalXData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
+ return this._tableData.map(record => ({ [this.xAxis]: record[this.xAxis] }));
}
- const [ax0, ax1] = this._props.axes;
- if (!/[A-Za-z-:]/.test(this._props.records[0][ax0])) {
- this.numericalXData = true;
+ if (!/[A-Za-z-:]/.test(this._props.records[0][this.xAxis] as string)) {
+ this._numericalXData = true;
}
- if (!/[A-Za-z-:]/.test(this._props.records[0][ax1])) {
- this.numericalYData = true;
+ if (!/[A-Za-z-:]/.test(this._props.records[0][this.yAxis] as string)) {
+ this._numericalYData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]], [ax1]: record[this._props.axes[1]] }));
+ return this._tableData.map(record => ({
+ [this.xAxis]: record[this.xAxis],
+ [this.yAxis]: record[this.yAxis],
+ }));
}
- @computed get defaultGraphTitle() {
- const [ax0, ax1] = this._props.axes;
- if (this._props.axes.length < 2 || !ax1 || !/\d/.test(this._props.records[0][ax1]) || !this.numericalYData) {
- return ax0 + ' Histogram';
+ @computed get defaultGraphTitle(): string {
+ if (!this.yAxis || !/\d/.test(this._props.records[0][this.yAxis] as string) || !this._numericalYData) {
+ return this.xAxis + ' Histogram';
}
- return ax0 + ' by ' + ax1 + ' Histogram';
+ return this.xAxis + ' by ' + this.yAxis + ' Histogram';
}
@computed get parentViz() {
- return DocCast(this._props.Document.dataViz_parentViz);
+ return DocCast(this._props.Doc.dataViz_parentViz);
+ }
+
+ @computed get defaultBarColor() {
+ return Cast(this.layoutDoc.dataViz_histogram_defaultColor, 'string', '#69b3a2');
+ }
+ @computed get barColors() {
+ return Cast(this.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
+ }
+ @computed get selectedBins() {
+ return Cast(this.layoutDoc.dataViz_histogram_selectedBins, listSpec('number'), null);
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
- if (this.numericalXData) {
- const data = this.data(this._histogramData);
- return { xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin: 0, yMax: 0 };
- }
- return { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };
+ const data = this._numericalXData ? this.data(this._histogramData) : [0];
+ return { xMin: Math.min(...data), xMax: Math.max(...data), yMin: 0, yMax: 0 };
}
componentWillUnmount() {
@@ -104,21 +127,31 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
}
componentDidMount() {
// restore selected bars
- const svg = this._histogramSvg;
- if (svg) {
- const selectedDataBars = StrListCast(this._props.layoutDoc.dataViz_histogram_selectedData);
- svg.selectAll('rect').attr('class', (d: any) => {
- let selected = false;
- selectedDataBars.forEach(eachSelectedData => {
- if (d[0] === eachSelectedData) selected = true;
- });
- if (selected) {
- this.selectedData.push(d);
- return 'histogram-bar hover';
- }
- return 'histogram-bar';
- });
- }
+ this._histogramSvg?.selectAll('rect').attr('class', dIn => {
+ const d = dIn as HistogramData;
+ if (this.selectedBins.some(selBin => d[0] === selBin)) {
+ this._selectedBars.push(d);
+ return 'histogram-bar hover';
+ }
+ return 'histogram-bar';
+ });
+ // setup filters to watch selections and filter toggle
+ this._disposers.selection = reaction(
+ () => ({ filter: this.layoutDoc.dataViz_filterSelection, hists: this._selectedBars.slice(), cur: this._currSelected }),
+ ({ filter, hists }) => {
+ this.layoutDoc.dataViz_selectedRows = !filter
+ ? undefined
+ : new List<number>(
+ this._tableDataIds.filter(rowID =>
+ hists.some(h => {
+ const val = this._props.records[rowID][this.xAxis];
+ return val == h.x0 || (+val >= +h.x0 && +val <= +h.x1);
+ })
+ )
+ );
+ },
+ { fireImmediately: true }
+ );
}
// create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc)
@@ -126,7 +159,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
const anchor = Docs.Create.ConfigDocument({
title: 'histogram doc selection' + this._currSelected,
});
- PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
return anchor;
};
@@ -139,110 +172,92 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
}
// cleans data by converting numerical data to numbers and taking out empty cells
- data = (dataSet: any) => {
- const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key] as any)));
+ data = (dataSet: HistogramData[]): number[] => {
+ const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key] as number)));
const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
return !field
? []
- : validData.map((d: { [x: string]: any }) =>
- !this.numericalXData //
- ? d[field]
- : +d[field!].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '')
+ : validData.map(d =>
+ !this._numericalXData //
+ ? (d[field] as number)
+ : +d[field].toString().replace(/\$/g, '').replace(/%/g, '').replace(/</g, '')
);
};
+ barLabel = (d: d3.Bin<number, number> | HistogramData) => '' + (Array.isArray(d) ? d[0] : d[0]);
+
// outlines the bar selected / hovered over
- highlightSelectedBar = (changeSelectedVariables: boolean, svg: any, eachRectWidth: any, pointerX: any, xAxisTitle: any, yAxisTitle: any, histDataSet: any) => {
+ highlightSelectedBar = (changeSelectedVariables: boolean, svg: d3.Selection<SVGGElement, unknown, null, undefined>, eachRectWidth: number, pointerX: number, xAxisTitle: string, yAxisTitle: string, histDataSet: HistogramData[]) => {
let barCounter = -1;
- const selected = svg.selectAll('.histogram-bar').filter((d: any) => {
+ let hoverOverBar: HistogramData | undefined;
+ svg.selectAll('.histogram-bar').filter(dIn => {
+ const d = dIn as HistogramData;
barCounter++; // uses the order of bars and width of each bar to find which one the pointer is over
- if (d.length && barCounter * eachRectWidth <= pointerX && pointerX <= (barCounter + 1) * eachRectWidth) {
- let showSelected = this.numericalYData
- ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') == d[0])[0]
- : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') == d[0])[0];
- if (this.numericalXData) {
- // calculating frequency
- if (d[0] && d[1] && d[0] !== d[1]) {
- showSelected = { [xAxisTitle]: d3.min(d) + ' to ' + d3.max(d), frequency: d.length };
- } else if (!this.numericalYData) showSelected = { [xAxisTitle]: showSelected[xAxisTitle], frequency: d.length };
- }
+ if (d.length && (barCounter * eachRectWidth <= pointerX + 1 || (!barCounter && pointerX <= 0)) && pointerX - 1 <= (barCounter + 1) * eachRectWidth) {
if (changeSelectedVariables) {
// for when a bar is selected - not just hovered over
- let sameAsAny = false;
- const selectedDataBars = Cast(this._props.layoutDoc.dataViz_histogram_selectedData, listSpec('number'), null);
- this.selectedData.forEach(eachData => {
- if (!sameAsAny) {
- let match = true;
- Object.keys(d).forEach(key => {
- if (d[key] !== eachData[key]) match = false;
- });
- if (match) {
- sameAsAny = true;
- const index = this.selectedData.indexOf(eachData);
- this.selectedData.splice(index, 1);
- selectedDataBars.splice(index, 1);
- this._currSelected = undefined;
- }
- }
- });
- if (!sameAsAny) {
- this.selectedData.push(d);
- selectedDataBars.push(d[0]);
- this._currSelected = this.selectedData.length > 1 ? undefined : showSelected;
+ const alreadySelected = this._selectedBars.findIndex(eachData => !Object.keys(d).some(key => d[key] !== eachData[key]));
+ if (alreadySelected !== -1) {
+ this._selectedBars.splice(alreadySelected, 1);
+ this.selectedBins.splice(alreadySelected, 1);
+ } else {
+ this._selectedBars.push(d);
+ this.selectedBins.push(d[0] as number);
}
+ const showSelectedLabel = (dataset: HistogramData[]) => {
+ const datum = dataset.lastElement();
+ const datumNum = datum as unknown as number[];
+ const showSelectedStart = this._numericalYData
+ ? this._histogramData.filter(data => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') == d[0])[0]
+ : histDataSet.filter(data => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') == d[0])[0];
- // for filtering child dataviz docs
- if (this._props.layoutDoc.dataViz_filterSelection) {
- const selectedRows = Cast(this._props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
- this._tableDataIds.forEach(rowID => {
- let match = false;
- for (let i = 0; i < d.length; i++) {
- console.log('Compare: ' + this._props.records[rowID][xAxisTitle].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') + ' = ' + d[i]);
- if (this._props.records[rowID][xAxisTitle].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') == d[i]) match = true;
- }
- if (match && !selectedRows?.includes(rowID))
- selectedRows?.push(rowID); // adding to filtered rows
- else if (match && sameAsAny) selectedRows.splice(selectedRows.indexOf(rowID), 1); // removing from filtered rows
- });
- }
- } else this.hoverOverData = d;
+ const selectionLabel = dataset.length > 1
+ ? dataset.map(dd => this.barLabel(dd)).join('::')
+ : !this._numericalXData
+ ? this.barLabel(d)
+ : datum[0] !== undefined && datum[1] && datum[0] !== datum[1]
+ ? d3.min(datumNum) + ' to ' + d3.max(datumNum)
+ : !this._numericalYData
+ ? showSelectedStart?.[xAxisTitle]
+ : this.barLabel(d); // prettier-ignore
+ return { [xAxisTitle]: selectionLabel, frequency: dataset.length > 1 ? Number.NaN : datum.length } as { [key: string]: string | number; frequency: number };
+ };
+ this._currSelected = this._selectedBars.length ? showSelectedLabel(this._selectedBars) : undefined;
+ } else hoverOverBar = d;
return true;
}
return false;
});
- if (changeSelectedVariables) {
- if (this._currSelected) this.curBarSelected = selected;
- else this.curBarSelected = undefined;
- }
+ return hoverOverBar;
};
// draws the histogram
- drawChart = (dataSet: any, width: number, height: number) => {
+ drawChart = (dataSet: HistogramData[], width: number, height: number) => {
if (dataSet?.length <= 0) return;
d3.select(this._histogramRef).select('svg').remove();
d3.select(this._histogramRef).select('.tooltip').remove();
const data = this.data(dataSet);
const xAxisTitle = Object.keys(dataSet[0])[0];
- const yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
+ const yAxisTitle = this._numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
const uniqueArr: unknown[] = [...new Set(data)];
- let numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
- let translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
- if (numBins > this.maxBins) numBins = this.maxBins;
- const startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
- const endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
+ let numBins = this._numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
+ let translateXAxis = !this._numericalXData || numBins < this._maxBins ? width / (numBins + 1) / 2 : 0;
+ if (numBins > this._maxBins) numBins = this._maxBins;
+ const startingPoint = this._numericalXData ? this.rangeVals.xMin! : 0;
+ const endingPoint = this._numericalXData ? this.rangeVals.xMax! : numBins;
// converts data into Objects
- let histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key] as any)));
- if (!this.numericalXData) {
- const histStringDataSet: { [x: string]: unknown }[] = [];
- if (this.numericalYData) {
+ let histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key] as number)));
+ if (!this._numericalXData) {
+ const histStringDataSet: { [x: string]: number }[] = [];
+ if (this._numericalYData) {
for (let i = 0; i < dataSet.length; i++) {
- histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle] });
+ histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle] as number, [xAxisTitle]: dataSet[i][xAxisTitle] as number });
}
} else {
for (let i = 0; i < uniqueArr.length; i++) {
- histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] });
+ histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] as number });
}
for (let i = 0; i < data.length; i++) {
const barData = histStringDataSet.filter(each => each[xAxisTitle] == data[i]);
@@ -263,12 +278,12 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
.attr('transform', 'translate(' + this._props.margin.left + ',' + this._props.margin.top + ')'));
let x = d3
.scaleLinear()
- .domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
+ .domain(this._numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
.range([0, width]);
const histogram = d3
- .histogram()
+ .bin()
.value(d => d)
- .domain([startingPoint!, endingPoint!])
+ .domain([startingPoint, endingPoint])
.thresholds(x.ticks(numBins));
const bins = histogram(data);
let eachRectWidth = width / bins.length;
@@ -279,7 +294,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
// more calculations based on bins
// x-axis
- if (!this.numericalXData) {
+ if (!this._numericalXData) {
// reorganize to match data if the data is strings rather than numbers
// uniqueArr.sort()
histDataSet.sort();
@@ -294,9 +309,6 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
}
bins.pop();
eachRectWidth = width / bins.length;
- bins.forEach(d => {
- d.x0 = d.x0!;
- });
xAxis = d3
.axisBottom(x)
.ticks(bins.length > 1 ? bins.length - 1 : 1)
@@ -329,15 +341,15 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
x.range([0, width - eachRectWidth]);
}
// y-axis
- const maxFrequency = this.numericalYData ?
- d3.max(histDataSet, (d: any) => (d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '')
- .replace(/%/g, '').replace(/</g, '')) : 0)) :
+ const maxFrequency = this._numericalYData ?
+ d3.max(histDataSet, d => (d[yAxisTitle] ?
+ Number(StrCast(d[yAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '')) : 0)) :
d3.max(bins, d => d.length); // prettier-ignore
const y = d3.scaleLinear().range([height, 0]);
- y.domain([0, +maxFrequency!]);
+ y.domain([0, maxFrequency ?? 0]);
const yAxis = d3.axisLeft(y).ticks(maxFrequency!);
- if (this.numericalYData) {
- const yScale = scaleCreatorNumerical(0, Number(maxFrequency), height, 0);
+ if (this._numericalYData) {
+ const yScale = scaleCreatorNumerical(0, maxFrequency ?? 0, height, 0);
yAxisCreator(svg.append('g'), width, yScale);
} else {
svg.append('g').call(yAxis);
@@ -347,29 +359,14 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
.call(xAxis);
// click/hover
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const updateHighlights = (hoverOverBar?: HistogramData) => svg.selectAll('rect').attr('class', (d: any) => 'histogram-bar' + (hoverOverBar?.[0] == d[0] || this._selectedBars.some(hist => d[0] === hist[0]) ? ' hover' : ''));
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
- const onHover = action((e: any) => {
- this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
- // eslint-disable-next-line no-use-before-define
- updateHighlights();
- });
- const mouseOut = action(() => {
- this.hoverOverData = undefined;
- // eslint-disable-next-line no-use-before-define
- updateHighlights();
- });
- const updateHighlights = () => {
- const hoverOverBar = this.hoverOverData;
- const { selectedData } = this;
- svg.selectAll('rect').attr('class', (d: any) => {
- let selected = false;
- selectedData.forEach(eachSelectedData => {
- if (d[0] === eachSelectedData[0]) selected = true;
- });
- return (hoverOverBar && hoverOverBar[0] == d[0]) || selected ? 'histogram-bar hover' : 'histogram-bar';
- });
- };
- svg.on('click', onPointClick).on('mouseover', onHover).on('mouseout', mouseOut);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const mouseEnter = (e: any) => updateHighlights(this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
+ svg.on('click', onPointClick).on('pointerenter', mouseEnter).on('pointerleave', updateHighlights);
// axis titles
svg.append('text')
@@ -385,138 +382,54 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
d3.format('.0f');
// draw bars
- const selected = this.selectedData;
+ const selected = this._selectedBars;
svg.selectAll('rect')
.data(bins)
.enter()
.append('rect')
- .attr(
- 'transform',
- this.numericalYData
+ .attr('transform', this._numericalYData
? d => {
- const eachData = histDataSet.filter((hData: { [x: string]: number }) => hData[xAxisTitle] == d[0]);
+ const eachData = histDataSet.filter((hData: HistogramData) => hData[xAxisTitle] == d[0]);
const length = eachData.length ? StrCast(eachData[0][yAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') : 0;
return 'translate(' + x(d.x0!) + ',' + y(Number(length)) + ')';
}
- : d => 'translate(' + x(d.x0!) + ',' + y(d.length) + ')'
- )
- .attr(
- 'height',
- this.numericalYData
+ : d => 'translate(' + x(d.x0!) + ',' + y(d.length) + ')')
+ .attr('height', this._numericalYData
? d => {
- const eachData = histDataSet.filter((hData: { [x: string]: number }) => hData[xAxisTitle] == d[0]);
+ const eachData = histDataSet.filter(hData => hData[xAxisTitle] == d[0]);
const length = eachData.length ? StrCast(eachData[0][yAxisTitle]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') : 0;
- return height - y(Number(length));
+ return height - y(+length);
}
- : d => height - y(d.length)
- )
+ : d => height - y(d.length))
.attr('width', eachRectWidth)
- .attr('class', selected ? d => (selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar') : () => 'histogram-bar')
- .attr('fill', d => {
- let barColor;
- const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
- barColors.forEach(each => {
- // eslint-disable-next-line prefer-destructuring
- if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
- else {
- const range = StrCast(each[0]).split(' to ');
- // eslint-disable-next-line prefer-destructuring
- if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
- }
- });
- return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
- });
- };
-
- @action changeSelectedColor = (color: string) => {
- this.curBarSelected.attr('fill', color);
- const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
-
- const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
- barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
- barColors.push(StrCast(barName + '::' + color));
+ .attr('class', selected ? d => (selected?.[0]?.x0 == d.x0 ? 'histogram-bar hover' : 'histogram-bar') : () => 'histogram-bar')
+ .attr('fill', d => this.barColors?.map(bar => bar.split('::')).find(([barLabel]) => barLabel === this.barLabel(d))?.[1] ?? this.defaultBarColor); // prettier-ignore
};
- @action eraseSelectedColor = () => {
- this.curBarSelected.attr('fill', this._props.layoutDoc.dataViz_histogram_defaultColor);
- const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
-
- const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
- barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
- };
-
- // reloads the bar colors and selected bars
- updateSavedUI = () => {
- const svg = this._histogramSvg;
- if (svg) {
- // bar color
- svg.selectAll('rect').attr('fill', (d: any) => {
- let barColor;
- const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
- barColors.forEach(each => {
- // eslint-disable-next-line prefer-destructuring
- if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
- else {
- const range = StrCast(each[0]).split(' to ');
- // eslint-disable-next-line prefer-destructuring
- if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
- }
- });
- return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
- });
- }
+ @action changeSelectedColor = (color: string, erase?: boolean) => {
+ if (!this.barColors) this.layoutDoc.dataViz_histogram_barColors = new List<string>();
+ this._selectedBars.map(this.barLabel).forEach(barName => {
+ this.barColors.forEach(bar => bar.split('::')[0] === barName && this.barColors.splice(this.barColors.indexOf(bar), 1));
+ !erase && this.barColors.push(barName + '::' + color);
+ });
};
render() {
- this.updateSavedUI();
- this._histogramData;
- let curSelectedBarName = '';
- let titleAccessor: any = 'dataViz_histogram_title';
- if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
- else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
- if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
- if (!this._props.layoutDoc.dataViz_histogram_defaultColor) this._props.layoutDoc.dataViz_histogram_defaultColor = '#69b3a2';
- if (!this._props.layoutDoc.dataViz_histogram_barColors) this._props.layoutDoc.dataViz_histogram_barColors = new List<string>();
- if (!this._props.layoutDoc.dataViz_histogram_selectedData) this._props.layoutDoc.dataViz_histogram_selectedData = new List<string>();
- let selected = 'none';
- if (this._currSelected) {
- curSelectedBarName = StrCast(this._currSelected![this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
- selected = '{ ';
- Object.keys(this._currSelected).forEach(key => {
- key //
- ? (selected += key + ': ' + this._currSelected[key] + ', ')
- : '';
- });
- selected = selected.substring(0, selected.length - 2) + ' }';
- if (this._props.titleCol !== '' && (!this._currSelected.frequency || this._currSelected.frequency < 10)) {
- selected += '\n' + this._props.titleCol + ': ';
- this._tableData.forEach(each => {
- if (this._currSelected[this._props.axes[0]] === each[this._props.axes[0]]) {
- if (this._props.axes[1]) {
- if (this._currSelected[this._props.axes[1]] === each[this._props.axes[1]]) selected += each[this._props.titleCol] + ', ';
- } else selected += each[this._props.titleCol] + ', ';
- }
- });
- selected = selected.slice(0, -1).slice(0, -1);
- }
- }
- let selectedBarColor;
- const barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.forEach(each => {
- // eslint-disable-next-line prefer-destructuring
- each[0] === curSelectedBarName && (selectedBarColor = each[1]);
- });
+ if (!this.selectedBins) this.layoutDoc.dataViz_histogram_selectedBins = new List<string>();
+
+ const titleAccessor = 'dataViz_histogram_title ' + this.xAxis + (this.yAxis ? '-' + this._props.axes[1] : '');
+ const selected = !this._currSelected ? 'none' : '{ ' + Object.keys(this._currSelected).map(key => key ? key + ': ' + this._currSelected?.[key]:'').join(", ") + ' }'; // prettier-ignore
+ const curSelectedBarName = this._selectedBars.length && this.barLabel(this._selectedBars.lastElement()); //.[this.xAxis]).replace(/\$/g, '').replace(/%/g, '').replace(/</g, '');
+ const selectedBarColor = this.barColors?.map(bar => bar.split('::'))?.find(([barLabel]) => barLabel === curSelectedBarName)?.[1];
if (this._histogramData.length > 0 || !this.parentViz) {
return this._props.axes.length >= 1 ? (
<div className="chart-container" style={{ width: this._props.width + this._props.margin.right }}>
<div className="graph-title">
<EditableText
- val={StrCast(this._props.layoutDoc[titleAccessor])}
+ val={StrCast(this.layoutDoc[titleAccessor], this.defaultGraphTitle)}
setVal={undoable(
- action(val => {
- this._props.layoutDoc[titleAccessor] = val as string;
- }),
+ action(val => (this.layoutDoc[titleAccessor] = val)),
'Change Graph Title'
)}
color="black"
@@ -528,13 +441,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
tooltip="Change Default Bar Color"
type={Type.SEC}
icon={<FaFillDrip />}
- selectedColor={StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor)}
- setFinalColor={undoable(color => {
- this._props.layoutDoc.dataViz_histogram_defaultColor = color;
- }, 'Change Default Bar Color')}
- setSelectedColor={undoable(color => {
- this._props.layoutDoc.dataViz_histogram_defaultColor = color;
- }, 'Change Default Bar Color')}
+ selectedColor={this.defaultBarColor}
+ setFinalColor={undoable(color => (this.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
+ setSelectedColor={undoable(color => (this.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
size={Size.XSMALL}
/>
</div>
@@ -552,9 +461,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
tooltip="Change Bar Color"
type={Type.SEC}
icon={<FaFillDrip />}
- selectedColor={selectedBarColor || this.curBarSelected.attr('fill')}
- setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
- setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
+ selectedColor={selectedBarColor}
+ setFinalColor={undoable(this.changeSelectedColor, 'Change Selected Bar Color')}
+ setSelectedColor={undoable(this.changeSelectedColor, 'Change Selected Bar Color')}
size={Size.XSMALL}
/>
&nbsp;
@@ -564,7 +473,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
color="black"
type={Type.SEC}
tooltip="Revert to the default bar color" //
- onClick={undoable(this.eraseSelectedColor, 'Change Selected Bar Color')}
+ onClick={undoable(() => this.changeSelectedColor(this.defaultBarColor, true), 'Change Selected Bar Color')}
/>
</div>
) : null}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index b55d509ff..6b047546c 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -22,7 +22,7 @@ export interface SelectedDataPoint extends DataPoint {
}
export interface LineChartProps {
vizBox: DataVizBox;
- Document: Doc;
+ Doc: Doc;
layoutDoc: Doc;
axes: string[];
titleCol: string;
@@ -53,7 +53,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
}
@computed get titleAccessor() {
- let titleAccessor: any = 'dataViz_lineChart_title';
+ let titleAccessor = 'dataViz_lineChart_title';
if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
return titleAccessor;
@@ -74,7 +74,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
return this._props.axes[1] + ' vs. ' + this._props.axes[0] + ' Line Chart';
}
@computed get parentViz() {
- return DocCast(this._props.Document.dataViz_parentViz);
+ return DocCast(this._props.Doc.dataViz_parentViz);
}
@computed get incomingHighlited() {
// return selected x and y axes
@@ -113,7 +113,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
//
title: 'line doc selection' + (this._currSelected?.x ?? ''),
});
- PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
return anchor;
};
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 86e6ad8e4..0ae70786f 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -16,7 +16,7 @@ import { PinProps, PinDocView } from '../../../PinFuncs';
import './Chart.scss';
export interface PieChartProps {
- Document: Doc;
+ Doc: Doc;
layoutDoc: Doc;
axes: string[];
titleCol: string;
@@ -83,7 +83,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
}
@computed get parentViz() {
- return DocCast(this._props.Document.dataViz_parentViz);
+ return DocCast(this._props.Doc.dataViz_parentViz);
}
componentWillUnmount() {
@@ -114,7 +114,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
//
title: 'piechart doc selection' + this._currSelected,
});
- PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Doc);
return anchor;
};
@@ -169,7 +169,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
// inside the slice of it crosses an odd number of edges
const showSelected = this.byCategory ? pieDataSet[index] : this._pieChartData[index];
let key = 'data'; // key that represents slice
- // eslint-disable-next-line prefer-destructuring
if (Object.keys(showSelected)[0] === 'frequency') key = Object.keys(showSelected)[1];
if (changeSelectedVariables) {
let sameAsAny = false;
@@ -296,7 +295,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
if (descriptionField) dataPointVal[descriptionField] = each[descriptionField];
try {
dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, ''));
- } catch (error) {
+ } catch {
/* empty */
}
possibleDataPointVals.push(dataPointVal);
@@ -306,7 +305,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
// to make sure all important slice information is on 'd' object
let addKey: any = false;
if (pieDataSet.length && Object.keys(pieDataSet[0])[0] === 'frequency') {
- // eslint-disable-next-line prefer-destructuring
addKey = Object.keys(pieDataSet[0])[1];
}
arcs.append('path')
@@ -324,7 +322,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
const sliceTitle = dataPoint[this._props.axes[0]];
const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '') : sliceTitle;
sliceColors.forEach(each => {
- // eslint-disable-next-line prefer-destructuring
each[0] === accessByName && (sliceColor = each[1]);
});
}
@@ -337,7 +334,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
});
return selectThisData ? 'slice hover' : 'slice';
})
- // @ts-ignore
+ // @ts-expect-error types don't match
.attr('d', arc)
.on('click', onPointClick)
.on('mouseover', onHover)
@@ -388,7 +385,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
};
render() {
- let titleAccessor: any = 'dataViz_pie_title';
+ let titleAccessor = 'dataViz_pie_title';
if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
@@ -420,7 +417,6 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
let selectedSliceColor;
const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
sliceColors.forEach(each => {
- // eslint-disable-next-line prefer-destructuring
if (each[0] === curSelectedSliceName!) selectedSliceColor = each[1];
});
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 7ef4bca6b..b73123691 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -5,7 +5,6 @@ import * as React from 'react';
import { ClientUtils, setupMoveUpEvents } from '../../../../../ClientUtils';
import { emptyFunction } from '../../../../../Utils';
import { Doc, Field, NumListCast } from '../../../../../fields/Doc';
-import { DocData } from '../../../../../fields/DocSymbols';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast } from '../../../../../fields/Types';
@@ -20,7 +19,7 @@ import './Chart.scss';
const { DATA_VIZ_TABLE_ROW_HEIGHT } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
interface TableBoxProps {
- Document: Doc;
+ Doc: Doc;
layoutDoc: Doc;
records: { [key: string]: unknown }[];
selectAxes: (axes: string[]) => void;
@@ -82,7 +81,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
}
@computed get parentViz() {
- return DocCast(this._props.Document.dataViz_parentViz);
+ return DocCast(this._props.Doc.dataViz_parentViz);
}
@computed get columns() {
@@ -140,28 +139,28 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
e,
moveEv => {
// dragging off a column to create a brushed DataVizBox
- const sourceAnchorCreator = () => this._props.docView?.()?.Document || this._props.Document;
+ const sourceAnchorCreator = () => this._props.docView?.()?.Document || this._props.Doc;
const targetCreator = (annotationOn: Doc | undefined) => {
const doc = this._props.docView?.()?.Document;
if (doc) {
const embedding = Doc.MakeEmbedding(doc);
embedding._dataViz = DataVizView.TABLE;
embedding._dataViz_axes = new List<string>([col]);
- embedding._dataViz_parentViz = this._props.Document;
+ embedding._dataViz_parentViz = this._props.Doc;
embedding.annotationOn = annotationOn;
embedding.histogramBarColors = Field.Copy(this._props.layoutDoc.histogramBarColors);
embedding.defaultHistogramColor = this._props.layoutDoc.defaultHistogramColor;
embedding.pieSliceColors = Field.Copy(this._props.layoutDoc.pieSliceColors);
return embedding;
}
- return this._props.Document;
+ return this._props.Doc;
};
if (this._props.docView?.() && !ClientUtils.isClick(moveEv.clientX, moveEv.clientY, downX, downY, Date.now())) {
DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
dragComplete: completeEv => {
if (!completeEv.aborted && completeEv.annoDragData && completeEv.annoDragData.linkSourceDoc && completeEv.annoDragData.dropDocument && completeEv.linkDocument) {
- completeEv.linkDocument[DocData].link_matchEmbeddings = true;
- completeEv.linkDocument[DocData].stroke_startMarker = true;
+ completeEv.linkDocument.$link_matchEmbeddings = true;
+ completeEv.linkDocument.$stroke_startMarker = true;
this._props.docView?.()?._props.addDocument?.(completeEv.linkDocument);
}
},
diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx
index a49c69be3..3b666bad5 100644
--- a/src/client/views/nodes/DiagramBox.tsx
+++ b/src/client/views/nodes/DiagramBox.tsx
@@ -3,7 +3,6 @@ import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { Cast, DocCast, NumCast, RTFCast, StrCast } from '../../../fields/Types';
import { Gestures } from '../../../pen-gestures/GestureTypes';
@@ -46,7 +45,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable _errorMessage = '';
@computed get mermaidcode() {
- return StrCast(this.Document[DocData].text, RTFCast(this.Document[DocData].text)?.Text);
+ return StrCast(this.Document.$text, RTFCast(this.Document.$text)?.Text);
}
componentDidMount() {
@@ -89,7 +88,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
setMermaidCode = undoable((res: string) => {
- this.Document[DocData].text = new RichTextField(
+ this.Document.$text = new RichTextField(
JSON.stringify({
doc: {
type: 'doc',
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index cac276535..c355e57d4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -9,7 +9,7 @@ import { Fade, JackInTheBox } from 'react-awesome-reveal';
import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simMouseEvent, simulateMouseClick } from '../../../ClientUtils';
import { Utils, emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc';
-import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocViews } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -40,6 +40,7 @@ import { FieldsDropdown } from '../FieldsDropdown';
import { ObserverJsxParser } from '../ObservableReactComponent';
import { PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
+import { TagsView } from '../TagsView';
import { ViewBoxInterface } from '../ViewBoxInterface';
import { GroupActive } from './CollectionFreeFormDocumentView';
import { DocumentContentsView } from './DocumentContentsView';
@@ -52,7 +53,6 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails/PresEnums';
import SpringAnimation from './trails/SlideEffect';
import { SpringType, springMappings } from './trails/SpringUtils';
-import { TagsView } from '../TagsView';
export interface DocumentViewProps extends FieldViewSharedProps {
hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
@@ -88,7 +88,7 @@ export interface DocumentViewProps extends FieldViewSharedProps {
@observer
export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps & { showAIEditor: boolean }>() {
// this makes mobx trace() statements more descriptive
- public get displayName() { return 'DocumentViewInternal(' + this.Document.title + ')'; } // prettier-ignore
+ public get displayName() { return 'DocumentViewInternal(' + this.Document.$title + ')'; } // prettier-ignore
public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
/**
@@ -322,7 +322,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
DocumentView.DeselectAll();
Doc.UnBrushDoc(this.Document);
} else this._singleClickFunc?.();
- }, 'on double click: ' + this.Document.title)();
+ }, 'on double click: ' + this.Document.$title)();
this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
this._doubleClickTimeout = undefined;
this._singleClickFunc = undefined;
@@ -340,7 +340,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
if (this.layoutDoc.onDragStart && !(e.ctrlKey || e.button > 0)) stopPropagate = false;
preventDefault = false;
}
- this._singleClickFunc = undoable(clickFunc ?? sendToBack ?? selectFunc, 'click: ' + this.Document.title);
+ this._singleClickFunc = undoable(clickFunc ?? sendToBack ?? selectFunc, 'click: ' + this.Document.$title);
const waitForDblClick = this._props.waitForDoubleClickToClick?.() ?? this.Document.waitForDoubleClickToClick;
if ((clickFunc && waitForDblClick !== 'never') || waitForDblClick === 'always') {
this._doubleClickTimeout && clearTimeout(this._doubleClickTimeout);
@@ -426,7 +426,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
noOnClick = undoable(() => {
this.Document.ignoreClick = false;
- this.Document.onClick = this.Document[DocData].onClick = undefined;
+ this.Document.onClick = this.Document.$onClick = undefined;
}, 'default on click');
deleteClicked = undoable(() => this._props.removeDocument?.(this.Document), 'delete doc');
@@ -519,7 +519,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
if (e && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) {
const onDisplay = () => {
- if (this.Document.type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && this._props.select(false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
+ if (this.Document.$type !== DocumentType.MAP) DocumentViewInternal.SelectAfterContextMenu && this._props.select(false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
setTimeout(() => simulateMouseClick(document.elementFromPoint(e.clientX, e.clientY), e.clientX, e.clientY, e.screenX, e.screenY));
};
if (navigator.userAgent.includes('Macintosh')) {
@@ -545,7 +545,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
cm.addItem({ description: item.label, event: () => (item.method ? item.method() : item.script?.script.run({ this: this.Document, documentView: this, scriptContext: this._props.scriptContext })), icon: item.icon as IconProp })
);
- if (!this.Document.isFolder) {
+ if (!this.Document.$isFolder) {
const templateDoc = Cast(this.Document[StrCast(this.Document.layout_fieldKey)], Doc, null);
const appearance = cm.findByDescription('Appearance...');
const appearanceItems = appearance?.subitems ?? [];
@@ -635,7 +635,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
let documentationDescription: string | undefined;
let documentationLink: string | undefined;
- switch (this.Document.type) {
+ switch (this.Document.$type) {
case DocumentType.COL:
documentationDescription = 'See collection documentation';
documentationLink = 'https://brown-dash.github.io/Dash-Documentation/views/';
@@ -722,9 +722,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
private _tagsBtnHeight = 22;
@computed get currentScale() {
const viewXfScale = this._props.DocumentView!().screenToLocalScale();
- const x = NumCast(this.Document.height) / viewXfScale / 80;
+ const x = NumCast(this.Document._height) / viewXfScale / 80;
const xscale = x >= 1 ? 0 : 1 / (1 + x * (viewXfScale - 1));
- const y = NumCast(this.Document.width) / viewXfScale / 200;
+ const y = NumCast(this.Document._width) / viewXfScale / 200;
const yscale = y >= 1 ? 0 : 1 / (1 + y * viewXfScale - 1);
return Math.max(xscale, yscale, 1 / viewXfScale);
}
@@ -735,7 +735,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
/**
* The maximum size a UI widget can be scaled so that it won't be bigger in screen pixels than its normal 35 pixel size.
*/
- @computed get maxWidgetSize() { return Math.min(this._tagsBtnHeight * this.viewScaling, 0.25 * Math.min(NumCast(this.Document.width), NumCast(this.Document.height))); } // prettier-ignore
+ @computed get maxWidgetSize() { return Math.min(this._tagsBtnHeight * this.viewScaling, 0.25 * Math.min(NumCast(this.Document._width), NumCast(this.Document._height))); } // prettier-ignore
/**
* How much to reactively scale a UI element so that it is as big as it can be (up to its normal 35pixel size) without being too big for the Doc content
*/
@@ -746,7 +746,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
@computed get viewBoxContents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
- const noBackground = this.Document.isGroup && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
+ const noBackground = this.Document.$isGroup && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
return (
<>
<div
@@ -758,7 +758,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
}}>
<DocumentContentsView
{...this._props}
- layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
+ layoutFieldKey={StrCast(this._renderDoc.layout_fieldKey, 'layout')}
pointerEvents={this.contentPointerEvents}
setContentViewBox={this.setContentView}
childFilters={this.childFilters}
@@ -831,7 +831,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
onPointerDown={action(() => { this._changingTitleField = true; })} // prettier-ignore
style={{ width: 'max-content', background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
placeholder={placeholder}
selectFunc={action((field: string | number) => {
if (this.layoutDoc.layout_showTitle) {
@@ -957,7 +957,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
: (this.docContents ?? (
<div
className="documentView-node"
- id={this.Document.type !== DocumentType.LINK ? this._docView?.DocUniqueId : undefined}
+ id={this.Document.$type !== DocumentType.LINK ? this._docView?.DocUniqueId : undefined}
style={{
...style,
background: this.backgroundBoxColor,
@@ -1015,7 +1015,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
borderRadius: this._componentView?.isUnstyledView?.() ? undefined : this.borderRounding,
pointerEvents: this._pointerEvents === 'visiblePainted' ? 'none' : this._pointerEvents, // visible painted means that the underlying doc contents are irregular and will process their own pointer events (otherwise, the contents are expected to fill the entire doc view box so we can handle pointer events here)
}}>
- {this._componentView?.isUnstyledView?.() || this.Document.type === DocumentType.CONFIG || !renderDoc ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)}
+ {this._componentView?.isUnstyledView?.() || this.Document.$type === DocumentType.CONFIG || !renderDoc ? renderDoc : DocumentViewInternal.AnimationEffect(renderDoc, this.Document[Animation], this.Document)}
{jsx}
</div>
);
@@ -1210,7 +1210,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
}
@computed private get nativeScaling() {
if (this.shouldNotScale) return 1;
- const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
+ const minTextScale = this.Document.$type === DocumentType.RTF ? 0.1 : 0;
const ai = this._showAIEditor && this.nativeWidth === this.layoutDoc.width ? 95 : 0;
const effNW = Math.max(this.effectiveNativeWidth - ai, 1);
const effNH = Math.max(this.effectiveNativeHeight - ai, 1);
@@ -1325,7 +1325,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public startDragging = (x: number, y: number, dropAction: dropActionType | undefined, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
public showContextMenu = (pageX: number, pageY: number) => this._docViewInternal?.onContextMenu(undefined, pageX, pageY);
- public toggleNativeDimensions = () => this._docViewInternal && this.Document.type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.NativeDimScaling() ?? 1, this._props.PanelWidth(), this._props.PanelHeight());
+ public toggleNativeDimensions = () => this._docViewInternal && this.Document.$type !== DocumentType.INK && Doc.toggleNativeDimensions(this.layoutDoc, this.NativeDimScaling() ?? 1, this._props.PanelWidth(), this._props.PanelHeight());
public iconify(finished?: () => void, animateTime?: number) {
this.ComponentView?.updateIcon?.();
@@ -1706,7 +1706,7 @@ ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuff
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSource: Doc) {
- const collectedLinks = DocListCast(linkCollection[DocData].data);
+ const collectedLinks = DocListCast(linkCollection.$data);
let wid = NumCast(linkSource._width);
let embedding: Doc | undefined;
const links = Doc.Links(linkSource);
@@ -1729,7 +1729,7 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour
ScriptingGlobals.add(function updateTagsCollection(collection: Doc) {
const tag = StrCast(collection.title).split('-->')[1];
const matchedTags = Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, tag, false, ['tags']).keys());
- const collectionDocs = DocListCast(collection[DocData].data).concat(collection);
+ const collectionDocs = DocListCast(collection.$data).concat(collection);
let wid = 100;
let created = false;
const matchedDocs = matchedTags
@@ -1749,6 +1749,6 @@ ScriptingGlobals.add(function updateTagsCollection(collection: Doc) {
return aset;
}, new Set<Doc>());
- created && (collection[DocData].data = new List<Doc>(Array.from(matchedDocs)));
+ created && (collection.$data = new List<Doc>(Array.from(matchedDocs)));
return true;
});
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 2e40f39ed..f82e980f5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -15,6 +15,7 @@ import { FocusViewOptions } from './FocusViewOptions';
import { OpenWhere } from './OpenWhere';
import { WebField } from '../../../fields/URLField';
import { ContextMenuProps } from '../ContextMenuItem';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number>;
export type StyleProviderFuncType = (
@@ -83,8 +84,7 @@ export interface FieldViewSharedProps {
onDoubleClickScript?: () => ScriptField;
onPointerDownScript?: () => ScriptField;
onPointerUpScript?: () => ScriptField;
- // eslint-disable-next-line no-use-before-define
- onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
+ onKey?: (e: KeyboardEvent, textBox: FormattedTextBox) => boolean | undefined;
fitWidth?: (doc: Doc) => boolean | undefined;
dontCenter?: 'x' | 'y' | 'xy' | undefined;
searchFilterDocs: () => Doc[];
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 5b06e9fc5..3b3bc808a 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -284,10 +284,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
crop = (region: Doc | undefined, addCrop?: boolean) => {
if (!region) return undefined;
const cropping = Doc.MakeCopy(region, true);
- const regionData = region[DocData];
- regionData.lockedPosition = true;
- regionData.title = 'region:' + this.Document.title;
- regionData.followLinkToggle = true;
+ region.$lockedPosition = true;
+ region.$title = 'region:' + this.Document.title;
+ region.$followLinkToggle = true;
this.addDocument(region);
const anchx = NumCast(cropping.x);
const anchy = NumCast(cropping.y);
@@ -300,24 +299,23 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
cropping._width = anchw * (this._props.NativeDimScaling?.() || 1);
cropping._height = anchh * (this._props.NativeDimScaling?.() || 1);
cropping.onClick = undefined;
- const croppingProto = cropping[DocData];
- croppingProto.annotationOn = undefined;
- croppingProto.isDataDoc = true;
- croppingProto.backgroundColor = undefined;
- croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
- croppingProto.type = DocumentType.IMG;
- croppingProto.layout = ImageBox.LayoutString('data');
- croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
- croppingProto.data_nativeWidth = anchw;
- croppingProto.data_nativeHeight = anchh;
- croppingProto.freeform_scale = viewScale;
- croppingProto.freeform_panX = anchx / viewScale;
- croppingProto.freeform_panY = anchy / viewScale;
- croppingProto.freeform_scale_min = viewScale;
- croppingProto.freeform_panX_min = anchx / viewScale;
- croppingProto.freeform_panX_max = anchw / viewScale;
- croppingProto.freeform_panY_min = anchy / viewScale;
- croppingProto.freeform_panY_max = anchh / viewScale;
+ cropping.$annotationOn = undefined;
+ cropping.$isDataDoc = true;
+ cropping.$backgroundColor = undefined;
+ cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ cropping.$type = DocumentType.IMG;
+ cropping.$layout = ImageBox.LayoutString('data');
+ cropping.$data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
+ cropping.$data_nativeWidth = anchw;
+ cropping.$data_nativeHeight = anchh;
+ cropping.$freeform_scale = viewScale;
+ cropping.$reeform_panX = anchx / viewScale;
+ cropping.$freeform_panY = anchy / viewScale;
+ cropping.$freeform_scale_min = viewScale;
+ cropping.$freeform_panX_min = anchx / viewScale;
+ cropping.$freeform_panX_max = anchw / viewScale;
+ cropping.$freeform_panY_min = anchy / viewScale;
+ cropping.$freeform_panY_max = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
@@ -401,15 +399,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
// updateIcon = () => new Promise<void>(res => res());
- updateIcon = (usePanelDimensions?: boolean) => {
+ updateIcon = (/* usePanelDimensions?: boolean */) => {
const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
- usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
- usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
this._props.PanelWidth(),
this._props.PanelHeight(),
0,
@@ -650,7 +648,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onClick={action(async () => {
this._regenerateLoading = true;
if (this._fireflyRefStrength) {
- DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
+ DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title), this.Document)?.then(action(() => (this._regenerateLoading = false)));
} else {
SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then(
action(newImgs => {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 40c687b7e..8911fac6d 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -54,16 +54,12 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable private rows: KeyValuePair[] = [];
@observable _splitPercentage = 50;
- get fieldDocToLayout() {
- return DocCast(this.Document);
- }
-
@action
onEnterKey = (e: React.KeyboardEvent): void => {
if (e.key === 'Enter') {
e.stopPropagation();
- if (this._keyInput.current?.value && this._valInput.current?.value && this.fieldDocToLayout) {
- if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput.current.value, this._valInput.current.value)) {
+ if (this._keyInput.current?.value && this._valInput.current?.value && this.Document) {
+ if (KeyValueBox.SetField(this.Document, this._keyInput.current.value, this._valInput.current.value)) {
this._keyInput.current.value = '';
this._valInput.current.value = '';
document.body.focus();
@@ -109,12 +105,12 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (setResult) setResult?.(value);
else target[key] = field;
};
- const res = script.run({ this: Doc.Layout(doc), _setCacheResult_ }, console.log);
+ const res = script.run({ this: doc, _setCacheResult_ }, console.log);
if (!res.success) {
if (key) target[key] = script.originalScript;
return false;
}
- field === undefined && (field = res.result instanceof Array ? new List<FieldType>(res.result) : (typeof res.result === 'function' ? res.result.name : res.result as FieldType));
+ field === undefined && (field = res.result instanceof Array ? new List<FieldType>(res.result) : typeof res.result === 'function' ? res.result.name : (res.result as FieldType));
}
}
if (!key) return false;
@@ -141,7 +137,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
rowHeight = () => 30;
@computed get createTable() {
- const doc = this.fieldDocToLayout;
+ const doc = this.Document;
if (!doc) {
return (
<tr>
@@ -149,25 +145,35 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
</tr>
);
}
- const realDoc = doc;
const ids: { [key: string]: string } = {};
const protos = Doc.GetAllPrototypes(doc);
protos.forEach(proto => {
Object.keys(proto).forEach(key => {
- if (!(key in ids) && realDoc[key] !== ComputedField.undefined) {
+ if (!(key in ids) && doc[key] !== ComputedField.undefined) {
ids[key] = key;
}
});
});
+ const layoutProtos = Doc.GetAllPrototypes(this.layoutDoc);
+ layoutProtos.forEach(proto => {
+ Object.keys(proto)
+ .map(key => '_' + key)
+ .forEach(key => {
+ if (!(key.replace(/^_/, '') in ids) && doc[key] !== ComputedField.undefined) {
+ ids[key] = key;
+ }
+ });
+ });
+
const rows: JSX.Element[] = [];
let i = 0;
const keys = Object.keys(ids).slice();
// for (const key of [...keys.filter(id => id !== 'layout' && !id.includes('_')).sort(), ...keys.filter(id => id === 'layout' || id.includes('_')).sort()]) {
const sortedKeys = keys.sort((a: string, b: string) => {
- const a_ = a.split('_')[0];
- const b_ = b.split('_')[0];
+ const a_ = a.replace(/^_/, '').split('_')[0];
+ const b_ = b.replace(/^_/, '').split('_')[0];
if (a_ < b_) return -1;
if (a_ > b_) return 1;
if (a === a_) return -1;
@@ -177,7 +183,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
sortedKeys.forEach(key => {
rows.push(
<KeyValuePair
- doc={realDoc}
+ doc={doc}
addDocTab={this._props.addDocTab}
PanelWidth={this._props.PanelWidth}
PanelHeight={this.rowHeight}
@@ -300,7 +306,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
description: 'Default Perspective',
event: () => {
this._props.addDocTab(this.Document, OpenWhere.close);
- this._props.addDocTab(this.fieldDocToLayout, OpenWhere.addRight);
+ this._props.addDocTab(this.Document, OpenWhere.addRight);
},
icon: 'image',
});
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 85aff04c3..93f5231cb 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -61,17 +61,18 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
render() {
// let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
- let protoCount = 0;
- let { doc } = this._props;
+ const layoutField = this._props.keyName.startsWith('_');
+ let doc = layoutField ? Doc.Layout(this._props.doc) : this._props.doc;
+ let protoCount = doc !== this._props.doc && !layoutField ? 1 : 0;
while (doc) {
- if (Object.keys(doc).includes(this._props.keyName)) {
+ if (Object.keys(doc).includes(this._props.keyName.replace(/^_/, ''))) {
break;
}
protoCount++;
doc = DocCast(doc.proto);
}
- const parenCount = Math.max(0, protoCount - 1);
- const keyStyle = protoCount === 0 ? 'black' : 'blue';
+ const parenCount = Math.max(0, protoCount);
+ const keyStyle = protoCount === 0 && doc === this._props.doc ? 'black' : 'blue';
const hover = { transition: '0.3s ease opacity', opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
@@ -99,10 +100,9 @@ export class KeyValuePair extends ObservableReactComponent<KeyValuePairProps> {
</button>
<input className="keyValuePair-td-key-check" type="checkbox" style={hover} onChange={this.handleCheck} ref={this.checkbox} />
<Tooltip title={Object.entries(new DocumentOptions()).find((pair: [string, FInfo]) => pair[0].replace(/^_/, '') === this._props.keyName)?.[1].description ?? ''}>
- <div className="keyValuePair-keyField" style={{ marginLeft: 20 * (this._props.keyName.replace(/__/g, '').match(/_/g)?.length || 0), color: keyStyle }}>
- {'('.repeat(parenCount)}
- {this._props.keyName}
- {')'.repeat(parenCount)}
+ <div className="keyValuePair-keyField" style={{ marginLeft: 20 * (this._props.keyName.replace(/__/g, '').replace(/^_/, '').match(/_/g)?.length || 0), color: keyStyle }}>
+ {(layoutField ? '_' : '$').repeat(parenCount)}
+ {(keyStyle === 'blue' && !layoutField && !parenCount ? '$' : '') + this._props.keyName}
</div>
</Tooltip>
</div>
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 7fb83571f..b08ed84b7 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -13,11 +13,11 @@ import { undoable } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
+import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import './LabelBox.scss';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { RichTextMenu } from './formattedText/RichTextMenu';
-import { DocumentView } from './DocumentView';
@observer
export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -103,11 +103,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
if (r) {
if (!r.offsetHeight || !r.offsetWidth) {
- //console.log("CAN'T FIT TO EMPTY BOX");
- this._timeout && clearTimeout(this._timeout);
+ r.style.opacity = '0';
this._timeout = setTimeout(() => this.fitTextToBox(r));
return textfitParams;
}
+ r.style.opacity = '1';
r.style.whiteSpace = ''; // textfit sets to nowrap if not multiline, but doesn't reeset if it becomes multiline
r.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']); // textfit doesn't reset textAlign if it has been set to center, so we just set it to what we want
r.firstChild instanceof HTMLElement && (r.firstChild.style.textAlign = StrCast(this.layoutDoc[this.fieldKey + '_align']));
@@ -222,7 +222,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
FormattedTextBox.LiveTextUndo = undefined;
}}
dangerouslySetInnerHTML={{
- __html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title.startsWith('#') ? null : (this.Title ?? '')}</span>`,
+ __html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title?.startsWith('#') ? '' : (this.Title ?? '')}</span>`,
}}
contentEditable={this._props.onClickScript?.() ? undefined : true}
ref={r => {
@@ -239,7 +239,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this.Title) {
this.resetCursor();
}
- }
+ } else this._timeout && clearTimeout(this._timeout);
}}
/>
</div>
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index d5dc256d9..8bf65b637 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -252,7 +252,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
fontSize={fontSize}
GetValue={() => linkDesc}
SetValue={action(val => {
- this.Document[DocData].link_description = val;
+ this.Document.$link_description = val;
return true;
})}
/>
@@ -262,8 +262,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
background={color}
color={fontColor || lightOrDark(DashColor(color).fade(0.5).toString())}
type={Type.PRIM}
- val={StrCast(this.Document[DocData].link_description)}
- setVal={action(val => (this.Document[DocData].link_description = val))}
+ val={StrCast(this.Document.$link_description)}
+ setVal={action(val => (this.Document.$link_description = val))}
fillWidth
/> */}
</div>
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index ff95f8547..aeac100f4 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,25 +1,23 @@
import { action, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { DocData } from '../../../fields/DocSymbols';
import { StrCast } from '../../../fields/Types';
import { LinkManager } from '../../util/LinkManager';
import './LinkDescriptionPopup.scss';
import { TaskCompletionBox } from './TaskCompletedBox';
@observer
-export class LinkDescriptionPopup extends React.Component<{}> {
+export class LinkDescriptionPopup extends React.Component<object> {
// eslint-disable-next-line no-use-before-define
public static Instance: LinkDescriptionPopup;
@observable public display: boolean = false;
- // eslint-disable-next-line react/no-unused-class-component-methods
@observable public showDescriptions: string = 'ON';
@observable public popupX: number = 700;
@observable public popupY: number = 350;
@observable description: string = '';
@observable popupRef = React.createRef<HTMLDivElement>();
- constructor(props: any) {
+ constructor(props: object) {
super(props);
makeObservable(this);
LinkDescriptionPopup.Instance = this;
@@ -47,15 +45,13 @@ export class LinkDescriptionPopup extends React.Component<{}> {
@action
onDismiss = (add: boolean) => {
this.display = false;
- if (add) {
- LinkManager.Instance.currentLink && (LinkManager.Instance.currentLink[DocData].link_description = this.description);
- }
+ add && LinkManager.Instance.currentLink && (LinkManager.Instance.currentLink.$link_description = this.description);
this.description = '';
};
@action
onClick = (e: PointerEvent) => {
- if (this.popupRef && !this.popupRef.current?.contains(e.target as any)) {
+ if (this.popupRef && !this.popupRef.current?.contains(e.target as Node)) {
this.display = false;
this.description = '';
TaskCompletionBox.taskCompleted = false;
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index a3ac68b99..f6509b885 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -3,7 +3,7 @@ import * as d3 from 'd3';
import { Feature, GeoJsonProperties, Geometry, LineString } from 'geojson';
import { MercatorCoordinate } from 'mapbox-gl';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
-import { MapRef } from 'react-map-gl';
+import { MapRef } from 'react-map-gl/mapbox';
export type Position = [number, number];
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index 8079d96ea..fc5377ba4 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,7 +1,8 @@
+import { IconButton } from '@dash/components';
import { IconLookup, faAdd, faArrowDown, faArrowLeft, faArrowsRotate, faBicycle, faCalendarDays, faCar, faDiamondTurnRight, faEdit, faPersonWalking, faRoute } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material';
-import { IconButton } from '@dash/components';
+import { LngLatLike } from 'mapbox-gl';
import { IReactionDisposer, ObservableMap, action, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -14,12 +15,10 @@ import { CalendarManager } from '../../../util/CalendarManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
import { DocumentView } from '../DocumentView';
+import { Position } from './AnimationUtility';
import './MapAnchorMenu.scss';
import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { MarkerIcons } from './MarkerIcons';
-import { LngLatLike } from 'mapbox-gl';
-import { Position } from './AnimationUtility';
-// import { GPTPopup, GPTPopupMode } from './../../GPTPopup/GPTPopup';
type MapAnchorMenuType = 'standard' | 'routeCreation' | 'calendar' | 'customize' | 'route';
@@ -46,7 +45,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public IsTargetToggler: () => boolean = returnFalse;
public DisplayRoute: (routeInfoMap: Record<TransportationType, { coordinates: Position[] }> | undefined, type: TransportationType) => void = unimplementedFunction;
public AddNewRouteToMap: (coordinates: Position[], origin: string, destination: { place_name: string; center: number[] }, createPinForDestination: boolean) => void = unimplementedFunction;
- public CreatePin: (feature: { place_name: string; center: LngLatLike; properties: { wikiData: unknown } }) => void = unimplementedFunction;
+ public CreatePin: (feature: { place_name: string; center: LngLatLike; properties?: { wikiData: string } }) => void = unimplementedFunction;
public UpdateMarkerColor: (color: string) => void = unimplementedFunction;
public UpdateMarkerIcon: (iconKey: string) => void = unimplementedFunction;
@@ -293,7 +292,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
return undefined;
};
- getDirectionsButton: JSX.Element = (<IconButton tooltip="Get directions" onPointerDown={this.DirectionsClick} icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} color={SettingsManager.userColor} />);
+ getDirectionsButton = () => <IconButton tooltip="Get directions" onPointerDown={this.DirectionsClick} icon={<FontAwesomeIcon icon={faDiamondTurnRight as IconLookup} />} color={SettingsManager.userColor} />;
getAddToCalendarButton = (docType: string): JSX.Element => (
<IconButton
@@ -305,9 +304,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
color={SettingsManager.userColor}
/>
);
- addToCalendarButton: JSX.Element = (
- <IconButton tooltip="Add to calendar" onPointerDown={() => CalendarManager.Instance.open(undefined, this.pinDoc)} icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
- );
+ addToCalendarButton = () => <IconButton tooltip="Add to calendar" onPointerDown={() => CalendarManager.Instance.open(undefined, this.pinDoc)} icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />;
getLinkNoteToDocButton = (docType: string): JSX.Element => (
<div ref={this._commentRef}>
@@ -320,7 +317,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
</div>
);
- linkNoteToPinOrRoutenButton: JSX.Element = (
+ linkNoteToPinOrRoutenButton = () => (
<div ref={this._commentRef}>
<IconButton
tooltip="Link Note to Pin" //
@@ -331,9 +328,9 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
</div>
);
- customizePinButton: JSX.Element = (<IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />);
+ customizePinButton = () => <IconButton tooltip="Customize pin" onPointerDown={this.CustomizeClick} icon={<FontAwesomeIcon icon={faEdit as IconLookup} />} color={SettingsManager.userColor} />;
- centerOnPinButton: JSX.Element = (
+ centerOnPinButton = () => (
<IconButton
tooltip="Center on pin" //
onPointerDown={this.Center}
@@ -342,7 +339,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
/>
);
- backButton: JSX.Element = (
+ backButton = () => (
<IconButton
tooltip="Go back" //
onPointerDown={this.BackClick}
@@ -351,7 +348,7 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
/>
);
- addRouteButton: JSX.Element = (
+ addRouteButton = () => (
<IconButton
tooltip="Add route" //
onPointerDown={this.HandleAddRouteClick}
@@ -369,9 +366,9 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
/>
);
- animateRouteButton: JSX.Element = (<IconButton tooltip="Animate route" onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />);
+ animateRouteButton = () => <IconButton tooltip="Animate route" onPointerDown={() => this.OpenAnimationPanel(this.routeDoc)} icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />;
- revertToOriginalMarkerButton = (
+ revertToOriginalMarkerButton = () => (
<IconButton
tooltip="Revert to original" //
onPointerDown={() => this.revertToOriginalMarker()}
@@ -386,31 +383,31 @@ export class MapAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
{this.menuType === 'standard' && (
<>
{this.getDeleteButton('pin')}
- {this.getDirectionsButton}
+ {this.getDirectionsButton()}
{this.getAddToCalendarButton('pin')}
{this.getLinkNoteToDocButton('pin')}
- {this.customizePinButton}
- {this.centerOnPinButton}
+ {this.customizePinButton()}
+ {this.centerOnPinButton()}
</>
)}
{this.menuType === 'routeCreation' && (
<>
- {this.backButton}
- {this.addRouteButton}
+ {this.backButton()}
+ {this.addRouteButton()}
</>
)}
{this.menuType === 'route' && (
<>
{this.getDeleteButton('route')}
- {this.animateRouteButton}
+ {this.animateRouteButton()}
{this.getAddToCalendarButton('route')}
{this.getLinkNoteToDocButton('route')}
</>
)}
{this.menuType === 'customize' && (
<>
- {this.backButton}
- {this.revertToOriginalMarkerButton}
+ {this.backButton()}
+ {this.revertToOriginalMarkerButton()}
</>
)}
diff --git a/src/client/views/nodes/MapBox/MapBox.scss b/src/client/views/nodes/MapBox/MapBox.scss
index fdd8a29d7..bd4b51038 100644
--- a/src/client/views/nodes/MapBox/MapBox.scss
+++ b/src/client/views/nodes/MapBox/MapBox.scss
@@ -7,6 +7,9 @@
overflow: hidden;
display: flex;
position: absolute;
+ .mapboxgl-marker {
+ cursor: default;
+ }
.mapboxgl-map {
overflow: unset !important;
@@ -27,6 +30,9 @@
gap: 5px;
align-items: center;
width: calc(100% - 40px);
+ z-index: 1;
+ position: relative;
+ background: lightGray;
}
.mapbox-settings-panel {
@@ -171,6 +177,8 @@
.mapBox-wrapper {
width: 100%;
+ transform-origin: top left;
+
.mapBox-input {
box-sizing: border-box;
border: 1px solid transparent;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 792cb6b46..a563b7c1b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,8 +1,8 @@
-import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
+import { IconButton, Size, Type } from '@dash/components';
+import { faCircleXmark, faGear, faPause, faPlay, faRotate } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Checkbox, FormControlLabel, TextField } from '@mui/material';
import * as turf from '@turf/turf';
-import { IconButton, Size, Type } from '@dash/components';
import * as d3 from 'd3';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString } from 'geojson';
import { LngLatBoundsLike, LngLatLike, MapLayerMouseEvent } from 'mapbox-gl';
@@ -10,11 +10,14 @@ import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObserv
import { observer } from 'mobx-react';
import * as React from 'react';
import { CirclePicker, ColorResult } from 'react-color';
-import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl';
+import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl/mapbox';
import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types';
+import { TraceMobx } from '../../../../fields/util';
import { DocUtils } from '../../../documents/DocUtils';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
@@ -34,7 +37,6 @@ import { MapAnchorMenu } from './MapAnchorMenu';
import './MapBox.scss';
import { MapboxApiUtility, TransportationType } from './MapboxApiUtility';
import { MarkerIcons } from './MarkerIcons';
-import { RichTextField } from '../../../../fields/RichTextField';
// import { GeocoderControl } from './GeocoderControl';
// amongus
@@ -76,7 +78,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
makeObservable(this);
}
- @observable _featuresFromGeocodeResults: { place_name: string; center: LngLatLike | undefined }[] = [];
+ @observable _featuresFromGeocodeResults: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: string } }[] = [];
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable _selectedPinOrRoute: Doc | undefined = undefined; // The pin that is selected
@observable _mapReady = false;
@@ -123,7 +125,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const originalCoordinates: Position[] = JSON.parse(StrCast(this._routeToAnimate.routeCoordinates));
// const index = Math.floor(this.animationPhase * originalCoordinates.length);
const index = this._animationPhase * (originalCoordinates.length - 1); // Calculate the fractional index
- console.log('Animation phase', this._animationPhase);
const startIndex = Math.floor(index);
const endIndex = Math.ceil(index);
let feature: Feature<Geometry, GeoJsonProperties>;
@@ -183,7 +184,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
});
return feature;
}
- console.log('ERROR');
return {
type: 'Feature',
properties: {},
@@ -199,7 +199,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get allRoutesGeoJson(): FeatureCollection {
const features: Feature<Geometry, GeoJsonProperties>[] = this.allRoutes.map((routeDoc: Doc) => {
- console.log('Route coords: ', routeDoc.routeCoordinates);
const geometry: LineString = {
type: 'LineString',
coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
@@ -215,7 +214,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return {
type: 'FeatureCollection',
- features: features,
+ features,
};
}
@@ -241,7 +240,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
toList(docs).forEach(doc => {
let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this._selectedPinOrRoute;
if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
- existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
+ existingPin = this.createPushpin({ lng: NumCast(doc.longitude), lat: NumCast(doc.latitude) }, StrCast(doc.map));
}
if (existingPin) {
setTimeout(() => {
@@ -477,7 +476,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
deleteSelectedPinOrRoute = undoable(() => {
- console.log('deleting');
if (this._selectedPinOrRoute) {
// Removes filter
Doc.setDocFilter(this.Document, 'latitude', NumCast(this._selectedPinOrRoute.latitude), 'remove');
@@ -542,17 +540,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
* Creates Pushpin doc and adds it to the list of annotations
*/
@action
- createPushpin = undoable((center: LngLatLike, location?: string, wikiData?: string) => {
- const lat = 'lat' in center ? center.lat : center[0];
- const lon = 'lng' in center ? center.lng : 'lon' in center ? center.lon : center[1];
+ createPushpin = (center: LngLatLike, location?: string, wikiData?: string) => {
+ const [lng, lat] = center instanceof Array ? center : ['lng' in center ? center.lng : center.lon, center.lat];
// Stores the pushpin as a MapMarkerDocument
const pushpin = Docs.Create.PushpinDocument(
lat,
- lon,
+ lng,
false,
[],
{
- title: location ?? `lat=${lat},lng=${lon}`,
+ title: location ?? `lat=${lat},lng=${lng}`,
map: location,
description: '',
wikiData: wikiData,
@@ -563,11 +560,10 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// ,'pushpinIDamongus'+ this.incrementer++
);
this.addDocument(pushpin, this.annotationKey);
- console.log(pushpin);
return pushpin;
// mapMarker.infoWindowOpen = true;
- }, 'createpin');
+ };
@action
createMapRoute = undoable((coordinates: Position[], originName: string, destination: { place_name: string; center: number[] }, createPinForDestination: boolean) => {
@@ -575,7 +571,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const mapRoute = Docs.Create.MapRouteDocument(false, [], { title: `${originName} --> ${destination.place_name}`, routeCoordinates: JSON.stringify(coordinates) });
this.addDocument(mapRoute, this.annotationKey);
if (createPinForDestination) {
- this.createPushpin(destination.center[1], destination.center[0], destination.place_name);
+ this.createPushpin({ lng: destination.center[0], lat: destination.center[1] }, destination.place_name);
}
this._temporaryRouteSource = {
type: 'FeatureCollection',
@@ -598,18 +594,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
- addMarkerForFeature = (feature: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: unknown } }) => {
- const location = feature.place_name;
+ addMarkerForFeature = (feature: { place_name: string; center: LngLatLike | undefined; properties?: { wikiData: string } }) => {
if (feature.center) {
- const wikiData = feature.properties?.wikiData;
-
- this.createPushpin(feature.center, location, wikiData);
-
- if (this._mapRef.current) {
- this._mapRef.current.flyTo({
- center: feature.center,
- });
- }
+ this.createPushpin(feature.center, feature.place_name, feature.properties?.wikiData);
+ this._mapRef.current?.flyTo({
+ center: feature.center,
+ });
this._featuresFromGeocodeResults = [];
} else {
// TODO: handle error
@@ -632,7 +622,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// try {
// const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`;
// const response = await fetch(url);
- // const data = await response.json();
+ // const data = await response.jchildDocson();
// runInAction(() => {
// this.featuresFromGeocodeResults = data.features;
// })
@@ -653,7 +643,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
layers: ['map-routes-layer'],
});
- console.error(features);
+ this.Document._childFilters = new List<string>(StrListCast(this.Document._childFilters).filter(filter => !filter.includes(LinkedTo)));
if (features && features.length > 0 && features[0].properties && features[0].geometry) {
const { routeTitle } = features[0].properties;
const routeDoc: Doc | undefined = this.allRoutes.find(rtDoc => rtDoc.title === routeTitle);
@@ -668,9 +658,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;
-
MapAnchorMenu.Instance.Reset();
-
MapAnchorMenu.Instance.setRouteDoc(routeDoc);
// TODO: Subject to change
@@ -833,10 +821,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed
get preAnimationViewState() {
- if (!this._isAnimating) {
- return this.mapboxMapViewState;
- }
- return undefined;
+ return !this._isAnimating ? this.mapboxMapViewState : undefined;
}
@action
@@ -846,24 +831,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
updateAnimationSpeed = () => {
- let newAnimationSpeed: AnimationSpeed;
+ this._animationSpeed = (() => {
switch (this._animationSpeed) {
- case AnimationSpeed.SLOW:
- newAnimationSpeed = AnimationSpeed.MEDIUM;
- break;
- case AnimationSpeed.MEDIUM:
- newAnimationSpeed = AnimationSpeed.FAST;
- break;
- case AnimationSpeed.FAST:
- newAnimationSpeed = AnimationSpeed.SLOW;
- break;
- default:
- newAnimationSpeed = AnimationSpeed.MEDIUM;
- break;
- }
- this._animationSpeed = newAnimationSpeed;
+ case AnimationSpeed.SLOW: return AnimationSpeed.MEDIUM;
+ case AnimationSpeed.MEDIUM: return AnimationSpeed.FAST;
+ case AnimationSpeed.FAST: return AnimationSpeed.SLOW;
+ default: return AnimationSpeed.MEDIUM;
+ }})(); // prettier-ignore
if (this._animationUtility) {
- this._animationUtility.updateAnimationSpeed(newAnimationSpeed);
+ this._animationUtility.updateAnimationSpeed(this._animationSpeed);
}
};
@computed get animationSpeedTooltipText(): string {
@@ -890,19 +866,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._animationUtility?.updateIsStreetViewAnimation(newVal);
};
- getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => {
- const geometry: LineString = {
+ getFeatureFromRouteDoc = (routeDoc: Doc): Feature<Geometry, GeoJsonProperties> => ({
+ type: 'Feature',
+ properties: {
+ routeTitle: routeDoc.title,
+ },
+ geometry: {
type: 'LineString',
coordinates: JSON.parse(StrCast(routeDoc.routeCoordinates)),
- };
- return {
- type: 'Feature',
- properties: {
- routeTitle: routeDoc.title,
- },
- geometry: geometry,
- };
- };
+ },
+ });
@action
playAnimation = (status: AnimationStatus) => {
@@ -936,7 +909,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const updateAnimationPhase = (newAnimationPhase: number) => this.setAnimationPhase(newAnimationPhase);
if (status !== AnimationStatus.RESUME) {
- const result = await animationUtil.flyInAndRotate({
+ await animationUtil.flyInAndRotate({
map: this._mapRef.current!,
// targetLngLat,
// duration 3000
@@ -948,9 +921,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// endPitch: this.isStreetViewAnimation ? 80 : 50,
updateFrameId,
});
-
- console.log('Bearing: ', result.bearing);
- console.log('Altitude: ', result.altitude);
}
runInAction(() => {
@@ -1028,7 +998,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.playAnimation(AnimationStatus.START); // Play from the beginning
}
}}
- icon={this._isAnimating && this._finishedFlyTo ? <FontAwesomeIcon icon={faPause as IconLookup} /> : <FontAwesomeIcon icon={faPlay as IconLookup} />}
+ icon={<FontAwesomeIcon icon={this._isAnimating && this._finishedFlyTo ? faPause : faPlay} />}
color="black"
size={Size.MEDIUM}
/>
@@ -1039,12 +1009,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.stopAnimation(false);
this.playAnimation(AnimationStatus.START);
}}
- icon={<FontAwesomeIcon icon={faRotate as IconLookup} />}
+ icon={<FontAwesomeIcon icon={faRotate} />}
color="black"
size={Size.MEDIUM}
/>
)}
- <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark as IconLookup} />} color="black" size={Size.MEDIUM} />
+ <IconButton style={{ marginRight: '10px' }} tooltip="Stop and close animation" onPointerDown={() => this.stopAnimation(true)} icon={<FontAwesomeIcon icon={faCircleXmark} />} color="black" size={Size.MEDIUM} />
<div className="animation-suboptions">
<div>|</div>
<FormControlLabel className="first-person-label" label="1st person animation:" labelPlacement="start" control={<Checkbox color="success" checked={this._isStreetViewAnimation} onChange={this.toggleIsStreetViewAnimation} />} />
@@ -1085,7 +1055,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onBearingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const bearing = parseInt(e.target.value);
if (!isNaN(bearing) && this._mapRef.current) {
- console.log('bearing change');
const fixedBearing = Math.max(0, Math.min(360, bearing));
this._mapRef.current.setBearing(fixedBearing);
this.dataDoc.map_bearing = fixedBearing;
@@ -1096,7 +1065,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onPitchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const pitch = parseInt(e.target.value);
if (!isNaN(pitch) && this._mapRef.current) {
- console.log('pitch change');
const fixedPitch = Math.max(0, Math.min(85, pitch));
this._mapRef.current.setPitch(fixedPitch);
this.dataDoc.map_pitch = fixedPitch;
@@ -1141,16 +1109,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._showTerrain = !this._showTerrain;
};
- getMarkerIcon = (pinDoc: Doc): JSX.Element | null => {
- const markerType = StrCast(pinDoc.markerType);
- const markerColor = StrCast(pinDoc.markerColor);
-
- return MarkerIcons.getFontAwesomeIcon(markerType, '2x', markerColor) ?? null;
- };
+ getMarkerIcon = (pinDoc: Doc) => MarkerIcons.getFontAwesomeIcon(StrCast(pinDoc.markerType), '2x', StrCast(pinDoc.markerColor)) ?? null;
render() {
- const scale = this._props.NativeDimScaling?.() || 1;
- const parscale = scale === 1 ? 1 : (this.ScreenToLocalBoxXf().Scale ?? 1);
+ TraceMobx();
+ const scale = (this._props.NativeDimScaling?.() || 1) + 0.001; // bcz: weird, but without this hack, MapBox doesn't locate map correctly
+ const parscale = this.ScreenToLocalBoxXf().Scale;
return (
<div className="mapBox" ref={this._ref}>
@@ -1158,13 +1122,13 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
className="mapBox-wrapper"
onWheel={e => e.stopPropagation()}
onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
- style={{ transformOrigin: 'top left', transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+ style={{ transform: `scale(${scale})`, width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
{!this._routeToAnimate && (
- <div className="mapBox-searchbar" style={{ width: `${100 / scale}%`, zIndex: 1, position: 'relative', background: 'lightGray' }}>
+ <div className="mapBox-searchbar" style={{ width: `${100 / scale}%` }}>
<TextField fullWidth placeholder="Enter a location" onKeyDown={this.searchbarKeyDown} onChange={e => this.handleSearchChange(e.target.value)} />
- <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={() => this.toggleSettings()} />
+ <IconButton icon={<FontAwesomeIcon icon={faGear} size="1x" />} type={Type.TERT} onClick={this.toggleSettings} />
<div style={{ opacity: 0 }}>
- <IconButton icon={<FontAwesomeIcon icon={faGear as IconLookup} size="1x" />} type={Type.TERT} onClick={() => this.toggleSettings()} />
+ <IconButton icon={<FontAwesomeIcon icon={faGear} size="1x" />} type={Type.TERT} onClick={this.toggleSettings} />
</div>
</div>
)}
@@ -1188,15 +1152,15 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
</div>
<div className="mapbox-bearing-selection">
<div>Bearing: </div>
- <input value={NumCast(this.mapboxMapViewState.bearing).toFixed(0)} type="number" onChange={this.onBearingChange} />
+ <input value={this.mapboxMapViewState.bearing.toFixed(0)} type="number" onChange={this.onBearingChange} />
</div>
<div className="mapbox-pitch-selection">
<div>Pitch: </div>
- <input value={NumCast(this.mapboxMapViewState.pitch).toFixed(0)} type="number" onChange={this.onPitchChange} />
+ <input value={this.mapboxMapViewState.pitch.toFixed(0)} type="number" onChange={this.onPitchChange} />
</div>
<div className="mapbox-pitch-selection">
<div>Zoom: </div>
- <input value={NumCast(this.mapboxMapViewState.zoom).toFixed(0)} type="number" onChange={this.onZoomChange} />
+ <input value={this.mapboxMapViewState.zoom.toFixed(0)} type="number" onChange={this.onZoomChange} />
</div>
<div className="mapbox-terrain-selection">
<div>Show terrain: </div>
@@ -1230,17 +1194,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
)}
<MapProvider>
<MapboxMap
+ key={'' + this.Document.x + this.Document.y} // force map to rerender after dragging, otherwise it will display the wrong location until it gets re-rendered
ref={this._mapRef}
mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
- viewState={this._isAnimating || this._routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: NumCast(this.layoutDoc._width), height: NumCast(this.layoutDoc._height) }}
+ viewState={this._isAnimating || this._routeToAnimate ? undefined : { ...this.mapboxMapViewState, width: this._props.PanelWidth(), height: this._props.PanelHeight() }}
mapStyle={this.dataDoc.map_style ? StrCast(this.dataDoc.map_style) : 'mapbox://styles/mapbox/streets-v11'}
style={{
position: 'absolute',
top: 0,
left: 0,
zIndex: '0',
- width: NumCast(this.layoutDoc._width) * parscale,
- height: NumCast(this.layoutDoc._height) * parscale,
+ width: this._props.PanelWidth() * parscale,
+ height: this._props.PanelHeight() * parscale,
}}
initialViewState={this._isAnimating ? undefined : this.mapboxMapViewState}
onZoom={this.onMapZoom}
@@ -1315,19 +1280,18 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
/>
</>
)}
-
- {!this._isAnimating &&
- this._animationPhase === 0 &&
- this.allPushpins // .filter(anno => !anno.layout_unrendered)
- .map((pushpin, idx) => (
- <Marker key={idx} longitude={NumCast(pushpin.longitude)} latitude={NumCast(pushpin.latitude)} anchor="bottom" onClick={e => this.handleMarkerClick(e.originalEvent.clientX, e.originalEvent.clientY, pushpin)}>
- {this.getMarkerIcon(pushpin)}
- </Marker>
- ))}
-
- {/* {this.mapMarkers.length > 0 && this.mapMarkers.map((marker, idx) => (
- <Marker key={idx} longitude={marker.longitude} latitude={marker.latitude}/>
- ))} */}
+ {this._isAnimating || this._animationPhase
+ ? null
+ : this.allPushpins.map(p => (
+ <Marker
+ key={'' + p.longitude + p.latitude}
+ longitude={NumCast(p.longitude)}
+ latitude={NumCast(p.latitude)}
+ anchor="bottom"
+ onClick={e => this.handleMarkerClick(e.originalEvent.clientX, e.originalEvent.clientY, p)}>
+ {this.getMarkerIcon(p)}
+ </Marker>
+ ))}
</MapboxMap>
</MapProvider>
</div>
@@ -1336,7 +1300,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ref={this._sidebarRef}
{...this._props}
fieldKey={this.fieldKey}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 0627d382e..e0efab576 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -4,7 +4,7 @@ import { Button, EditableText, IconButton, Type } from '@dash/components';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { MapProvider, Map as MapboxMap } from 'react-map-gl';
+import { MapProvider, Map as MapboxMap } from 'react-map-gl/mapbox';
import { ClientUtils, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
import { Doc, DocListCast, Field, LinkedTo, Opt, returnEmptyDoclist } from '../../../../fields/Doc';
@@ -798,7 +798,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
PanelHeight={returnOne}
NativeWidth={returnOne}
NativeHeight={returnOne}
- onKey={undefined}
onDoubleClickScript={undefined}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
@@ -830,7 +829,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
ref={this._sidebarRef}
{...this._props}
fieldKey={this.fieldKey}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 06b75e243..78ddafa88 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -129,15 +129,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
cropping._width = anchw;
cropping._height = anchh;
cropping.onClick = undefined;
- const croppingProto = cropping[DocData];
- croppingProto.annotationOn = undefined;
- croppingProto.isDataDoc = true;
- croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
- croppingProto.type = DocumentType.IMG;
- croppingProto.layout = ImageBox.LayoutString('data');
- croppingProto.data = new ImageField(ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
- croppingProto.data_nativeWidth = anchw;
- croppingProto.data_nativeHeight = anchh;
+ cropping.$annotationOn = undefined;
+ cropping.$isDataDoc = true;
+ cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ cropping.$type = DocumentType.IMG;
+ cropping.$layout = ImageBox.LayoutString('data');
+ cropping.$data = new ImageField(ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
+ cropping.$data_nativeWidth = anchw;
+ cropping.$data_nativeHeight = anchh;
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
}
@@ -157,7 +156,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ClientUtils.convertDataUri(dataUrl, region[Id]).then(returnedfilename =>
setTimeout(
action(() => {
- croppingProto.data = new ImageField(returnedfilename);
+ cropping.$data = new ImageField(returnedfilename);
}),
500
)
@@ -536,7 +535,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<SidebarAnnos
ref={this._sidebarRef}
{...this._props}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
setHeight={emptyFunction}
@@ -607,6 +606,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
pdfBox={this}
sidebarAddDoc={this.sidebarAddDocument}
addDocTab={this.sidebarAddDocTab}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
pdf={this._pdf}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 7ba313e92..53783e8a3 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast } from '../../../../fields/Doc';
-import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { BoolCast, DocCast } from '../../../../fields/Types';
@@ -99,7 +98,7 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
});
screengrabber.overlayX = 70; // was -400
screengrabber.overlayY = 590; // was 0
- screengrabber[DocData][Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
+ screengrabber['$' + Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true;
Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay
DocumentView.addViewRenderedCb(screengrabber, docView => {
RecordingBox.screengrabber = docView.ComponentView as RecordingBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 6289470b6..999f9c1cd 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -301,7 +301,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<div className="videoBox-viewer">
<div style={{ position: 'relative', height: this.videoPanelHeight() }}>
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
setContentViewBox={emptyFunction}
NativeWidth={returnZero}
@@ -329,7 +328,6 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<div style={{ background: SettingsManager.userColor, position: 'relative', height: this.formattedPanelHeight() }}>
{!(this.dataDoc[this.fieldKey + '_dictation'] instanceof Doc) ? null : (
<FormattedTextBox
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
Document={DocCast(this.dataDoc[this.fieldKey + '_dictation'])}
fieldKey="text"
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9adee53e8..fa099178c 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -331,7 +331,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(this.layoutDoc));
this._props.addDocument?.(imageSnapshot);
DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' });
- // link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1
+ // link && (DocCast(link.link_anchor_2).$timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1
setTimeout(() => downX !== undefined && downY !== undefined && DocumentView.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true));
};
@@ -918,11 +918,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
crop = (region: Doc | undefined, addCrop?: boolean) => {
if (!region) return undefined;
const cropping = Doc.MakeCopy(region, true);
- const regionData = region[DocData];
- regionData.backgroundColor = 'transparent';
- regionData.lockedPosition = true;
- regionData.title = 'region:' + this.Document.title;
- regionData.followLinkToggle = true;
+ region.$backgroundColor = 'transparent';
+ region.$lockedPosition = true;
+ region.$title = 'region:' + this.Document.title;
+ region.$followLinkToggle = true;
region._timecodeToHide = NumCast(region._timecodeToShow) + 0.0001;
this.addDocument(region);
const anchx = NumCast(cropping.x);
@@ -938,25 +937,24 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
cropping.timecodeToHide = undefined;
cropping.timecodeToShow = undefined;
cropping.onClick = undefined;
- const croppingProto = cropping[DocData];
- croppingProto.annotationOn = undefined;
- croppingProto.isDataDoc = true;
- croppingProto.proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
- croppingProto.type = DocumentType.VID;
- croppingProto.layout = VideoBox.LayoutString('data');
- croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
- croppingProto.data_nativeWidth = anchw;
- croppingProto.data_nativeHeight = anchh;
- croppingProto.videoCrop = true;
- croppingProto.layout_currentTimecode = this.layoutDoc._layout_currentTimecode;
- croppingProto.freeform_scale = viewScale;
- croppingProto.freeform_scale_min = viewScale;
- croppingProto.freeform_ = anchx / viewScale;
- croppingProto.freeform_panY = anchy / viewScale;
- croppingProto.freeform_panX_min = anchx / viewScale;
- croppingProto.freeform_panX_max = anchw / viewScale;
- croppingProto.freeform_panY_min = anchy / viewScale;
- croppingProto.freeform_panY_max = anchh / viewScale;
+ cropping.$annotationOn = undefined;
+ cropping.$isDataDoc = true;
+ cropping.$proto = Cast(this.Document.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+ cropping.$type = DocumentType.VID;
+ cropping.$layout = VideoBox.LayoutString('data');
+ cropping.$data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField);
+ cropping.$data_nativeWidth = anchw;
+ cropping.$data_nativeHeight = anchh;
+ cropping.$videoCrop = true;
+ cropping.$layout_currentTimecode = this.layoutDoc._layout_currentTimecode;
+ cropping.$freeform_scale = viewScale;
+ cropping.$freeform_scale_min = viewScale;
+ cropping.$freeform_ = anchx / viewScale;
+ cropping.$freeform_panY = anchy / viewScale;
+ cropping.$freeform_panX_min = anchx / viewScale;
+ cropping.$freeform_panX_max = anchw / viewScale;
+ cropping.$freeform_panY_min = anchy / viewScale;
+ cropping.$freeform_panY_max = anchh / viewScale;
if (addCrop) {
DocUtils.MakeLink(region, cropping, { link_relationship: 'cropped image' });
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index e7a10cc29..4b3f96bcf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1220,7 +1220,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
{...this._props}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
fieldKey={this.fieldKey + '_' + this._urlHash}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
setHeight={emptyFunction}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 009eb82cd..6f1f58a4c 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -1,23 +1,22 @@
import { Calendar, EventClickArg, EventDropArg, EventSourceInput } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
+import interactionPlugin from '@fullcalendar/interaction';
import multiMonthPlugin from '@fullcalendar/multimonth';
import timeGrid from '@fullcalendar/timegrid';
-import interactionPlugin from '@fullcalendar/interaction';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { dateRangeStrToDates } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
-import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
-import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
-import './CalendarBox.scss';
import { Id } from '../../../../fields/FieldSymbols';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
import { DocServer } from '../../../DocServer';
-import { DocumentView } from '../DocumentView';
-import { OpenWhere } from '../OpenWhere';
import { DragManager } from '../../../util/DragManager';
-import { DocData } from '../../../../fields/DocSymbols';
+import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
import { ContextMenu } from '../../ContextMenu';
+import { DocumentView } from '../DocumentView';
+import { OpenWhere } from '../OpenWhere';
+import './CalendarBox.scss';
type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
@@ -113,7 +112,7 @@ export class CalendarBox extends CollectionSubView() {
if (!super.onInternalDrop(e, de)) return false;
de.complete.docDragData?.droppedDocuments.forEach(doc => {
const today = new Date().toISOString();
- if (!doc.date_range) doc[DocData].date_range = `${today}|${today}`;
+ if (!doc.date_range) doc.$date_range = `${today}|${today}`;
});
return true;
};
diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
index 6e9307d37..b023b1de6 100644
--- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
+++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx
@@ -733,9 +733,9 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const x2 = parseFloat(values[2]) * Doc.NativeWidth(doc);
const y2 = parseFloat(values[3]) * Doc.NativeHeight(doc) + foundChunk.startPage * Doc.NativeHeight(doc);
- const annotationKey = Doc.LayoutFieldKey(doc) + '_annotations';
+ const annotationKey = '$' + Doc.LayoutFieldKey(doc) + '_annotations';
- const existingDoc = DocListCast(doc[DocData][annotationKey]).find(d => d.citation_id === citation.citation_id);
+ const existingDoc = DocListCast(doc[annotationKey]).find(d => d.citation_id === citation.citation_id);
const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc);
DocumentManager.Instance.showDocument(highlightDoc, { willZoomCentered: true }, () => {});
@@ -779,7 +779,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
_height: y2 - y1,
backgroundColor: 'rgba(255, 255, 0, 0.5)',
});
- highlight_doc[DocData].citation_id = citation.citation_id;
+ highlight_doc.$citation_id = citation.citation_id;
Doc.AddDocToList(pdfDoc[DocData], annotationKey, highlight_doc);
highlight_doc.annotationOn = pdfDoc;
Doc.SetContainer(highlight_doc, pdfDoc);
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 78bbb520e..2e2e1d41c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -3,7 +3,7 @@
.dashFieldView-active,
.dashFieldView {
position: relative;
- display: inline-flex;
+ display: contents;
align-items: center;
.dashFieldView-enumerables {
@@ -33,8 +33,11 @@
margin-left: 2px;
margin-right: 5px;
padding-left: 2px;
- display: inline-block;
- background-color: rgba(155, 155, 155, 0.24);
+ font-size: smaller;
+ display: contents;
+ > div {
+ background-color: rgba(155, 155, 155, 0.24);
+ }
span {
user-select: all;
min-width: 100%;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index e899b49bc..bb0efa917 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,8 +1,10 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import { Node } from 'prosemirror-model';
import { NodeSelection } from 'prosemirror-state';
+import { EditorView } from 'prosemirror-view';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
@@ -13,6 +15,7 @@ import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
import { Cast, DocCast } from '../../../../fields/Types';
import { emptyFunction } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
+import { DocumentOptions, FInfo } from '../../../documents/Documents';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { Transform } from '../../../util/Transform';
import { undoable, undoBatch } from '../../../util/UndoManager';
@@ -23,9 +26,6 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import { OpenWhere } from '../OpenWhere';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
-import { Node } from 'prosemirror-model';
-import { EditorView } from 'prosemirror-view';
-import { DocumentOptions, FInfo } from '../../../documents/Documents';
@observer
export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -99,7 +99,6 @@ interface IDashFieldViewInternal {
width: number;
height: number;
editable: boolean;
- nodeSelected: () => boolean;
node: Node;
getPos: () => number;
unclickable: () => boolean;
@@ -112,7 +111,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
_fieldKey: string;
_fieldRef = React.createRef<HTMLDivElement>();
@observable _dashDoc: Doc | undefined = undefined;
- @observable _expanded = this._props.nodeSelected();
+ @observable _expanded = false;
constructor(props: IDashFieldViewInternal) {
super(props);
@@ -140,7 +139,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
componentWillUnmount() {
this._reactionDisposer?.();
}
- isRowActive = () => (this._props.nodeSelected() || this._expanded) && this._props.editable;
+ isRowActive = () => this._props.tbox._props.isContentActive() && this._props.editable;
finishEdit = action(() => {
if (this._expanded) {
this._expanded = false;
@@ -149,7 +148,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
setTimeout(() => !this._props.tbox.ProseRef?.contains(document.activeElement) && this._props.tbox._props.onBlur?.());
}
});
- selectedCells = () => (this._dashDoc ? [this._dashDoc] : undefined);
+ selectedCells = () => (this._dashDoc && this._expanded ? [this._dashDoc] : undefined);
columnWidth = () => Math.min(this._props.tbox._props.PanelWidth(), Math.max(50, this._props.tbox._props.PanelWidth() - 100)); // try to leave room for the fieldKey
finfo = (fieldKey: string) => (new DocumentOptions() as Record<string, FInfo>)[fieldKey];
@@ -158,17 +157,18 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
@computed get fieldValueContent() {
return !this._dashDoc ? null : (
<div
+ className="dashFieldView-fieldSpan"
onPointerDown={action(() => {
this._expanded = !this._props.editable ? false : !this._expanded;
- })}
- style={{ fontSize: 'smaller', width: !this._hideKey && this._expanded ? this.columnWidth() : undefined }}>
+ })}>
<SchemaTableCell
- Document={this._dashDoc}
+ Doc={this._dashDoc}
col={0}
deselectCell={emptyFunction}
- selectCell={emptyFunction}
+ selectCell={() => (this._expanded ? true : undefined)}
+ autoFocus={true}
maxWidth={this._props.hideKey || this._hideKey ? undefined : this._props.tbox._props.PanelWidth}
- columnWidth={this._expanded || this._props.nodeSelected() ? () => undefined : returnZero}
+ columnWidth={returnZero}
selectedCells={this.selectedCells}
selectedCol={returnZero}
fieldKey={this._fieldKey}
@@ -184,11 +184,10 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
getFinfo={this.finfo}
setColumnValues={returnFalse}
allowCRs
- oneLine={!this._expanded && !this._props.nodeSelected()}
+ oneLine={!this._expanded}
finishEdit={this.finishEdit}
transform={Transform.Identity}
menuTarget={null}
- autoFocus
rootSelected={this._props.tbox._props.rootSelected}
/>
</div>
@@ -233,7 +232,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
}
@computed get _hideValue() {
- return this._props.hideValue && !this._props.nodeSelected();
+ return this._props.hideValue;
}
// clicking on the label creates a pivot view collection of all documents
@@ -255,7 +254,6 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
};
@computed get values() {
- if (this._props.nodeSelected()) return [];
const vals = FilterPanel.gatherFieldValues(DocListCast(Doc.ActiveDashboard?.data), this._fieldKey, []);
return vals.strings.map(facet => ({ value: facet, label: facet }));
@@ -297,8 +295,6 @@ export class DashFieldView {
node: Node;
tbox: FormattedTextBox;
getpos: () => number | undefined;
- @observable _nodeSelected = false;
- NodeSelected = () => this._nodeSelected;
unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some(m => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
constructor(node: Node, view: EditorView, getPos: () => number | undefined, tbox: FormattedTextBox) {
@@ -311,26 +307,13 @@ export class DashFieldView {
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
this.dom.style.position = 'relative';
- this.dom.style.display = 'inline-block';
+ this.dom.style.display = 'inline-flex';
this.dom.onkeypress = function (e: KeyboardEvent) {
e.stopPropagation();
};
- this.dom.onkeydown = (e: KeyboardEvent) => {
+ this.dom.onkeydown = action((e: KeyboardEvent) => {
e.stopPropagation();
- if (e.key === 'Tab') {
- e.preventDefault();
- const editor = tbox.EditorView;
- if (editor) {
- const { state } = editor;
- for (let i = getPosition() + 1; i < state.doc.content.size; i++) {
- if (state.doc.nodeAt(i)?.type.name === state.schema.nodes.dashField.name) {
- editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(i))));
- return;
- }
- }
- }
- }
- };
+ });
this.dom.onkeyup = function (e: KeyboardEvent) {
e.stopPropagation();
};
@@ -351,7 +334,6 @@ export class DashFieldView {
hideKey={node.attrs.hideKey}
hideValue={node.attrs.hideValue}
editable={node.attrs.editable}
- nodeSelected={this.NodeSelected}
tbox={tbox}
/>
);
@@ -365,19 +347,6 @@ export class DashFieldView {
}
});
}
- deselectNode() {
- runInAction(() => {
- this._nodeSelected = false;
- });
- this.dom.classList.remove('ProseMirror-selectednode');
- }
- selectNode() {
- setTimeout(
- action(() => {
- this._nodeSelected = true;
- }),
- 100
- );
- this.dom.classList.add('ProseMirror-selectednode');
- }
+ deselectNode() {}
+ selectNode() {}
}
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index e0450b202..827db190a 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -6,7 +6,6 @@ import { EditorView } from 'prosemirror-view';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
-import { DocData } from '../../../../fields/DocSymbols';
import { StrCast } from '../../../../fields/Types';
import './DashFieldView.scss';
import EquationEditor from './EquationEditor';
@@ -63,9 +62,9 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal>
}}>
<EquationEditor
ref={this._ref}
- value={StrCast(this._textBoxDoc[DocData][this._fieldKey])}
+ value={StrCast(this._textBoxDoc['$' + this._fieldKey])}
onChange={str => {
- this._textBoxDoc[DocData][this._fieldKey] = str;
+ this._textBoxDoc['$' + this._fieldKey] = str;
}}
autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
autoOperatorNames="sin cos tan"
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9d3050d90..5f132ecdf 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -14,11 +14,11 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti
import { EditorView, NodeViewConstructor } from 'prosemirror-view';
import * as React from 'react';
import { BsMarkdownFill } from 'react-icons/bs';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, imageUrlToBase64, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
-import { Id } from '../../../../fields/FieldSymbols';
+import { Id, ToString } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -27,7 +27,7 @@ import { ComputedField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, RTFCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
import { emptyFunction, numberRange, unimplementedFunction, Utils } from '../../../../Utils';
-import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
+import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -78,14 +78,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
- public static MakeConfig(rules?: RichTextRules, props?: FormattedTextBoxProps) {
+ public static MakeConfig(rules?: RichTextRules, textBox?: FormattedTextBox) {
return {
schema,
plugins: [
inputRules(rules?.inpRules ?? { rules: [] }),
- ...(props ? [FormattedTextBox.richTextMenuPlugin(props)] : []),
+ ...(textBox?._props ? [FormattedTextBox.richTextMenuPlugin(textBox._props)] : []),
history(),
- keymap(buildKeymap(schema, props ?? {})),
+ keymap(buildKeymap(schema, textBox)),
keymap(baseKeymap),
new Plugin({ props: { attributes: { class: 'ProseMirror-example-setup-style' } } }),
new Plugin({ view: () => new FormattedTextBoxComment() }),
@@ -153,7 +153,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
// eslint-disable-next-line no-return-assign
- @computed get config() { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this._props); } // prettier-ignore
+ @computed get config() { return FormattedTextBox.MakeConfig(this._rules = new RichTextRules(this.Document, this), this); } // prettier-ignore
@computed get _recordingDictation() { return this.dataDoc?.mediaState === mediaState.Recording; } // prettier-ignore
@computed get SidebarShown() { return !!(this._showSidebar || this.layoutDoc._layout_showSidebar); } // prettier-ignore
@computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore
@@ -226,22 +226,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return anchor;
};
- gptPDFFlashcards = async () => {
- const queryText = window.getSelection()?.toString() ?? '';
- try {
- if (queryText) {
- const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
- AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this.layoutDoc.x), NumCast(this.layoutDoc.y));
- }
- } catch (err) {
- console.error(err);
- }
- };
-
@action
setupAnchorMenu = () => {
AnchorMenu.Instance.Status = 'marquee';
- AnchorMenu.Instance.gptFlashcards = this.gptPDFFlashcards;
+ // AnchorMenu.Instance.gptFlashcards = this.selectionToFlashcards;
+ AnchorMenu.Instance.makeLabels = unimplementedFunction;
AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;
AnchorMenu.Instance.OnClick = () => {
!this.layoutDoc.layout_showSidebar && this.toggleSidebar();
@@ -256,9 +245,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (target) {
anchor.followLinkAudio = true;
let stopFunc: () => void = emptyFunction;
- const targetData = target[DocData];
- targetData.mediaState = mediaState.Recording;
- DictationManager.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
+ target.$mediaState = mediaState.Recording;
+ DictationManager.recordAudioAnnotation(target[DocData], Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore
const reactionDisposer = reaction(
() => target.mediaState,
@@ -327,6 +315,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
(node.attrs.hideValue ? '' : Field.toJavascriptString(refDoc[fieldKey] as FieldType))
);
}
+ if (node.type === this.EditorView?.state.schema.nodes.dashDoc) {
+ const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
+ return refDoc[ToString]();
+ }
return '';
};
dispatchTransaction = (tx: Transaction) => {
@@ -1008,31 +1000,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
!help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' });
};
- findImageTags = async () => {
- const c = this.ProseRef?.getElementsByTagName('img');
- if (c) {
- for (const i of c) {
- // console.log(canvas.toDataURL());
- // canvas.style.zIndex = '2000000';
- // document.body.appendChild(canvas);
- if (i.className !== 'ProseMirror-separator') this.getImageDesc(i.src);
- }
- }
- };
-
- getImageDesc = async (u: string) => {
- try {
- const hrefBase64 = await imageUrlToBase64(u);
- const response = await gptImageLabel(
- hrefBase64,
- 'Make flashcards out of this text and image with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' + (this.dataDoc.text as RichTextField)?.Text
- );
- AnchorMenu.Instance.transferToFlashcard(response || 'Something went wrong', NumCast(this.dataDoc['x']), NumCast(this.dataDoc['y']));
- } catch (error) {
- console.log('Error', error);
- }
- };
-
animateRes = (resIndex: number, newText: string) => {
if (resIndex < newText.length) {
const marks = this.EditorView?.state.storedMarks ?? [];
@@ -1100,7 +1067,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
const link = CreateLinkToActiveAudio(textanchorFunc, false).lastElement();
if (link) {
- link[DocData].isDictation = true;
+ link.$isDictation = true;
const audioanchor = Cast(link.link_anchor_2, Doc, null);
const textanchor = Cast(link.link_anchor_1, Doc, null);
if (audioanchor) {
@@ -1110,7 +1077,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
audioId: audioanchor[Id],
textId: textanchor[Id],
});
- textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode;
+ textanchor.$title = 'dictation:' + audiotag.attrs.timeCode;
const tr = this.EditorView.state.tr.insert(this.EditorView.state.doc.content.size, audiotag);
const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size));
this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
@@ -1491,6 +1458,29 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
_didScroll = false;
_scrollStopper: undefined | (() => void);
+ scrollToSelection = () => {
+ if (this.EditorView && this._ref.current) {
+ const editorView = this.EditorView;
+ const docPos = editorView.coordsAtPos(editorView.state.selection.to);
+ const viewRect = this._ref.current.getBoundingClientRect();
+ const scrollRef = this._scrollRef;
+ const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
+ const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
+ if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
+ const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
+ const scrollPos = scrollRef.scrollTop + shift * this.ScreenToLocalBoxXf().Scale;
+ if (this._focusSpeed !== undefined) {
+ setTimeout(() => {
+ scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed || 0, scrollRef, scrollPos, 'ease', this._scrollStopper));
+ });
+ } else {
+ scrollRef.scrollTo({ top: scrollPos });
+ }
+ this._didScroll = true;
+ }
+ }
+ return true;
+ };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setupEditor(config: any, fieldKey: string) {
const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
@@ -1499,31 +1489,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.EditorView?.destroy();
this._editorView = new EditorView(this.ProseRef, {
state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
- handleScrollToSelection: editorView => {
- const docPos = editorView.coordsAtPos(editorView.state.selection.to);
- const viewRect = this._ref.current!.getBoundingClientRect();
- const scrollRef = this._scrollRef;
- const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
- const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
- if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
- const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE);
- const scrollPos = scrollRef.scrollTop + shift * this.ScreenToLocalBoxXf().Scale;
- if (this._focusSpeed !== undefined) {
- setTimeout(() => {
- scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed || 0, scrollRef, scrollPos, 'ease', this._scrollStopper));
- });
- } else {
- scrollRef.scrollTo({ top: scrollPos });
- }
- this._didScroll = true;
- }
- return true;
- },
+ handleScrollToSelection: this.scrollToSelection,
dispatchTransaction: this.dispatchTransaction,
nodeViews: FormattedTextBox._nodeViews(this),
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
+ // bcz: major hack! a patch to prosemirror broke scrolling to selection when the selection is not a dom selection
+ // this replaces prosemirror's scrollToSelection function with Dash's
+ (this.EditorView as unknown as { scrollToSelection: unknown }).scrollToSelection = this.scrollToSelection;
const { state } = this._editorView;
if (!rtfField) {
const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
@@ -1980,7 +1954,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
<SidebarAnnos
ref={this._sidebarRef}
{...this._props}
- Document={this.Document}
+ Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth
@@ -2108,7 +2082,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
const scrSize = (which: number, view = this._props.docViewPath().slice(-which)[0]) =>
- [view._props.PanelWidth() / view.screenToLocalScale(), view._props.PanelHeight() / view.screenToLocalScale()]; // prettier-ignore
+ [view?._props.PanelWidth() /(view?.screenToLocalScale()??1), view?._props.PanelHeight() / (view?.screenToLocalScale()??1)]; // prettier-ignore
const scrMargin = [Math.max(0, (scrSize(2)[0] - scrSize(1)[0]) / 2), Math.max(0, (scrSize(2)[1] - scrSize(1)[1]) / 2)];
const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), this._props.xPadding ?? 0, 0, ((this._props.screenXPadding?.() ?? 0) - scrMargin[0]) * this.ScreenToLocalBoxXf().Scale);
const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, ((this._props.yPadding ?? 0) - scrMargin[1]) * this.ScreenToLocalBoxXf().Scale);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 6c0eac103..1d790f5bb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -134,20 +134,16 @@ export class FormattedTextBoxComment {
// this checks if the selection is a hyperlink. If so, it displays the target doc's text for internal links, and the url of the target for external links.
if (state.selection.$from && hrefs?.length) {
- const nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
- const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef;
- // nbef &&
- naft &&
- LinkInfo.SetLinkInfo({
- DocumentView: textBox.DocumentView,
- styleProvider: textBox._props.styleProvider,
- linkSrc: textBox.Document,
- linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
- location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))),
- hrefs,
- showHeader: true,
- noPreview,
- });
+ LinkInfo.SetLinkInfo({
+ DocumentView: textBox.DocumentView,
+ styleProvider: textBox._props.styleProvider,
+ linkSrc: textBox.Document,
+ linkDoc: linkDoc ? (DocServer.GetCachedRefField(linkDoc) as Doc) : undefined,
+ location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, 0 - 1))),
+ hrefs,
+ showHeader: true,
+ noPreview,
+ });
}
}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 3c84e5a10..eabc6455f 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,29 +1,30 @@
import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from 'prosemirror-commands';
import { redo, undo } from 'prosemirror-history';
-import { Schema } from 'prosemirror-model';
+import { MarkType, Node, Schema } from 'prosemirror-model';
import { liftListItem, sinkListItem, splitListItem, wrapInList } from 'prosemirror-schema-list';
-import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
+import { Command, EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
import { liftTarget } from 'prosemirror-transform';
import { EditorView } from 'prosemirror-view';
import { ClientUtils } from '../../../../ClientUtils';
-import { Utils } from '../../../../Utils';
+import { numberRange, Utils } from '../../../../Utils';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { GetEffectiveAcl } from '../../../../fields/util';
import { Docs } from '../../../documents/Documents';
import { RTFMarkup } from '../../../util/RTFMarkup';
import { DocumentView } from '../DocumentView';
import { OpenWhere } from '../OpenWhere';
+import { FormattedTextBox } from './FormattedTextBox';
const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
-export type KeyMap = { [key: string]: any };
+export type KeyMap = { [key: string]: Command };
-export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) => {
+export function updateBullets(tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) {
let mapStyle = assignedMapStyle;
- tx2.doc.descendants((node: any, offset: any /* , index: any */) => {
+ tx2.doc.descendants((node: Node, offset: number /* , index: any */) => {
if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
- const { path } = tx2.doc.resolve(offset) as any;
- let depth = Array.from(path).reduce((p: number, c: any) => p + (c.type === schema.nodes.ordered_list ? 1 : 0), 0);
+ const resolved = tx2.doc.resolve(offset);
+ let depth = [0, ...numberRange(resolved.depth)].reduce((p, c, idx) => p + (resolved.node(idx).type === schema.nodes.ordered_list ? 1 : 0), 0);
if (node.type === schema.nodes.ordered_list) {
if (depth === 0 && !assignedMapStyle) mapStyle = node.attrs.mapStyle;
depth++;
@@ -32,28 +33,30 @@ export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle
}
});
return tx2;
-};
+}
-export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMap {
- const keys: { [key: string]: any } = {};
+export function buildKeymap<S extends Schema<string>>(schema: S, tbox?: FormattedTextBox): KeyMap {
+ const keys: { [key: string]: Command } = {};
- function bind(key: string, cmd: any) {
+ function bind(key: string, cmd: Command) {
keys[key] = cmd;
}
function onKey(): boolean | undefined {
- // bcz: this is pretty hacky -- prosemirror doesn't send us the keyboard event, but the 'event' variable is in scope.. so we access it anyway
+ // bcz: hack -- prosemirror doesn't send us the keyboard event, but the 'event' variable is in scope.. so we access it anyway
// eslint-disable-next-line no-restricted-globals
- return props.onKey?.(event, props);
+ return event && tbox?._props.onKey?.(event as unknown as KeyboardEvent, tbox);
}
- const canEdit = (state: any) => {
- const permissions = GetEffectiveAcl(props.TemplateDataDocument ?? props.Document[DocData]);
+ const canEdit = (state: EditorState) => {
+ if (!tbox) return true;
+ const permissions = GetEffectiveAcl(tbox._props.TemplateDataDocument ?? tbox.Document[DocData]);
switch (permissions) {
case AclAugment:
{
- const prevNode = state.selection.$cursor.nodeBefore;
- const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid;
+ // previously used hack: (state.selection as any).$cursor.nodeBefore;
+ const prevNode = state.selection?.$anchor.nodeBefore;
+ const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : Array.from(prevNode.marks).lastElement()?.attrs.userid;
if (prevUser !== ClientUtils.CurrentUserEmail()) {
return false;
}
@@ -64,7 +67,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
return true;
};
- const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch);
+ const toggleEditableMark = (mark: MarkType) => (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && toggleMark(mark)(state, dispatch);
// History commands
bind('Mod-z', undo);
@@ -84,13 +87,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
bind('Mod-U', toggleEditableMark(schema.marks.underline));
// Commands for lists
- bind('Ctrl-i', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
+ bind('Ctrl-i', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch));
bind('Ctrl-Tab', () => onKey() || true);
bind('Alt-Tab', () => onKey() || true);
bind('Meta-Tab', () => onKey() || true);
bind('Meta-Enter', () => onKey() || true);
- bind('Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+ bind('Tab', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
if (onKey()) return true;
if (!canEdit(state)) return true;
const ref = state.selection;
@@ -101,13 +104,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
- dispatch(tx3);
+ dispatch?.(tx3);
})
) {
// couldn't sink into an existing list, so wrap in a new one
const newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)));
if (
- !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
+ !wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => {
const tx25 = updateBullets(tx2, schema);
const olNode = tx25.doc.nodeAt(range!.start)!;
const tx3 = tx25.setNodeMarkup(range!.start, olNode.type, olNode.attrs, marks);
@@ -115,16 +118,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
const tx4 = tx3.setSelection(TextSelection.near(tx3.doc.resolve(state.selection.to + 2)));
- dispatch(tx4);
+ dispatch?.(tx4);
})
) {
console.log('bullet promote fail');
}
}
- return undefined;
+ return false;
});
- bind('Shift-Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+ bind('Shift-Tab', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
if (onKey()) return true;
if (!canEdit(state)) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -134,119 +137,136 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
- dispatch(tx3);
+ dispatch?.(tx3);
})
) {
console.log('bullet demote fail');
}
- return undefined;
+ return false;
});
// Command to create a new Tab with a PDF of all the command shortcuts
- bind('Mod-/', () => {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ bind('Mod-/', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
const newDoc = Docs.Create.PdfDocument(ClientUtils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 });
- props.addDocTab(newDoc, OpenWhere.addRight);
+ tbox?._props.addDocTab(newDoc, OpenWhere.addRight);
+ return false;
});
// Commands to modify BlockType
- bind('Ctrl->', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
- bind('Alt-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
- bind('Shift-Ctrl-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
+ bind('Ctrl->', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && wrapIn(schema.nodes.blockquote)(state, dispatch));
+ bind('Alt-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch));
+ bind('Shift-Ctrl-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch));
- bind('Ctrl-m', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+ bind('Ctrl-m', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
if (canEdit(state)) {
const tr = state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: 'math' + Utils.GenerateGuid() }));
- dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1))));
+ dispatch?.(tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1))));
+ return true;
}
+ return false;
});
for (let i = 1; i <= 6; i++) {
- bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
+ bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch));
}
// Command to create a horizontal break line
const hr = schema.nodes.horizontal_rule;
- bind('Mod-_', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()));
+ bind('Mod-_', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ if (canEdit(state)) {
+ dispatch?.(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
+ return true;
+ }
+ return false;
+ });
// Command to unselect all
- bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
- (document.activeElement as any).blur?.();
+ bind('Escape', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ dispatch?.(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+ (document.activeElement as HTMLElement)?.blur?.();
DocumentView.DeselectAll();
+ return true;
});
bind('Alt-Enter', () => onKey() || true);
bind('Ctrl-Enter', () => onKey() || true);
- bind('Cmd-a', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
+ bind('Cmd-a', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ dispatch?.(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
return true;
});
bind('Cmd-?', () => {
RTFMarkup.Instance.setOpen(true);
return true;
});
- bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+ bind('Cmd-e', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
if (!state.selection.empty) {
const mark = state.schema.marks.summarizeInclusive.create();
const tr = state.tr.addMark(state.selection.$from.pos, state.selection.$to.pos, mark);
const content = tr.selection.content();
- tr.selection.replaceWith(tr, schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }));
- dispatch(tr);
+ tr.selection.replaceWith(tr, schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }, undefined, state.selection.$anchor.marks() ?? []));
+ dispatch?.(tr);
}
return true;
});
- bind('Cmd-]', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- const resolved = state.doc.resolve(state.selection.from) as any;
- const { tr } = state;
- if (resolved?.parent.type.name === 'paragraph') {
- tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks);
+ bind('Cmd-]', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ const {
+ tr,
+ selection: { $from },
+ } = state;
+ if ($from?.parent.type.name === 'paragraph') {
+ tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'right' }, $from.parent.marks);
} else {
- const node = resolved.nodeAfter;
+ const node = $from.nodeAfter;
const sm = state.storedMarks || undefined;
if (node) {
tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm || [])]);
}
}
- dispatch(tr);
+ dispatch?.(tr);
return true;
});
- bind('Cmd-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- const resolved = state.doc.resolve(state.selection.from) as any;
- const { tr } = state;
- if (resolved?.parent.type.name === 'paragraph') {
- tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks);
+ bind('Cmd-\\', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ const {
+ tr,
+ selection: { $from },
+ } = state;
+ if ($from?.parent.type.name === 'paragraph') {
+ tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'center' }, $from.parent.marks);
} else {
- const node = resolved.nodeAfter;
+ const node = $from.nodeAfter;
const sm = state.storedMarks || undefined;
if (node) {
tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm || [])]);
}
}
- dispatch(tr);
+ dispatch?.(tr);
return true;
});
- bind('Cmd-[', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- const resolved = state.doc.resolve(state.selection.from) as any;
- const { tr } = state;
- if (resolved?.parent.type.name === 'paragraph') {
- tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks);
+ bind('Cmd-[', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ const {
+ tr,
+ selection: { $from },
+ } = state;
+ if ($from?.parent.type.name === 'paragraph') {
+ tr.setNodeMarkup(state.selection.from - state.selection.$from.parentOffset - 1, schema.nodes.paragraph, { ...$from.parent.attrs, align: 'left' }, $from.parent.marks);
} else {
- const node = resolved.nodeAfter;
+ const node = $from.nodeAfter;
const sm = state.storedMarks || undefined;
if (node) {
tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm || [])]);
}
}
- dispatch(tr);
+ dispatch?.(tr);
return true;
});
- bind('Cmd-f', (state: EditorState, dispatch: (tx: Transaction) => void) => {
+ bind('Cmd-f', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
const content = state.tr.selection.empty ? undefined : state.tr.selection.content().content.textBetween(0, state.tr.selection.content().size + 1);
const newNode = schema.nodes.footnote.create({}, content ? state.schema.text(content) : undefined);
const { tr } = state;
tr.replaceSelectionWith(newNode); // replace insertion with a footnote.
- dispatch(
+ dispatch?.(
tr.setSelection(
new NodeSelection( // select the footnote node to open its display
tr.doc.resolve(
@@ -259,25 +279,25 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
return true;
});
- bind('Ctrl-a', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
+ bind('Ctrl-a', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => {
+ dispatch?.(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1))));
return true;
});
// backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
- const backspace = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView) => {
+ const backspace = (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined, view?: EditorView) => {
if (onKey()) return true;
if (!canEdit(state)) return true;
if (
!deleteSelection(state, (tx: Transaction) => {
- dispatch(updateBullets(tx, schema));
+ dispatch?.(updateBullets(tx, schema));
})
) {
if (
!joinBackward(state, (tx: Transaction) => {
- dispatch(updateBullets(tx, schema));
- if (view.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) {
+ dispatch?.(updateBullets(tx, schema));
+ if (view?.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) {
// gets rid of an extra paragraph when joining two list items together.
joinBackward(view.state, (tx2: Transaction) => view.dispatch(tx2));
}
@@ -285,7 +305,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
) {
if (
!selectNodeBackward(state, (tx: Transaction) => {
- dispatch(updateBullets(tx, schema));
+ dispatch?.(updateBullets(tx, schema));
})
) {
return false;
@@ -299,7 +319,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
// newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
// command to break line
- const enter = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView, once = true) => {
+ const enter = (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined, view?: EditorView, once = true) => {
if (onKey()) return true;
if (!canEdit(state)) return true;
@@ -311,31 +331,31 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
!state.selection.$from.node().content.size &&
trange
) {
- dispatch(state.tr.lift(trange, depth) as any);
+ dispatch?.(state.tr.lift(trange, depth));
return true;
}
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
- if (!newlineInCode(state, dispatch as any)) {
- const olNode = view.state.selection.$anchor.node(-2);
- const liNode = view.state.selection.$anchor.node(-1);
+ if (!newlineInCode(state, dispatch)) {
+ const olNode = view?.state.selection.$anchor.node(-2);
+ const liNode = view?.state.selection.$anchor.node(-1);
// prettier-ignore
if (liNode?.type === schema.nodes.list_item && !liNode.textContent &&
- olNode?.type === schema.nodes.ordered_list && once && view.state.selection.$from.depth === 3)
+ olNode?.type === schema.nodes.ordered_list && once && view?.state.selection.$from.depth === 3)
{
// handles case of hitting enter at then end of a top-level empty list item - the result is to create a paragraph
- for (let i = 0; i < 10 && view.state.selection.$from.depth > 1 && liftListItem(schema.nodes.list_item)(view.state, view.dispatch); i++);
+ for (let i = 0; i < 10 && view?.state.selection.$from.depth > 1 && liftListItem(schema.nodes.list_item)(view.state, view.dispatch); i++);
} else if (
- !splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
+ !splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
const tx3 = updateBullets(tx2, schema);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
- dispatch(tx3);
+ dispatch?.(tx3);
// removes an extra paragraph created when selecting text across two list items or splitting an empty list item
- !once && view.dispatch(view.state.tr.deleteRange(view.state.selection.from - 5, view.state.selection.from - 2));
+ !once && view?.dispatch(view?.state.tr.deleteRange(view.state.selection.from - 5, view.state.selection.from - 2));
})
) {
- if (once && view.state.selection.$from.node(-2)?.type === schema.nodes.ordered_list && view.state.selection.$from.node(-1)?.type === schema.nodes.list_item && view.state.selection.$from.node(-1)?.textContent === '') {
+ if (once && view?.state.selection.$from.node(-2)?.type === schema.nodes.ordered_list && view?.state.selection.$from.node(-1)?.type === schema.nodes.list_item && view.state.selection.$from.node(-1)?.textContent === '') {
// handles case of hitting enter on an empty list item which needs to create a second empty paragraph, then split it by calling enter() again
view.dispatch(view.state.tr.insert(view.state.selection.from, schema.nodes.paragraph.create({})));
enter(view.state, view.dispatch, view, false);
@@ -346,12 +366,12 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
const tonode = tx3.selection.$to.node();
if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) {
const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks).setStoredMarks(marks || []);
- dispatch(tx4);
+ dispatch?.(tx4);
}
- if (view.state.selection.$anchor.depth > 0 &&
- view.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
- view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
+ if ((view?.state.selection.$anchor.depth ??0) > 0 &&
+ view?.state.selection.$anchor.node(view.state.selection.$anchor.depth-1).type === schema.nodes.list_item &&
+ view?.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
// if text is selected across list items, then we need to forcibly insert a new line since the splitBlock code joins the two list items.
enter(view.state, dispatch, view, false);
}
@@ -368,14 +388,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
// Command to create a blank space
bind('Space', () => {
- const editDoc = props.TemplateDataDocument ?? props.Document[DocData];
+ const editDoc = tbox?._props.TemplateDataDocument ?? tbox?.Document[DocData];
if (editDoc && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(editDoc))) return true;
return false;
});
- bind('Alt-ArrowUp', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any));
- bind('Alt-ArrowDown', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any));
- bind('Mod-BracketLeft', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any));
+ bind('Alt-ArrowUp', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && joinUp(state, dispatch));
+ bind('Alt-ArrowDown', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && joinDown(state, dispatch));
+ bind('Mod-BracketLeft', (state: EditorState, dispatch: ((tx: Transaction) => void) | undefined) => canEdit(state) && lift(state, dispatch));
const cmd = chainCommands(exitCode, (state, dispatch) => {
if (dispatch) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index c332c592b..77c00537b 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -84,9 +84,8 @@ export class RichTextRules {
// Create annotation to a field on the text document
new InputRule(/>::$/, (state, match, start, end) => {
const creator = (doc: Doc) => {
- const textDoc = this.Document[DocData];
- const numInlines = NumCast(textDoc.inlineTextCount);
- textDoc.inlineTextCount = numInlines + 1;
+ const numInlines = NumCast(this.Document.$inlineTextCount);
+ this.Document.$inlineTextCount = numInlines + 1;
const node = state.doc.resolve(start).nodeAfter;
const newNode = schema.nodes.dashComment.create({ docId: doc[Id], reflow: false });
const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: doc[Id], float: 'right' });
@@ -109,16 +108,15 @@ export class RichTextRules {
}),
// Create annotation to a field on the text document
new InputRule(/>>$/, (state, match, start, end) => {
- const textDoc = this.Document[DocData];
- const numInlines = NumCast(textDoc.inlineTextCount);
- textDoc.inlineTextCount = numInlines + 1;
- const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
- const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
+ const numInlines = NumCast(this.Document.$inlineTextCount);
+ this.Document.$inlineTextCount = numInlines + 1;
+ const inlineFieldKey = '$inline' + numInlines; // which field on the text document this annotation will write to
+ const inlineLayoutKey = '$layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
const textDocInline = Docs.Create.TextDocument('', {
_layout_fieldKey: inlineLayoutKey,
_width: 75,
_height: 35,
- annotationOn: textDoc,
+ annotationOn: this.Document[DocData],
_layout_fitWidth: true,
_layout_autoHeight: true,
text_fontSize: '9px',
@@ -128,9 +126,9 @@ export class RichTextRules {
textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
textDocInline.isDataDoc = true;
- textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
- textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
- textDoc[inlineFieldKey] = ''; // set a default value for the annotation
+ textDocInline.proto = this.Document[DocData]; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]]
+ this.Document[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
+ this.Document[inlineFieldKey] = ''; // set a default value for the annotation
const node = state.doc.resolve(start).nodeAfter;
const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id], reflow: true });
const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: textDocInline[Id], float: 'right' });
@@ -319,10 +317,10 @@ export class RichTextRules {
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
- // [@{this,doctitle,}.fieldKey{:,=,:=,=:=}value]
- // [@{this,doctitle,}.fieldKey]
+ // @{this,doctitle,}.fieldKey{:,=,:=,=:=}value
+ // @{this,doctitle,}.fieldKey
new InputRule(
- /\[(@|@this\.|@[a-zA-Z_? \-0-9]+\.)([a-zA-Z_?\-0-9]+)((:|=|:=|=:=)([a-zA-Z,_().@?+\-*/ 0-9()]*))?\]/,
+ /(@|@this\.|@[a-zA-Z_? \-0-9]+\.)([a-zA-Z_?\-0-9]+)((:|=|:=|=:=)([a-zA-Z,_().@?+\-*/ 0-9()]*))?\s/,
(state, match, start, end) => {
const docTitle = match[1].substring(1).replace(/\.$/, '');
const fieldKey = match[2];
@@ -334,18 +332,14 @@ export class RichTextRules {
if (value?.includes(',') && !value.startsWith('((')) {
const values = value.split(',');
const strs = values.some(v => !v.match(/^[-]?[0-9.]$/));
- this.Document[DocData][fieldKey] = strs ? new List<string>(values) : new List<number>(values.map(v => Number(v)));
+ this.Document['$' + fieldKey] = strs ? new List<string>(values) : new List<number>(values.map(v => Number(v)));
} else if (value) {
Doc.SetField(
this.Document,
fieldKey,
assign + value,
Doc.IsDataProto(this.Document) ? true : undefined,
- assign.includes(':=')
- ? undefined
- : (gptval: FieldResult) => {
- (dataDoc ? this.Document[DocData] : this.Document)[fieldKey] = gptval as string;
- }
+ assign.includes(':=') ? undefined : (gptval: FieldResult) => (this.Document[(dataDoc ? '$' : '_') + fieldKey] = gptval as string)
);
if (fieldKey === this.TextBox.fieldKey) return this.TextBox.EditorView!.state.tr;
}
@@ -399,11 +393,11 @@ export class RichTextRules {
new InputRule(/#(@?[a-zA-Z_-]+[a-zA-Z_\-0-9]*)\s$/, (state, match, start, end) => {
const tag = match[1];
if (!tag) return state.tr;
- // this.Document[DocData]['#' + tag] = '#' + tag;
- const tags = StrListCast(this.Document[DocData].tags);
+ // this.Document[['$#' + tag] = '#' + tag;
+ const tags = StrListCast(this.Document.$tags);
if (!tags.includes(tag)) {
tags.push(tag);
- this.Document[DocData].tags = new List<string>(tags);
+ this.Document.$tags = new List<string>(tags);
this.Document._layout_showTags = true;
}
const fieldView = state.schema.nodes.dashField.create({ fieldKey: tag.startsWith('@') ? tag.replace(/^@/, '') : '#' + tag });
diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 238267f6e..6dea891a0 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,12 +1,11 @@
import { TextSelection } from 'prosemirror-state';
-import { Fragment, Node, Slice } from 'prosemirror-model';
+import { Attrs, Fragment, Node, Slice } from 'prosemirror-model';
import * as ReactDOM from 'react-dom/client';
import * as React from 'react';
+import { EditorView } from 'prosemirror-view';
-interface ISummaryView {}
// currently nothing needs to be rendered for the internal view of a summary.
-// eslint-disable-next-line react/prefer-stateless-function
-export class SummaryViewInternal extends React.Component<ISummaryView> {
+export class SummaryViewInternal extends React.Component<object> {
render() {
return null;
}
@@ -18,30 +17,30 @@ export class SummaryViewInternal extends React.Component<ISummaryView> {
// method instead of changing prosemirror's text when the expand/elide buttons are clicked.
export class SummaryView {
dom: HTMLSpanElement; // container for label and value
- root: any;
+ root: ReactDOM.Root;
- constructor(node: any, view: any, getPos: any) {
+ constructor(node: Node, view: EditorView, getPos: () => number | undefined) {
this.dom = document.createElement('span');
this.dom.className = this.className(node.attrs.visibility);
- this.dom.onpointerdown = (e: any) => {
+ this.dom.onpointerdown = (e: PointerEvent) => {
this.onPointerDown(e, node, view, getPos);
};
- this.dom.onkeypress = function (e: any) {
+ this.dom.onkeypress = function (e: KeyboardEvent) {
e.stopPropagation();
};
- this.dom.onkeydown = function (e: any) {
+ this.dom.onkeydown = function (e: KeyboardEvent) {
e.stopPropagation();
};
- this.dom.onkeyup = function (e: any) {
+ this.dom.onkeyup = function (e: KeyboardEvent) {
e.stopPropagation();
};
- this.dom.onmousedown = function (e: any) {
+ this.dom.onmousedown = function (e: MouseEvent) {
e.stopPropagation();
};
const js = node.toJSON;
- node.toJSON = function (...args: any[]) {
- return js.apply(this, args);
+ node.toJSON = function (...args: unknown[]) {
+ return js.apply(this, args as []);
};
this.root = ReactDOM.createRoot(this.dom);
@@ -54,7 +53,7 @@ export class SummaryView {
}
selectNode() {}
- updateSummarizedText(start: any, view: any) {
+ updateSummarizedText(start: number, view: EditorView) {
const mtype = view.state.schema.marks.summarize;
const mtypeInc = view.state.schema.marks.summarizeInclusive;
let endPos = start;
@@ -65,7 +64,7 @@ export class SummaryView {
// eslint-disable-next-line no-loop-func
view.state.doc.nodesBetween(start, i, (node: Node /* , pos: number, parent: Node, index: number */) => {
if (node.isLeaf && !visited.has(node) && !skip) {
- if (node.marks.find((m: any) => m.type === mtype || m.type === mtypeInc)) {
+ if (node.marks.find(m => m.type === mtype || m.type === mtypeInc)) {
visited.add(node);
endPos = i + node.nodeSize - 1;
} else skip = true;
@@ -75,21 +74,18 @@ export class SummaryView {
return TextSelection.create(view.state.doc, start, endPos);
}
- onPointerDown = (e: any, node: any, view: any, getPos: any) => {
+ onPointerDown = (e: PointerEvent, node: Node, view: EditorView, getPos: () => number | undefined) => {
const visible = !node.attrs.visibility;
- const attrs = { ...node.attrs, visibility: visible };
- let textSelection = TextSelection.create(view.state.doc, getPos() + 1);
- if (!visible) {
- // update summarized text and save in attrs
- textSelection = this.updateSummarizedText(getPos() + 1, view);
- attrs.text = textSelection.content();
- attrs.textslice = attrs.text.toJSON();
- }
+ const textSelection = visible //
+ ? TextSelection.create(view.state.doc, (getPos() ?? 0) + 1)
+ : this.updateSummarizedText((getPos() ?? 0) + 1, view); // update summarized text and save in attrs
+ const text = textSelection.content();
+ const attrs = { ...node.attrs, visibility: visible, ...(!visible ? { text, textslice: text.toJSON() } : {}) } as Attrs;
view.dispatch(
view.state.tr
.setSelection(textSelection) // select the current summarized text (or where it will be if its collapsed)
.replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text) // collapse/expand it
- .setNodeMarkup(getPos(), undefined, attrs)
+ .setNodeMarkup(getPos() ?? 0, undefined, attrs)
); // update the attrs
e.preventDefault();
e.stopPropagation();
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index ba8e4faed..dc1e4772e 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -316,9 +316,9 @@ export const marks: { [index: string]: MarkSpec } = {
attrs: {
selected: { default: false },
},
- parseDOM: [{ style: 'background: yellow' }],
+ parseDOM: [{ style: 'background: lightGray' }],
toDOM: node => {
- return ['span', { style: `background: ${node.attrs.selected ? 'orange' : 'yellow'}` }];
+ return ['span', { style: `background: ${node.attrs.selected ? 'orange' : 'lightGray'}` }];
},
},
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 02ded3103..fe7b77e74 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -386,10 +386,6 @@ export const nodes: { [index: string]: NodeSpec } = {
},
},
{
- style: 'list-style-type=disc',
- getAttrs: () => ({ mapStyle: 'bullet' }),
- },
- {
tag: 'ol',
getAttrs: dom => {
return {
@@ -443,6 +439,7 @@ export const nodes: { [index: string]: NodeSpec } = {
mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
visibility: { default: true },
},
+ marks: '_',
content: '(paragraph|audiotag)+ | ((paragraph|audiotag)+ ordered_list)',
parseDOM: [
{
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 657e689bb..85bd95d15 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -24,8 +24,8 @@ import { PointerHandler } from './imageEditorUtils/PointerHandler';
import { activeColor, bgColor, brushWidthOffset, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './imageEditorUtils/imageEditorConstants';
import { CutMode, CursorData, ImageDimensions, ImageEditTool, ImageToolType, Point } from './imageEditorUtils/imageEditorInterfaces';
import { DocumentView } from '../DocumentView';
-import { DocData } from '../../../../fields/DocSymbols';
import { SettingsManager } from '../../../util/SettingsManager';
+import { Upload } from '../../../../server/SharedMediaTypes';
interface GenerativeFillProps {
imageEditorOpen: boolean;
@@ -397,9 +397,8 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
const newImgDoc = await createNewImgDoc(finalImg, firstDoc);
if (newImgDoc) {
// set the image to transparent to remove the background / brushstrokes
- const docData = newImgDoc[DocData];
- docData.backgroundColor = 'transparent';
- docData.disableMixBlend = true;
+ newImgDoc.$backgroundColor = 'transparent';
+ newImgDoc.$disableMixBlend = true;
if (firstDoc) setIsFirstDoc(false);
setEdits([...prevEdits, { url: finalImgURL, saveRes: undefined }]);
}
@@ -476,7 +475,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean /*, parent?: Doc */): Promise<Doc | undefined> => {
if (!imageRootDoc) return undefined;
const { src } = img;
- const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] });
+ const [result] = (await Networking.PostToServer('/uploadRemoteImage', { sources: [src] })) as Upload.ImageInformation[];
const source = ClientUtils.prepend(result.accessPaths.agnostic.client);
if (firstDoc) {
diff --git a/src/client/views/nodes/imageEditor/imageMeshTool/ImageMeshTool.ts b/src/client/views/nodes/imageEditor/imageMeshTool/ImageMeshTool.ts
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/client/views/nodes/imageEditor/imageMeshTool/ImageMeshTool.ts
diff --git a/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.scss b/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.scss
new file mode 100644
index 000000000..253f48f77
--- /dev/null
+++ b/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.scss
@@ -0,0 +1,24 @@
+/* MeshTransformGrid.scss */
+.meshTransformGrid {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ pointer-events: none; /* Prevents interaction with the grid itself */
+ opacity: 5%;
+}
+
+.grid-line {
+ position: absolute;
+ background-color: rgba(255, 255, 255, 0.6); /* Light grid lines */
+}
+
+.control-point {
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ background-color: rgba(255, 255, 255, 1); /* White control points */
+ border-radius: 50%;
+ cursor: pointer;
+ z-index: 10;
+ pointer-events: auto; /* Allows dragging of control points */
+}
diff --git a/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.tsx b/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.tsx
new file mode 100644
index 000000000..ee5c597e9
--- /dev/null
+++ b/src/client/views/nodes/imageEditor/imageMeshTool/imageMesh.tsx
@@ -0,0 +1,109 @@
+import React, { useState, useEffect } from 'react';
+import './MeshTransformGrid.scss';
+
+interface MeshTransformGridProps {
+ imageRef: React.RefObject<HTMLImageElement>; // Reference to the image element
+ gridXSize: number; // Number of X subdivisions
+ gridYSize: number; // Number of Y subdivisions
+ isInteractive: boolean; // Whether control points are interactive (can be dragged)
+}
+
+const MeshTransformGrid: React.FC<MeshTransformGridProps> = ({ imageRef, gridXSize, gridYSize, isInteractive }) => {
+ const [controlPoints, setControlPoints] = useState<any[]>([]);
+
+ // Set up control points based on image size and grid sizes
+ useEffect(() => {
+ if (imageRef.current) {
+ const { width, height, left, top } = imageRef.current.getBoundingClientRect();
+ const newControlPoints = [];
+
+ for (let i = 0; i <= gridYSize; i++) {
+ for (let j = 0; j <= gridXSize; j++) {
+ newControlPoints.push({
+ id: `${i}-${j}`,
+ x: (j * width) / gridXSize + left,
+ y: (i * height) / gridYSize + top,
+ });
+ }
+ }
+
+ setControlPoints(newControlPoints);
+ }
+ }, [imageRef, gridXSize, gridYSize]);
+
+ // Handle dragging of control points
+ const handleDrag = (e: React.MouseEvent, pointId: string) => {
+ if (!isInteractive) return; // Prevent dragging if grid is not interactive
+
+ const { clientX, clientY } = e;
+ const updatedPoints = controlPoints.map((point) => {
+ if (point.id === pointId) {
+ return { ...point, x: clientX, y: clientY };
+ }
+ return point;
+ });
+ setControlPoints(updatedPoints);
+ };
+
+ // Render grid lines between control points
+ const renderGridLines = () => {
+ const lines = [];
+ for (let i = 0; i < controlPoints.length; i++) {
+ const point = controlPoints[i];
+ const nextPoint = controlPoints[i + 1];
+
+ // Horizontal lines
+ if (nextPoint && i % (gridXSize + 1) !== gridXSize) {
+ lines.push({
+ start: { x: point.x, y: point.y },
+ end: { x: nextPoint.x, y: nextPoint.y },
+ });
+ }
+
+ // Vertical lines
+ if (i + gridXSize + 1 < controlPoints.length) {
+ const downPoint = controlPoints[i + gridXSize + 1];
+ lines.push({
+ start: { x: point.x, y: point.y },
+ end: { x: downPoint.x, y: downPoint.y },
+ });
+ }
+ }
+ return lines.map((line, index) => (
+ <div
+ key={index}
+ className="grid-line"
+ style={{
+ position: 'absolute',
+ left: `${line.start.x}px`,
+ top: `${line.start.y}px`,
+ width: `${Math.abs(line.end.x - line.start.x)}px`,
+ height: `${Math.abs(line.end.y - line.start.y)}px`,
+ border: '1px solid rgba(255, 255, 255, 0.6)',
+ }}
+ />
+ ));
+ };
+
+ return (
+ <div className="meshTransformGrid">
+ {renderGridLines()}
+
+ {controlPoints.map((point) => (
+ <div
+ key={point.id}
+ className="control-point"
+ style={{
+ left: `${point.x}px`,
+ top: `${point.y}px`,
+ transform: 'translate(-50%, -50%)',
+ }}
+ draggable={isInteractive} // Only allow dragging if interactive
+ onDrag={(e) => handleDrag(e, point.id)}
+ />
+ ))}
+ </div>
+ );
+};
+
+export default MeshTransformGrid;
diff --git a/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.scss b/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.scss
new file mode 100644
index 000000000..a72b2de6a
--- /dev/null
+++ b/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.scss
@@ -0,0 +1,21 @@
+/* MeshTransformButton.scss */
+.meshTransformBtnContainer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.grid-line {
+ position: absolute;
+ background-color: rgba(255, 255, 255, 0.6);
+}
+
+.control-point {
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ background-color: rgba(255, 255, 255, 1);
+ border-radius: 50%;
+ cursor: pointer;
+ z-index: 10;
+}
diff --git a/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.tsx b/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.tsx
new file mode 100644
index 000000000..eb68410b0
--- /dev/null
+++ b/src/client/views/nodes/imageEditor/imageMeshTool/imageMeshToolButton.tsx
@@ -0,0 +1,90 @@
+import './MeshTransformButton.scss';
+import * as React from 'react';
+import ReactLoading from 'react-loading';
+import { Button, IconButton, Type } from '@dash/components';
+import { AiOutlineInfo } from 'react-icons/ai';
+import { SettingsManager } from '../../../../util/SettingsManager';
+import MeshTransformGrid from './imageMesh';
+
+interface ButtonContainerProps {
+ onClick: () => Promise<void>;
+ loading: boolean;
+ onReset: () => void;
+ btnText: string;
+ imageWidth: number;
+ imageHeight: number;
+ gridXSize: number; // X subdivisions
+ gridYSize: number; // Y subdivisions
+}
+
+export function MeshTransformButton({
+ loading,
+ onClick: startMeshTransform,
+ onReset,
+ btnText,
+ imageWidth,
+ imageHeight,
+ gridXSize,
+ gridYSize
+}: ButtonContainerProps) {
+ const [showGrid, setShowGrid] = React.useState(false);
+ const [isGridInteractive, setIsGridInteractive] = React.useState(false); // Controls the dragging of control points
+ const imageRef = React.useRef<HTMLImageElement>(null); // Reference to the image element
+
+ const handleGridToggle = () => {
+ if (showGrid) {
+ setShowGrid(false); // Hide the grid
+ setIsGridInteractive(false); // Disable control points manipulation
+ } else {
+ setShowGrid(true); // Show the grid
+ setIsGridInteractive(true); // Enable control points manipulation
+ }
+ };
+
+ return (
+ <div className="meshTransformBtnContainer">
+ <Button text="RESET" type={Type.PRIM} color={SettingsManager.userVariantColor} onClick={onReset} />
+ {loading ? (
+ <Button
+ text={btnText}
+ type={Type.TERT}
+ color={SettingsManager.userVariantColor}
+ icon={<ReactLoading type="spin" color="#ffffff" width={20} height={20} />}
+ iconPlacement="right"
+ onClick={() => {
+ if (!loading) handleGridToggle(); // Toggle the grid visibility and control points manipulation
+ }}
+ />
+ ) : (
+ <Button
+ text={btnText}
+ type={Type.TERT}
+ color={SettingsManager.userVariantColor}
+ onClick={() => {
+ if (!loading) handleGridToggle(); // Toggle the grid visibility and control points manipulation
+ }}
+ />
+ )}
+
+ {/* The IconButton will toggle the grid */}
+ <IconButton
+ type={Type.SEC}
+ color={SettingsManager.userVariantColor}
+ tooltip="Toggle Grid"
+ icon={<AiOutlineInfo size="16px" />}
+ onClick={handleGridToggle} // Toggle the grid when clicked
+ />
+
+ {/* Only show the grid if `showGrid` is true */}
+ {showGrid && (
+ <MeshTransformGrid
+ imageRef={imageRef}
+ gridXSize={gridXSize}
+ gridYSize={gridYSize}
+ isInteractive={isGridInteractive} // Pass the interactive flag to control point manipulation
+ />
+ )}
+ <img ref={imageRef} src="your-image-source.jpg" alt="Mesh" style={{ width: imageWidth, height: imageHeight }} />
+ </div>
+ );
+}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 9ab5fb1bd..23155ebf3 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -210,6 +210,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
componentDidMount() {
+ this._props.setContentViewBox?.(this);
this._disposers.pause = reaction(
() => SnappingManager.UserPanned,
() => this.pauseAutoPres()
@@ -531,11 +532,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const setData = bestTargetView?.ComponentView?.setData;
if (setData) setData(activeItem.config_data);
else {
- const bestTargetData = bestTarget[DocData];
- const current = bestTargetData[fkey];
- const hash = bestTargetData[fkey] ? stringHash(Field.toString(bestTargetData[fkey] as FieldType)) : undefined;
- if (hash) bestTargetData[fkey + '_' + hash] = current instanceof ObjectField ? current[Copy]() : current;
- bestTargetData[fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
+ const current = bestTarget['$' + fkey];
+ const hash = bestTarget['$' + fkey] ? stringHash(Field.toString(bestTarget['$' + fkey] as FieldType)) : undefined;
+ if (hash) bestTarget['$' + fkey + '_' + hash] = current instanceof ObjectField ? current[Copy]() : current;
+ bestTarget['$' + fkey] = activeItem.config_data instanceof ObjectField ? activeItem.config_data[Copy]() : activeItem.config_data;
}
bestTarget[fkey + '_usePath'] = activeItem.config_usePath;
setTimeout(() => {
@@ -595,11 +595,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
if (pinDataTypes?.inkable || (!pinDataTypes && (activeItem.config_fillColor !== undefined || activeItem.color !== undefined))) {
if (bestTarget.fillColor !== activeItem.config_fillColor) {
- bestTarget[DocData].fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor));
+ bestTarget.$fillColor = StrCast(activeItem.config_fillColor, StrCast(bestTarget.fillColor));
changed = true;
}
if (bestTarget.color !== activeItem.config_color) {
- bestTarget[DocData].color = StrCast(activeItem.config_color, StrCast(bestTarget.color));
+ bestTarget.$color = StrCast(activeItem.config_color, StrCast(bestTarget.color));
changed = true;
}
if (bestTarget.width !== activeItem.width) {
@@ -668,7 +668,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return doc;
});
const newList = new List<Doc>([...oldItems, ...hiddenItems, ...newItems]);
- bestTarget[DocData][fkey + '_annotations'] = newList;
+ bestTarget['$' + fkey + '_annotations'] = newList;
}
if (pinDataTypes?.poslayoutview || (!pinDataTypes && activeItem.config_pinLayoutData !== undefined)) {
changed = true;
@@ -689,8 +689,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
data.fill && (doc._fillColor = data.fill);
doc._width = data.w;
doc._height = data.h;
- data.data && (doc[DocData].data = field);
- data.text && (doc[DocData].text = tfield);
+ data.data && (doc.$data = field);
+ data.text && (doc.$text = tfield);
Doc.AddDocToList(bestTarget[DocData], layoutField, doc);
}
});
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index a76805960..31cd1603f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -52,7 +52,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
// the presentation view that renders this slide
@computed get presBoxView() {
- return this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as PresBox;
+ return this.DocumentView?.()
+ .containerViewPath?.()
+ .slice()
+ .reverse()
+ .find(dv => dv?.ComponentView instanceof PresBox)?.ComponentView as PresBox;
}
// the presentation view document that renders this slide
@@ -235,10 +239,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
e.clientY,
undefined,
action(() => {
- Array.from(classesToRestore).forEach(pair => {
- // eslint-disable-next-line prefer-destructuring
- pair[0].className = pair[1];
- });
+ Array.from(classesToRestore).forEach(pair => (pair[0].className = pair[1]));
this._dragging = false;
})
);
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 9aa8fe649..eb6516403 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,24 +1,19 @@
import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorResult } from 'react-color';
-import ReactLoading from 'react-loading';
import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction, unimplementedFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { SettingsManager } from '../../util/SettingsManager';
-import { undoBatch } from '../../util/UndoManager';
import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
-import { ComparisonBox } from '../nodes/ComparisonBox';
import { DocumentView } from '../nodes/DocumentView';
-import { DrawingOptions, SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
import './AnchorMenu.scss';
import { GPTPopup } from './GPTPopup/GPTPopup';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -73,7 +68,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public MakeTargetToggle: () => void = unimplementedFunction;
public ShowTargetTrail: () => void = unimplementedFunction;
public IsTargetToggler: () => boolean = returnFalse;
- public gptFlashcards: () => void = unimplementedFunction;
public makeLabels: () => void = unimplementedFunction;
public marqueeWidth = 0;
public marqueeHeight = 0;
@@ -98,57 +92,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
* Invokes the API with the selected text and stores it in the summarized text.
* @param e pointer down event
*/
- gptSummarize = () => GPTPopup.Instance.generateSummary(this._selectedText);
-
- /*
- * Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
- */
-
- transferToFlashcard = (text: string, x: number, y: number) => {
- ComparisonBox.createFlashcardDeck(text, 250, 200, 'data_front', 'data_back').then(
- action(newCol => {
- newCol.x = x;
- newCol.y = y;
- newCol.zIndex = 1000;
- this.addToCollection?.(newCol);
- this._loading = false;
- })
- );
- };
-
- /**
- * Creates a GPT drawing based on selected text.
- */
- gptDraw = (e: React.PointerEvent) => {
- try {
- SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation;
- runInAction(() => (this._isLoading = true));
- SmartDrawHandler.Instance.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._selectedText, 5, 100, true)?.then(
- action(() => {
- this._isLoading = false;
- })
- );
- } catch (err) {
- console.error(err);
- }
+ gptAskAboutSelection = () => {
+ GPTPopup.Instance.askAIAboutSelection(this._selectedText);
+ AnchorMenu.Instance.fadeOut(true);
};
- /**
- * Defines how a GPT drawing should be added to the current document.
- */
- @undoBatch
- createDrawingAnnotation = action((drawing: Doc, opts: DrawingOptions, gptRes: string) => {
- this.AddDrawingAnnotation(drawing);
- const docData = drawing[DocData];
- docData.title = opts.text?.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
- docData.ai_drawing_input = opts.text;
- docData.ai_drawing_complexity = opts.complexity;
- docData.ai_drawing_colored = opts.autoColor;
- docData.ai_drawing_size = opts.size;
- docData.ai_drawing_data = gptRes;
- docData.ai = 'gpt';
- });
-
pointerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
@@ -225,23 +173,14 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
{/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection */}
{this._selectedText && (
<IconButton
- tooltip="Summarize with AI" //
- onPointerDown={this.gptSummarize}
+ tooltip="Ask AI..." //
+ onPointerDown={this.gptAskAboutSelection}
icon={<FontAwesomeIcon icon="comment-dots" size="lg" />}
color={SettingsManager.userColor}
/>
)}
{/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
- <IconButton tooltip="Create flashcards" onPointerDown={this.gptFlashcards} icon={<FontAwesomeIcon icon="layer-group" size="lg" />} color={SettingsManager.userColor} />
- <IconButton tooltip="Create labels" onPointerDown={this.makeLabels} icon={<FontAwesomeIcon icon="tag" size="lg" />} color={SettingsManager.userColor} />
- {this._selectedText && (
- <IconButton
- tooltip="Create drawing"
- onPointerDown={e => this.gptDraw(e)}
- icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon="paintbrush" size="lg" />}
- color={SettingsManager.userColor}
- />
- )}
+ {this.makeLabels === unimplementedFunction ? null : <IconButton tooltip="Create labels" onPointerDown={this.makeLabels} icon={<FontAwesomeIcon icon="tag" size="lg" />} color={SettingsManager.userColor} />}
{this._selectedText && RichTextMenu.Instance?.createLinkButton()}
{AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
<IconButton
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.scss b/src/client/views/pdf/GPTPopup/GPTPopup.scss
index c8903e09f..18441f76e 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.scss
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.scss
@@ -15,12 +15,8 @@ $headingHeight: 32px;
height: 100%;
top: 0;
left: 0;
- pointer-events: none;
border-top: solid gray 20px;
border-radius: 16px;
- padding: 16px;
- padding-bottom: 0;
- padding-top: 0px;
z-index: 999;
display: flex;
flex-direction: column;
@@ -29,11 +25,13 @@ $headingHeight: 32px;
box-shadow: 0 2px 5px #7474748d;
color: $textgrey;
- .gptPopup-sortBox {
+ .gptPopup-summaryBox-content {
+ padding-right: 16px;
+ padding-left: 16px;
+ position: relative;
+ overflow: hidden;
display: flex;
flex-direction: column;
- height: calc(100% - $inputHeight - $headingHeight);
- pointer-events: all;
}
.summary-heading {
@@ -65,7 +63,9 @@ $headingHeight: 32px;
.gptPopup-content-wrapper {
padding-top: 10px;
min-height: 50px;
- height: calc(100% - 32px);
+ white-space: pre-line;
+ overflow: auto;
+ margin-bottom: 10px;
}
.inputWrapper {
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 4dc45e6a0..67213382d 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -31,6 +31,7 @@ import { OpenWhere } from '../../nodes/OpenWhere';
import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
import { ImageField } from '../../../../fields/URLField';
import { List } from '../../../../fields/List';
+import { ComparisonBox } from '../../nodes/ComparisonBox';
export enum GPTPopupMode {
SUMMARY, // summary of seleted document text
@@ -56,7 +57,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
private _dataJson: string = '';
private _documentDescriptions: Promise<string> | undefined; // a cache of the descriptions of all docs in the selected collection. makes it more efficient when asking GPT multiple questions about the collection.
private _sidebarFieldKey: string = '';
- private _textToSummarize: string = '';
+ private _aiReferenceText: string = '';
private _imageDescription: string = '';
private _textToDocMap = new Map<string, Doc>(); // when GPT answers with a doc's content, this helps us find the Doc
private _addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -79,7 +80,7 @@ export class GPTPopup extends ObservableReactComponent<object> {
};
componentDidUpdate() {
- this._gptProcessing && this.setStopAnimatingResponse(false);
+ //this._gptProcessing && this.setStopAnimatingResponse(false);
}
componentDidMount(): void {
reaction(
@@ -100,14 +101,14 @@ export class GPTPopup extends ObservableReactComponent<object> {
);
}
+ @observable private _showOriginal = true;
+ @observable private _responseText: string = '';
@observable private _conversationArray: string[] = ['Hi! In this pop up, you can ask ChatGPT questions about your documents and filter / sort them. '];
@observable private _fireflyArray: string[] = ['Hi! In this pop up, you can ask Firefly to create images. '];
@observable private _chatEnabled: boolean = false;
@action private setChatEnabled = (start: boolean) => (this._chatEnabled = start);
@observable private _gptProcessing: boolean = false;
@action private setGptProcessing = (loading: boolean) => (this._gptProcessing = loading);
- @observable private _responseText: string = '';
- @action private setResponseText = (text: string) => (this._responseText = text);
@observable private _imgUrls: string[][] = [];
@action private setImgUrls = (imgs: string[][]) => (this._imgUrls = imgs);
@observable private _collectionContext: Doc | undefined = undefined;
@@ -286,18 +287,30 @@ export class GPTPopup extends ObservableReactComponent<object> {
/**
* Completes an API call to generate a summary of the specified text
*
- * @param text the text to summarizz
+ * @param text the text to summarize
*/
- generateSummary = (text: string) => {
+ private generateSummary = action((text: string) => {
SnappingManager.SetChatVisible(true);
- this._textToSummarize = text;
- this.setMode(GPTPopupMode.SUMMARY);
+ this._showOriginal = false;
this.setGptProcessing(true);
return gptAPICall(text, GPTCallType.SUMMARY)
- .then(res => this.setResponseText(res || 'Something went wrong.'))
+ .then(action(res => (this._responseText = res || 'Something went wrong.')))
.catch(err => console.error(err))
.finally(() => this.setGptProcessing(false));
- };
+ });
+
+ /**
+ * Completes an API call to generate a summary of the specified text
+ *
+ * @param text the text to summarizz
+ */
+ askAIAboutSelection = action((text: string) => {
+ SnappingManager.SetChatVisible(true);
+ this._aiReferenceText = text;
+ this._responseText = '';
+ this._showOriginal = true;
+ this.setMode(GPTPopupMode.SUMMARY);
+ });
/**
* Completes an API call to generate an analysis of
@@ -306,14 +319,16 @@ export class GPTPopup extends ObservableReactComponent<object> {
generateDataAnalysis = () => {
this.setGptProcessing(true);
return gptAPICall(this._dataJson, GPTCallType.DATA, this._dataChatPrompt)
- .then(res => {
- const json = JSON.parse(res! as string);
- const keys = Object.keys(json);
- this._correlatedColumns = [];
- this._correlatedColumns.push(json[keys[0]]);
- this._correlatedColumns.push(json[keys[1]]);
- this.setResponseText(json[keys[2]] || 'Something went wrong.');
- })
+ .then(
+ action(res => {
+ const json = JSON.parse(res! as string);
+ const keys = Object.keys(json);
+ this._correlatedColumns = [];
+ this._correlatedColumns.push(json[keys[0]]);
+ this._correlatedColumns.push(json[keys[1]]);
+ this._responseText = json[keys[2]] || 'Something went wrong.';
+ })
+ )
.catch(err => console.error(err))
.finally(() => this.setGptProcessing(false));
};
@@ -336,6 +351,24 @@ export class GPTPopup extends ObservableReactComponent<object> {
});
}
};
+ /**
+ * Create Flashcards for the selected text
+ */
+ private createFlashcards = action(
+ () =>
+ this.setGptProcessing(true) &&
+ gptAPICall(this._aiReferenceText, GPTCallType.FLASHCARD, undefined, true)
+ .then(res =>
+ ComparisonBox.createFlashcardDeck(res, 250, 200, 'data_front', 'data_back').then(
+ action(newCol => {
+ newCol.zIndex = 1000;
+ DocumentViewInternal.addDocTabFunc(newCol, OpenWhere.addRight);
+ })
+ )
+ )
+ .catch(console.error)
+ .finally(action(() => (this._gptProcessing = false)))
+ );
/**
* Creates a histogram to show the correlation relationship that was found
@@ -536,35 +569,80 @@ export class GPTPopup extends ObservableReactComponent<object> {
summaryBox = () => (
<>
- <div style={{ height: 'calc(100% - 60px)', overflow: 'auto' }}>
- {this.heading('SUMMARY')}
+ <div className="gptPopup-summaryBox-content">
+ <div onClick={action(() => (this._showOriginal = !this._showOriginal))}>{this.heading(this._showOriginal ? 'SELECTION' : 'SUMMARY')}</div>
<div className="gptPopup-content-wrapper">
- {!this._gptProcessing &&
- (!this._stopAnimatingResponse ? (
- <TypeAnimation
- speed={50}
- sequence={[
- this._responseText,
- () => {
- setTimeout(() => this.setStopAnimatingResponse(true), 500);
- },
- ]}
- />
+ {!this._gptProcessing && !this._stopAnimatingResponse && this._responseText ? (
+ <TypeAnimation
+ speed={50}
+ sequence={[
+ this._responseText,
+ () => {
+ setTimeout(() => this.setStopAnimatingResponse(true), 500);
+ },
+ ]}
+ />
+ ) : this._showOriginal ? (
+ this._gptProcessing ? (
+ '...generating cards...'
) : (
- this._responseText
- ))}
+ this._aiReferenceText
+ )
+ ) : (
+ this._responseText || (this._gptProcessing ? '...generating summary...' : '-no ai summary-')
+ )}
</div>
</div>
- {!this._gptProcessing && (
- <div className="btns-wrapper" style={{ position: 'absolute', bottom: 0, width: 'calc(100% - 32px)' }}>
- {this._stopAnimatingResponse ? (
- <>
- <IconButton tooltip="Generate Again" onClick={() => this.generateSummary(this._textToSummarize + ' ')} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(SettingsManager.userVariantColor)} />
- <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
- </>
+ {this._gptProcessing ? null : (
+ <div className="btns-wrapper" style={{ position: 'relative', width: 'calc(100% - 32px)' }}>
+ {this._stopAnimatingResponse || !this._responseText ? (
+ <div style={{ display: 'flex' }}>
+ {!this._showOriginal ? (
+ <>
+ <Button
+ tooltip="Show originally selected text" //
+ text="Selection"
+ onClick={action(() => (this._showOriginal = true))}
+ color={StrCast(SettingsManager.userVariantColor)}
+ type={Type.TERT}
+ />
+ <Button
+ tooltip="Create a text Doc with this text and link to the text selection" //
+ text="Transfer To Text"
+ onClick={this.transferToText}
+ color={StrCast(SettingsManager.userVariantColor)}
+ type={Type.TERT}
+ />
+ </>
+ ) : (
+ <>
+ <Button
+ tooltip="Show AI summary of original selection text (Shift+Click to regenerate)"
+ text="Summary"
+ onClick={action(e => {
+ if (e.shiftKey) {
+ this.setStopAnimatingResponse(false);
+ this._aiReferenceText += ' ';
+ this._responseText = '';
+ }
+ this.generateSummary(this._aiReferenceText);
+ })}
+ color={StrCast(SettingsManager.userVariantColor)}
+ type={Type.TERT}
+ />
+ <Button
+ tooltip="Create Flashcards" //
+ text="Create Flashcards"
+ onClick={this.createFlashcards}
+ color={StrCast(SettingsManager.userVariantColor)}
+ type={Type.TERT}
+ />
+ </>
+ )}
+ </div>
) : (
<div className="summarizing">
- <span>Summarizing</span>
+ <span>{this._showOriginal ? 'Creating Cards...' : 'Summarizing'}</span>
<ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
<Button text="Stop Animation" onClick={() => this.setStopAnimatingResponse(true)} color={StrCast(SettingsManager.userVariantColor)} type={Type.TERT} />
</div>
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss
index 030251762..1aab2b853 100644
--- a/src/client/views/pdf/PDFViewer.scss
+++ b/src/client/views/pdf/PDFViewer.scss
@@ -29,7 +29,11 @@
.textLayer {
opacity: unset;
mix-blend-mode: multiply; // bcz: makes text fuzzy!
+ // all these below fix issues with PDFjs
transform: scale(var(--devicePixelRatio));
+ --total-scale-factor: var(--scale-factor); // these 3 are used by PDFjs but not defined. why???
+ --scale-round-x: 1px;
+ --scale-round-y: 1px;
}
[data-main-rotation='90'] {
transform: scale(var(--devicePixelRatio)) rotate(90deg) translateY(-100%);
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 167421a4a..73c2f5eb8 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,9 +1,10 @@
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
+import { GlobalWorkerOptions } from 'pdfjs-dist/build/pdf.mjs';
import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
-import 'pdfjs-dist/webpack.mjs'; // sets the PDF workerSrc
import * as React from 'react';
+import ReactLoading from 'react-loading';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils';
import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData, Height } from '../../../fields/DocSymbols';
@@ -11,9 +12,10 @@ import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, numberRange } from '../../../Utils';
+import { emptyFunction, numberRange, unimplementedFunction } from '../../../Utils';
import { DocUtils } from '../../documents/DocUtils';
import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { MarqueeAnnotator } from '../MarqueeAnnotator';
@@ -28,13 +30,12 @@ import { AnchorMenu } from './AnchorMenu';
import { Annotation } from './Annotation';
import { GPTPopup } from './GPTPopup/GPTPopup';
import './PDFViewer.scss';
-import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
-import ReactLoading from 'react-loading';
-import { Transform } from '../../util/Transform';
+if (window?.Worker) GlobalWorkerOptions.workerSrc = 'files/pdf.worker.min.mjs';
+export * from 'pdfjs-dist/build/pdf.mjs';
interface IViewerProps extends FieldViewProps {
pdfBox: PDFBox;
- Document: Doc;
+ Doc: Doc;
dataDoc: Doc;
layoutDoc: Doc;
fieldKey: string;
@@ -112,8 +113,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
() => this._props.layoutDoc._layout_autoHeight,
layoutAutoHeight => {
if (layoutAutoHeight) {
- this._props.layoutDoc._nativeHeight = NumCast(this._props.Document[this._props.fieldKey + '_nativeHeight']);
- this._props.setHeight?.(NumCast(this._props.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
+ this._props.layoutDoc._nativeHeight = NumCast(this._props.Doc[this._props.fieldKey + '_nativeHeight']);
+ this._props.setHeight?.(NumCast(this._props.Doc[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1));
}
}
);
@@ -124,7 +125,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
{ fireImmediately: true }
);
this._disposers.curPage = reaction(
- () => Cast(this._props.Document._layout_curPage, 'number', null),
+ () => Cast(this._props.Doc._layout_curPage, 'number', null),
page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
{ fireImmediately: true }
);
@@ -180,7 +181,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
scrollFocus = (doc: Doc, scrollTop: number, options: FocusViewOptions) => {
const mainCont = this._mainCont.current;
let focusSpeed: Opt<number>;
- if (doc !== this._props.Document && mainCont) {
+ if (doc !== this._props.Doc && mainCont) {
const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1);
const scrollTo = ClientUtils.scrollIntoView(scrollTop, doc[Height](), NumCast(this._props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight);
if (scrollTo !== undefined && scrollTo !== this._props.layoutDoc._layout_scrollTop) {
@@ -215,11 +216,11 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
{ fireImmediately: true }
);
this._disposers.scroll = reaction(
- () => Math.abs(NumCast(this._props.Document._layout_scrollTop)),
+ () => Math.abs(NumCast(this._props.Doc._layout_scrollTop)),
pos => {
if (!this._ignoreScroll) {
this._showWaiting && this.setupPdfJsViewer();
- const viewTrans = quickScroll?.loc ?? StrCast(this._props.Document._viewTransition);
+ const viewTrans = quickScroll?.loc ?? StrCast(this._props.Doc._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
@@ -370,9 +371,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
// if alt+left click, drag and annotate
this._downX = e.clientX;
this._downY = e.clientY;
- if ((this._props.Document._freeform_scale || 1) !== 1) return;
+ if ((this._props.Doc._freeform_scale || 1) !== 1) return;
if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) {
- this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document);
+ this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Doc);
}
if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
this._props.select(false);
@@ -392,25 +393,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
}
};
- /**
- * Create a flashcard pile based on the selected text of a pdf.
- */
- gptPDFFlashcards = async () => {
- const queryText = this._selectionText;
- this._loading = true;
- try {
- const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
-
- AnchorMenu.Instance.transferToFlashcard(res || 'Something went wrong', NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
- this._selectionText = '';
- } catch (err) {
- console.error(err);
- }
- this._loading = false;
- };
-
@action
finishMarquee = (/* x?: number, y?: number */) => {
+ AnchorMenu.Instance.makeLabels = unimplementedFunction;
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.isAnnotating = false;
this._marqueeref.current?.onTerminateSelection();
@@ -429,7 +414,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
if (sel) {
AnchorMenu.Instance.setSelectedText(sel.toString());
- AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc['x']), NumCast(this._props.layoutDoc['y']));
+ AnchorMenu.Instance.setLocation(NumCast(this._props.layoutDoc.x), NumCast(this._props.layoutDoc.y));
}
if (sel?.type === 'Range') {
@@ -441,14 +426,14 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
GPTPopup.Instance.addDoc = this._props.sidebarAddDoc;
// allows for creating collection
AnchorMenu.Instance.addToCollection = this._props.DocumentView?.()._props.addDocument;
- AnchorMenu.Instance.gptFlashcards = this.gptPDFFlashcards;
+ AnchorMenu.Instance.makeLabels = unimplementedFunction;
AnchorMenu.Instance.AddDrawingAnnotation = this.addDrawingAnnotation;
};
addDrawingAnnotation = (drawing: Doc) => {
- // drawing[DocData].x = this._props.pdfBox.ScreenToLocalBoxXf().TranslateX
+ // drawing.x = this._props.pdfBox.ScreenToLocalBoxXf().TranslateX
// const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
- drawing.y = NumCast(drawing.y) + NumCast(this._props.Document.layout_scrollTop);
+ drawing.y = NumCast(drawing.y) + NumCast(this._props.Doc.layout_scrollTop);
this._props.addDocument?.(drawing);
};
@@ -493,7 +478,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
onClick = (e: React.MouseEvent) => {
this._scrollStopper?.();
if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < ClientUtils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < ClientUtils.DRAG_THRESHOLD) {
- this._setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document);
+ this._setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Doc);
}
// e.stopPropagation(); // bcz: not sure why this was here. We need to allow the DocumentView to get clicks to process doubleClicks
};
@@ -521,7 +506,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
@computed get annotationLayer() {
const inlineAnnos = this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).filter(anno => !anno.hidden);
return (
- <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this._props.Document), transform: `scale(${NumCast(this._props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
+ <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this._props.Doc), transform: `scale(${NumCast(this._props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
{inlineAnnos.map(anno => (
<Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} containerDataDoc={this._props.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
))}
@@ -612,7 +597,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
onClick={this.onClick}
style={{
overflowX: NumCast(this._props.layoutDoc._freeform_scale, 1) !== 1 ? 'scroll' : undefined,
- height: !this._props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this._props.Document) : `100%`,
+ height: !this._props.Doc._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this._props.Doc) : `100%`,
}}>
{this.pdfViewerDiv}
{this.annotationLayer}
@@ -621,12 +606,12 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
{!this._mainCont.current || !this._annotationLayer.current || !this.props.pdfBox.DocumentView ? null : (
<MarqueeAnnotator
ref={this._marqueeref}
- Document={this._props.Document}
+ Document={this._props.Doc}
getPageFromScroll={this.getPageFromScroll}
anchorMenuClick={this._props.anchorMenuClick}
scrollTop={0}
annotationLayerScaling={() => Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS}
- annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)}
+ annotationLayerScrollTop={NumCast(this._props.Doc._layout_scrollTop)}
addDocument={this.addDocumentWrapper}
docView={this.props.pdfBox.DocumentView}
screenTransform={this.screenToMarqueeXf}
diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx
index 6f70e96ab..841546a04 100644
--- a/src/client/views/search/FaceRecognitionHandler.tsx
+++ b/src/client/views/search/FaceRecognitionHandler.tsx
@@ -66,7 +66,7 @@ export class FaceRecognitionHandler {
* returns a list of all face collection Docs on the current dashboard
* @returns face collection Doc list
*/
- public static UniqueFaces = () => DocListCast(Doc.ActiveDashboard?.[DocData].myUniqueFaces);
+ public static UniqueFaces = () => DocListCast(Doc.ActiveDashboard?.$myUniqueFaces);
/**
* Find a unique face from its name
@@ -87,22 +87,22 @@ export class FaceRecognitionHandler {
* @param faceDoc unique face Doc
* @returns label string
*/
- public static UniqueFaceLabel = (faceDoc: Doc) => StrCast(faceDoc[DocData].face);
+ public static UniqueFaceLabel = (faceDoc: Doc) => StrCast(faceDoc.$face);
- public static SetUniqueFaceLabel = (faceDoc: Doc, value: string) => (faceDoc[DocData].face = value);
+ public static SetUniqueFaceLabel = (faceDoc: Doc, value: string) => (faceDoc.$face = value);
/**
* Returns all the face descriptors associated with a unique face Doc
* @param faceDoc unique face Doc
* @returns face descriptors
*/
- public static UniqueFaceDescriptors = (faceDoc: Doc) => DocListCast(faceDoc[DocData].face_annos).map(face => face.faceDescriptor as List<number>);
+ public static UniqueFaceDescriptors = (faceDoc: Doc) => DocListCast(faceDoc.$face_annos).map(face => face.faceDescriptor as List<number>);
/**
* Returns a list of all face image Docs associated with a unique face Doc
* @param faceDoc unique face Doc
* @returns image Docs
*/
- public static UniqueFaceImages = (faceDoc: Doc) => DocListCast(faceDoc[DocData].face_annos).map(face => DocCast(face.annotationOn, face));
+ public static UniqueFaceImages = (faceDoc: Doc) => DocListCast(faceDoc.$face_annos).map(face => DocCast(face.annotationOn, face));
/**
* Adds a face image to a unique face Doc, adds the unique face Doc to the images list of reognized faces,
@@ -145,8 +145,8 @@ export class FaceRecognitionHandler {
* @returns a unique face Doc
*/
private createUniqueFaceDoc = (dashboard: Doc) => {
- const faceDocNum = NumCast(dashboard[DocData].myUniqueFaces_count) + 1;
- dashboard[DocData].myUniqueFaces_count = faceDocNum; // TODO: improve to a better name
+ const faceDocNum = NumCast(dashboard.$myUniqueFaces_count) + 1;
+ dashboard.$myUniqueFaces_count = faceDocNum; // TODO: improve to a better name
const uniqueFaceDoc = Docs.Create.UniqeFaceDocument({
title: ComputedField.MakeFunction('this.face', undefined, undefined, 'this.face = value') as unknown as string,
@@ -160,9 +160,8 @@ export class FaceRecognitionHandler {
_width: 400,
_height: 100,
});
- const uface = uniqueFaceDoc[DocData];
- uface.face = `Face${faceDocNum}`;
- uface.face_annos = new List<Doc>();
+ uniqueFaceDoc.$face = `Face${faceDocNum}`;
+ uniqueFaceDoc.$face_annos = new List<Doc>();
Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
@@ -241,7 +240,7 @@ export class FaceRecognitionHandler {
annos.push(faceAnno);
});
- imgDoc[DocData].data_annotations = new List<Doc>(annos);
+ imgDoc.$data_annotations = new List<Doc>(annos);
imgDoc._layout_showTags = annos.length > 0;
return imgDocFaceDescriptions;
})
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index ae0838dd5..1f6e80bd1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
@@ -29,7 +27,7 @@ const MAX_ITERATIONS = 25;
const ERROR = 0.03;
export interface SearchBoxItemProps {
- Document: Doc;
+ Doc: Doc;
searchString: string;
isLinkSearch: boolean;
matchedKeys: string[];
@@ -52,9 +50,7 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps>
* This method selects a doc by either jumping to it (centering/zooming in on it)
* or opening it in a new tab.
*/
- selectElement = async (doc: Doc, finishFunc: () => void) => {
- await DocumentView.showDocument(doc, { willZoomCentered: true }, finishFunc);
- };
+ selectElement = (doc: Doc, finishFunc: () => void) => DocumentView.showDocument(doc, { willPan: true }, finishFunc);
/**
* @param {Doc} doc - doc of the search result that has been clicked on
@@ -68,7 +64,7 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps>
});
componentWillUnmount(): void {
- const doc = this._props.Document;
+ const doc = this._props.Doc;
DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true);
}
@@ -83,23 +79,23 @@ export class SearchBoxItem extends ObservableReactComponent<SearchBoxItemProps>
render() {
// eslint-disable-next-line no-use-before-define
- const formattedType = SearchBox.formatType(StrCast(this._props.Document.type), StrCast(this._props.Document.type_collection));
- const { title } = this._props.Document;
+ const formattedType = SearchBox.formatType(StrCast(this._props.Doc.type), StrCast(this._props.Doc.type_collection));
+ const { title } = this._props.Doc;
return (
<Tooltip placement="right" title={<div className="dash-tooltip">{title as string}</div>}>
<div
onClick={
this._props.isLinkSearch
- ? () => this.makeLink(this._props.Document)
+ ? () => this.makeLink(this._props.Doc)
: e => {
- this.onResultClick(this._props.Document);
+ this.onResultClick(this._props.Doc);
e.stopPropagation();
}
}
style={{
fontWeight: Doc.Links(this._props.linkFrom).find(
- link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document)
+ link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Doc) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Doc)
)
? 'bold'
: '',
@@ -183,7 +179,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
* (Note: There is no longer a need to press enter to submit a search. Any update to the input
* causes a search to be submitted automatically.)
*/
- _timeout: any = undefined;
+ _timeout: NodeJS.Timeout | undefined = undefined;
onInputChange = action((e: React.ChangeEvent<HTMLInputElement>) => {
this._searchString = e.target.value;
this._timeout && clearTimeout(this._timeout);
@@ -242,7 +238,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
* which the first letter is capitalized. This is used when displaying the type on the
* right side of each search result.
*/
- static formatType(type: string, colType: string): String {
+ static formatType(type: string, colType: string): string {
switch (type) {
case DocumentType.PDF : return 'PDF';
case DocumentType.IMG : return 'Img';
@@ -437,7 +433,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
render() {
const isLinkSearch: boolean = this._props.linkSearch;
const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0)); // sorted by page rank
- const resultsJSX = [] as any[];
+ const resultsJSX = [] as JSX.Element[];
const linkFrom = this._props.linkFrom?.();
let validResults = 0;
@@ -453,7 +449,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
resultsJSX.push(
<SearchBoxItem
key={Document[Id]}
- Document={Document}
+ Doc={Document}
selectItem={action((doc: Doc) => {
this._selectedResult = doc;
})}
@@ -469,7 +465,6 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
}
});
- // eslint-disable-next-line react/jsx-props-no-spreading
const recommendationsJSX: JSX.Element[] = []; // this._recommendations.map(props => <Recommendation {...props} />);
return (
diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx
index c672bc718..f1d5f2cfe 100644
--- a/src/client/views/smartdraw/DrawingFillHandler.tsx
+++ b/src/client/views/smartdraw/DrawingFillHandler.tsx
@@ -1,6 +1,5 @@
import { imageUrlToBase64 } from '../../../ClientUtils';
import { Doc, StrListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { List } from '../../../fields/List';
import { DocCast, ImageCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
@@ -15,8 +14,7 @@ import { AspectRatioLimits, FireflyDimensionsMap, FireflyImageDimensions, Firefl
const DashDropboxId = '2m86iveqdr9vzsa';
export class DrawingFillHandler {
static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string, styleDoc?: Doc) => {
- const docData = drawing[DocData];
- const tags = StrListCast(docData.tags).map(tag => tag.slice(1));
+ const tags = StrListCast(drawing.$tags).map(tag => tag.slice(1));
const styles = tags.filter(tag => FireflyStylePresets.has(tag));
const styleDocs = !Doc.Links(drawing).length
? styleDoc && !tags.length
@@ -47,7 +45,7 @@ export class DrawingFillHandler {
Networking.PostToServer('/queryFireflyImageFromStructure', { prompt: `${newPrompt}`, width: dims.width, height: dims.height, structureUrl, strength, presets: styles, styleUrl })
.then(res => {
const genratedDocs = DocCast(drawing.ai_firefly_generatedDocs) ?? Docs.Create.MasonryDocument([], { _width: 400, _height: 400 });
- drawing[DocData].ai_firefly_generatedDocs = genratedDocs;
+ drawing.$ai_firefly_generatedDocs = genratedDocs;
(res as Upload.ImageInformation[]).map(info =>
Doc.AddDocToList(
genratedDocs,
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 1cceabed3..2283ef965 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -10,9 +10,11 @@ import { INode, parse } from 'svgson';
import { imageUrlToBase64, setupMoveUpEvents } from '../../../ClientUtils';
import { unimplementedFunction } from '../../../Utils';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { InkData, InkField, InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { PointData } from '../../../pen-gestures/GestureTypes';
+import { Upload } from '../../../server/SharedMediaTypes';
import { Networking } from '../../Network';
import { GPTCallType, gptAPICall, gptDrawingColor } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -26,9 +28,6 @@ import { MarqueeView } from '../collections/collectionFreeForm';
import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkBezierApprox, ActiveInkColor, ActiveInkDash, ActiveInkFillColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
import { FireflyDimensionsMap, FireflyImageData, FireflyImageDimensions } from './FireflyConstants';
import './SmartDrawHandler.scss';
-import { Upload } from '../../../server/SharedMediaTypes';
-import { PointData } from '../../../pen-gestures/GestureTypes';
-import { List } from '../../../fields/List';
export interface DrawingOptions {
text?: string;
@@ -153,9 +152,9 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
this._display = false;
this.ShowRegenerate = true;
this._showEditBox = false;
- const docData = this._selectedDocs[0][DocData];
- this._lastResponse = StrCast(docData.drawingData);
- this._lastInput = { text: StrCast(docData.ai_drawing_input), complexity: NumCast(docData.ai_drawing_complexity), size: NumCast(docData.ai_drawing_size), autoColor: BoolCast(docData.ai_drawing_colored), x: this._pageX, y: this._pageY };
+ const docData = this._selectedDocs[0];
+ this._lastResponse = StrCast(docData.$drawingData);
+ this._lastInput = { text: StrCast(docData.$ai_drawing_input), complexity: NumCast(docData.$ai_drawing_complexity), size: NumCast(docData.$ai_drawing_size), autoColor: BoolCast(docData.$ai_drawing_colored), x: this._pageX, y: this._pageY };
};
/**
@@ -404,7 +403,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
try {
const hrefBase64 = await imageUrlToBase64(hrefComplete);
- const strokes = DocListCast(drawing[DocData].data);
+ const strokes = DocListCast(drawing.$data);
const coords: string[] = [];
strokes.forEach((stroke, i) => {
const inkingStroke = DocumentView.getDocumentView(stroke)?.ComponentView as InkingStroke;
@@ -423,14 +422,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
*/
colorStrokes = undoable((res: string, drawing: Doc) => {
const colorList = res.match(/\{.*?\}/g);
- const strokes = DocListCast(drawing[DocData].data);
+ const strokes = DocListCast(drawing.$data);
colorList?.forEach((colors, index) => {
const strokeAndFill = colors.match(/#[0-9A-Fa-f]{6}/g);
if (strokeAndFill && strokeAndFill.length == 2) {
- strokes[index][DocData].color = strokeAndFill[0];
+ strokes[index].$color = strokeAndFill[0];
const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke;
const { inkData } = inkStroke.inkScaledData();
- InkingStroke.IsClosed(inkData) ? (strokes[index][DocData].fillColor = strokeAndFill[1]) : (strokes[index][DocData].fillColor = undefined);
+ InkingStroke.IsClosed(inkData) ? (strokes[index].$fillColor = strokeAndFill[1]) : (strokes[index].$fillColor = undefined);
}
});
}, 'color strokes');
diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx
index d5307974f..2260d1f73 100644
--- a/src/client/views/smartdraw/StickerPalette.tsx
+++ b/src/client/views/smartdraw/StickerPalette.tsx
@@ -9,7 +9,6 @@ import ReactLoading from 'react-loading';
import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils';
import { emptyFunction, numberRange } from '../../../Utils';
import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { ImageCast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -26,7 +25,7 @@ import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler';
import './StickerPalette.scss';
interface StickerPaletteProps {
- Document: Doc;
+ Doc: Doc;
}
enum StickerPaletteMode {
@@ -133,7 +132,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
this._canInteract = true;
this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 };
this._gptRes = [];
- this._props.Document[DocData].data = undefined;
+ this._props.Doc.$data = undefined;
});
/**
@@ -143,15 +142,15 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
@undoBatch
generateDrawings = action(() => {
this._isLoading = true;
- const prevDrawings = DocListCast(this._props.Document[DocData].data);
- this._props.Document[DocData].data = undefined;
+ const prevDrawings = DocListCast(this._props.Doc.$data);
+ this._props.Doc.$data = undefined;
SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
this._canInteract = false;
Promise.all(
numberRange(3).map(i => {
return this._showRegenerate
? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput)
- : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor);
+ : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity || 0, this._opts.size || 0, !!this._opts.autoColor);
})
).then(() => {
this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput);
@@ -164,8 +163,8 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
@action
addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => {
this._gptRes.push(gptRes);
- drawing[DocData].freeform_fitContentsToBox = true;
- Doc.AddDocToList(this._props.Document, 'data', drawing);
+ drawing.$freeform_fitContentsToBox = true;
+ Doc.AddDocToList(this._props.Doc, 'data', drawing);
};
/**
@@ -174,19 +173,18 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
* presses the "save drawing" button.
*/
saveDrawing = () => {
- const cIndex = NumCast(this._props.Document.carousel_index);
- const focusedDrawing = DocListCast(this._props.Document.data)[cIndex];
- const docData = focusedDrawing[DocData];
- docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
- docData.ai_drawing_input = this._opts.text;
- docData.ai_drawing_complexity = this._opts.complexity;
- docData.ai_drawing_colored = this._opts.autoColor;
- docData.ai_drawing_size = this._opts.size;
- docData.ai_drawing_data = this._gptRes[cIndex];
- docData.ai = 'gpt';
+ const cIndex = NumCast(this._props.Doc.carousel_index);
+ const focusedDrawing = DocListCast(this._props.Doc.data)[cIndex];
+ focusedDrawing.$title = this._opts.text?.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
+ focusedDrawing.$ai_drawing_input = this._opts.text;
+ focusedDrawing.$ai_drawing_complexity = this._opts.complexity;
+ focusedDrawing.$ai_drawing_colored = this._opts.autoColor;
+ focusedDrawing.$ai_drawing_size = this._opts.size;
+ focusedDrawing.$ai_drawing_data = this._gptRes[cIndex];
+ focusedDrawing.$ai = 'gpt';
focusedDrawing.width = this._opts.size;
- docData.x = this._opts.x;
- docData.y = this._opts.y;
+ focusedDrawing.x = this._opts.x;
+ focusedDrawing.y = this._opts.y;
StickerPalette.addToPalette(focusedDrawing).then(() => this.resetPalette(true));
};
@@ -315,7 +313,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps
<>
{this.renderCreateInput()}
{this.renderCreateOptions()}
- {this.renderDoc(this._props.Document, (r: DocumentView) => {
+ {this.renderDoc(this._props.Doc, (r: DocumentView) => {
this._docCarouselView = r;
})}
<div className="palette-buttons">
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
index f0a851ce6..5db201c72 100644
--- a/src/fields/DateField.ts
+++ b/src/fields/DateField.ts
@@ -39,6 +39,6 @@ export class DateField extends ObjectField {
}
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function d(...dateArgs: any[]) {
- return new DateField(new (Date as any)(...dateArgs));
+ScriptingGlobals.add(function d(...dateArgs: ConstructorParameters<typeof Date>) {
+ return new DateField(new Date(...dateArgs));
});
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index fc89dcbe7..4bf04da5c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -273,12 +273,12 @@ export class Doc extends RefField {
public static AddToMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
public static RemFromMyOverlay(doc: Doc) { return Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
public static AddToMyPublished(doc: Doc) {
- doc[DocData].title_custom = true;
- doc[DocData].layout_showTitle = 'title';
+ doc.$title_custom = true;
+ doc.$layout_showTitle = 'title';
Doc.ActiveDashboard ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static RemFromMyPublished(doc: Doc){
- doc[DocData].title_custom = false;
- doc[DocData].layout_showTitle = undefined;
+ doc.$title_custom = false;
+ doc.$layout_showTitle = undefined;
Doc.ActiveDashboard ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
@@ -320,10 +320,11 @@ export class Doc extends RefField {
UpdatingFromServer,
Width,
'__LAYOUT__',
+ '__DATA__',
];
},
getOwnPropertyDescriptor: (target, prop) => {
- if (prop.toString() === '__LAYOUT__' || !(prop in target[FieldKeys])) {
+ if (prop.toString() === '__DATA__' || prop.toString() === '__LAYOUT__' || !(prop in target[FieldKeys])) {
return Reflect.getOwnPropertyDescriptor(target, prop);
}
return {
@@ -400,6 +401,9 @@ export class Doc extends RefField {
public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
public get [DocLayout]() { return this[SelfProxy].__LAYOUT__; } // prettier-ignore
public get [DocData](): Doc {
+ return this[SelfProxy].__DATA__;
+ }
+ @computed get __DATA__(): Doc {
const self = this[SelfProxy];
return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
}
@@ -414,7 +418,7 @@ export class Doc extends RefField {
} else {
return Cast(layoutField, Doc, null);
}
- return Cast(self[renderFieldKey + '_layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+ return Cast(self['layout_' + templateLayoutDoc.title + '(' + renderFieldKey + ')'], Doc, null) || templateLayoutDoc;
}
return undefined;
}
@@ -625,7 +629,7 @@ export namespace Doc {
*/
export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, ignoreProto = false) {
const key = fieldKey || Doc.LayoutFieldKey(listDoc);
- const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
+ const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
const ind = list.indexOf(doc);
if (ind !== -1) {
@@ -642,7 +646,7 @@ export namespace Doc {
*/
export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean, ignoreProto?: boolean) {
const key = fieldKey || Doc.LayoutFieldKey(listDoc);
- const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc[DocData][key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
+ const list = Doc.Get(listDoc, key, ignoreProto) === undefined ? (listDoc['$' + key] = new List<Doc>()) : Cast(listDoc[key], listSpec(Doc));
if (list) {
if (!allowDuplicates) {
const pind = list.findIndex(d => d instanceof Doc && d[Id] === doc[Id]);
@@ -687,7 +691,7 @@ export namespace Doc {
Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
}
embedding.createdFrom = doc;
- embedding.proto_embeddingId = doc[DocData].proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
+ embedding.proto_embeddingId = doc.$proto_embeddingId = Doc.GetEmbeddings(doc).length - 1;
!Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`));
embedding.author = ClientUtils.CurrentUserEmail();
@@ -695,9 +699,8 @@ export namespace Doc {
}
export function BestEmbedding(doc: Doc) {
- const dataDoc = doc[DocData];
- const availableEmbeddings = Doc.GetEmbeddings(dataDoc);
- const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail());
+ const availableEmbeddings = Doc.GetEmbeddings(doc);
+ const bestEmbedding = [...(doc[DocData] !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail());
bestEmbedding && Doc.AddEmbedding(doc, doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -865,7 +868,7 @@ export namespace Doc {
// If it doesn't find the expanded layout, then it makes a delegate of the template layout and
// saves it on the data doc indexed by the template layout's id.
//
- const expandedLayoutFieldKey = templateField + '_layout[' + templateLayoutDoc[Id] + ']';
+ const expandedLayoutFieldKey = 'layout_' + templateLayoutDoc.title + '(' + templateField + ')';
let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey];
if (templateLayoutDoc.resolvedDataDoc instanceof Promise) {
@@ -1152,7 +1155,7 @@ export namespace Doc {
// the document containing the view layout information - will be the Document itself unless the Document has
// a layout field or 'layout' is given.
export function Layout(doc: Doc, layout?: Doc): Doc {
- const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, 'data')}_layout[` + layout[Id] + ']'], Doc, null);
+ const overrideLayout = layout && Cast(doc[layout.title + '(' + StrCast(layout.isTemplateForField, 'data') + ')'], Doc, null);
return overrideLayout || doc[DocLayout] || doc;
}
export function SetLayout(doc: Doc, layout: Doc | string) {
@@ -1471,7 +1474,7 @@ export namespace Doc {
* @returns
*/
export function getDescription(doc: Doc) {
- const curDescription = StrCast(doc[DocData][Doc.LayoutFieldKey(doc) + '_description']);
+ const curDescription = StrCast(doc['$' + Doc.LayoutFieldKey(doc) + '_description']);
const docText = (async (tdoc:Doc) => {
switch (tdoc.type) {
case DocumentType.PDF: return curDescription || StrCast(tdoc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
@@ -1480,7 +1483,7 @@ export namespace Doc {
case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutFieldKey(tdoc)]).Text;
default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
}}); // prettier-ignore
- return docText(doc).then(text => (doc[DocData][Doc.LayoutFieldKey(doc) + '_description'] = text));
+ return docText(doc).then(text => (doc['$' + Doc.LayoutFieldKey(doc) + '_description'] = text));
}
// prettier-ignore
@@ -1733,7 +1736,7 @@ ScriptingGlobals.add(function idToDoc(id: string): Doc {
});
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function renameEmbedding(doc: Doc) {
- return StrCast(doc[DocData].title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
+ return StrCast(doc.$title).replace(/\([0-9]*\)/, '') + `(${doc.proto_embeddingId})`;
});
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function getProto(doc: Doc) {
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index 81b6c9ea9..68a3737bf 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -25,7 +25,7 @@ export class RichTextField extends ObjectField {
}
Empty() {
- return !(this.Text || this.Data.toString().includes('dashField') || this.Data.toString().includes('align'));
+ return !(this.Text || this.Data.toString().includes('dashField') || this.Data.toString().includes('dashDoc') || this.Data.toString().includes('align'));
}
[Copy]() {
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 42dd0d432..e16073ff4 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -20,6 +20,7 @@ import { Doc, Opt } from './Doc';
import { Id } from './FieldSymbols';
import { RichTextField } from './RichTextField';
import { Cast, StrCast } from './Types';
+import { Upload } from '../server/SharedMediaTypes';
export namespace RichTextUtils {
const delimiter = '\n';
@@ -127,7 +128,7 @@ export namespace RichTextUtils {
return { baseUrl: embeddedObject.imageProperties!.contentUri! };
});
- const uploads = await Networking.PostToServer('/googlePhotosMediaGet', { mediaItems });
+ const uploads = (await Networking.PostToServer('/googlePhotosMediaGet', { mediaItems })) as Upload.FileInformation[];
if (uploads.length !== mediaItems.length) {
throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: 'Error with internally uploading inlineObjects!' });
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index b294ee8c6..6e2d7eb78 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -210,6 +210,7 @@ export class ComputedField extends ScriptField {
this._lastComputedResult =
this._cachedResult ??
computedFn(() =>
+ ((val) => val instanceof Array ? new List<number>(val) : val)(
this.script.compiled &&
this.script.run(
{
@@ -220,7 +221,7 @@ export class ComputedField extends ScriptField {
_readOnly_: true,
},
console.log
- ).result as FieldResult
+ ).result as FieldResult)
)(); // prettier-ignore
return this._lastComputedResult;
};
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 474882959..af9cb1180 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -60,16 +60,13 @@ export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
-// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
-// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
}
if (field !== undefined && !(field instanceof Promise)) {
if (typeof ctor === 'string') {
- // eslint-disable-next-line valid-typeof
if (typeof field === ctor) {
return field as ToType<T>;
}
@@ -140,9 +137,7 @@ export function ImageCastWithSuffix(field: FieldResult, suffix: string, defaultV
}
export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
-// eslint-disable-next-line no-redeclare
export function FieldValue<T extends FieldType>(field: FieldResult<T>): Opt<T>;
-// eslint-disable-next-line no-redeclare
export function FieldValue<T extends FieldType>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
return field instanceof Promise || field === undefined ? defaultValue : field;
}
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 33764aca5..abbe543e8 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -314,6 +314,13 @@ export function setter(target: ListImpl<FieldType> | Doc, inProp: string | symbo
// if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value as SharingPermissions))) return true;
+ if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('$')) {
+ prop = prop.substring(1);
+ if (target.__DATA__ instanceof Doc) {
+ target.__DATA__[prop] = value as FieldResult;
+ return true;
+ }
+ }
if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) {
if (!prop.startsWith('__')) prop = prop.substring(1);
if (target.__LAYOUT__ instanceof Doc) {
@@ -351,6 +358,7 @@ export function getter(target: Doc | ListImpl<FieldType>, prop: string | symbol,
case DocAcl : return target[DocAcl];
case $mobx: return target.__fieldTuples[prop];
case DocLayout: return target.__LAYOUT__;
+ case DocData: return target.__DATA__;
case Height: case Width: if (GetEffectiveAcl(target) === AclPrivate) return returnZero;
// eslint-disable-next-line no-fallthrough
default :
@@ -362,6 +370,8 @@ export function getter(target: Doc | ListImpl<FieldType>, prop: string | symbol,
const layoutProp = prop.startsWith('_') ? prop.substring(1) : undefined;
if (layoutProp && target.__LAYOUT__) return (target.__LAYOUT__ as Doc)[layoutProp];
+ const dataProp = prop.startsWith('$') ? prop.substring(1) : undefined;
+ if (dataProp && target.__DATA__) return (target.__DATA__ as Doc)[dataProp];
return getFieldImpl(target, layoutProp ?? prop, proxy);
}
diff --git a/src/server/ApiManagers/FlashcardManager.ts b/src/server/ApiManagers/FlashcardManager.ts
new file mode 100644
index 000000000..fd7c42437
--- /dev/null
+++ b/src/server/ApiManagers/FlashcardManager.ts
@@ -0,0 +1,161 @@
+/**
+ * @file FlashcardManager.ts
+ * @description This file defines the FlashcardManager class, responsible for managing API routes
+ * related to flashcard creation and manipulation. It provides functionality for handling file processing,
+ * running Python scripts in a virtual environment, and managing dependencies.
+ */
+
+import { spawn } from 'child_process';
+import * as fs from 'fs';
+import * as path from 'path';
+import { Method } from '../RouteManager';
+import ApiManager, { Registration } from './ApiManager';
+
+/**
+ * Runs a Python script using the provided virtual environment and passes file and option arguments.
+ * @param {string} venvPath - Path to the virtual environment.
+ * @param {string} scriptPath - Path to the Python script.
+ * @param {string} [file] - Optional file to pass to the Python script.
+ * @param {string} [drag] - Optional argument to control drag mode.
+ * @param {string} [smart] - Optional argument to control smart mode.
+ * @returns {Promise<string>} - Resolves with the output from the Python script, or rejects on error.
+ */
+function runPythonScript(venvPath: string, scriptPath: string, file?: string, drag?: string, smart?: string): Promise<string> {
+ return new Promise((resolve, reject) => {
+ const pythonPath = process.platform === 'win32' ? path.join(venvPath, 'Scripts', 'python.exe') : path.join(venvPath, 'bin', 'python3');
+
+ const tempFilePath = path.join(__dirname, `temp_data.txt`); // Unique temp file name
+
+ if (file) {
+ // Write the raw file data to the temp file without conversion
+ fs.writeFileSync(tempFilePath, file, 'utf8');
+ }
+
+ const pythonProcess = spawn(
+ pythonPath,
+ [scriptPath, file ? tempFilePath : undefined, drag, smart].filter(arg => arg !== undefined)
+ );
+
+ let pythonOutput = '';
+ let stderrOutput = '';
+
+ pythonProcess.stdout.on('data', data => {
+ pythonOutput += data.toString();
+ });
+
+ pythonProcess.stderr.on('data', data => {
+ stderrOutput += data.toString();
+ });
+
+ pythonProcess.on('close', code => {
+ if (code === 0) {
+ resolve(pythonOutput);
+ } else {
+ reject(`Python process exited with code ${code}: ${stderrOutput}`);
+ }
+ });
+ });
+}
+
+/**
+ * Installs Python dependencies using pip in the specified virtual environment.
+ * @param {string} venvPath - Path to the virtual environment.
+ * @param {string} requirementsPath - Path to the requirements.txt file.
+ * @returns {Promise<void>} - Resolves when dependencies are successfully installed, rejects on failure.
+ */
+function installDependencies(venvPath: string, requirementsPath: string): Promise<void> {
+ return new Promise((resolve, reject) => {
+ const pipPath = process.platform === 'win32' ? path.join(venvPath, 'Scripts', 'pip.exe') : path.join(venvPath, 'bin', 'pip3');
+
+ const installProcess = spawn(pipPath, ['install', '-r', requirementsPath]);
+
+ installProcess.stdout.on('data', data => {
+ console.log(`pip stdout: ${data}`);
+ });
+
+ installProcess.stderr.on('data', data => {
+ console.error(`pip stderr: ${data}`);
+ });
+
+ installProcess.on('close', code => {
+ if (code !== 0) {
+ reject(`Failed to install dependencies. Exit code: ${code}`);
+ } else {
+ resolve();
+ }
+ });
+ });
+}
+
+/**
+ * Creates a new Python virtual environment.
+ * @param {string} venvPath - Path to the virtual environment that will be created.
+ * @returns {Promise<void>} - Resolves when the virtual environment is successfully created, rejects on failure.
+ */
+function createVirtualEnvironment(venvPath: string): Promise<void> {
+ return new Promise((resolve, reject) => {
+ const createVenvProcess = spawn('python3', ['-m', 'venv', venvPath]);
+
+ createVenvProcess.on('close', code => {
+ if (code !== 0) {
+ reject(`Failed to create virtual environment. Exit code: ${code}`);
+ } else {
+ resolve();
+ }
+ });
+ });
+}
+
+/**
+ * Manages the creation of the virtual environment, installation of dependencies, and running of the Python script.
+ * @param {string} [file] - Optional file data to be processed by the Python script.
+ * @param {string} [drag] - Optional argument controlling drag mode.
+ * @param {string} [smart] - Optional argument controlling smart mode.
+ * @returns {Promise<string>} - Resolves with the Python script output, or rejects on failure.
+ */
+async function manageVenvAndRunScript(file?: string, drag?: string, smart?: string): Promise<string> {
+ const venvPath = path.join(__dirname, '../flashcard/venv'); // Virtual environment path
+ const requirementsPath = path.join(__dirname, '../flashcard/requirements.txt');
+ const pythonScriptPath = path.join(__dirname, '../flashcard/labels.py');
+ console.log('venvPath:', venvPath);
+
+ // Check if the virtual environment exists
+ if (!fs.existsSync(path.join(venvPath, 'bin', 'python3')) && !fs.existsSync(path.join(venvPath, 'Scripts', 'python.exe'))) {
+ await createVirtualEnvironment(venvPath);
+
+ await installDependencies(venvPath, requirementsPath);
+ }
+
+ return runPythonScript(venvPath, pythonScriptPath, file, drag, smart);
+}
+
+/**
+ * FlashcardManager class responsible for managing API routes related to flashcard functionality.
+ * It initializes API routes for handling YouTube subscriptions and label creation using a Python backend.
+ */
+export default class FlashcardManager extends ApiManager {
+ /**
+ * Initializes the API routes for the FlashcardManager class.
+ * @param {Registration} register - The registration function for defining API routes.
+ */
+ protected initialize(register: Registration): void {
+ register({
+ method: Method.POST,
+ subscription: '/labels',
+ secureHandler: async ({ req, res }) => {
+ const { file, drag, smart } = req.body;
+
+ try {
+ // Run the Python process
+ const result = await manageVenvAndRunScript(file, drag, smart);
+ res.status(200).send({ result });
+ } catch (error) {
+ console.error('Error initiating document creation:', error);
+ res.status(500).send({
+ error: 'Failed to initiate document creation',
+ });
+ }
+ },
+ });
+ }
+}
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index 891316b80..8688ec049 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -213,9 +213,9 @@ export class DashSessionAgent extends AppliedSessionAgent {
// indicate success or failure
mainLog(`${error === null ? green('successfully dispatched') : red('failed to dispatch')} ${zipName} to ${cyan(to)}`);
error && mainLog(red(error.message));
- } catch (error: any) {
+ } catch (error: unknown) {
mainLog(red('unable to dispatch zipped backup...'));
- mainLog(red(error.message));
+ mainLog(red((error as { message: string }).message));
}
}
}
diff --git a/src/server/index.ts b/src/server/index.ts
index 1f9af9ee0..3b77359ec 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -4,6 +4,7 @@ import * as mobileDetect from 'mobile-detect';
import * as path from 'path';
import { logExecution } from './ActionUtilities';
import AssistantManager from './ApiManagers/AssistantManager';
+import FlashcardManager from './ApiManagers/FlashcardManager';
import DataVizManager from './ApiManagers/DataVizManager';
import DeleteManager from './ApiManagers/DeleteManager';
import DownloadManager from './ApiManagers/DownloadManager';
@@ -72,6 +73,7 @@ function routeSetter({ addSupervisedRoute, logRegistrationOutcome }: RouteManage
new GeneralGoogleManager(),
/* new GooglePhotosManager(), */ new DataVizManager(),
new AssistantManager(),
+ new FlashcardManager(),
new FireflyManager(),
];
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index dbfabed51..abcec13c0 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -11,6 +11,7 @@ declare module 'bezier-curve';
declare module 'fit-curve';
declare module 'iink-js';
declare module 'pdfjs-dist/web/pdf_viewer';
+declare module 'pdfjs-dist/build/pdf.mjs';
declare module 'react-jsx-parser';
declare module 'type_decls.d';
declare module 'standard-http-error';