aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoralyssaf16 <alyssa_feinberg@brown.edu>2024-10-30 23:18:17 -0400
committeralyssaf16 <alyssa_feinberg@brown.edu>2024-10-30 23:18:17 -0400
commitd1fc9d98172210090f5ed1aa8aaa8512f5afd0eb (patch)
treeb996e6a0c07ec902a45c77548afc3492ebd5d846 /src
parentcd54cba6269dbc4e10b457fe7eddd5114a9d301e (diff)
parentc92e9af531b6277de4e838d75d65c3d1d1efa11d (diff)
Merge branch 'master' of https://github.com/brown-dash/Dash-Web into alyssa-agent
Diffstat (limited to 'src')
-rw-r--r--src/ClientUtils.ts11
-rw-r--r--src/client/apis/gpt/GPT.ts9
-rw-r--r--src/client/documents/DocUtils.ts23
-rw-r--r--src/client/documents/Documents.ts21
-rw-r--r--src/client/util/CurrentUserUtils.ts85
-rw-r--r--src/client/util/DocumentManager.ts8
-rw-r--r--src/client/util/InteractionUtils.tsx6
-rw-r--r--src/client/util/LinkFollower.ts2
-rw-r--r--src/client/util/SettingsManager.tsx28
-rw-r--r--src/client/util/SnappingManager.ts7
-rw-r--r--src/client/views/FilterPanel.tsx2
-rw-r--r--src/client/views/GestureOverlay.tsx171
-rw-r--r--src/client/views/GlobalKeyHandler.ts9
-rw-r--r--src/client/views/InkingStroke.tsx3
-rw-r--r--src/client/views/LightboxView.tsx4
-rw-r--r--src/client/views/MarqueeAnnotator.tsx3
-rw-r--r--src/client/views/PinFuncs.ts23
-rw-r--r--src/client/views/SidebarAnnos.tsx4
-rw-r--r--src/client/views/StyleProp.ts4
-rw-r--r--src/client/views/StyleProvider.tsx9
-rw-r--r--src/client/views/StyleProviderQuiz.tsx2
-rw-r--r--src/client/views/ViewBoxInterface.ts2
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx66
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx43
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx28
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx5
-rw-r--r--src/client/views/collections/CollectionSubView.tsx17
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx4
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.tsx6
-rw-r--r--src/client/views/collections/TabDocView.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx94
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx2
-rw-r--r--src/client/views/global/globalScripts.ts134
-rw-r--r--src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx6
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx5
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx21
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx6
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx35
-rw-r--r--src/client/views/nodes/DocumentView.tsx84
-rw-r--r--src/client/views/nodes/EquationBox.tsx2
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.scss7
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx190
-rw-r--r--src/client/views/nodes/ImageBox.tsx10
-rw-r--r--src/client/views/nodes/LabelBox.scss1
-rw-r--r--src/client/views/nodes/LabelBox.tsx168
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx2
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx5
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx13
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx273
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts4
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx82
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx57
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx23
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx12
-rw-r--r--src/extensions/ExtensionsTypings.ts8
-rw-r--r--src/extensions/Extensions_Array.ts10
-rw-r--r--src/fields/Doc.ts6
-rw-r--r--src/fields/InkField.ts27
-rw-r--r--src/fields/ScriptField.ts6
-rw-r--r--src/fields/documentSchemas.ts7
-rw-r--r--src/pen-gestures/GestureTypes.ts1
-rw-r--r--src/pen-gestures/ndollar.ts96
70 files changed, 1105 insertions, 922 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index e7aee1c2a..baad9a06c 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -107,12 +107,12 @@ export namespace ClientUtils {
default: return type.charAt(0).toUpperCase() + type.substring(1,3);
} // prettier-ignore
}
- export function cleanDocumentType(type: DocumentType, colType: CollectionViewType) {
+ export function cleanDocumentType(type: DocumentType, colType?: CollectionViewType) {
switch (type) {
case DocumentType.PDF: return 'PDF';
case DocumentType.IMG: return 'Image';
case DocumentType.AUDIO: return 'Audio';
- case DocumentType.COL: return 'Collection:'+colType;
+ case DocumentType.COL: return 'Collection:'+ (colType ?? "");
case DocumentType.RTF: return 'Text';
default: return type.charAt(0).toUpperCase() + type.slice(1);
} // prettier-ignore
@@ -212,7 +212,7 @@ export namespace ClientUtils {
return { r: r, g: g, b: b, a: a };
}
- const isTransparentFunctionHack = 'isTransparent(__value__)';
+ export const isTransparentFunctionHack = 'isTransparent(__value__)';
export const noRecursionHack = '__noRecursion';
// special case filters
@@ -223,11 +223,6 @@ export namespace ClientUtils {
export function IsRecursiveFilter(val: string) {
return !val.includes(noRecursionHack);
}
- export function HasFunctionFilter(val: string) {
- if (val.includes(isTransparentFunctionHack)) return (color: string) => color !== '' && DashColor(color).alpha() !== 1;
- // add other function filters here...
- return undefined;
- }
export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) {
return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')';
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 8a2c91269..03380e4d6 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -42,7 +42,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
model: 'gpt-4o',
maxTokens: 2048,
temp: 0.7,
- prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
+ prompt: 'Create a stack of flashcards out of this text with each question and answer labeled as question and answer. Create a title that represents the question in just a few words and label it "title". For some questions, ask "what is this image of" but tailored to stacks theme and the image and write a keyword that represents the image and label it "keyword". Otherwise, write none. Do not label each flashcard and do not include asterisks.',
},
completion: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: "You are a helpful assistant. Answer the user's prompt." },
@@ -65,7 +65,12 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
prompt: "The user is going to give you a list of descriptions. Each one is separated by `======` on either side. Descriptions will vary in length, so make sure to only separate when you see `======`. Sort them by the user's specifications. Make sure each description is only in the list once. Each item should be separated by `======`. Immediately afterward, surrounded by `------` on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). It is VERY important that you format it exactly as described, ensuring the proper number of `=` and `-` (6 of each) and NO commas",
},
describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' },
- flashcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Make flashcards out of this text with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' },
+ flashcard: {
+ model: 'gpt-4-turbo',
+ maxTokens: 512,
+ temp: 0.5,
+ prompt: 'Make flashcards out of this text with each question and answer labeled as question and answer. Create a title for each question and asnwer that is labeled as "title". Do not label each flashcard and do not include asterisks: ',
+ },
chatcard: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Answer the following question as a short flashcard response. Do not include a label.' },
quiz: {
model: 'gpt-4-turbo',
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 19f3c89ef..067b9c5e0 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -3,7 +3,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { saveAs } from 'file-saver';
import * as JSZip from 'jszip';
import { action, runInAction } from 'mobx';
-import { ClientUtils } from '../../ClientUtils';
+import { ClientUtils, DashColor } from '../../ClientUtils';
import * as JSZipUtils from '../../JSZipUtils';
import { decycle } from '../../decycler/decycler';
import { DateField } from '../../fields/DateField';
@@ -36,11 +36,16 @@ import { DocumentView } from '../views/nodes/DocumentView';
import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
export namespace DocUtils {
+ function HasFunctionFilter(val: string) {
+ if (val.includes(ClientUtils.isTransparentFunctionHack)) return (d: Doc, color: string) => !d.disableMixBlend && color !== '' && DashColor(color).alpha() !== 1;
+ // add other function filters here...
+ return undefined;
+ }
function matchFieldValue(doc: Doc, key: string, valueIn: unknown): boolean {
let value = valueIn;
- const hasFunctionFilter = ClientUtils.HasFunctionFilter(value as string);
+ const hasFunctionFilter = HasFunctionFilter(value as string);
if (hasFunctionFilter) {
- return hasFunctionFilter(StrCast(doc[key]));
+ return hasFunctionFilter(doc, StrCast(doc[key]));
}
if (key === LinkedTo) {
// links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("..."))
@@ -678,11 +683,19 @@ export namespace DocUtils {
...(defaultTextTemplate
? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
: {
- _width: width || 200,
- _height: 35,
+ _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200,
+ _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35,
_layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
_layout_fitWidth: true,
_layout_autoHeight: true,
+ backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor),
+ text_fitBox: BoolCast(Doc.UserDoc().fitBox),
+ text_align: StrCast(Doc.UserDoc().textAlign),
+ text_fontColor: StrCast(Doc.UserDoc().fontColor),
+ text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+ text_fontWeight: StrCast(Doc.UserDoc().fontWeight),
+ text_fontStyle: StrCast(Doc.UserDoc().fontStyle),
+ text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration),
}),
});
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e539e3c65..efd6ce3c9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -281,7 +281,7 @@ export class DocumentOptions {
_layout_fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
_layout_fieldKey?: STRt = new StrInfo('the field key containing the current layout definition', false);
_layout_enableAltContentUI?: BOOLt = new BoolInfo('whether to show alternate content button');
- _layout_isFlashcard?: BOOLt = new BoolInfo('whether comparison node should be displayed as a flashcard');
+ _layout_flashcardType?: STRt = new StrInfo('flashcard style to render in ComparisonBox. currently just "flashcard".');
_layout_showTitle?: string; // field name to display in header (:hover is an optional suffix)
_layout_showSidebar?: BOOLt = new BoolInfo('whether an annotationsidebar should be displayed for text docuemnts');
_layout_showCaption?: string; // which field to display in the caption area. leave empty to have no caption
@@ -294,7 +294,6 @@ export class DocumentOptions {
_yMargin?: NUMt = new NumInfo('gap between top edge of dcoument and start of masonry/stacking layouts', false);
_xPadding?: NUMt = new NumInfo('x padding', false);
_yPadding?: NUMt = new NumInfo('y padding', false);
- _singleLine?: boolean; // whether label box is restricted to one line of text
_createDocOnCR?: boolean; // whether carriage returns and tabs create new text documents
_columnWidth?: NUMt = new NumInfo('width of table column', false);
_columnsHideIfEmpty?: BOOLt = new BoolInfo('whether stacking view column headings should be hidden');
@@ -302,10 +301,14 @@ export class DocumentOptions {
_caption_yMargin?: NUMt = new NumInfo('y margin of caption inside of a carousel collection', false, true);
icon_nativeWidth?: NUMt = new NumInfo('native width of icon view', false, true);
icon_nativeHeight?: NUMt = new NumInfo('native height of icon view', false, true);
- _text_fontSize?: string;
- _text_fontFamily?: string;
- _text_fontWeight?: string;
- text_align?: STRt = new StrInfo('horizontal text alignment default');
+ text_fontSize?: string;
+ text_fontFamily?: string;
+ text_fontWeight?: string;
+ text_fitBox?: BOOLt = new BoolInfo("whether text box should be scaled to fit it's containing render box");
+ text_align?: STRt = new StrInfo('horizontal text alignment default', undefined, undefined, ['left', 'center', 'right']);
+ title_align?: STRt = new StrInfo('horizontal title alignment in label box', undefined, undefined, ['left', 'center', 'right']);
+ title_transform?: STRt = new StrInfo('transformation to apply to title in label box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
+ text_transform?: STRt = new StrInfo('transformation to apply to text in text box (eg., uppercase)', undefined, undefined, ['uppercase', 'lowercase', 'capitalize']);
text_placeholder?: BOOLt = new BoolInfo('makes the text act like a placeholder and automatically select when the text box is selected');
fontSize?: string;
_pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views
@@ -372,6 +375,8 @@ export class DocumentOptions {
config_panX?: NUMt = new NumInfo('panX saved as a view spec', false);
config_panY?: NUMt = new NumInfo('panY saved as a view spec', false);
config_zoom?: NUMt = new NumInfo('zoom saved as a view spec', false);
+ config_carousel_index?: NUMt = new NumInfo('saved carousel index', false);
+ config_card_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView');
config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false);
presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document', false);
presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false);
@@ -494,7 +499,6 @@ export class DocumentOptions {
sidebar_type_collection?: string; // collection type of text sidebar
data_dashboards?: List<FieldType>; // list of dashboards used in shareddocs;
- textTransform?: string;
letterSpacing?: string;
iconTemplate?: string; // name of icon template style
icon_fieldKey?: string; // specifies the icon template to use (e.g., icon_fieldKey='george', then the icon template's name is icon_george; otherwise, the template's name would be icon_<type> where type is the Doc's type(pdf,rich text, etc))
@@ -821,7 +825,7 @@ export namespace Docs {
data_front: front ?? CenteredTextCreator('question', 'hint: Enter a topic, select this document and click the stack button to have GPT create a deck of cards', { text_placeholder: true, cloneOnCopy: true }, undefined),
data_back: back ?? CenteredTextCreator('answer', 'answer here', { text_placeholder: true, cloneOnCopy: true }, undefined),
_layout_fitWidth: true,
- _layout_isFlashcard: true,
+ _layout_flashcardType: 'flashcard',
title,
...options,
});
@@ -923,7 +927,6 @@ export namespace Docs {
I.text_align = 'center';
I.rotation = 0;
I.defaultDoubleClick = 'ignore';
- I.keepZWhenDragged = true;
I.author_date = new DateField();
I.acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View;
// I.acl_Override = SharingPermissions.Unset;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 30c75c659..98a3e8914 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -4,7 +4,7 @@ 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 { InkTool } from "../../fields/InkField";
+import { InkEraserTool, InkInkTool, InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
@@ -39,6 +39,7 @@ import { SelectionManager } from "./SelectionManager";
import { ColorScheme } from "./SettingsManager";
import { SnappingManager } from "./SnappingManager";
import { UndoManager } from "./UndoManager";
+import { DocumentView } from "../views/nodes/DocumentView";
export interface Button {
// DocumentOptions fields a button can set
@@ -186,7 +187,7 @@ export class CurrentUserUtils {
const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
- layout: LabelBox.LayoutString(fieldKey), textTransform: "unset", letterSpacing: "unset", _singleLine: false, _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
+ layout: LabelBox.LayoutString(fieldKey), letterSpacing: "unset", _label_minFontSize: 14, _label_maxFontSize: 14, layout_borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
});
const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
@@ -268,7 +269,7 @@ export class CurrentUserUtils {
MakeTemplate(Docs.Create.MultirowDocument(
[
Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200, isSystem: true }),
- Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
+ Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, text_fontFamily: StrCast(Doc.UserDoc().fontFamily), text_fontSize: StrCast(Doc.UserDoc().fontSize) })
], {...opts, title: "Slide View Template"}));
const plotlyApi = () => {
let plotly = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@plotly");
@@ -392,7 +393,7 @@ pie title Minerals in my tap water
{key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, _layout_dontCenter:'xy', dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
{key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
{key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree,
- treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true,
+ treeView_HasOverlay: true, text_fontSize: "20px", _layout_autoHeight: true,
dropAction:dropActionType.move, treeView_Type: TreeViewType.outline,
backgroundColor: "white", _xMargin: 0, _yMargin: 0, _createDocOnCR: true
}, funcs: {title: 'this.text?.Text'}},
@@ -731,11 +732,11 @@ pie title Minerals in my tap water
return [
{ title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, toolType:"font", ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'},
btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
- { title: "Font Size",toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
+ { title: " Size", toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 9 },
{ title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
{ title: "Highlight",toolTip: "Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'} },
{ title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
- { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
+ { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italic", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", toolType:"bullet", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", toolType:"decimal", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
@@ -746,6 +747,7 @@ pie title Minerals in my tap water
{ title: "Center", toolTip: "Center align (Cmd-\\)",btnType: ButtonType.ToggleButton, icon: "align-center",toolType:"center",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Right", toolTip: "Right align (Cmd-])", btnType: ButtonType.ToggleButton, icon: "align-right", toolType:"right", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
]},
+ { title: "Fit Box", toolTip: "Fit text to box", btnType: ButtonType.ToggleButton, icon: "object-group",toolType:"fitBox", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Elide", toolTip: "Elide selection", btnType: ButtonType.ToggleButton, icon: "eye", toolType:"elide", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Dictate", toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", toolType:"dictation", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", toolType:"noAutoLink", expertMode:true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'}, funcs: {hidden: 'IsNoviceMode()'}},
@@ -757,24 +759,27 @@ pie title Minerals in my tap water
static inkTools():Button[] {
return [
- { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }},
- { title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter",toolType: "highlighter", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }},
- { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
- { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' },
+ { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
+ { title: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' },
+ subMenu: [
+ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: InkInkTool.Pen, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }},
+ { title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", toolType: InkInkTool.Highlight, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }},
+ { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: InkInkTool.Write, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }},
+ ]},
+ { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearBtnWidth:40},
+ { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}},
+ { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' },
subMenu: [
- { title: "Stroke", toolTip: "Stroke Erase", btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkTool.StrokeEraser, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
- { title: "Segment", toolTip: "Segment Erase", btnType: ButtonType.ToggleButton, icon: "xmark", toolType:InkTool.SegmentEraser,ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
- { title: "Radius", toolTip: "Radius Erase", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkTool.RadiusEraser, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} },
+ { title: "Stroke", toolTip: "Eraser complete strokes",btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkEraserTool.Stroke, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
+ { title: "Segment", toolTip: "Erase segments up to intersections",btnType: ButtonType.ToggleButton,icon: "xmark",toolType:InkEraserTool.Segment,ignoreClick:true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
+ { title: "Area", toolTip: "Erase like a pencil", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkEraserTool.Radius, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}},
]},
- { title: "Eraser Width", toolTip: "Eraser Width", btnType: ButtonType.NumberSliderButton, toolType: "eraserWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1, funcs: {hidden:"NotRadiusEraser()"}},
- { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} },
- { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
- { title: "Labels", toolTip: "Labels", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, },
- { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1},
- { title: "Ink", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'} },
- { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: "smartdraw", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
+ { title: " Size", toolTip: "Size of area pencil eraser", btnType: ButtonType.NumberSliderButton, toolType: "eraserWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"NotRadiusEraser()"}, numBtnMin: 1, linearBtnWidth:40},
+ { title: "Mask", toolTip: "Make Stroke a Stencil Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } },
+ { title: "Labels", toolTip: "Show Labels Inside Shapes", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}},
+ { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: "smartdraw", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}},
];
}
@@ -813,11 +818,11 @@ pie title Minerals in my tap water
{ title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
{ title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} },
{ title: "Template",icon: "scroll", toolTip: "Default Note Template",btnType: ButtonType.ToggleButton, expertMode: false, toolType:DocumentType.RTF, scripts: { onClick: '{ return setDefaultTemplate(_readOnly_); }'} },
- { title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
- { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
- { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
+ { title: "Fill", icon: "fill-drip", toolTip: "Fill/Background Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'} }, // Only when a document is selected
+ { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
+ { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
{ title: "Num", icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
- { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
+ { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
{ title: "Filter", icon: "=", toolTip: "Filter cards by tags", subMenu: CurrentUserUtils.tagGroupTools(),ignoreClick:true, toolType:DocumentType.COL, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, btnType: ButtonType.MultiToggleButton, width: 30, backgroundColor: doc.userVariantColor as string},
{ title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available
@@ -862,7 +867,7 @@ pie title Minerals in my tap water
}
// linear view
const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, undoIgnoreFields: new List<string>(['width', "linearView_IsOpen"]),
- childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: !params.scripts?.onClick,
+ childDontRegisterViews: true, flexGap: 0, _height: 30, _width: 30, ignoreClick: !params.scripts?.onClick,
linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc};
const items = (menutBtn?:Doc) => !menutBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menutBtn) );
@@ -986,15 +991,23 @@ pie title Minerals in my tap water
Doc.noviceMode ?? (Doc.noviceMode = true);
doc._showLabel ?? (doc._showLabel = true);
doc.textAlign ?? (doc.textAlign = "left");
+ doc.textBackgroundColor ?? (doc.textBackgroundColor = Colors.LIGHT_GRAY);
doc.activeTool = InkTool.None;
doc.openInkInLightbox ?? (doc.openInkInLightbox = false);
- doc.activeInkHideTextLabels ?? (doc.activeInkHideTextLabels = false);
- doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");
- doc.activeInkWidth ?? (doc.activeInkWidth = 1);
- doc.activeInkBezier ?? (doc.activeInkBezier = "0");
- doc.activeFillColor ?? (doc.activeFillColor = "");
- doc.activeArrowStart ?? (doc.activeArrowStart = "");
- doc.activeArrowEnd ?? (doc.activeArrowEnd = "");
+ doc.activeHideTextLabels ?? (doc.activeHideTextLabels = false);
+ doc[`active${InkInkTool.Pen}Color`] ?? (doc[`active${InkInkTool.Pen}Color`] = "rgb(0, 0, 0)");
+ doc[`active${InkInkTool.Pen}Width`] ?? (doc[`active${InkInkTool.Pen}Width`] = 2);
+ doc[`active${InkInkTool.Pen}Bezier`] ?? (doc[`active${InkInkTool.Pen}Bezier`] = "0");
+ doc[`active${InkInkTool.Write}Color`] ?? (doc[`active${InkInkTool.Write}Color`] = "rgb(255, 0, 0)");
+ doc[`active${InkInkTool.Write}Width`] ?? (doc[`active${InkInkTool.Write}Width`] = 1);
+ doc[`active${InkInkTool.Write}Bezier`] ?? (doc[`active${InkInkTool.Write}Bezier`] = "0");
+ doc[`active${InkInkTool.Highlight}Color`] ?? (doc[`active${InkInkTool.Highlight}Color`] = 'transparent');
+ doc[`active${InkInkTool.Highlight}Width`] ?? (doc[`active${InkInkTool.Highlight}Width`] = 20);
+ doc[`active${InkInkTool.Highlight}Bezier`] ?? (doc[`active${InkInkTool.Highlight}Bezier`] = "0");
+ doc[`active${InkInkTool.Highlight}Fill`] ?? (doc[`active${InkInkTool.Highlight}Fill`] = "rgba(0, 255, 255, 0.4)");
+ doc.activeInkTool ?? (doc.activeInkTool = InkInkTool.Pen);
+ doc.activeEraserTool ?? (doc.activeEraserTool = InkEraserTool.Stroke);
+ doc.activeEraserWidth ?? (doc.activeEraserWidth = 20);
doc.activeDash ?? (doc.activeDash === "0");
doc.fontSize ?? (doc.fontSize = "12px");
doc.fontFamily ?? (doc.fontFamily = "Arial");
@@ -1129,7 +1142,9 @@ pie title Minerals in my tap water
}
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function NotRadiusEraser() { return Doc.ActiveTool !== InkTool.RadiusEraser; }, "is the active tool anything but the radius eraser");
+ScriptingGlobals.add(function activeInkTool() { return Doc.ActiveTool=== InkTool.Ink || DocumentView.Selected().some(dv => dv.layoutDoc.layout_isSvg); }, "is a pen tool or an ink stroke active");
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function NotRadiusEraser() { return Doc.ActiveTool !== InkTool.Eraser || Doc.ActiveEraser !== InkEraserTool.Radius; }, "is the active tool anything but the radius eraser");
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
// eslint-disable-next-line prefer-arrow-callback
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 4ab2e8d05..286112bf9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -256,7 +256,11 @@ export class DocumentManager {
Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
const docContextPath = DocumentManager.GetContextPath(targetDoc, true);
if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false;
- if (DocumentView.activateTabView(docContextPath[0])) options.toggleTarget = false;
+ let activatedTab = false;
+ if (DocumentView.activateTabView(docContextPath[0])) {
+ options.toggleTarget = false;
+ activatedTab = true;
+ }
const rootContextView =
docContextPath.length &&
@@ -268,7 +272,7 @@ export class DocumentManager {
return;
}
options.didMove = true;
- (!DocumentView.LightboxDoc() && docContextPath.some(doc => DocumentView.activateTabView(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
+ (!DocumentView.LightboxDoc() && (activatedTab || docContextPath.some(doc => DocumentView.activateTabView(doc)))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
this.AddViewRenderedCb(docContextPath[0], dv => res(dv));
}));
if (options.openLocation?.includes(OpenWhere.lightbox)) {
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 4231c2ca8..ca1cb8014 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -109,7 +109,7 @@ export namespace InteractionUtils {
dash: string | undefined,
scalexIn: number,
scaleyIn: number,
- shape: Gestures,
+ shape: Gestures | undefined,
pevents: Property.PointerEvents,
opacity: number,
nodefs: boolean,
@@ -136,7 +136,7 @@ export namespace InteractionUtils {
const arrowLengthFactor = 5 * (markerScale || 0.5);
const arrowNotchFactor = 2 * (markerScale || 0.5);
return (
- <svg fill={color} style={{ transition: 'inherit' }} onPointerDown={downHdlr}>
+ <svg fill={color || 'transparent'} style={{ transition: 'inherit' }} onPointerDown={downHdlr}>
{' '}
{/* setting the svg fill sets the arrowStart fill */}
{nodefs ? null : (
@@ -186,7 +186,7 @@ export namespace InteractionUtils {
opacity: 1.0,
// opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents === 'all' ? 'visiblePainted' : pevents,
- stroke: color ?? 'rgb(0, 0, 0)',
+ stroke: (color ?? 'rgb(0, 0, 0)') || 'transparent',
strokeWidth,
strokeLinecap: strokeLineCap,
strokeDasharray: dashArray,
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 0a3a0ba49..0e67dcfaa 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -113,12 +113,10 @@ export class LinkFollower {
}
const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
- // eslint-disable-next-line prefer-destructuring
target.x = moveTo[0];
movedTarget = true;
}
if (srcAnchor.followLinkYoffset !== undefined && moveTo[1] !== target.y) {
- // eslint-disable-next-line prefer-destructuring
target.y = moveTo[1];
movedTarget = true;
}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 9200d68db..628097ca8 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
+import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -45,7 +45,6 @@ export class SettingsManager extends React.Component<object> {
public closeMgr = action(() => {
this._isOpen = false;
});
- // eslint-disable-next-line react/no-unused-class-component-methods
public openMgr = action(() => {
this._isOpen = true;
});
@@ -269,9 +268,9 @@ export class SettingsManager extends React.Component<object> {
formLabelPlacement="right"
toggleType={ToggleType.SWITCH}
onClick={() => {
- Doc.UserDoc().activeInkHideTextLabels = !Doc.UserDoc().activeInkHideTextLabels;
+ Doc.UserDoc().activeHideTextLabels = !Doc.UserDoc().activeHideTextLabels;
}}
- toggleStatus={BoolCast(Doc.UserDoc().activeInkHideTextLabels)}
+ toggleStatus={BoolCast(Doc.UserDoc().activeHideTextLabels)}
size={Size.XSMALL}
color={SettingsManager.userColor}
/>
@@ -354,6 +353,27 @@ export class SettingsManager extends React.Component<object> {
Doc.UserDoc().fontSize = val + 'px';
}}
/>
+ <ColorPicker
+ color={SettingsManager.userColor}
+ type={Type.PRIM}
+ defaultPickerType="Classic"
+ selectedColor={StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY)}
+ background={SnappingManager.userBackgroundColor}
+ icon={<FontAwesomeIcon icon="palette" size="lg" />}
+ tooltip="default text background color"
+ label="background"
+ setSelectedColor={value => {
+ Doc.UserDoc().textBackgroundColor = value;
+ // if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+ // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ }}
+ setFinalColor={value => {
+ Doc.UserDoc().textBackgroundColor = value;
+ // this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ // this.colorBatch?.end();
+ // this.colorBatch = undefined;
+ }}
+ />
<Dropdown
items={fontFamilies.map(val => ({
text: val,
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 5f6c7d9ac..2a150dc5a 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -1,4 +1,5 @@
import { observable, action, runInAction, makeObservable } from 'mobx';
+import { Gestures } from '../../pen-gestures/GestureTypes';
export enum freeformScrollMode {
Pan = 'pan',
@@ -29,6 +30,8 @@ export class SnappingManager {
@observable _propertyWid: number = 0;
@observable _printToConsole: boolean = false;
@observable _hideDecorations: boolean = false;
+ @observable _keepGestureMode: boolean = false; // for whether primitive selection enters a one-shot or persistent mode
+ @observable _inkShape: Gestures | undefined = undefined;
private constructor() {
SnappingManager._manager = this;
@@ -61,6 +64,8 @@ export class SnappingManager {
public static get PropertiesWidth(){ return this.Instance._propertyWid; } // prettier-ignore
public static get PrintToConsole() { return this.Instance._printToConsole; } // prettier-ignore
public static get HideDecorations(){ return this.Instance._hideDecorations; } // prettier-ignore
+ public static get KeepGestureMode(){ return this.Instance._keepGestureMode; } // prettier-ignore
+ public static get InkShape() { return this.Instance._inkShape; } // prettier-ignore
public static SetLongPress = (press: boolean) => runInAction(() => {this.Instance._longPress = press}); // prettier-ignore
public static SetShiftKey = (down: boolean) => runInAction(() => {this.Instance._shiftKey = down}); // prettier-ignore
@@ -78,6 +83,8 @@ export class SnappingManager {
public static SetPropertiesWidth= (wid:number) =>runInAction(() => {this.Instance._propertyWid = wid}); // prettier-ignore
public static SetPrintToConsole = (state:boolean) =>runInAction(() => {this.Instance._printToConsole = state}); // prettier-ignore
public static SetHideDecorations= (state:boolean) =>runInAction(() => {this.Instance._hideDecorations = state}); // prettier-ignore
+ public static SetKeepGestureMode= (state:boolean) =>runInAction(() => {this.Instance._keepGestureMode = state}); // prettier-ignore
+ public static SetInkShape = (shape?:Gestures)=>runInAction(() => {this.Instance._inkShape = shape}); // prettier-ignore
public static userColor: string | undefined;
public static userVariantColor: string | undefined;
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 11425e477..99738052d 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -102,7 +102,7 @@ const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, sel
return (
<div
- className={`filterHotKey-button`}
+ className="filterHotKey-button"
onClick={e => {
e.stopPropagation();
state.startEditing();
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index afeecaa63..2d6cd03e0 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -2,9 +2,9 @@ import * as fitCurve from 'fit-curve';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
+import { setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction, intersectRect } from '../../Utils';
-import { Doc, Opt, returnEmptyDoclist } from '../../fields/Doc';
+import { Doc } from '../../fields/Doc';
import { InkData, InkField, InkTool } from '../../fields/InkField';
import { NumCast } from '../../fields/Types';
import { Gestures } from '../../pen-gestures/GestureTypes';
@@ -14,27 +14,25 @@ import { DocumentType } from '../documents/DocumentTypes';
import { Docs } from '../documents/Documents';
import { InteractionUtils } from '../util/InteractionUtils';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { Transform } from '../util/Transform';
+import { SnappingManager } from '../util/SnappingManager';
import { undoable } from '../util/UndoManager';
import './GestureOverlay.scss';
import { InkingStroke } from './InkingStroke';
import { ObservableReactComponent } from './ObservableReactComponent';
-import { returnEmptyDocViewList } from './StyleProvider';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import {
- ActiveArrowEnd,
- ActiveArrowScale,
- ActiveArrowStart,
- ActiveDash,
- ActiveFillColor,
- ActiveInkBezierApprox,
+ ActiveInkArrowEnd,
+ ActiveInkArrowScale,
+ ActiveInkArrowStart,
ActiveInkColor,
+ ActiveInkDash,
+ ActiveInkFillColor,
ActiveInkWidth,
DocumentView,
- SetActiveArrowStart,
- SetActiveDash,
- SetActiveFillColor,
+ SetActiveInkArrowStart,
SetActiveInkColor,
+ SetActiveInkDash,
+ SetActiveInkFillColor,
SetActiveInkWidth,
} from './nodes/DocumentView';
export enum ToolglassTools {
@@ -57,18 +55,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
// eslint-disable-next-line no-use-before-define
static Instances: GestureOverlay[] = [];
- @observable public InkShape: Opt<Gestures> = undefined;
@observable public SavedColor?: string = undefined;
@observable public SavedWidth?: number = undefined;
@observable public Tool: ToolglassTools = ToolglassTools.None;
- @observable public KeepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
@observable private _thumbX?: number = undefined;
@observable private _thumbY?: number = undefined;
@observable private _pointerY?: number = undefined;
@observable private _points: { X: number; Y: number }[] = [];
- @observable private _strokes: InkData[] = [];
- @observable private _palette?: JSX.Element = undefined;
@observable private _clipboardDoc?: JSX.Element = undefined;
@computed private get height(): number {
@@ -95,8 +89,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
}
@action
onPointerDown = (e: React.PointerEvent) => {
+ (document.activeElement as HTMLElement)?.blur();
if (!(e.target as HTMLElement)?.className?.toString().startsWith('lm_')) {
- if ([InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (Doc.ActiveTool === InkTool.Ink) {
this._points.push({ X: e.clientX, Y: e.clientY });
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
}
@@ -122,13 +117,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
};
@action primCreated() {
- if (!this.KeepPrimitiveMode) {
- this.InkShape = undefined;
- // get out of ink mode after each stroke=
- // if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+ if (!SnappingManager.KeepGestureMode) {
+ SnappingManager.SetInkShape(undefined);
Doc.ActiveTool = InkTool.None;
- // SetActiveArrowStart('none');
- // SetActiveArrowEnd('none');
}
}
/**
@@ -244,15 +235,16 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
this.dispatchGesture(Gestures.Stroke);
};
@action
- onPointerUp = () => {
+ onPointerUp = (e: PointerEvent) => {
const ffView = DocumentView.DownDocView?.ComponentView instanceof CollectionFreeFormView && DocumentView.DownDocView.ComponentView;
+ const downView = DocumentView.DownDocView;
DocumentView.DownDocView = undefined;
if (this._points.length > 1) {
const B = this.svgBounds;
const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
const { Name, Score } =
- (this.InkShape
- ? new Result(this.InkShape, 1, Date.now)
+ (SnappingManager.InkShape
+ ? new Result(SnappingManager.InkShape, 1, Date.now)
: Doc.UserDoc().recognizeGestures && points.length > 2
? GestureUtils.GestureRecognizer.Recognize([points])
: undefined) ??
@@ -286,7 +278,11 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
this.dryInk();
}
}
+ } else {
+ (downView?.ComponentView as CollectionFreeFormView)?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
+ e.preventDefault();
}
+ this.primCreated();
this._points.length = 0;
};
/**
@@ -552,97 +548,38 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
B.bottom += width / 2;
B.width += width;
B.height += width;
- const fillColor = ActiveFillColor();
+ const fillColor = ActiveInkFillColor();
const strokeColor = fillColor && fillColor !== 'transparent' ? fillColor : ActiveInkColor();
return [
this.props.children,
- this._palette,
- [
- this._strokes.map((l, i) => {
- const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(l, true);
- return (
- <svg key={i} width={b.width} height={b.height} style={{ top: 0, left: 0, transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
- {InteractionUtils.CreatePolyline(
- l,
- b.left,
- b.top,
- strokeColor,
- width,
- width,
- 'miter',
- 'round',
- ActiveInkBezierApprox(),
- 'none' /* ActiveFillColor() */,
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveArrowScale(),
- ActiveDash(),
- 1,
- 1,
- this.InkShape as Gestures,
- 'none',
- 1.0,
- false
- )}
- </svg>
- );
- }),
- this._points.length <= 1 ? null : (
- <svg key="svg" width={B.width} height={B.height} style={{ top: 0, left: 0, transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
- {InteractionUtils.CreatePolyline(
- this._points.map(p => ({ X: p.X - (rect?.x || 0), Y: p.Y - (rect?.y || 0) })),
- B.left,
- B.top,
- ActiveInkColor(),
- width,
- width,
- 'miter',
- 'round',
- '',
- 'none' /* ActiveFillColor() */,
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveArrowScale(),
- ActiveDash(),
- 1,
- 1,
- this.InkShape as Gestures,
- 'none',
- 1.0,
- false
- )}
- </svg>
- ),
- ],
+ this._points.length <= 1 ? null : (
+ <svg key="svg" width={B.width} height={B.height} style={{ top: 0, left: 0, transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+ {InteractionUtils.CreatePolyline(
+ this._points.map(p => ({ X: p.X - (rect?.x || 0), Y: p.Y - (rect?.y || 0) })),
+ B.left,
+ B.top,
+ strokeColor,
+ width,
+ width,
+ 'miter',
+ 'round',
+ '',
+ 'none' /* ActiveFillColor() */,
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkArrowScale(),
+ ActiveInkDash(),
+ 1,
+ 1,
+ SnappingManager.InkShape,
+ 'none',
+ 1.0,
+ false
+ )}
+ </svg>
+ ),
];
}
- screenToLocalTransform = () => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1);
- return300 = () => 300;
- @action
- public openFloatingDoc = (doc: Doc) => {
- this._clipboardDoc = (
- <DocumentView
- Document={doc}
- addDocument={undefined}
- addDocTab={returnFalse}
- pinToPres={emptyFunction}
- removeDocument={undefined}
- ScreenToLocalTransform={this.screenToLocalTransform}
- PanelWidth={this.return300}
- PanelHeight={this.return300}
- isDocumentActive={returnFalse}
- isContentActive={returnFalse}
- renderDepth={0}
- styleProvider={returnEmptyString}
- containerViewPath={returnEmptyDocViewList}
- focus={emptyFunction}
- whenChildContentsActiveChanged={emptyFunction}
- childFiltersByRanges={returnEmptyFilter}
- childFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- />
- );
- };
@action
public closeFloatingDoc = () => {
@@ -689,10 +626,10 @@ ScriptingGlobals.add(function setPen(width: string, color: string, fill: string,
SetActiveInkColor(color);
GestureOverlay.Instance.SavedWidth = ActiveInkWidth();
SetActiveInkWidth(width);
- SetActiveFillColor(fill);
- SetActiveArrowStart(arrowStart);
- SetActiveArrowStart(arrowEnd);
- SetActiveDash(dash);
+ SetActiveInkFillColor(fill);
+ SetActiveInkArrowStart(arrowStart);
+ SetActiveInkArrowStart(arrowEnd);
+ SetActiveInkDash(dash);
});
});
// eslint-disable-next-line prefer-arrow-callback
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index d7d8e9506..f16338361 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -2,7 +2,7 @@ import { random } from 'lodash';
import { action } from 'mobx';
import { Doc, DocListCast } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { InkTool } from '../../fields/InkField';
+import { InkInkTool, InkTool } from '../../fields/InkField';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, PromiseValue } from '../../fields/Types';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -280,10 +280,10 @@ export class KeyManager {
}
break;
case 'e':
- Doc.ActiveTool = [InkTool.StrokeEraser, InkTool.SegmentEraser, InkTool.RadiusEraser].includes(Doc.ActiveTool) ? InkTool.None : InkTool.StrokeEraser;
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Eraser ? InkTool.None : InkTool.Eraser;
break;
case 'p':
- Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Ink ? InkTool.None : InkTool.Ink;
break;
case 'r':
preventDefault = false;
@@ -378,7 +378,8 @@ export class KeyManager {
UndoManager.Redo();
break;
case 'p':
- Doc.ActiveTool = InkTool.Write;
+ Doc.ActiveInk = InkInkTool.Write;
+ Doc.ActiveTool = InkTool.Ink;
break;
default:
}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 270266a94..5199eb02b 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -470,14 +470,13 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
className="inkStroke"
style={{
transform: isInkMask ? `rotate(-${NumCast(this._props.LocalRotation?.() ?? 0)}deg) translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined,
- // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset',
cursor: this._props.isSelected() ? 'default' : undefined,
}}
{...interactions}>
{clickableLine(this.onPointerDown, isInkMask)}
{isInkMask ? null : inkLine}
</svg>
- {!closed || this.dataDoc[this.fieldKey + '_showLabel'] === false || (!RTFCast(this.dataDoc.text)?.Text && !this.dataDoc[this.fieldKey + '_showLabel'] && (!this._props.isSelected() || Doc.UserDoc().activeInkHideTextLabels)) ? null : (
+ {!closed || this.dataDoc[this.fieldKey + '_showLabel'] === false || (!RTFCast(this.dataDoc.text)?.Text && !this.dataDoc[this.fieldKey + '_showLabel'] && (!this._props.isSelected() || Doc.UserDoc().activeHideTextLabels)) ? null : (
<div
className="inkStroke-text"
style={{
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index a543b4875..3a8a38073 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -214,7 +214,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
// if (this._showPalette === false) AnnotationPalette.Instance.resetPalette(true);
};
togglePen = () => {
- Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Ink ? InkTool.None : InkTool.Ink;
};
toggleExplore = () => SnappingManager.SetExploreMode(!SnappingManager.ExploreMode);
@@ -332,7 +332,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
{toggleBtn('lightboxView-navBtn', 'toggle reading view', BoolCast(this._doc?._layout_fitWidth), 'book-open', 'book', this.toggleFitWidth)}
{toggleBtn('lightboxView-tabBtn', 'open document in a tab', false, 'file-export', '', this.downloadDoc)}
{toggleBtn('lightboxView-paletteBtn', 'toggle annotation palette', this._showPalette === true, 'palette', '', this.togglePalette)}
- {toggleBtn('lightboxView-penBtn', 'toggle pen annotation', Doc.ActiveTool === InkTool.Pen, 'pen', '', this.togglePen)}
+ {toggleBtn('lightboxView-penBtn', 'toggle pen annotation', Doc.ActiveTool === InkTool.Ink, 'pen', '', this.togglePen)}
{toggleBtn('lightboxView-exploreBtn', 'toggle navigate only mode', SnappingManager.ExploreMode, 'globe-americas', '', this.toggleExplore)}
</div>
);
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 7266875c5..fa1123a2d 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -34,7 +34,7 @@ export interface MarqueeAnnotatorProps {
getPageFromScroll?: (top: number) => number;
finishMarquee: (x?: number, y?: number) => void;
anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
- anchorMenuFlashcard?: () => Promise<String>;
+ anchorMenuFlashcard?: () => Promise<string>;
anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
highlightDragSrcColor?: string;
}
@@ -220,7 +220,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
e.preventDefault();
e.stopPropagation();
let cropRegion: Doc | undefined;
- // eslint-disable-next-line no-return-assign
const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
const targetCreator = (/* annotationOn: Doc | undefined */) => this.props.anchorMenuCrop!(cropRegion, false)!;
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts
index 430455644..ab02c2d07 100644
--- a/src/client/views/PinFuncs.ts
+++ b/src/client/views/PinFuncs.ts
@@ -1,4 +1,4 @@
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
import { Copy, Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
@@ -16,7 +16,7 @@ export interface pinDataTypes {
scrollable?: boolean;
dataviz?: number[];
pannable?: boolean;
- type_collection?: boolean;
+ collectionType?: boolean;
inkable?: boolean;
filters?: boolean;
pivot?: boolean;
@@ -39,9 +39,14 @@ export interface PinProps {
pinData?: pinDataTypes;
}
-/// copies values from the targetDoc (which is the prototype of the pinDoc) to
-/// reserved fields on the pinDoc so that those values can be restored to the
-/// target doc when navigating to it.
+/**
+ * copies values from the targetDoc (which is the prototype of the pinDoc) to
+ * reserved fields on the pinDoc so that those values can be restored to the
+ * target doc when navigating to it.
+ * @param pinDoc Doc that will store pinned metadata
+ * @param pinProps description of props to pin
+ * @param targetDoc Doc that is being pinned
+ */
export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
const pinDoc = pinDocIn;
pinDoc.presentation = true;
@@ -60,7 +65,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
pinProps.pinData.scrollable ||
pinProps.pinData.temporal ||
pinProps.pinData.pannable ||
- pinProps.pinData.type_collection ||
+ pinProps.pinData.collectionType ||
pinProps.pinData.clippable ||
pinProps.pinData.datarange ||
pinProps.pinData.dataview ||
@@ -69,7 +74,7 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
const fkey = Doc.LayoutFieldKey(targetDoc);
if (pinProps.pinData.dataview) {
pinDoc.config_usePath = targetDoc[fkey + '_usePath'];
- pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data;
+ pinDoc.config_data = Field.Copy(targetDoc[fkey]);
}
if (pinProps.pinData.dataannos) {
const fieldKey = Doc.LayoutFieldKey(targetDoc);
@@ -113,8 +118,8 @@ export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) {
})
)
);
- if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection;
- if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField);
+ if (pinProps.pinData.collectionType) pinDoc.config_type_collection = targetDoc._type_collection;
+ if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField) ?? new List<string>();
if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField;
if (pinProps.pinData.pannable) {
pinDoc.config_panX = NumCast(targetDoc._freeform_panX);
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 1f3ad8444..87076bf65 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -78,8 +78,8 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
_height: 50,
_layout_fitWidth: true,
_layout_autoHeight: true,
- _text_fontSize: StrCast(Doc.UserDoc().fontSize),
- _text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
+ text_fontSize: StrCast(Doc.UserDoc().fontSize),
+ text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
});
Doc.SetSelectOnLoad(target);
FormattedTextBox.DontSelectInitialText = true;
diff --git a/src/client/views/StyleProp.ts b/src/client/views/StyleProp.ts
index 44d3bf757..56367e70b 100644
--- a/src/client/views/StyleProp.ts
+++ b/src/client/views/StyleProp.ts
@@ -19,7 +19,9 @@ export enum StyleProp {
FontColor = 'fontColor', // color o tet
FontSize = 'fontSize', // size of text font
FontFamily = 'fontFamily', // font family of text
- FontWeight = 'fontWeight', // font weight of text
+ FontWeight = 'fontWeight', // font weight of text (eg bold)
+ FontStyle = 'fontStyle', // font style of text (eg italic)
+ FontDecoration = 'fontDecoration', // text decoration of text (eg underline)
Highlighting = 'highlighting', // border highlighting
ContextMenuItems = 'contextMenuItems', // menu items to add to context menu
AnchorMenuItems = 'anchorMenuItems',
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 8859f6464..3a5f57908 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -25,6 +25,7 @@ import { styleProviderQuiz } from './StyleProviderQuiz';
import { StyleProp } from './StyleProp';
import './StyleProvider.scss';
import { TagsView } from './TagsView';
+import { InkInkTool } from '../../fields/InkField';
function toggleLockedPosition(doc: Doc) {
UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -171,7 +172,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(Doc.UserDoc().fontSize));
case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(Doc.UserDoc().fontFamily));
case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(Doc.UserDoc().fontWeight));
- case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, 'transparent')));
+ case StyleProp.FontStyle: return StrCast(doc?.[fieldKey + 'fontStyle'], StrCast(Doc.UserDoc().fontStyle));
+ case StyleProp.FontDecoration:return StrCast(doc?.[fieldKey + 'fontDecoration'], StrCast(Doc.UserDoc().fontDecoration));
+ case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, StrCast(doc?.backgroundColor, StrCast(Doc.UserDoc()[Doc.ActiveInk === InkInkTool.Highlight ? "inkHighlighterColor": "inkFillColor"], 'transparent'))));
case StyleProp.ShowCaption: return hideCaptions || doc?._type_collection === CollectionViewType.Carousel ? undefined: StrCast(doc?._layout_showCaption);
case StyleProp.TitleHeight: return Math.min(4,(docView?.().screenToViewTransform().Scale ?? 1)) * NumCast(Doc.UserDoc().headerHeight,30);
case StyleProp.ShowTitle: return (
@@ -251,7 +254,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
case DocumentType.PRES: docColor = docColor || 'transparent'; break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
- case DocumentType.RTF: docColor = docColor || Colors.LIGHT_GRAY; break;
+ case DocumentType.RTF: docColor = docColor || StrCast(Doc.UserDoc().textBackgroundColor, Colors.LIGHT_GRAY); break;
case DocumentType.LINK: docColor = (isAnchor ? docColor : undefined); break;
case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
case DocumentType.EQUATION: docColor = docColor || 'transparent'; break;
@@ -313,7 +316,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
? undefined // if it's a background & has a cluster color, make the shadow spread really big
: fieldKey.includes('_inline') // if doc is an inline document in a text box
? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0vw 0vw 0.1vw')}`
- : DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline)
+ :doc.rootDocument !== doc.embedContainer && DocCast(doc.embedContainer)?.type === DocumentType.RTF && !isInk() // if doc is embedded in a text document (but not an inline) and this isn't a simple text template (where the layoutDoc's rootDocument is its embed container)
? `${Colors.DARK_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
: StrCast(doc.layout_boxShadow, '');
}
diff --git a/src/client/views/StyleProviderQuiz.tsx b/src/client/views/StyleProviderQuiz.tsx
index 1f2ad1485..f07a2c803 100644
--- a/src/client/views/StyleProviderQuiz.tsx
+++ b/src/client/views/StyleProviderQuiz.tsx
@@ -66,7 +66,7 @@ export namespace styleProviderQuiz {
newCol.zIndex = 1000;
newCol.forceActive = true;
newCol.quiz = text;
- newCol[DocData].textTransform = 'none';
+ newCol[DocData][Doc.LayoutFieldKey(newCol) + '_transform'] = 'none';
Doc.AddDocToList(img.Document, '_quizBoxes', newCol);
img.addDocument(newCol);
// img._loading = false;
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
index f66f6062e..b7980d74e 100644
--- a/src/client/views/ViewBoxInterface.ts
+++ b/src/client/views/ViewBoxInterface.ts
@@ -24,7 +24,7 @@ export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React
promoteCollection?: () => void; // moves contents of collection to parent
updateIcon?: (usePanelDimensions?: boolean) => Promise<void>; // updates the icon representation of the document
getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
- restoreView?: (viewSpec: Doc) => boolean;
+ restoreView?: (viewSpec: Doc) => boolean; // DEPRECATED: do not use, it will go away. see PresBox.restoreTargetDocView
scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index b86dad9d7..0ba6b679c 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -13,18 +13,20 @@ import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } fr
import { URLField } from '../../../fields/URLField';
import { gptImageLabel } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { dropActionType } from '../../util/DropActionTypes';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable } from '../../util/UndoManager';
+import { PinDocView } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FocusViewOptions } from '../nodes/FocusViewOptions';
enum cardSortings {
Time = 'time',
@@ -49,14 +51,13 @@ export class CollectionCardView extends CollectionSubView() {
private _textToDoc = new Map<string, Doc>();
private _oldWheel: HTMLElement | null = null;
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
- private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
+ private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
@observable _forceChildXf = 0;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap<Doc, DocumentView>();
@observable _maxRowCount = 10;
@observable _docDraggedIndex: number = -1;
- @observable _curDoc: Doc | undefined = undefined;
constructor(props: SubCollectionViewProps) {
super(props);
@@ -163,10 +164,10 @@ export class CollectionCardView extends CollectionSubView() {
/**
* When in quiz mode, randomly selects a document
*/
- quizMode = action(() => {
+ quizMode = () => {
const randomIndex = Math.floor(Math.random() * this.childDocs.length);
- this._curDoc = this.childDocs[randomIndex];
- });
+ this.layoutDoc._card_curDoc = this.childDocs[randomIndex];
+ };
setHoveredNodeIndex = action((index: number) => {
if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
@@ -317,7 +318,7 @@ export class CollectionCardView extends CollectionSubView() {
(doc: Doc) => () =>
this._props.isContentActive?.() === false
? false
- : this._props.isDocumentActive?.() && this._curDoc === doc
+ : this._props.isDocumentActive?.() && this.curDoc() === doc
? true
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
@@ -345,7 +346,7 @@ export class CollectionCardView extends CollectionSubView() {
scriptContext={this}
focus={this.focus}
onDoubleClickScript={this.onChildDoubleClick}
- onClickScript={this._curDoc === doc ? undefined : this._clickScript}
+ onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript}
dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
showTags={BoolCast(this.layoutDoc.showChildTags)}
@@ -570,8 +571,8 @@ export class CollectionCardView extends CollectionSubView() {
* @param doc doc that will be animated away from center focus
*/
releaseCurDoc = action(() => {
- const selDoc = this._curDoc;
- this._curDoc = undefined;
+ const selDoc = this.curDoc();
+ this.layoutDoc._card_curDoc = undefined;
const cardDocView = DocumentView.getDocumentView(selDoc, this.DocumentView?.());
if (cardDocView && selDoc) {
DocumentView.DeselectView(cardDocView);
@@ -588,7 +589,7 @@ export class CollectionCardView extends CollectionSubView() {
* turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
*/
cardPointerUp = action((doc: Doc) => {
- if (this._curDoc === doc || this._dropped) {
+ if (this.curDoc() === doc || this._dropped) {
this._dropped = false;
} else {
this.releaseCurDoc(); // NOTE: the onClick script for the card will select the new card (ie, 'doc')
@@ -596,27 +597,36 @@ export class CollectionCardView extends CollectionSubView() {
});
focus = action((anchor: Doc, options: FocusViewOptions): Opt<number> => {
- const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
- if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
- options.didMove = true;
- const target = DocCast(anchor.annotationOn) ?? anchor;
- const index = docs.indexOf(target);
- index !== -1 && (this._curDoc = target);
+ const docs = DocListCast(this.Document[this.fieldKey]);
+ if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+ const foundDoc = DocCast(
+ anchor.config_card_curDoc,
+ docs.find(doc => doc === DocCast(anchor.annotationOn, anchor))
+ );
+ options.didMove = foundDoc !== this.curDoc() ? true : false;
+ options.didMove && (this.layoutDoc._card_curDoc = foundDoc);
+ }
return undefined;
});
+ getAnchor = (addAsAnnotation: boolean) => {
+ const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() });
+ PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+ addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+ return anchor;
+ };
+ addDocTab = this.addLinkedDocTab;
/**
* Actually renders all the cards
*/
@computed get renderCards() {
- console.log('CHILDPw = ' + this.childPanelWidth());
// Map sorted documents to their rendered components
return this.sortedDocs.map((doc, index) => {
const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
- const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this._curDoc);
+ const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this.curDoc());
- const translateToCenterIfActive = () => (doc === this._curDoc ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
+ const translateToCenterIfActive = () => (doc === this.curDoc() ? (cardsInRow / 2 - (index % this._maxRowCount)) * 100 - 50 : 0);
const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()),
@@ -625,15 +635,15 @@ export class CollectionCardView extends CollectionSubView() {
return (
<div
key={doc[Id]}
- className={`card-item${doc === this._curDoc ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+ className={`card-item${doc === this.curDoc() ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
onPointerUp={() => this.cardPointerUp(doc)}
style={{
width: this.childPanelWidth(),
height: 'max-content',
- transform: `translateY(${this.adjustCardYtoFitArch(doc === this._curDoc, index)}px)
+ transform: `translateY(${this.adjustCardYtoFitArch(doc === this.curDoc(), index)}px)
translateX(calc(${translateToCenterIfActive()}% + ${this.horizontalAdjustmentForPartialRows(index, cardsInRow)}px))
- rotate(${doc !== this._curDoc ? this.rotate(index) : 0}rad)
- scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
+ rotate(${doc !== this.curDoc()? this.rotate(index) : 0}rad)
+ scale(${doc === this.curDoc()? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
}} // prettier-ignore
onPointerEnter={() => this.setHoveredNodeIndex(index)}
onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
@@ -651,10 +661,10 @@ export class CollectionCardView extends CollectionSubView() {
isContentActive: emptyFunction,
ScreenToLocalTransform: this.contentScreenToLocalXf,
});
- answered = action(() => {
- this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
- });
- curDoc = () => this._curDoc;
+ answered = () => {
+ this.layoutDoc._card_curDoc = this.curDoc() ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(d => d === this.curDoc()) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+ };
+ curDoc = () => DocCast(this.layoutDoc._card_curDoc);
render() {
const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index a71cc43ba..c080ba27e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,6 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { returnZero } from '../../../ClientUtils';
import { Utils } from '../../../Utils';
@@ -8,14 +9,15 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { Transform } from '../../util/Transform';
+import { PinDocView } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCarousel3DView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { computedFn } from 'mobx-utils';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -30,6 +32,9 @@ export class CollectionCarousel3DView extends CollectionSubView() {
makeObservable(this);
}
+ componentDidMount(): void {
+ this._props.setContentViewBox?.(this);
+ }
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -70,33 +75,45 @@ export class CollectionCarousel3DView extends CollectionSubView() {
? false
: undefined
);
- contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().translate(0, (-(Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) / this.nativeScaling());
childScreenLeftToLocal = () =>
this.contentScreenToLocalXf()
- .translate(-(this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
+ .translate(
+ (-this.panelWidth() * (1 - this.sideScale)) / 2, //
+ (-this.panelHeight() * (1 - this.sideScale)) / 2
+ )
.scale(1 / this.sideScale);
childScreenRightToLocal = () =>
this.contentScreenToLocalXf()
- .translate(-2 * this.panelWidth() - (this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
+ .translate(
+ -2 * this.panelWidth() - (this.panelWidth() * (1 - this.sideScale)) / 2, //
+ (-this.panelHeight() * (1 - this.sideScale)) / 2
+ )
.scale(1 / this.sideScale);
childCenterScreenToLocal = () =>
this.contentScreenToLocalXf()
.translate(
-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, // Focused Doc is shifted right by 1/3 panel width then left by increased size percent of center * 1/2 * panel width / 3
- -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2
- ) // top is top margin % of panelHeight - increased size percent of center * panelHeight / 2
+ ((this.centerScale - 1) * this.panelHeight()) / 2
+ )
.scale(1 / this.centerScale);
focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
- const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
- if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
- options.didMove = true;
- const target = DocCast(anchor.annotationOn) ?? anchor;
- const index = docs.indexOf(target);
- index !== -1 && (this.layoutDoc._carousel_index = index);
+ const docs = DocListCast(this.Document[this.fieldKey]);
+ if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+ const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+ options.didMove = newIndex !== this.layoutDoc._carousel_index;
+ options.didMove && (this.layoutDoc._carousel_index = newIndex);
+ }
return undefined;
};
-
+ getAnchor = (addAsAnnotation: boolean) => {
+ const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number });
+ PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+ addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+ return anchor;
+ };
+ addDocTab = this.addLinkedDocTab;
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
const displayDoc = (child: Doc, dxf: () => Transform) => (
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 1f2bc908f..f714e2a00 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,12 +3,16 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
+import { PinDocView } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
@@ -27,6 +31,9 @@ export class CollectionCarouselView extends CollectionSubView() {
makeObservable(this);
}
+ componentDidMount(): void {
+ this._props.setContentViewBox?.(this);
+ }
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -67,6 +74,24 @@ export class CollectionCarouselView extends CollectionSubView() {
curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
+ focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
+ const docs = DocListCast(this.Document[this.fieldKey]);
+ if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+ const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+ options.didMove = newIndex !== this.layoutDoc._carousel_index;
+ options.didMove && (this.layoutDoc._carousel_index = newIndex);
+ }
+ return undefined;
+ };
+
+ getAnchor = (addAsAnnotation: boolean) => {
+ const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
+ PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+ addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+ return anchor;
+ };
+ addDocTab = this.addLinkedDocTab;
+
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
// first look for properties on the document in the carousel, then fallback to properties on the container
const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
@@ -113,6 +138,7 @@ export class CollectionCarouselView extends CollectionSubView() {
LayoutTemplateString={this._props.childLayoutString}
TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
childFilters={this.childDocFilters}
+ focus={this.focus}
hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
addDocument={this._props.addDocument}
ScreenToLocalTransform={this.contentScreenToLocalXf}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e1786d2c9..3e8138390 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -453,7 +453,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
}
}
- if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && Doc.ActiveTool !== InkTool.Ink) {
e.stopPropagation();
}
};
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 486c826b6..c3047e5fb 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable no-use-before-define */
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -59,7 +58,6 @@ export enum TrimScope {
@observer
export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
- // eslint-disable-next-line no-use-before-define
public static SelectingRegions: Set<CollectionStackedTimeline> = new Set();
public static StopSelecting() {
this.SelectingRegions.forEach(
@@ -171,7 +169,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
makeDocUnfiltered = (doc: Doc) => this.childDocList?.some(item => item === doc);
- getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+ getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(res => {
if (doc.hidden) options.didMove = !(doc.hidden = false);
const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv));
@@ -600,7 +598,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
pointerEvents: 'none',
}}>
<StackedTimelineAnchor
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
mark={d.anchor}
containerViewPath={this._props.containerViewPath}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 48aac3a68..0c059f729 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -27,6 +27,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
import { FieldViewProps } from '../nodes/FieldView';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FlashcardPracticeUI } from './FlashcardPracticeUI';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
@@ -130,6 +131,17 @@ export function CollectionSubView<X>() {
@computed get childDocList() {
return Cast(this.dataField, listSpec(Doc));
}
+
+ addLinkedDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+ const doc = toList(docsIn).lastElement();
+ const where = location.split(':')[0];
+ if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+ if (doc.hidden) doc.hidden = false;
+ if (!location.includes(OpenWhereMod.always)) return true;
+ }
+ return this._props.addDocTab(docsIn, location);
+ };
+
collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
// child filters apply to the descendants of the documents in this collection
@@ -519,6 +531,7 @@ export function CollectionSubView<X>() {
};
protected _sideBtnWidth = 35;
+ protected _sideBtnMaxPanelPct = 0.15;
@observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
/**
* How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
@@ -529,7 +542,7 @@ export function CollectionSubView<X>() {
* This takes the desired screen space size and converts into collection coordinates. It then returns the smaller of the converted
* size or a fraction of the collection view.
*/
- @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) && this._props.PanelWidth() > NumCast(this.layoutDoc._width)? 1: 0.25) * NumCast(this.layoutDoc.width, 1)); } // prettier-ignore
+ @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) && this._props.PanelWidth() > NumCast(this.layoutDoc._width)? 1: this._sideBtnMaxPanelPct) * NumCast(this.layoutDoc.width, 1)); } // prettier-ignore
/**
* This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
* Note, the scale factor does not allow for elements to grow larger than their native screen space size.
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 8a24db330..d75c633ac 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -52,7 +52,7 @@ export class CollectionTimeView extends CollectionSubView() {
title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as unknown as string, // title can take a functiono or a string
annotationOn: this.Document,
});
- PinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
+ PinDocView(anchor, { pinData: { collectionType: true, pivot: true, filters: true } }, this.Document);
if (addAsAnnotation) {
// when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
@@ -147,7 +147,6 @@ export class CollectionTimeView extends CollectionSubView() {
return (
<div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
fitContentsToBox={returnTrue}
@@ -257,7 +256,6 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
const pivotView = DocumentView.getDocumentView(pivotDoc);
if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
- // eslint-disable-next-line prefer-destructuring
pivotDoc._pivotField = filterVals[0];
}
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 9e9318c0a..c298bff22 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -58,7 +58,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
@computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
- @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+ @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
@@ -127,7 +127,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray');
const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
- return !this._props.allChildDocs().some(doc => doc._layout_isFlashcard) ? null : (
+ return !this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? null : (
<div
className="FlashcardPracticeUI-practiceModes"
style={{
@@ -179,7 +179,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
</div>
);
}
- tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._layout_isFlashcard) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
+ tryFilterOut = (doc: Doc) => (this.practiceMode && BoolCast(doc?._flashcardType) && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
render() {
return (
<div className="FlashcardPracticeUI">
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5bfdee1f5..60728877a 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -285,6 +285,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
static Activate = (tabDoc: Doc) => {
const tab = Array.from(CollectionDockingView.Instance?.tabMap ?? []).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue);
+ if (tab && tab.header.parent._activeContentItem === tab.contentItem) return false;
tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
return tab !== undefined;
};
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 6ad67a864..8530f7a23 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -1,4 +1,4 @@
-import { action, observable } from 'mobx';
+import { action, observable, untracked } from 'mobx';
import { CollectionFreeFormView } from '.';
import { intersectRect } from '../../../../Utils';
import { Doc, Opt } from '../../../../fields/Doc';
@@ -179,7 +179,9 @@ export class CollectionFreeFormClusters {
};
styleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
- if (doc && this.childDocs?.includes(doc))
+ // without untracked, every inquired style property for any Doc will be invalidated if a change is made to the collection's childDocs.
+ // this prevents that by assuming that a Doc is generally always (or never) a member of childDocs - if it's removed or added, then all of its properties get updated anyway.
+ if (doc && untracked(() => this.childDocs)?.includes(doc))
switch (property.split(':')[0]) {
case StyleProp.BackgroundColor:
{
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 5d8373fc7..8b9a3e0ec 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -29,7 +29,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
}
_firstDocPos = { x: 0, y: 0 };
- constructor(props: any) {
+ constructor(props: CollectionFreeFormInfoUIProps) {
super(props);
makeObservable(this);
this._currState = this.setupStates();
@@ -163,7 +163,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
return presentDocs;
}],
// eslint-disable-next-line no-use-before-define
- activePen: [() => activeTool() === InkTool.Pen, () => penMode],
+ activePen: [() => activeTool() === InkTool.Ink, () => penMode],
},
'documentation.png',
() => TopBar.Instance.FlipDocumentationIcon()
@@ -187,7 +187,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
const penMode = InfoState('You\'re in pen mode. Click and drag to draw your first masterpiece.', {
// activePen: [() => activeTool() === InkTool.Eraser, () => eraserMode],
- activePen: [() => activeTool() !== InkTool.Pen, () => viewedLink],
+ activePen: [() => activeTool() !== InkTool.Ink, () => viewedLink],
}); // prettier-ignore
// const eraserMode = InfoState('You\'re in eraser mode. Say goodbye to your first masterpiece.', {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d2bc8f2c2..f2a5780c5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -10,7 +10,7 @@ import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField';
+import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
@@ -36,11 +36,24 @@ import { ContextMenu } from '../../ContextMenu';
import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView, SetActiveInkColor, SetActiveInkWidth } from '../../nodes/DocumentView';
+import {
+ ActiveInkArrowEnd,
+ ActiveInkArrowStart,
+ ActiveInkDash,
+ ActiveEraserWidth,
+ ActiveInkFillColor,
+ ActiveInkBezierApprox,
+ ActiveInkColor,
+ 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, OpenWhereMod } from '../../nodes/OpenWhere';
+import { OpenWhere } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
import { AnnotationPalette } from '../../smartdraw/AnnotationPalette';
import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
@@ -389,7 +402,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return undefined;
};
- getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
+ getView = (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(res => {
if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false);
if (doc === this.Document) {
@@ -497,13 +510,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// prettier-ignore
const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
switch (Doc.ActiveTool) {
- case InkTool.Highlighter:
- case InkTool.Write:
- case InkTool.Pen:
+ case InkTool.Ink:
break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
- case InkTool.StrokeEraser:
- case InkTool.SegmentEraser:
- case InkTool.RadiusEraser:
+ case InkTool.Eraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, this.onEraserClick, hit !== -1);
@@ -543,7 +552,8 @@ 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.ActiveTool === InkTool.Write) {
+ if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent';
+ if (Doc.ActiveInk === InkInkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
}
@@ -606,7 +616,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const currPoint = { X: e.clientX, Y: e.clientY };
this._eraserPts.push([currPoint.X, currPoint.Y]);
this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ if (Doc.ActiveEraser === InkEraserTool.Radius) {
const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
strokeMap.forEach((intersects, stroke) => {
if (!this._deleteList.includes(stroke)) {
@@ -614,13 +624,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
const segments = this.radiusErase(stroke, intersects.sort());
- segments?.forEach(segment =>
- this.forceStrokeGesture(
- e,
- Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- )
- );
+ segments?.forEach(segment => {
+ const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
+ const bounds = InkField.getBounds(points);
+ 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];
+ });
+ this.addDocument(inkDoc);
+ });
}
stroke.layoutDoc.opacity = 0;
stroke.layoutDoc.dontIntersect = true;
@@ -632,7 +645,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- if (Doc.ActiveTool !== InkTool.StrokeEraser) {
+ if (Doc.ActiveEraser !== InkEraserTool.Stroke) {
// this._eraserLock++;
const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
const newStrokes = segments?.map(segment => {
@@ -1195,14 +1208,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
_height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ stroke_showLabel: !BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
inkWidth,
ActiveInkColor(),
ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
+ ActiveInkFillColor(),
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkDash(),
ActiveIsInkMask()
);
};
@@ -1235,14 +1248,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
y: B.y - inkWidth / 2,
_width: B.width + inkWidth,
_height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ stroke_showLabel: BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
inkWidth,
opts.autoColor ? stroke[1] : ActiveInkColor(),
ActiveInkBezierApprox(),
- stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
+ stroke[2] === 'none' ? ActiveInkFillColor() : stroke[2],
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkDash(),
ActiveIsInkMask()
);
this._drawing.push(inkDoc);
@@ -1596,21 +1609,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
case undefined:
case OpenWhere.lightbox:
- {
- const firstDoc = docs[0];
- if (this.layoutDoc._isLightbox) {
- this._lightboxDoc = firstDoc;
- return true;
- }
- if (firstDoc === this.Document || this.childDocList?.includes(firstDoc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(firstDoc)) {
- if (firstDoc.hidden) firstDoc.hidden = false;
- if (!location.includes(OpenWhereMod.always)) return true;
- }
+ if (this.layoutDoc._isLightbox) {
+ this._lightboxDoc = docs[0];
+ return true;
}
- break;
+ return this.addLinkedDocTab(docsIn, location);
default:
}
- return this._props.addDocTab(docs, location);
+ return this._props.addDocTab(docsIn, location);
});
getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
@@ -1750,7 +1756,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
PinDocView(
anchor,
- { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } },
+ { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, collectionType: true, filters: true } },
this.Document
);
@@ -2202,7 +2208,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
width: `${100 / this.nativeDimScaling}%`,
height: this._props.getScrollHeight?.() ?? `${100 / this.nativeDimScaling}%`,
}}>
- {Doc.ActiveTool === InkTool.RadiusEraser && this._showEraserCircle && (
+ {Doc.ActiveTool === InkTool.Eraser && Doc.ActiveEraser === InkEraserTool.Radius && this._showEraserCircle && (
<div
onPointerMove={this.onCursorMove}
style={{
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c865c681d..ddc50871d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -690,7 +690,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}}
style={{
overflow: StrCast(this._props.Document._overflow),
- cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
+ cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}
onScroll={e => {
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 6ffb0865a..7f519b065 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -188,7 +188,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
selectedCells={this.selectedCells}
selectedCol={this.selectedCol}
setColumnValues={this.setColumnValues}
- oneLine={BoolCast(this.schemaDoc?._singleLine)}
+ oneLine={BoolCast(this.schemaDoc?._schema_singleLine)}
menuTarget={this.schemaView.MenuTarget}
transform={() => {
const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 954c79f7d..40144c4ce 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -3,7 +3,7 @@ import { Colors } from 'browndash-components';
import { runInAction } from 'mobx';
import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
-import { InkTool } from '../../../fields/InkField';
+import { InkEraserTool, InkInkTool, InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
@@ -16,21 +16,20 @@ import { SnappingManager } from '../../util/SnappingManager';
import { UndoManager, undoable } from '../../util/UndoManager';
import { GestureOverlay } from '../GestureOverlay';
import { InkTranscription } from '../InkTranscription';
-import { InkingStroke } from '../InkingStroke';
import { PropertiesView } from '../PropertiesView';
import { CollectionFreeFormView } from '../collections/collectionFreeForm';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import {
ActiveEraserWidth,
- ActiveFillColor,
+ ActiveInkFillColor,
ActiveInkColor,
- ActiveInkHideTextLabels,
+ ActiveHideTextLabels,
ActiveInkWidth,
ActiveIsInkMask,
DocumentView,
- SetActiveFillColor,
+ SetActiveInkFillColor,
SetActiveInkColor,
- SetActiveInkHideTextLabels,
+ SetactiveHideTextLabels,
SetActiveInkWidth,
SetActiveIsInkMask,
SetEraserWidth,
@@ -60,20 +59,21 @@ ScriptingGlobals.add(function setView(view: string, getSelected: boolean) {
// eslint-disable-next-line prefer-arrow-callback
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 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 ActiveFillColor();
- }
- SetActiveFillColor(color ?? 'transparent');
+ if (checkResult) return defaultFill();
+ setDefaultFill(color ?? 'transparent');
} else if (selectedViews.length) {
if (checkResult) {
const selView = selectedViews.lastElement();
const fieldKey = selView.Document._layout_isSvg ? 'fillColor' : 'backgroundColor';
const layoutFrameNumber = Cast(selView.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
- return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] ?? 'transparent';
+ return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] || defaultFill();
}
- selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent');
+ setDefaultFill(color ?? 'transparent');
selectedViews.forEach(dv => {
const fieldKey = dv.Document._layout_isSvg ? 'fillColor' : 'backgroundColor';
const layoutFrameNumber = Cast(dv.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
@@ -92,10 +92,13 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
} else {
const selected = DocumentView.SelectedDocs().length ? DocumentView.SelectedDocs() : LinkManager.Instance.currentLink ? [LinkManager.Instance.currentLink] : [];
if (checkResult) {
- return selected.lastElement()?._backgroundColor ?? 'transparent';
+ return selected.lastElement()?._backgroundColor ?? defaultFill();
}
- SetActiveFillColor(color ?? 'transparent');
- selected.forEach(doc => { doc[DocData].backgroundColor = color; }); // prettier-ignore
+ if (!selected.length) setDefaultFill(color ?? 'transparent');
+ else
+ selected.forEach(doc => {
+ doc[DocData][doc._layout_isSvg ? 'fillColor' : 'backgroundColor'] = color;
+ });
}
return '';
});
@@ -295,10 +298,10 @@ ScriptingGlobals.add(function setTagFilter(tag: string, added: boolean, checkRes
}, '');
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize' | 'alignment', value: string | number, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highlight' | 'fontSize', value: string | number, checkResult?: boolean) {
const editorView = RichTextMenu.Instance?.TextView?.EditorView;
// prettier-ignore
- const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
+ const map: Map<'font'|'fontColor'|'highlight'|'fontSize', { checkResult: () => string | undefined; setDoc: () => void;}> = new Map([
['font', {
checkResult: () => RichTextMenu.Instance?.fontFamily,
setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontFamily'),
@@ -311,10 +314,6 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
checkResult: () => RichTextMenu.Instance?.fontColor,
setDoc: () => value && RichTextMenu.Instance?.setFontField(value.toString(), 'fontColor'),
}],
- ['alignment', {
- checkResult: () => RichTextMenu.Instance?.textAlign,
- setDoc: () => { value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value.toString() as "center"|"left"|"right"):(Doc.UserDoc().textAlign = value); },
- }],
['fontSize', {
checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
setDoc: () => {
@@ -334,7 +333,7 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
return undefined;
});
-type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
+type attrname = 'noAutoLink' | 'dictation' | 'fitBox' | 'bold' | 'italic' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => unknown }];
// eslint-disable-next-line prefer-arrow-callback
@@ -343,14 +342,10 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
const editorView = textView?.EditorView;
// prettier-ignore
const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
- [ where, { checkResult: () => editorView ? (where === 'vcent' ? RichTextMenu.Instance?.textVcenter ?? false:
- (RichTextMenu.Instance?.textAlign === where)):
- where === 'vcent' ? BoolCast(Doc.UserDoc()._layout_centered):
- (Doc.UserDoc().textAlign === where),
- toggle: () => { editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
- RichTextMenu.Instance?.align(editorView, editorView.dispatch, where)):
- where === 'vcent' ? Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
- (Doc.UserDoc().textAlign = where); }
+ [ where, { checkResult: () => (where === 'vcent' ? RichTextMenu.Instance?.textVcenter ?? false:
+ (RichTextMenu.Instance?.textAlign === where)),
+ toggle: () => { (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle():
+ RichTextMenu.Instance?.align(editorView, editorView?.dispatch, where)); }
}]); // prettier-ignore
// prettier-ignore
const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
@@ -360,16 +355,18 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
const attrs:attrfuncs[] = [
['dictation', { checkResult: () => !!textView?._recordingDictation,
toggle: () => textView && runInAction(() => { textView._recordingDictation = !textView._recordingDictation;} ) }],
+ ['fitBox', { checkResult: () => RichTextMenu.Instance?.fitBox ?? false,
+ toggle: () => RichTextMenu.Instance?.toggleFitBox()}],
['elide', { checkResult: () => false,
toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
['noAutoLink',{ checkResult: () => ((editorView && RichTextMenu.Instance?.noAutoLink) ?? false),
toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
['bold', { checkResult: () => (editorView ? RichTextMenu.Instance?.bold??false : (Doc.UserDoc().fontWeight === 'bold')),
toggle: editorView ? RichTextMenu.Instance?.toggleBold : () => { Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold'; }}],
- ['italics', { checkResult: () => (editorView ? RichTextMenu.Instance?.italics ?? false : (Doc.UserDoc().fontStyle === 'italics')),
- toggle: editorView ? RichTextMenu.Instance?.toggleItalics : () => { Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics'; }}],
- ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance?.underline ?? false: (Doc.UserDoc().textDecoration === 'underline')),
- toggle: editorView ? RichTextMenu.Instance?.toggleUnderline : () => { Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline'; } }]]
+ ['italic', { checkResult: () => (editorView ? RichTextMenu.Instance?.italic ?? false : (Doc.UserDoc().fontStyle === 'italic')),
+ toggle: editorView ? RichTextMenu.Instance?.toggleItalic : () => { Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italic' ? undefined : 'italic'; }}],
+ ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance?.underline ?? false: (Doc.UserDoc().fontDecoration === 'underline')),
+ toggle: editorView ? RichTextMenu.Instance?.toggleUnderline : () => { Doc.UserDoc().fontDecoration = Doc.UserDoc().fontDecoration === 'underline' ? undefined : 'underline'; } }]]
const map = new Map(attrs.concat(alignments).concat(listings));
if (checkResult) {
@@ -379,43 +376,46 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?:
return undefined;
});
-function setActiveTool(toolIn: InkTool | Gestures, keepPrim: boolean, checkResult?: boolean) {
+function setActiveTool(tool: InkTool | InkEraserTool | InkInkTool | Gestures, keepPrim: boolean, checkResult?: boolean) {
InkTranscription.Instance?.createInkGroup();
- const tool = toolIn === InkTool.Eraser ? Doc.UserDoc().activeEraserTool : toolIn;
if (checkResult) {
- return ((Doc.ActiveTool === tool || (Doc.UserDoc().activeEraserTool === tool && (tool === toolIn || Doc.ActiveTool === tool))) && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool
- ? GestureOverlay.Instance?.KeepPrimitiveMode || ![Gestures.Circle, Gestures.Line, Gestures.Rectangle].includes(tool as Gestures)
+ return Doc.ActiveTool === tool || Doc.ActiveEraser === tool || Doc.ActiveInk === tool || SnappingManager.InkShape === tool
+ ? true //SnappingManager.KeepGestureMode || ![Gestures.Circle, Gestures.Line, Gestures.Rectangle].includes(tool as Gestures)
: false;
}
runInAction(() => {
+ const eraserTool = tool === InkTool.Eraser ? Doc.ActiveEraser : [InkEraserTool.Stroke, InkEraserTool.Radius, InkEraserTool.Segment].includes(tool as InkEraserTool) ? (tool as InkEraserTool) : undefined;
+ const inkTool = tool === InkTool.Ink ? Doc.ActiveInk : [InkInkTool.Pen, InkInkTool.Write, InkInkTool.Highlight].includes(tool as InkInkTool) ? (tool as InkInkTool) : undefined;
if (GestureOverlay.Instance) {
- GestureOverlay.Instance.KeepPrimitiveMode = keepPrim;
+ SnappingManager.SetKeepGestureMode(keepPrim);
}
if (Object.values(Gestures).includes(tool as Gestures)) {
- if (GestureOverlay.Instance.InkShape === tool && !keepPrim) {
+ if (SnappingManager.InkShape === tool && !keepPrim) {
Doc.ActiveTool = InkTool.None;
- GestureOverlay.Instance.InkShape = undefined;
+ SnappingManager.SetInkShape(undefined);
} else {
- Doc.ActiveTool = InkTool.Pen;
- GestureOverlay.Instance.InkShape = tool as Gestures;
+ Doc.ActiveTool = InkTool.Ink;
+ SnappingManager.SetInkShape(tool as Gestures);
}
- } else if (tool) {
- if (Doc.UserDoc().ActiveTool === tool) {
+ } else if (eraserTool) {
+ if (Doc.ActiveTool === InkTool.Eraser && Doc.ActiveTool === tool) {
Doc.ActiveTool = InkTool.None;
} else {
- if ([InkTool.StrokeEraser, InkTool.RadiusEraser, InkTool.SegmentEraser].includes(tool as InkTool)) {
- Doc.UserDoc().activeEraserTool = tool;
- }
- // pen or eraser
- if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape && !keepPrim) {
- Doc.ActiveTool = InkTool.None;
- } else {
- Doc.ActiveTool = tool as InkTool;
- GestureOverlay.Instance.InkShape = undefined;
- }
+ Doc.ActiveEraser = eraserTool;
+ Doc.ActiveTool = InkTool.Eraser;
+ SnappingManager.SetInkShape(undefined);
+ }
+ } else if (inkTool) {
+ if (Doc.ActiveTool === InkTool.Ink && Doc.ActiveTool === tool) {
+ Doc.ActiveTool = InkTool.None;
+ } else {
+ Doc.ActiveInk = inkTool;
+ Doc.ActiveTool = InkTool.Ink;
+ SnappingManager.SetInkShape(undefined);
}
} else {
- Doc.ActiveTool = InkTool.None;
+ if ((Doc.ActiveTool === tool || !tool) && !keepPrim) Doc.ActiveTool = InkTool.None;
+ else Doc.ActiveTool = tool as InkTool;
}
});
return undefined;
@@ -425,44 +425,44 @@ ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode');
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function activeEraserTool() {
- return StrCast(Doc.UserDoc().activeEraserTool, InkTool.StrokeEraser);
+ return StrCast(Doc.UserDoc().activeEraserTool, InkEraserTool.Stroke);
}, 'returns the current eraser tool');
// toggle: Set overlay status of selected document
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: string | number, checkResult?: boolean) {
- const selected = DocumentView.SelectedDocs().lastElement() ?? Doc.UserDoc();
+ const selected = DocumentView.SelectedDocs().lastElement();
// prettier-ignore
const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
['inkMask', {
checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())),
setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; },
- setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
+ setMode: () => SetActiveIsInkMask(value ? true : false)
}],
['labels', {
- checkResult: () => ((selected?._stroke_showLabel ? BoolCast(selected[DocData].stroke_showLabel) : ActiveInkHideTextLabels())),
- setInk: (doc: Doc) => { doc[DocData].stroke_showLabel = !doc.stroke_showLabel; },
- setMode: () => selected?.type !== DocumentType.INK && SetActiveInkHideTextLabels(!ActiveInkHideTextLabels()),
+ checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_showLabel) : !ActiveHideTextLabels())),
+ setInk: (doc: Doc) => { doc[DocData].stroke_showLabel = value; },
+ setMode: () => SetactiveHideTextLabels(value? false : true),
}],
['fillColor', {
- checkResult: () => (selected?._layout_isSvg ? StrCast(selected[DocData].fillColor) : ActiveFillColor() ?? "transparent"),
+ checkResult: () => (selected?._layout_isSvg ? StrCast(selected[DocData].fillColor) : ActiveInkFillColor() ?? "transparent"),
setInk: (doc: Doc) => { doc[DocData].fillColor = StrCast(value); },
- setMode: () => SetActiveFillColor(StrCast(value)),
+ setMode: () => SetActiveInkFillColor(StrCast(value)),
}],
[ 'strokeWidth', {
checkResult: () => (selected?._layout_isSvg ? NumCast(selected[DocData].stroke_width) : ActiveInkWidth()),
setInk: (doc: Doc) => { doc[DocData].stroke_width = NumCast(value); },
- setMode: () => { SetActiveInkWidth(value.toString()); selected?.type === DocumentType.INK && setActiveTool( GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
+ setMode: () => SetActiveInkWidth(value.toString()),
}],
['strokeColor', {
checkResult: () => (selected?._layout_isSvg? StrCast(selected[DocData].color) : ActiveInkColor()),
setInk: (doc: Doc) => { doc[DocData].color = String(value); },
- setMode: () => { SetActiveInkColor(StrCast(value)); selected?.type === DocumentType.INK && setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
+ setMode: () => SetActiveInkColor(StrCast(value))
}],
[ 'eraserWidth', {
checkResult: () => ActiveEraserWidth() === 0 ? 1 : ActiveEraserWidth(),
setInk: (doc: Doc) => { },
- setMode: () => { SetEraserWidth(+value);},
+ setMode: () => SetEraserWidth(+value),
}]
]);
diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
index 3eb99f47a..8115bafbf 100644
--- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
+++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
import { action } from 'mobx';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
@@ -35,10 +33,10 @@ export function ButtonMenu() {
<div
className="newLightboxView-penBtn"
title="toggle pen annotation"
- style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
+ style={{ background: Doc.ActiveTool === InkTool.Ink ? 'white' : undefined }}
onClick={e => {
e.stopPropagation();
- Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Ink ? InkTool.None : InkTool.Ink;
}}
/>
<div
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index d51b1cd3a..3905403ea 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,8 @@
+import { Colors } from 'browndash-components';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { OmitKeys } from '../../../ClientUtils';
+import { DashColor, OmitKeys } from '../../../ClientUtils';
import { numberRange } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { TransitionTimer } from '../../../fields/DocSymbols';
@@ -296,12 +297,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transition: this.DataTransition(),
zIndex: this.ZIndex,
display: this.Width ? undefined : 'none',
+ mixBlendMode: !this.layoutDoc.disableMixBlend && DashColor(StrCast(this.layoutDoc[this.layoutDoc._layout_isSvg ? 'fillColor' : 'backgroundColor'], Colors.WHITE)).alpha() !== 1 ? 'multiply' : undefined,
}}>
{this.RenderCutoffProvider(this.Document) ? (
<div style={{ position: 'absolute', width: this.PanelWidth(), height: this.PanelHeight(), background: 'lightGreen' }} />
) : (
<DocumentView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore
Document={this._props.Document}
renderDepth={this._props.renderDepth}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index f6c33d6ba..672c189ba 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -63,8 +63,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
* @param useDoc doc to fill in instead of creating a Doc
* @returns the resulting flashcard Doc
*/
- public static createFlashcard(tuple: string, frontKey: string, backKey: string, useDoc?: Doc) {
- const [ktoken, atoken] = [ComparisonBox.ktoken, ComparisonBox.atoken];
+ public static createFlashcard(tuple3: string, frontKey: string, backKey: string, useDoc?: Doc) {
+ const [qtoken, ktoken, atoken] = [ComparisonBox.qtoken, ComparisonBox.ktoken, ComparisonBox.atoken];
+ const [title, tuple] = tuple3.split(qtoken);
const question = (tuple.includes(ktoken) ? tuple.split(ktoken)[0] : tuple).split(atoken)[0];
const rest = tuple.replace(question, '');
// prettier-ignore
@@ -82,7 +83,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
useDoc[DocData][backKey] = back;
return useDoc;
}
- return Docs.Create.FlashcardDocument('flashcard', front, back, { _width: 300, _height: 300 });
+ return Docs.Create.FlashcardDocument(title, front, back, { _width: 300, _height: 300 });
};
return keyword && keyword.toLowerCase() !== 'none' ? ComparisonBox.fetchImages(keyword).then(img => fillInFlashcard(img)) : fillInFlashcard();
}
@@ -95,12 +96,13 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
public static createFlashcardDeck(text: string, width: number, height: number, front: string, back: string) {
return Promise.all(
text
- .split(ComparisonBox.qtoken)
+ .toLowerCase()
+ .split(ComparisonBox.ttoken)
.filter(t => t)
.map(tuple => ComparisonBox.createFlashcard(tuple, front, back))
).then(docs => {
return Docs.Create.CarouselDocument(docs, {
- title: 'flashcard deck',
+ title: text,
_width: width,
_height: height,
_layout_fitWidth: false,
@@ -112,9 +114,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
private SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
- static qtoken = 'Question: ';
- static ktoken = 'Keyword: ';
- static atoken = 'Answer: ';
+ static qtoken = 'question: ';
+ static ktoken = 'keyword: ';
+ static atoken = 'answer: ';
+ static ttoken = 'title: ';
private _slideTiming = 200;
private _sideBtnWidth = 35;
private _closeRef = React.createRef<HTMLDivElement>();
@@ -183,7 +186,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
@computed get containerDoc() { return this._props.docViewPath().slice(-2)[0]?.Document; } // prettier-ignore
@computed get isQuizMode() { return this.containerDoc?.practiceMode === practiceMode.QUIZ; } // prettier-ignore
- @computed get isFlashcard() { return BoolCast(this.Document.layout_isFlashcard); } // prettier-ignore
+ @computed get isFlashcard() { return StrCast(this.Document.layout_flashcardType); } // prettier-ignore
@computed get frontKey() { return this._props.fieldKey + '_front'; } // prettier-ignore
@computed get backKey() { return this._props.fieldKey + '_back'; } // prettier-ignore
@computed get frontText() { return RTFCast(DocCast(this.dataDoc[this.frontKey]).text)?.Text; } // prettier-ignore
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 896048ab3..7925410e2 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -73,7 +73,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return <div className="dataVizBox-annotationLayer" style={{ height: this._props.PanelHeight(), width: this._props.PanelWidth() }} ref={this._annotationLayer} />;
}
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.Document._freeform_scale, 1) <= NumCast(this.Document.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
setupMoveUpEvents(
this,
e,
@@ -323,7 +323,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
() => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar')
);
};
- getView = async (doc: Doc, options: FocusViewOptions) => {
+ getView = (doc: Doc, options: FocusViewOptions) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
options.didMove = true;
this.toggleSidebar();
@@ -453,7 +453,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
onPointerDown = (e: React.PointerEvent): void => {
if ((this.Document._freeform_scale || 1) !== 1) return;
- if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
this._props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
index 16c016d6c..7c601185e 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
@@ -245,7 +245,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
}
updateIcons = (docs: Doc[]) => {
- console.log('called')
+ console.log('called');
docs.map(this.getIcon);
};
@@ -416,7 +416,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
const height = bottom - top;
const width = right - left;
const doc = !field.title.includes('$$')
- ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height / 2}` })
+ ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, text_fontSize: `${height / 2}` })
: Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/\$\$/g, ''), x: left, y: top });
return doc;
});
@@ -919,17 +919,17 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
@action setExpandedView = (info: { icon: ImageField; doc: Doc } | undefined) => {
if (info) {
const doc = info.doc;
- const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
- const newInfo = {icon: new ImageField(''), doc: wrapper}
+ const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: '' });
+ const newInfo = { icon: new ImageField(''), doc: wrapper };
this._expandedPreview = newInfo;
} else {
this._expandedPreview = info;
}
};
- get editingWindow(){
+ get editingWindow() {
const doc = this._expandedPreview?.doc ?? new Doc();
- const rendered =
+ const rendered = (
<div className="docCreatorMenu-expanded-template-preview">
<CollectionFreeFormView
Document={this._expandedPreview!.doc}
@@ -945,7 +945,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
removeDocument={returnFalse}
PanelWidth={() => this._menuDimensions.width - 10}
PanelHeight={() => this._menuDimensions.height - 60}
- ScreenToLocalTransform={() => new Transform(-this._pageX,-this._pageY, 1)}
+ ScreenToLocalTransform={() => new Transform(-this._pageX, -this._pageY, 1)}
renderDepth={5}
whenChildContentsActiveChanged={emptyFunction}
focus={emptyFunction}
@@ -961,14 +961,21 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
yPadding={0}
/>
</div>
-
+ );
return (
<div className="docCreatorMenu-expanded-template-preview">
- <div className="top-panel"/>
+ <div className="top-panel" />
{rendered}
<div className="right-buttons-panel">
- <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this.setUpButtonClick(e, () => {this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice()); this.setExpandedView(undefined)})}>
+ <button
+ className="docCreatorMenu-menu-button section-reveal-options top-right"
+ onPointerDown={e =>
+ this.setUpButtonClick(e, () => {
+ this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice());
+ this.setExpandedView(undefined);
+ })
+ }>
<FontAwesomeIcon icon="minimize" />
</button>
<button className="docCreatorMenu-menu-button section-reveal-options top-right-lower" onPointerDown={e => this.setUpButtonClick(e, () => this._expandedPreview && this._templateDocs.push(this._expandedPreview.doc))}>
@@ -976,7 +983,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
</button>
</div>
</div>
-
);
}
@@ -2260,11 +2266,11 @@ export class FieldUtils {
title: title,
x: coord.x,
y: coord.y,
- _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
+ text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
backgroundColor: opts.backgroundColor ?? '',
text_fontColor: opts.color,
contentBold: opts.fontBold,
- textTransform: opts.fontTransform,
+ text_transform: opts.fontTransform,
color: opts.color,
_layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
borderColor: opts.borderColor,
@@ -2306,7 +2312,7 @@ export class FieldUtils {
public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
- const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` });
+ const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, text_fontSize: `${height / 2}` });
return doc;
};
@@ -2359,4 +2365,3 @@ export class FieldUtils {
// }]
// };
// }
-
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a343b9a39..30f9e6363 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -380,7 +380,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
};
onPointerMove = (e: PointerEvent): void => {
- if (e.buttons !== 1 || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
+ if (e.buttons !== 1 || Doc.ActiveTool === InkTool.Ink) return;
if (!ClientUtils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
this.cleanupPointerEvents();
@@ -880,7 +880,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
background: this.backgroundBoxColor,
opacity: this.opacity,
cursor: Doc.ActiveTool === InkTool.None ? 'grab' : 'crosshair',
- color: StrCast(this.layoutDoc.color, 'inherit'),
+ color: StrCast(this.Document._color, 'inherit'),
fontFamily: StrCast(this.Document._text_fontFamily, 'inherit'),
fontSize: Cast(this.Document._text_fontSize, 'string', null),
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
@@ -981,12 +981,10 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
type: SpringType.GENTLE,
...springMappings.gentle,
};
- switch (StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect))) {
- case PresEffect.Expand: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Expand} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
- case PresEffect.Flip: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Flip} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
- case PresEffect.Rotate: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Rotate} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
- case PresEffect.Bounce: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Bounce} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
- case PresEffect.Roll: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={PresEffect.Roll} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
+ const presEffect = StrCast(presEffectDoc?.presentation_effect, StrCast(presEffectDoc?.followLinkAnimEffect));
+ switch (presEffect) {
+ case PresEffect.Expand: case PresEffect.Flip: case PresEffect.Rotate: case PresEffect.Bounce:
+ case PresEffect.Roll: return <SpringAnimation doc={root} startOpacity={0} dir={dir} presEffect={presEffect} springSettings={timingConfig}>{renderDoc}</SpringAnimation>
// case PresEffect.Fade: return <SlideEffect doc={root} dir={dir} presEffect={PresEffect.Fade} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}>{renderDoc}</SlideEffect>
case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>
// keep as preset, doesn't really make sense with spring config
@@ -1562,55 +1560,45 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
} else func();
}
}
-
-export function ActiveFillColor(): string {
- const dv = DocumentView.Selected().lastElement() ?.Document._layout_isSvg ? DocumentView.Selected().lastElement() : undefined;
- return StrCast(dv?.Document.fillColor, StrCast(ActiveInkPen()?.activeFillColor, ""));
-} // prettier-ignore
-export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore
-export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore
-export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore
-export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore
-export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore
-export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore
-export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore
-export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore
-export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore
-export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore
-export function ActiveEraserWidth(): number { return Number(ActiveInkPen()?.eraserWidth ?? 25); } // prettier-ignore
-
+export function ActiveHideTextLabels(): boolean { return BoolCast(Doc.UserDoc().activeHideTextLabels, false); } // prettier-ignore
+export function ActiveIsInkMask(): boolean { return BoolCast(Doc.UserDoc()?.activeIsInkMask, false); } // prettier-ignore
+export function ActiveEraserWidth(): number { return Number(Doc.UserDoc()?.activeEraserWidth ?? 25); } // prettier-ignore
+
+export function ActiveInkFillColor(): string { return StrCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}Fill`]); } // prettier-ignore
+export function ActiveInkColor(): string { return StrCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}Color`], 'black'); } // prettier-ignore
+export function ActiveInkArrowStart(): string { return StrCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}ArrowStart`], ''); } // prettier-ignore
+export function ActiveInkArrowEnd(): string { return StrCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}ArrowEnd`], ''); } // prettier-ignore
+export function ActiveInkArrowScale(): number { return NumCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}ArrowScale`], 1); } // prettier-ignore
+export function ActiveInkDash(): string { return StrCast(Doc.UserDoc()?.[`active${Doc.ActiveInk}Dash`], '0'); } // prettier-ignore
+export function ActiveInkWidth(): number { return Number(Doc.UserDoc()?.[`active${Doc.ActiveInk}Width`]); } // prettier-ignore
+export function ActiveInkBezierApprox(): string { return StrCast(Doc.UserDoc()[`active${Doc.ActiveInk}Bezier`]); } // prettier-ignore
+
+export function SetActiveIsInkMask(value: boolean) { Doc.UserDoc() && (Doc.UserDoc().activeIsInkMask = value); } // prettier-ignore
+export function SetactiveHideTextLabels(value: boolean) { Doc.UserDoc() && (Doc.UserDoc().activeHideTextLabels = value); } // prettier-ignore
+export function SetEraserWidth(width: number): void { Doc.UserDoc() && (Doc.UserDoc().activeEraserWidth = width); } // prettier-ignore
export function SetActiveInkWidth(width: string): void {
- !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width);
+ !isNaN(parseInt(width)) && Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}Width`] = width);
}
-export function SetActiveBezierApprox(bezier: string): void {
- ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier);
+export function SetActiveInkBezierApprox(bezier: string): void {
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}Bezier`] = isNaN(parseInt(bezier)) ? '' : bezier);
}
export function SetActiveInkColor(value: string) {
- ActiveInkPen() && (ActiveInkPen().activeInkColor = value);
-}
-export function SetActiveIsInkMask(value: boolean) {
- ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value);
-}
-export function SetActiveInkHideTextLabels(value: boolean) {
- ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value);
-}
-export function SetActiveFillColor(value: string) {
- ActiveInkPen() && (ActiveInkPen().activeFillColor = value);
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}Color`] = value);
}
-export function SetActiveArrowStart(value: string) {
- ActiveInkPen() && (ActiveInkPen().activeArrowStart = value);
+export function SetActiveInkFillColor(value: string) {
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}Fill`] = value);
}
-export function SetActiveArrowEnd(value: string) {
- ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value);
+export function SetActiveInkArrowStart(value: string) {
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}ArrowStart`] = value);
}
-export function SetActiveArrowScale(value: number) {
- ActiveInkPen() && (ActiveInkPen().activeArrowScale = value);
+export function SetActiveInkArrowEnd(value: string) {
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}ArrowEnd`] = value);
}
-export function SetActiveDash(dash: string): void {
- !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash);
+export function SetActiveInkArrowScale(value: number) {
+ Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}ArrowScale`] = value);
}
-export function SetEraserWidth(width: number): void {
- ActiveInkPen() && (ActiveInkPen().eraserWidth = width);
+export function SetActiveInkDash(dash: string): void {
+ !isNaN(parseInt(dash)) && Doc.UserDoc() && (Doc.UserDoc()[`active${Doc.ActiveInk}`] = dash);
}
// eslint-disable-next-line prefer-arrow-callback
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index fefe25764..290c90d6e 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -122,7 +122,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
width: 'fit-content', // `${100 / scale}%`,
height: `${100 / scale}%`,
pointerEvents: !this._props.isSelected() ? 'none' : undefined,
- fontSize: StrCast(this.layoutDoc._text_fontSize),
+ fontSize: StrCast(this.Document._text_fontSize),
}}
onKeyDown={e => e.stopPropagation()}>
<EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" />
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.scss b/src/client/views/nodes/FontIconBox/FontIconBox.scss
index 2db285910..2405889cf 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.scss
@@ -10,6 +10,13 @@
height: 3px !important;
}
}
+.fonticonbox {
+ margin: auto;
+ width: 100%;
+ .formLabel {
+ height: 5px;
+ }
+}
.menuButton {
height: 100%;
display: flex;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index d4898eb3c..8c138c2ee 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -4,7 +4,7 @@ import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ClientUtils, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
+import { ClientUtils, DashColor, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction } from '../../../../Utils';
@@ -21,6 +21,8 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { OpenWhere } from '../OpenWhere';
import './FontIconBox.scss';
import TrailsIcon from './TrailsIcon';
+import { InkTool } from '../../../../fields/InkField';
+import { ScriptField } from '../../../../fields/ScriptField';
export enum ButtonType {
TextButton = 'textBtn',
@@ -126,7 +128,8 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
background={SnappingManager.userBackgroundColor}
numberDropdownType={type}
showPlusMinus={false}
- tooltip={this.label}
+ formLabel={(StrCast(this.Document.title).startsWith(' ') ? '\u00A0' : '') + StrCast(this.Document.title)}
+ tooltip={StrCast(this.Document.toolTip, this.label)}
type={Type.PRIM}
min={NumCast(this.dataDoc.numBtnMin, 0)}
max={NumCast(this.dataDoc.numBtnMax, 100)}
@@ -149,64 +152,79 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
};
/**
+ * Displays custom dropdown menu for fonts -- this is a HACK -- fix for generality, don't copy
+ */
+ handleFontDropdown = (script: () => string, buttonList: string[]) => {
+ // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ return {
+ buttonList,
+ jsx: undefined,
+ selectedVal: script(),
+ getStyle: (val: string) => ({ fontFamily: val }),
+ };
+ };
+ /**
+ * Displays custom dropdown menu for view selection -- this is a HACK -- fix for generality, don't copy
+ */
+ handleViewDropdown = (script: ScriptField, buttonList: string[]) => {
+ const selected = Array.from(script?.script.run({ _readOnly_: true }).result as Doc[]);
+ const noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Card, CollectionViewType.Carousel3D, CollectionViewType.Carousel, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+ return selected.length === 1 && selected[0].type === DocumentType.COL
+ ? {
+ buttonList: buttonList.filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value as CollectionViewType)),
+ getStyle: undefined,
+ selectedVal: StrCast(selected[0]._type_collection),
+ }
+ : {
+ jsx: selected.length ? (
+ <Popup
+ icon={<FontAwesomeIcon size="1x" icon={selected.length > 1 ? 'caret-down' : (Doc.toIcon(selected.lastElement()) as IconProp)} />}
+ text={selected.length === 1 ? ClientUtils.cleanDocumentType(StrCast(selected[0].type) as DocumentType) : selected.length + ' selected'}
+ type={Type.TERT}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userVariantColor}
+ popup={<SelectedDocView selectedDocs={selected} />}
+ fillWidth
+ />
+ ) : (
+ <Button
+ text={`${Doc.ActiveTool === InkTool.None ? 'Text box' : Doc.ActiveInk} defaults`} //
+ type={Type.TERT}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userVariantColor}
+ fillWidth
+ inactive
+ />
+ ),
+ };
+ };
+
+ /**
* Dropdown list
*/
@computed get dropdownListButton() {
const script = ScriptCast(this.Document.script);
-
- let noviceList: string[] = [];
- let text: string | undefined;
- let getStyle: (val: string) => { [key: string]: string } = () => ({});
- let icon: IconProp = 'caret-down';
- const isViewDropdown = script?.script.originalScript.startsWith('{ return setView');
- if (isViewDropdown) {
- const selected = Array.from(script?.script.run({ _readOnly_: true }).result as Doc[]);
- // const selected = DocumentView.SelectedDocs();
- if (selected.lastElement()) {
- if (StrCast(selected.lastElement().type) === DocumentType.COL) {
- text = StrCast(selected.lastElement()._type_collection);
- } else {
- if (selected.length > 1) {
- text = selected.length + ' selected';
- } else {
- text = ClientUtils.cleanDocumentType(StrCast(selected.lastElement().type) as DocumentType, '' as CollectionViewType);
- icon = Doc.toIcon(selected.lastElement());
- }
- return (
- <Popup
- icon={<FontAwesomeIcon size="1x" icon={icon} />}
- text={text}
- type={Type.TERT}
- color={SnappingManager.userColor}
- background={SnappingManager.userVariantColor}
- popup={<SelectedDocView selectedDocs={selected} />}
- fillWidth
- />
- );
- }
- } else {
- return <Button text="None Selected" type={Type.TERT} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} fillWidth inactive />;
- }
- noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Card, CollectionViewType.Carousel3D, CollectionViewType.Carousel, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
- } else {
- text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
- // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
- if (this.Document.title === 'Font') getStyle = (val: string) => ({ fontFamily: val }); // bcz: major hack to style the font dropdown items --- needs to become part of the dropdown's metadata
- }
+ const selectedFunc = () => script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
+ const { buttonList, selectedVal, getStyle, jsx } = (() => {
+ switch (this.Document.title) {
+ case 'Font': return this.handleFontDropdown(selectedFunc, this.buttonList);
+ case 'Perspective': return this.handleViewDropdown(script, this.buttonList);
+ default: return { buttonList: this.buttonList, selectedVal: selectedFunc(), jsx: undefined, getStyle: undefined };
+ } // prettier-ignore
+ })();
+ if (jsx) return jsx;
// Get items to place into the list
- const list: IListItemProps[] = this.buttonList
- .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value))
- .map(value => ({
- text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
- val: value,
- style: getStyle(value),
- // shortcut: '#',
- }));
+ const list: IListItemProps[] = buttonList.map(value => ({
+ text: typeof value === 'string' ? value.charAt(0).toUpperCase() + value.slice(1) : StrCast(DocCast(value)?.title),
+ val: value,
+ style: getStyle?.(value),
+ // shortcut: '#',
+ }));
return (
<Dropdown
- selectedVal={text}
+ selectedVal={selectedVal}
setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)}
color={SnappingManager.userColor}
background={SnappingManager.userVariantColor}
@@ -215,7 +233,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
dropdownType={DropdownType.SELECT}
onItemDown={this.dropdownItemDown}
items={list}
- tooltip={this.label}
+ tooltip={StrCast(this.Document.toolTip, this.label)}
fillWidth
/>
);
@@ -235,49 +253,53 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
const tooltip: string = StrCast(this.Document.toolTip);
return (
- <ColorPicker
- setSelectedColor={value => {
- if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
- this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
- }}
- setFinalColor={value => {
- this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
- this.colorBatch?.end();
- this.colorBatch = undefined;
- }}
- defaultPickerType="Classic"
- selectedColor={curColor}
- type={Type.PRIM}
- color={color}
- background={SnappingManager.userBackgroundColor}
- icon={this.Icon(color) ?? undefined}
- tooltip={tooltip}
- label={this.label}
- />
+ <div
+ onPointerDown={e => {
+ e.stopPropagation();
+ }}>
+ <ColorPicker
+ setSelectedColor={value => {
+ if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`);
+ this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ }}
+ setFinalColor={value => {
+ this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false });
+ this.colorBatch?.end();
+ this.colorBatch = undefined;
+ }}
+ defaultPickerType="Classic"
+ selectedColor={curColor}
+ type={Type.PRIM}
+ color={color}
+ background={SnappingManager.userBackgroundColor}
+ icon={this.Icon(color) ?? undefined}
+ tooltip={tooltip}
+ label={this.label}
+ />
+ </div>
);
}
@computed get multiToggleButton() {
- // Determine the type of toggle button
- const tooltip: string = StrCast(this.Document.toolTip);
+ const tooltip = StrCast(this.Document.toolTip);
const script = ScriptCast(this.Document.onClick)?.script;
const toggleStatus = script?.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean;
- // Colors
+
const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
- const background = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string;
const items = DocListCast(this.dataDoc.data);
const selectedItems = items.filter(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result).map(item => StrCast(item.toolType));
+
return (
<MultiToggle
- tooltip={`Toggle ${tooltip}`}
+ tooltip={`Click to Toggle ${tooltip} or select new option`}
type={Type.PRIM}
color={color}
- background={background === SnappingManager.userBackgroundColor ? undefined : background}
+ background={undefined}
multiSelect={true}
onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))}
isToggle={false}
toggleStatus={toggleStatus}
- label={this.label}
+ label={selectedItems.length === 1 ? selectedItems[0] : this.label}
items={items.map(item => ({
icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as IconProp} color={color} />,
tooltip: StrCast(item.toolTip),
@@ -290,7 +312,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
// it would be better to pas the 'added' flag to the callback script, but our script generator from currentUserUtils makes it hard to define
// arbitrary parameter variables (but it could be done as a special case or with additional effort when creating the sript)
const itemsChanged = items.filter(item => (val instanceof Array ? val.includes(item.toolType as string | number) : item.toolType === val));
- itemsChanged.forEach(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, _added_: added, itemDoc, _readOnly_: false }));
+ itemsChanged.forEach(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, _added_: added, value: toggleStatus, itemDoc, _readOnly_: false }));
}}
/>
);
@@ -308,17 +330,19 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
const toggleStatus = (script?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean) ?? false;
// Colors
const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string;
- // const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor);
+ // bcz: ink shapes are tri-state - off, one-shot, and on. Need to update Toggle buttons to allow this and update currentUserUtils to set the tri-state on the Doc
+ // in the meantime, if the button matches a tool type that is not locked, we want to set the background color to something distinct.
+ const inkShapeHack = ((this.Document.toolType && this.Document.toolType === SnappingManager.InkShape) || this.Document.toolType === Doc.ActiveTool) && !SnappingManager.KeepGestureMode;
return (
<Toggle
tooltip={`Toggle ${tooltip}`}
toggleType={ToggleType.BUTTON}
- type={Type.PRIM}
+ type={inkShapeHack ? Type.TERT : Type.PRIM}
toggleStatus={toggleStatus}
text={buttonText}
color={color}
- // background={SnappingManager.userBackgroundColor}
+ background={inkShapeHack ? DashColor(SnappingManager.userBackgroundColor).darken(0.05).toString() : undefined}
icon={this.Icon(color)!}
label={this.label}
onPointerDown={e =>
@@ -392,7 +416,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
render() {
return (
- <div style={{ margin: 'auto', width: '100%' }} onContextMenu={this.specificContextMenu}>
+ <div className="fonticonbox" onContextMenu={this.specificContextMenu}>
{this.renderButton()}
</div>
);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 0dfc0ec28..ff879a2ab 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -338,7 +338,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get nativeSize() {
TraceMobx();
- if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0}
+ if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1);
@@ -465,13 +465,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
marqueeDown = (e: React.PointerEvent) => {
if (!this.dataDoc[this.fieldKey]) {
this.chooseImage();
- } else if (
- !e.altKey &&
- e.button === 0 &&
- NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) &&
- this._props.isContentActive() &&
- ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)
- ) {
+ } else if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) <= NumCast(this.dataDoc.freeform_scaleMin, 1) && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
setupMoveUpEvents(
this,
e,
diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss
index ca4b3d467..889cdc0ca 100644
--- a/src/client/views/nodes/LabelBox.scss
+++ b/src/client/views/nodes/LabelBox.scss
@@ -13,7 +13,6 @@
height: 100%;
border-radius: inherit;
//letter-spacing: 2px; // bcz: doesn't work with LabelBigText
- text-transform: uppercase;
overflow: hidden;
display: inline-block;
margin: auto;
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 94a9541f2..dcf9e1fed 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -1,19 +1,22 @@
import { Property } from 'csstype';
-import { action, computed, makeObservable } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import * as textfit from 'textfit';
-import { Field, FieldType } from '../../../fields/Doc';
-import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { Doc, Field } from '../../../fields/Doc';
+import { NumCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
+import { undoable } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { FieldView, FieldViewProps } from './FieldView';
import './LabelBox.scss';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { RichTextMenu } from './formattedText/RichTextMenu';
@observer
export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -22,7 +25,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: NodeJS.Timeout | undefined;
- _divRef: HTMLDivElement | null = null;
+ private _divRef: HTMLDivElement | null = null;
+ private _reaction: IReactionDisposer | undefined;
constructor(props: FieldViewProps) {
super(props);
@@ -36,21 +40,29 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- @computed get Title() {
- return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title);
- }
-
- @computed get backgroundColor() {
- return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string;
- }
-
componentDidMount() {
this._props.setContentViewBox?.(this);
+ this._reaction = reaction(
+ () => this.Title,
+ () => document.activeElement !== this._divRef && this._forceRerender++
+ );
}
componentWillUnMount() {
this._timeout && clearTimeout(this._timeout);
+ this.setText(this._divRef?.innerText ?? '');
+ this._reaction?.();
}
+ @observable _forceRerender = 0;
+
+ @computed get Title() { return Field.toString(this.dataDoc[this.fieldKey]); } // prettier-ignore
+ @computed get backgroundColor() { return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; } // prettier-ignore
+ @computed get boxShadow() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string; } // prettier-ignore
+
+ setText = undoable((text: string) => {
+ this.dataDoc[this.fieldKey] = text;
+ }, 'set label text');
+
drop = (/* e: Event, de: DragManager.DropEvent */) => {
return false;
};
@@ -82,10 +94,11 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
const textfitParams = {
minFontSize: NumCast(this.layoutDoc._label_minFontSize, 1),
maxFontSize: NumCast(this.layoutDoc._label_maxFontSize, 100),
- multiLine: BoolCast(this.layoutDoc._singleLine, true) ? false : true,
- alignHoriz: true,
+ multiLine: r?.textContent?.includes('\n') ? true : false,
+ // hack because tetFit doesn't support align 'right', but we need mobx to invalidate, so treat null as false and set to right inline
+ alignHoriz: StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'center' ? true : StrCast(this.layoutDoc[this.fieldKey + '_align']) === 'right' ? (null as unknown as boolean) : false,
alignVert: true,
- detectMultiLine: true,
+ detectMultiLine: false,
};
if (r) {
if (!r.offsetHeight || !r.offsetWidth) {
@@ -94,65 +107,140 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._timeout = setTimeout(() => this.fitTextToBox(r));
return textfitParams;
}
+ 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']));
textfit(r, textfitParams);
}
return textfitParams;
};
+ resetCursor = (cranchor?: number) => {
+ if (this._divRef && (cranchor || this._divRef === document.activeElement)) {
+ const range = document.createRange();
+ const anchor = cranchor ?? this._divRef.childNodes.length;
+ const container = cranchor === undefined ? this._divRef : (this._divRef.firstChild?.firstChild ?? this._divRef);
+ range.setStart(container, anchor);
+ range.setEnd(container, anchor);
+ const sel = window.getSelection();
+ sel?.removeAllRanges();
+ sel?.addRange(range);
+ }
+ };
+
+ beforeInput = action((event: InputEvent) => {
+ const spanChild = this._divRef?.firstChild?.firstChild;
+ if (spanChild?.nodeName === '#text' && ['insertLineBreak', 'insertParagraph'].includes(event.inputType)) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const selection = document.getSelection();
+ if (selection && document.activeElement === event.target) {
+ const text = spanChild.textContent ?? '';
+ const cranchor = selection.anchorNode === this._divRef ? (selection.anchorOffset ? text.length : 0) : selection.anchorOffset;
+ const addReturnHack = text.length <= cranchor && text[text.length - 1] !== '\n' ? '\n\n' : '\n'; // not sure why, but need to add a second carriage return if typing enter at the end of the text
+ const splitText = text.substring(0, cranchor) + addReturnHack + text.substring(cranchor);
+ spanChild.textContent = splitText;
+ this.resetCursor(cranchor + addReturnHack.length);
+ }
+ // const span = document.createElement('span');
+ // span.innerHTML = '&#8203;';
+ // this._divRef!.append(span);
+ }
+ });
+ // .labelBox-mainButton > div > span:nth-child(2) {
+
+ /**
+ * When an IconButton is clicked, it will receive focus. However, we don't want that since we want or need that since we really want
+ * to maintain focus in the label's editing div (and cursor position). so this relies on IconButton's having a tabindex set to -1 so that
+ * we can march up the tree from the 'relatedTarget' to determine if the loss of focus was caused by a fonticonbox. If it is, we then
+ * restore focus
+ * @param e focusout event on the editing div
+ */
+ keepFocus = (e: FocusEvent) => {
+ if (e.relatedTarget instanceof HTMLElement && e.relatedTarget.tabIndex === -1) {
+ for (let ele: HTMLElement | null = e.relatedTarget; ele; ele = (ele as HTMLElement)?.parentElement) {
+ if ((ele as HTMLElement)?.className === 'fonticonbox') {
+ setTimeout(() => this._divRef?.focus());
+ break;
+ }
+ }
+ }
+ };
+
render() {
TraceMobx();
const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
- const label = this.Title.startsWith('#') ? null : this.Title;
return (
- <div key={label?.length} className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) as string }}>
+ <div className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this.boxShadow }}>
<div
className="labelBox-mainButton"
style={{
backgroundColor: this.backgroundColor,
- // fontSize: StrCast(this.layoutDoc._text_fontSize),
- color: StrCast(this.layoutDoc._color),
- fontFamily: StrCast(this.layoutDoc._text_fontFamily) || 'inherit',
+ color: StrCast(this.layoutDoc._text_fontColor, StrCast(this.layoutDoc._color)),
+ fontFamily: StrCast(this.layoutDoc._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
letterSpacing: StrCast(this.layoutDoc.letterSpacing),
- textTransform: StrCast(this.layoutDoc.textTransform) as Property.TextTransform,
+ textTransform: StrCast(this.layoutDoc[this.fieldKey + '_transform']) as Property.TextTransform,
paddingLeft: NumCast(this.layoutDoc._xPadding),
paddingRight: NumCast(this.layoutDoc._xPadding),
paddingTop: NumCast(this.layoutDoc._yPadding),
paddingBottom: NumCast(this.layoutDoc._yPadding),
width: this._props.PanelWidth(),
height: this._props.PanelHeight(),
- whiteSpace: 'multiLine' in boxParams && boxParams.multiLine ? 'pre-wrap' : 'pre',
+ whiteSpace: boxParams.multiLine ? 'pre-wrap' : 'pre',
}}>
<div
+ key={this._forceRerender}
style={{
width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding),
height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding),
outline: 'unset !important',
}}
- onKeyDown={action(e => {
+ onKeyDown={e => {
e.stopPropagation();
- })}
+ }}
onKeyUp={action(e => {
e.stopPropagation();
- this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
- setTimeout(() => this._props.select(false));
+ const text = this._divRef?.firstChild;
+ if (text && (text as HTMLElement)?.nodeType === 3) {
+ this._divRef?.removeChild(text);
+ this._divRef?.firstChild?.appendChild(text);
+ this.resetCursor();
+ }
+ this.fitTextToBox(this._divRef);
})}
+ onFocus={() => {
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, this.dataDoc);
+ this._divRef?.removeEventListener('focusout', this.keepFocus);
+ this._divRef?.addEventListener('focusout', this.keepFocus);
+ }}
onBlur={() => {
- this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? '';
+ this._divRef?.removeEventListener('focusout', this.keepFocus);
+ this.setText(this._divRef?.innerText ?? '');
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
+ FormattedTextBox.LiveTextUndo?.end();
+ 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>`,
}}
contentEditable={this._props.onClickScript?.() ? undefined : true}
ref={r => {
+ this._divRef?.removeEventListener('beforeinput', this.beforeInput);
this._divRef = r;
- this.fitTextToBox(r);
- if (this._props.isSelected() && this._divRef) {
- const range = document.createRange();
- range.setStart(this._divRef, this._divRef.childNodes.length);
- range.setEnd(this._divRef, this._divRef.childNodes.length);
- const sel = window.getSelection();
- sel?.removeAllRanges();
- sel?.addRange(range);
+ if (this._divRef) {
+ this._divRef.addEventListener('beforeinput', this.beforeInput);
+
+ if (Doc.SelectOnLoad === this.Document) {
+ Doc.SelectOnLoad = undefined;
+ this._divRef.focus();
+ }
+ this.fitTextToBox(this._divRef);
+ if (this.Title) {
+ this.resetCursor();
+ }
}
- }}>
- {label}
- </div>
+ }}
+ />
</div>
</div>
);
@@ -161,9 +249,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, {
layout: { view: LabelBox, dataField: 'title' },
- options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+ options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
});
Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, {
layout: { view: LabelBox, dataField: 'title' },
- options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true },
+ options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, title_align: 'center', title_transform: 'uppercase' },
});
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c66f7c726..4a436319b 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -428,7 +428,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
};
- getView = async (doc: Doc, options: FocusViewOptions) => {
+ getView = (doc: Doc, options: FocusViewOptions) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
this.toggleSidebar();
options.didMove = true;
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index a4557196e..d5d8f8afa 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -383,7 +383,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
}
};
- getView = async (doc: Doc, options: FocusViewOptions) => {
+ getView = (doc: Doc, options: FocusViewOptions) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
this.toggleSidebar();
options.didMove = true;
@@ -732,7 +732,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
MapBoxContainer._rerenderDelay = 0;
}
this._rerenderTimeout = undefined;
- // eslint-disable-next-line operator-assignment
this.Document[DocCss] = this.Document[DocCss] + 1;
}), MapBoxContainer._rerenderDelay);
return null;
@@ -792,7 +791,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
.map(pushpin => (
<DocumentView
key={pushpin[Id]}
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
renderDepth={this._props.renderDepth + 1}
Document={pushpin}
@@ -830,7 +828,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
<div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
<SidebarAnnos
ref={this._sidebarRef}
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
fieldKey={this.fieldKey}
Document={this.Document}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 816d4a3b0..2e4c87d38 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -233,7 +233,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
};
- getView = async (doc: Doc, options: FocusViewOptions) => {
+ getView = (doc: Doc, options: FocusViewOptions) => {
if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
options.didMove = true;
this.toggleSidebar(false);
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index de51f6447..3bf7de2fe 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -776,7 +776,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// starts marquee selection
marqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._freeform_scale, 1) === 1 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
setupMoveUpEvents(
this,
e,
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a5788d02a..9ba4f8ead 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -44,6 +44,7 @@ import { LinkInfo } from './LinkDocPreview';
import { OpenWhere } from './OpenWhere';
import './WebBox.scss';
+// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CreateImage } = require('./WebBoxRenderer');
@observer
@@ -335,7 +336,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ele = document.createElement('div');
ele.append(contents);
}
- } catch (e) {
+ } catch {
/* empty */
}
const visibleAnchor = this._getAnchor(this._savedAnnotations, true);
@@ -506,7 +507,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
let href: Opt<string>;
try {
href = iframe?.contentWindow?.location.href;
- } catch (e) {
+ } catch {
runInAction(() => this._warning++);
href = undefined;
}
@@ -713,7 +714,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._webUrl = this._url;
}
}
- } catch (e) {
+ } catch {
console.log('WebBox URL error:' + this._url);
}
return true;
@@ -805,7 +806,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
this.marqueeing = [e.clientX, e.clientY];
- if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
setupMoveUpEvents(
this,
e,
@@ -855,7 +856,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
})}
contentEditable
onPointerDown={this.webClipDown}
- // eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: field.html }}
/>
);
@@ -1031,7 +1031,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
{this.inlineTextAnnotations
.sort((a, b) => NumCast(a.y) - NumCast(b.y))
.map(anno => (
- // eslint-disable-next-line react/jsx-props-no-spreading
<Annotation {...this._props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} containerDataDoc={this.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
@@ -1042,7 +1041,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
renderAnnotations = (childFilters: () => string[]) => (
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
setContentViewBox={this.setInnerContent}
NativeWidth={returnZero}
@@ -1217,7 +1215,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>
<SidebarAnnos
ref={this._sidebarRef}
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
fieldKey={this.fieldKey + '_' + this._urlHash}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29be8d285..905c69bb8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,6 +65,7 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
import { Property } from 'csstype';
+import { LabelBox } from '../LabelBox';
// import * as applyDevTools from 'prosemirror-dev-tools';
export interface FormattedTextBoxProps extends FieldViewProps {
@@ -134,10 +135,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
@observable _showSidebar = false;
- @computed get fontColor() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontColor) as string; } // prettier-ignore
- @computed get fontSize() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) as string; } // prettier-ignore
- @computed get fontFamily() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) as string; } // prettier-ignore
- @computed get fontWeight() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontWeight) as string; } // prettier-ignore
+ @computed get fontColor() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontColor) as string; } // prettier-ignore
+ @computed get fontSize() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) as string; } // prettier-ignore
+ @computed get fontFamily() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) as string; } // prettier-ignore
+ @computed get fontWeight() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontWeight) as string; } // prettier-ignore
+ @computed get fontStyle() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontStyle) as string; } // prettier-ignore
+ @computed get fontDecoration() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontDecoration) as string; } // prettier-ignore
set _recordingDictation(value) {
!this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? mediaState.Recording : undefined);
@@ -158,6 +161,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
@computed get titleHeight() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin) as number || 0; } // prettier-ignore
@computed get layout_autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins); } // prettier-ignore
@computed get sidebarKey() { return this.fieldKey + '_sidebar'; } // prettier-ignore
+ @computed get isLabel() { return this.dataDoc[this.fieldKey+"_fitBox"]; } // prettier-ignore
constructor(props: FormattedTextBoxProps) {
super(props);
@@ -165,7 +169,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._recordingStart = Date.now();
}
- public get EditorView() { return this._editorView; } // prettier-ignore
+ public get EditorView() { return this.isLabel ? undefined : this._editorView; } // prettier-ignore
// public makeAIFlashcards: () => void = unimplementedFunction;
public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;
@@ -175,10 +179,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
public RemoveLinkFromDoc(linkDoc?: Doc) {
this.unhighlightSearchTerms();
- const state = this._editorView?.state;
+ const state = this.EditorView?.state;
const a1 = DocCast(linkDoc?.link_anchor_1);
const a2 = DocCast(linkDoc?.link_anchor_2);
- if (state && a1 && a2 && this._editorView) {
+ if (state && a1 && a2 && this.EditorView) {
this.removeDocument(a1);
this.removeDocument(a2);
let allFoundLinkAnchors: { href: string; title: string; anchorId: string }[] = [];
@@ -188,7 +192,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return true;
});
if (allFoundLinkAnchors.length) {
- this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
+ this.EditorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
this.setupEditor(this.config, this.fieldKey);
}
@@ -197,16 +201,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// removes all the specified link references from the selection.
// NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
public RemoveAnchorFromSelection(allAnchors: { href: string; title: string; linkId: string; targetId: string }[]) {
- const state = this._editorView?.state;
- if (state && this._editorView) {
- this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
+ const state = this.EditorView?.state;
+ if (state && this.EditorView) {
+ this.EditorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
this.setupEditor(this.config, this.fieldKey);
}
}
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const rootDoc: Doc = Doc.isTemplateDoc(this._props.docViewPath().lastElement()?.Document) ? this.Document : DocCast(this.Document.rootDocument, this.Document);
- if (!pinProps && this._editorView?.state.selection.empty) return rootDoc;
+ if (!pinProps && this.EditorView?.state.selection.empty) return rootDoc;
const anchor = Docs.Create.ConfigDocument({ title: StrCast(rootDoc.title), annotationOn: rootDoc });
this.addDocument(anchor);
this._finishingLink = true;
@@ -263,7 +267,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
});
};
- AnchorMenu.Instance.Highlight = undoable((color: string) => this._editorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
+ AnchorMenu.Instance.Highlight = undoable((color: string) => this.EditorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'), 'highlght text');
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
/**
@@ -292,7 +296,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
AnchorMenu.Instance.setSelectedText(window.getSelection()?.toString() ?? '');
- const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
+ const coordsB = this.EditorView!.coordsAtPos(this.EditorView!.state.selection.to);
this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom);
let ele: Opt<HTMLDivElement>;
try {
@@ -309,7 +313,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
leafText = (node: Node) => {
- if (node.type === this._editorView?.state.schema.nodes.dashField) {
+ if (node.type === this.EditorView?.state.schema.nodes.dashField) {
const refDoc = !node.attrs.docId ? DocCast(this.Document.rootDocument, this.Document) : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
const fieldKey = StrCast(node.attrs.fieldKey);
return (
@@ -320,16 +324,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return '';
};
dispatchTransaction = (tx: Transaction) => {
- if (this._editorView && !this._editorView.isDestroyed) {
- const state = this._editorView.state.apply(tx);
- this._editorView.updateState(state);
+ if (this.EditorView && !this.EditorView.isDestroyed) {
+ const state = this.EditorView.state.apply(tx);
+ this.EditorView.updateState(state);
this.tryUpdateDoc(false);
}
};
tryUpdateDoc = (force: boolean) => {
- if (this._editorView) {
- const { state } = this._editorView;
+ if (this.EditorView) {
+ const { state } = this.EditorView;
const { dataDoc } = this;
const newText = state.doc.textBetween(0, state.doc.content.size, ' \n', this.leafText);
const newJson = JSON.stringify(state.toJSON());
@@ -372,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
// if we've deleted all the text in a note driven by a template, then restore the template data
dataDoc[this.fieldKey] = undefined;
- this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
+ this.EditorView.updateState(EditorState.fromJSON(this.config, JSON.parse(rtField.Data)));
ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, text: newText });
unchanged = false;
}
@@ -387,7 +391,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (jsonstring) {
const json = JSON.parse(jsonstring);
json.selection = state.toJSON().selection;
- this._editorView.updateState(EditorState.fromJSON(this.config, json));
+ this.EditorView.updateState(EditorState.fromJSON(this.config, json));
}
}
if (window.getSelection()?.isCollapsed && this._props.rootSelected?.()) {
@@ -407,8 +411,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
linkAnchor = anchor;
}
});
- if (this._editorView && linkTime) {
- const { state } = this._editorView;
+ if (this.EditorView && linkTime) {
+ const { state } = this.EditorView;
const node = state.selection.$from.node();
if (linkAnchor && node.type !== state.schema.nodes.code_block) {
const time = linkTime + Date.now() / 1000 - this._recordingStart / 1000;
@@ -416,7 +420,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const { from } = state.selection;
const value = state.schema.nodes.audiotag.create({ timeCode: time, audioId: linkAnchor[Id] });
const replaced = state.tr.insert(from - 1, value);
- this._editorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
+ this.EditorView.dispatch(replaced.setSelection(new TextSelection(replaced.doc.resolve(from + 1))));
}
}
};
@@ -431,16 +435,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
(Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
link.link_relationship === LinkManager.AutoKeywords
); // prettier-ignore
- if (this._editorView?.state.doc.textContent) {
- let { tr } = this._editorView.state;
- const { from, to } = this._editorView.state.selection;
- const { autoLinkAnchor } = this._editorView.state.schema.marks;
+ if (this.EditorView?.state.doc.textContent) {
+ let { tr } = this.EditorView.state;
+ const { from, to } = this.EditorView.state.selection;
+ const { autoLinkAnchor } = this.EditorView.state.schema.marks;
tr = tr.removeMark(0, tr.doc.content.size, autoLinkAnchor);
Doc.MyPublishedDocs.filter(term => term.title).forEach(term => {
tr = this.hyperlinkTerm(tr, term, newAutoLinks);
});
tr = tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)));
- this._editorView?.dispatch(tr);
+ this.EditorView?.dispatch(tr);
}
oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc));
};
@@ -450,11 +454,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (
!this._props.dontRegisterView && // (this.Document.isTemplateForField === "text" || !this.Document.isTemplateForField) && // only update the title if the data document's data field is changing
title.startsWith('-') &&
- this._editorView &&
+ this.EditorView &&
!this.dataDoc.title_custom &&
(Doc.LayoutFieldKey(this.Document) === this.fieldKey || this.fieldKey === 'text')
) {
- let node = this._editorView.state.doc;
+ let node = this.EditorView.state.doc;
while (node.firstChild && node.firstChild.type.name !== 'text') node = node.firstChild;
const str = node.textContent;
const prefix = '-';
@@ -477,7 +481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
*/
hyperlinkTerm = (trIn: Transaction, target: Doc, newAutoLinks: Set<Doc>) => {
let tr = trIn;
- const editorView = this._editorView;
+ const editorView = this.EditorView;
if (editorView && !Doc.AreProtosEqual(target, this.Document)) {
const autoLinkTerm = Field.toString(target.title as FieldType).replace(/^@/, '');
let alink: Doc | undefined;
@@ -553,18 +557,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
unhighlightSearchTerms = () => {
- if (this._editorView) {
- const { state } = this._editorView;
+ if (this.EditorView) {
+ const { state } = this.EditorView;
if (state) {
const mark = state.schema.mark(state.schema.marks.search_highlight);
const activeMark = state.schema.mark(state.schema.marks.search_highlight, { selected: true });
const end = state.doc.nodeSize - 2;
- this._editorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
+ this.EditorView.dispatch(state.tr.removeMark(0, end, mark).removeMark(0, end, activeMark));
}
}
};
adoptAnnotation = (start: number, end: number, mark: Mark) => {
- const view = this._editorView!;
+ const view = this.EditorView!;
const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: ClientUtils.CurrentUserEmail() });
view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark));
};
@@ -616,7 +620,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (added) {
draggedDoc._freeform_fitContentsToBox = true;
Doc.SetContainer(draggedDoc, this.Document);
- const view = this._editorView!;
+ const view = this.EditorView!;
try {
this._inDrop = true;
const pos = view.posAtCoords({ left: de.x, top: de.y })?.pos;
@@ -810,7 +814,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
let target: Element | HTMLElement | null = e.target as HTMLElement; // hrefs are stored on the database of the <a> node that wraps the hyerlink <span>
while (target && (!(target instanceof HTMLElement) || !target.dataset?.targethrefs)) target = target.parentElement;
- const editor = this._editorView;
+ const editor = this.EditorView;
if (editor && target && !(e.nativeEvent instanceof simMouseEvent ? e.nativeEvent.dash : false)) {
const hrefs = (target.dataset?.targethrefs as string)
?.trim()
@@ -996,8 +1000,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const c = this.ProseRef?.getElementsByTagName('img');
if (c) {
for (const i of c) {
- console.log(i);
-
// console.log(canvas.toDataURL());
// canvas.style.zIndex = '2000000';
// document.body.appendChild(canvas);
@@ -1022,8 +1024,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
animateRes = (resIndex: number, newText: string) => {
if (resIndex < newText.length) {
- const marks = this._editorView?.state.storedMarks ?? [];
- this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
+ const marks = this.EditorView?.state.storedMarks ?? [];
+ this.EditorView?.dispatch(this.EditorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
setTimeout(() => this.animateRes(resIndex + 1, newText), 20);
}
};
@@ -1035,8 +1037,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const res = await gptAPICall((this.dataDoc.text as RichTextField)?.Text, GPTCallType.COMPLETION);
if (!res) {
this.animateRes(0, 'Something went wrong.');
- } else if (this._editorView) {
- const { dispatch, state } = this._editorView;
+ } else if (this.EditorView) {
+ const { dispatch, state } = this.EditorView;
// for no animation, use: dispatch(state.tr.insertText(res));
// for animted response starting at end of text, use:
dispatch(state.tr.setSelection(Selection.atEnd(state.doc)));
@@ -1057,13 +1059,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
breakupDictation = () => {
- if (this._editorView && this._recordingDictation) {
+ if (this.EditorView && this._recordingDictation) {
this.stopDictation(/* true */);
this._break = true;
- const { state } = this._editorView;
+ const { state } = this.EditorView;
const { to } = state.selection;
const updated = TextSelection.create(state.doc, to, to);
- this._editorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
+ this.EditorView.dispatch(state.tr.setSelection(updated).insert(to, state.schema.nodes.paragraph.create({})));
if (this._recordingDictation) {
this.recordDictation();
}
@@ -1082,7 +1084,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
stopDictation = (/* abort: boolean */) => DictationManager.Controls.stop(/* !abort */);
setDictationContent = (value: string) => {
- if (this._editorView && this._recordingStart) {
+ if (this.EditorView && this._recordingStart) {
if (this._break) {
const textanchorFunc = () => {
const tanch = Docs.Create.ConfigDocument({ title: 'dictation anchor' });
@@ -1095,22 +1097,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const textanchor = Cast(link.link_anchor_1, Doc, null);
if (audioanchor) {
audioanchor.backgroundColor = 'tan';
- const audiotag = this._editorView.state.schema.nodes.audiotag.create({
+ const audiotag = this.EditorView.state.schema.nodes.audiotag.create({
timeCode: NumCast(audioanchor._timecodeToShow),
audioId: audioanchor[Id],
textId: textanchor[Id],
});
textanchor[DocData].title = 'dictation:' + audiotag.attrs.timeCode;
- const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag);
+ 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)));
+ this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size)));
}
}
}
- const { from } = this._editorView.state.selection;
+ const { from } = this.EditorView.state.selection;
this._break = false;
- const tr = this._editorView.state.tr.insertText(value);
- this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
+ const tr = this.EditorView.state.tr.insertText(value);
+ this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, from, tr.doc.content.size)).scrollIntoView());
}
};
@@ -1143,7 +1145,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
});
this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
- this._editorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
+ this.EditorView!.dispatch(tr.removeMark(selection.from, selection.to, splitter));
this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
anchor.text = selectedText;
anchor.text_html = this._selectionHTML ?? selectedText;
@@ -1156,7 +1158,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return anchorDoc ?? this.Document;
}
- getView = async (doc: Doc, options: FocusViewOptions) => {
+ getView = (doc: Doc, options: FocusViewOptions) => {
if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
if (!this.SidebarShown) {
this.toggleSidebar(false);
@@ -1177,7 +1179,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
let hadStart = start !== 0;
frag.forEach((node, index) => {
const examinedNode = findAnchorNode(node, editor);
- if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) {
+ if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this.EditorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this.EditorView?.state.schema.nodes.audiotag)) {
nodes.push(examinedNode.node);
!hadStart && (start = index + examinedNode.start);
hadStart = true;
@@ -1186,13 +1188,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return { frag: Fragment.fromArray(nodes), start };
};
const findAnchorNode = (node: Node, editor: EditorView) => {
- if (node.type === this._editorView?.state.schema.nodes.audiotag) {
+ if (node.type === this.EditorView?.state.schema.nodes.audiotag) {
if (node.attrs.textId === textAnchorId) {
return { node, start: 0 };
}
return undefined;
}
- if (node.type === this._editorView?.state.schema.nodes.dashDoc) {
+ if (node.type === this.EditorView?.state.schema.nodes.dashDoc) {
if (node.attrs.docId === textAnchorId) {
return { node, start: 0 };
}
@@ -1208,9 +1210,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
this._didScroll = false; // assume we don't need to scroll. if we do, this will get set to true in handleScrollToSelextion when we dispatch the setSelection below
- if (this._editorView && textAnchorId) {
- const { state } = this._editorView;
- const ret = findAnchorFrag(state.doc.content, this._editorView);
+ if (this.EditorView && textAnchorId) {
+ const { state } = this.EditorView;
+ const ret = findAnchorFrag(state.doc.content, this.EditorView);
const firstChild = ret.frag.childCount ? ret.frag.child(0) : undefined;
if (ret.start >= 0 && (ret.frag.size || (firstChild && [state.schema.nodes.dashDoc, state.schema.nodes.audioTag].includes(firstChild.type)))) {
@@ -1219,7 +1221,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (ret.frag.firstChild) {
selection = TextSelection.between(state.doc.resolve(ret.start), state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
}
- this._editorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+ this.EditorView.dispatch(state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
const escAnchorId = textAnchorId[0] >= '0' && textAnchorId[0] <= '9' ? `\\3${textAnchorId[0]} ${textAnchorId.substr(1)}` : textAnchorId;
addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: 'yellow', transform: 'scale(3)', 'transform-origin': 'left bottom' });
setTimeout(() => {
@@ -1307,15 +1309,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
},
incomingValue => {
- if (this._editorView && this._applyingChange !== this.fieldKey) {
+ if (this.EditorView && this._applyingChange !== this.fieldKey) {
if (incomingValue?.data) {
const updatedState = JSON.parse(incomingValue.data.Data);
- if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
- this._editorView.updateState(EditorState.fromJSON(this.config, updatedState));
+ if (JSON.stringify(this.EditorView.state.toJSON()) !== JSON.stringify(updatedState)) {
+ this.EditorView.updateState(EditorState.fromJSON(this.config, updatedState));
this.tryUpdateScrollHeight();
}
- } else if (this._editorView.state.doc.textContent !== incomingValue?.str) {
- selectAll(this._editorView.state, tx => this._editorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
+ } else if (this.EditorView.state.doc.textContent !== (incomingValue?.str ?? '')) {
+ selectAll(this.EditorView.state, tx => this.EditorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
}
}
},
@@ -1333,9 +1335,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
action(selected => {
if (selected && this.dataDoc[this.fieldKey + '_placeholder']) {
setTimeout(() => {
- selectAll(this._editorView!.state, (tx: Transaction) => {
- this._editorView?.dispatch(tx);
- this._editorView!.focus();
+ selectAll(this.EditorView!.state, (tx: Transaction) => {
+ this.EditorView?.dispatch(tx);
+ this.EditorView!.focus();
});
});
}
@@ -1343,12 +1345,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (FormattedTextBox._globalHighlights.has('Bold Text')) {
this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
}
- if (RichTextMenu.Instance?.view === this._editorView && !selected) {
+ if (((RichTextMenu.Instance?.view === this.EditorView && this.EditorView) || this.isLabel) && !selected) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
}
- if (this._editorView && selected) {
- RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc);
- setTimeout(this.autoLink, 20);
+ if (selected) {
+ RichTextMenu.Instance?.updateMenu(this.EditorView, undefined, this._props, this.dataDoc);
+ this.EditorView && setTimeout(this.autoLink, 20);
}
}),
{ fireImmediately: true }
@@ -1389,7 +1391,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
// DocCast(this.Document.image)._freeform_fitContentsToBox = true;
// Doc.SetContainer(DocCast(this.Document.image), this.Document);
- // const view = this._editorView!;
+ // const view = this.EditorView!;
// try {
// this._inDrop = true;
// const pos = view.posAtCoords({ left: 0, top: 0 })?.pos;
@@ -1436,7 +1438,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
addPdfReference = (pdfAnchorId: string) => {
- const view = this._editorView!;
+ const view = this.EditorView!;
if (pdfAnchorId) {
DocServer.GetRefField(pdfAnchorId).then(pdfAnchor => {
if (pdfAnchor instanceof Doc) {
@@ -1487,7 +1489,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]);
const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField);
if (this.ProseRef) {
- this._editorView?.destroy();
+ this.EditorView?.destroy();
this._editorView = new EditorView(this.ProseRef, {
state: rtfField?.Data ? EditorState.fromJSON(config, JSON.parse(rtfField.Data)) : EditorState.create(config),
handleScrollToSelection: editorView => {
@@ -1519,14 +1521,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (!rtfField) {
const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
const startupText = Field.toString(dataDoc[fieldKey] as FieldType);
- const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign)) || 'left';
+ const textAlign = StrCast(this.dataDoc[this.fieldKey + '_align'], StrCast(Doc.UserDoc().textAlign)) || 'left';
if (textAlign !== 'left') {
selectAll(this._editorView.state, tr => {
- this._editorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
+ this.EditorView?.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
});
}
if (startupText) {
- this._editorView?.dispatch(this._editorView.state.tr.insertText(startupText));
+ this.EditorView?.dispatch(this.EditorView.state.tr.insertText(startupText));
}
this.tryUpdateDoc(true);
}
@@ -1539,56 +1541,57 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
Doc.SetSelectOnLoad(undefined);
FormattedTextBox.SelectOnLoadChar = '';
}
- if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+ if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
this._props.select(false);
if (selLoadChar) {
- const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+ const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+ const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
- const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks);
- const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1);
+ const tr1 = this.EditorView.state.tr.setStoredMarks(storedMarks);
+ const tr2 = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
const tr = tr2.setStoredMarks(storedMarks);
- this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
+ this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
} else if (!FormattedTextBox.DontSelectInitialText) {
const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- selectAll(this._editorView.state, (tx: Transaction) => {
- this._editorView?.dispatch(tx.addStoredMark(mark));
+ selectAll(this.EditorView.state, (tx: Transaction) => {
+ this.EditorView?.dispatch(tx.addStoredMark(mark));
});
+ this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(new TextSelection(this.EditorView.state.doc.resolve(1))));
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
} else {
- const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
+ const $from = this.EditorView.state.selection.anchor ? this.EditorView.state.doc.resolve(this.EditorView.state.selection.anchor - 1) : undefined;
const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
+ const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
- const { tr } = this._editorView.state;
- this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
+ const { tr } = this.EditorView.state;
+ this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
}
}
if (selectOnLoad) {
FormattedTextBox.DontSelectInitialText = false;
- this._editorView!.focus();
+ this.EditorView!.focus();
}
if (this._props.isContentActive()) this.prepareForTyping();
- if (this._editorView && FormattedTextBox.PasteOnLoad) {
+ if (this.EditorView && FormattedTextBox.PasteOnLoad) {
const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
FormattedTextBox.PasteOnLoad = undefined;
pdfAnchorId && this.addPdfReference(pdfAnchorId);
}
- if (this._props.autoFocus) setTimeout(() => this._editorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
+ if (this._props.autoFocus) setTimeout(() => this.EditorView!.focus()); // not sure why setTimeout is needed but editing dashFieldView's doesn't work without it.
}
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
prepareForTyping = () => {
- if (this._editorView) {
+ if (this.EditorView) {
const { text, paragraph } = schema.nodes;
- const selNode = this._editorView.state.selection.$anchor.node();
- if (this._editorView.state.selection.from === 1 && this._editorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
+ const selNode = this.EditorView.state.selection.$anchor.node();
+ if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
- this._editorView.state.selection.empty && this._editorView.state.selection.from === 1 && this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
+ this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
}
}
};
@@ -1602,7 +1605,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
FormattedTextBox.LiveTextUndo?.end();
FormattedTextBox.LiveTextUndo = undefined;
this.unhighlightSearchTerms();
- this._editorView?.destroy();
+ this.EditorView?.destroy();
RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = 'none');
}
@@ -1681,26 +1684,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
setFocus = (ipos?: number) => {
- const pos = ipos ?? (this._editorView?.state.selection.$from.pos || 1);
- setTimeout(() => this._editorView?.dispatch(this._editorView.state.tr.setSelection(TextSelection.near(this._editorView.state.doc.resolve(pos)))), 100);
+ const pos = ipos ?? (this.EditorView?.state.selection.$from.pos || 1);
+ setTimeout(() => this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(TextSelection.near(this.EditorView.state.doc.resolve(pos)))), 100);
setTimeout(() => (this.ProseRef?.children?.[0] as HTMLElement).focus(), 200);
};
@action
onFocused = (e: React.FocusEvent): void => {
- // applyDevTools.applyDevTools(this._editorView);
+ // applyDevTools.applyDevTools(this.EditorView);
e.stopPropagation();
};
onClick = (e: React.MouseEvent): void => {
if (!this._props.isContentActive()) return;
- const editorView = this._editorView;
+ const editorView = this.EditorView;
const editorRoot = editorView?.root instanceof Document ? editorView.root : undefined;
if (editorView && (!this._forceUncollapse || editorRoot?.getSelection()?.isCollapsed)) {
// this is a hack to allow the cursor to be placed at the end of a document when the document ends in an inline dash comment. Apparently Chrome on Windows has a bug/feature which breaks this when clicking after the end of the text.
const pcords = editorView.posAtCoords({ left: e.clientX, top: e.clientY });
const node = pcords && editorView.state.doc.nodeAt(pcords.pos); // get what prosemirror thinks the clicked node is (if it's null, then we didn't click on any text)
if (pcords && node?.type === editorView.state.schema.nodes.dashComment) {
- this._editorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
+ this.EditorView!.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, pcords.pos + 2)));
e.preventDefault();
}
if (!node && this.ProseRef) {
@@ -1726,33 +1729,33 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
hitBulletTargets(x: number, y: number, collapse: boolean, highlightOnly: boolean, selectOrderedList: boolean = false) {
this._forceUncollapse = false;
clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
- const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
+ const clickPos = this.EditorView!.posAtCoords({ left: x, top: y });
const clickPosVal = clickPos?.pos || 1;
let olistPos = clickPosVal;
if (clickPos && olistPos && this._props.rootSelected?.()) {
- const clickNode = this._editorView?.state.doc.resolve(olistPos).node();
- const nodeBef = this._editorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
- olistPos = nodeBef?.type === this._editorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
- let $olistPos = this._editorView?.state.doc.resolve(olistPos);
- let olistNode = (nodeBef !== null || clickNode?.type === this._editorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
- if (olistNode?.type === this._editorView?.state.schema.nodes.list_item) {
+ const clickNode = this.EditorView?.state.doc.resolve(olistPos).node();
+ const nodeBef = this.EditorView?.state.doc.resolve(Math.max(0, olistPos - 1)).node();
+ olistPos = nodeBef?.type === this.EditorView?.state.schema.nodes.ordered_list ? olistPos - 1 : olistPos;
+ let $olistPos = this.EditorView?.state.doc.resolve(olistPos);
+ let olistNode = (nodeBef !== null || clickNode?.type === this.EditorView?.state.schema.nodes.list_item) && olistPos === clickPos?.pos ? clickNode : nodeBef;
+ if (olistNode?.type === this.EditorView?.state.schema.nodes.list_item) {
if ($olistPos && $olistPos.depth) {
olistNode = $olistPos.parent;
- $olistPos = this._editorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
+ $olistPos = this.EditorView?.state.doc.resolve($olistPos.start($olistPos.depth - 1));
}
}
- const maxSize = this._editorView?.state.doc.content.size ?? 0;
- const listPos = this._editorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
+ const maxSize = this.EditorView?.state.doc.content.size ?? 0;
+ const listPos = this.EditorView?.state.doc.resolve(Math.min(maxSize, clickPosVal === olistPos ? clickPosVal + 1 : clickPosVal));
const listNode = listPos?.node();
- if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list && listNode) {
+ if (olistNode && olistNode.type === this.EditorView?.state.schema.nodes.ordered_list && listNode) {
if (!highlightOnly) {
if (selectOrderedList) {
- this._editorView.dispatch(this._editorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
+ this.EditorView.dispatch(this.EditorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
} else {
const nodePos = clickPosVal - (olistPos === clickPosVal ? 0 : 1);
- if (this._editorView.state.doc.nodeAt(nodePos)) {
- const tr = this._editorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
- this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
+ if (this.EditorView.state.doc.nodeAt(nodePos)) {
+ const tr = this.EditorView.state.tr.setNodeMarkup(nodePos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
+ this.EditorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, nodePos)));
}
}
}
@@ -1772,19 +1775,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
onBlur = (e: React.FocusEvent) => {
if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
- const stordMarks = this._editorView?.state.storedMarks?.slice();
+ const stordMarks = this.EditorView?.state.storedMarks?.slice();
if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
this.autoLink();
- if (this._editorView?.state.tr) {
+ if (this.EditorView?.state.tr) {
const tr = stordMarks?.reduce((tr2, m) => {
tr2.addStoredMark(m);
return tr2;
- }, this._editorView.state.tr);
- tr && this._editorView.dispatch(tr);
+ }, this.EditorView.state.tr);
+ tr && this.EditorView.dispatch(tr);
}
}
}
- if (RichTextMenu.Instance?.view === this._editorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
+ if (RichTextMenu.Instance?.view === this.EditorView && !(this._props.isContentActive() || this._props.rootSelected?.())) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
}
@@ -1831,7 +1834,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
switch (e.key) {
case 'Escape':
- this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
+ this.EditorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
(document.activeElement as HTMLElement).blur?.();
DocumentView.DeselectAll();
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
@@ -1857,7 +1860,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.startUndoTypingBatch();
};
ondrop = (e: React.DragEvent) => {
- this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
+ this.EditorView?.dispatch(updateBullets(this.EditorView.state.tr, this.EditorView.state.schema));
e.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
};
onScroll = (e: React.UIEvent) => {
@@ -1872,7 +1875,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
tryUpdateScrollHeight = () => {
const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
- if (children && !SnappingManager.IsDragging) {
+ if (this.EditorView && children && !SnappingManager.IsDragging) {
const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
const toNum = (val: string) => Number(val.replace('px', ''));
const toHgt = (node: Element): number => {
@@ -2093,7 +2096,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
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);
const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._header_height}px' >
- return styleFromLayout?.height === '0px' ? null : (
+ return this.isLabel ? (
+ <LabelBox {...this._props} />
+ ) : styleFromLayout?.height === '0px' ? null : (
<div
className="formattedTextBox"
ref={r => {
@@ -2116,6 +2121,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
fontSize: this.fontSize,
fontFamily: this.fontFamily,
fontWeight: this.fontWeight,
+ fontStyle: this.fontStyle,
+ textDecoration: this.fontDecoration,
...styleFromLayout,
}}>
<div
@@ -2156,8 +2163,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
color: StrCast(this.layoutDoc.text_fontColor),
- fontWeight: `${this.layoutDoc.contentBold ? 'bold' : ''}`,
- textTransform: `${this.layoutDoc.textTransform}` as Property.TextTransform,
+ fontWeight: this.layoutDoc.contentBold ? 'bold' : '',
+ textTransform: StrCast(this.dataDoc[this.fieldKey + '_transform']) as Property.TextTransform,
}}
/>
</div>
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 7a8b72be0..3c84e5a10 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -349,7 +349,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMa
dispatch(tx4);
}
- if (view.state.selection.$anchor.nodeAfter?.type === schema.nodes.text && once) {
+ 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 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);
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 55e6a3a5b..62fbd23ea 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { lift, toggleMark, wrapIn } from 'prosemirror-commands';
import { Mark, MarkType } from 'prosemirror-model';
@@ -32,7 +32,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
private _linkToRef = React.createRef<HTMLInputElement>();
- layoutDoc: Doc | undefined;
+ dataDoc: Doc | undefined;
@observable public view?: EditorView & { TextView?: FormattedTextBox } = undefined;
public editorProps: FieldViewProps | AntimodeMenuProps | undefined;
@@ -41,7 +41,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private collapsed: boolean = false;
@observable private _noLinkActive: boolean = false;
@observable private _boldActive: boolean = false;
- @observable private _italicsActive: boolean = false;
+ @observable private _italicActive: boolean = false;
@observable private _underlineActive: boolean = false;
@observable private _strikethroughActive: boolean = false;
@observable private _subscriptActive: boolean = false;
@@ -49,6 +49,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private _activeFontSize: string = '13px';
@observable private _activeFontFamily: string = '';
+ @observable private _activeFitBox: boolean = false;
@observable private _activeListType: string = '';
@observable private _activeAlignment: string = 'left';
@@ -64,13 +65,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private currentLink: string | undefined = '';
@observable private showLinkDropdown: boolean = false;
- _reaction: IReactionDisposer | undefined;
constructor(props: AntimodeMenuProps) {
super(props);
makeObservable(this);
runInAction(() => {
RichTextMenu._instance.menu = this;
- this.updateMenu(undefined, undefined, props, this.layoutDoc);
+ this.updateMenu(undefined, undefined, props, this.dataDoc);
this._canFade = false;
this.Pinned = true;
});
@@ -89,8 +89,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get underline() {
return this._underlineActive;
}
- @computed get italics() {
- return this._italicsActive;
+ @computed get italic() {
+ return this._italicActive;
}
@computed get strikeThrough() {
return this._strikethroughActive;
@@ -101,6 +101,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get fontHighlight() {
return this._activeHighlightColor;
}
+ @computed get fitBox() {
+ return this._activeFitBox;
+ }
@computed get fontFamily() {
return this._activeFontFamily;
}
@@ -114,26 +117,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
return this._activeAlignment;
}
@computed get textVcenter() {
- return BoolCast(this.layoutDoc?._layout_centered);
- }
- _disposer: IReactionDisposer | undefined;
- componentDidMount() {
- // this._disposer = reaction(
- // () => DocumentView.Selected().slice(),
- // () => this.updateMenu(undefined, undefined, undefined, undefined)
- // );
- }
- componentWillUnmount() {
- this._disposer?.();
+ return BoolCast(this.dataDoc?._layout_centered, BoolCast(Doc.UserDoc().layout_centered));
}
@action
- public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, layoutDoc: Doc | undefined) {
+ public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, dataDoc: Doc | undefined) {
if (this._linkToRef.current?.getBoundingClientRect().width) {
return;
}
this.view = view;
- this.layoutDoc = layoutDoc;
+ this.dataDoc = dataDoc;
props && (this.editorProps = props);
// Don't do anything if the document/selection didn't change
@@ -147,12 +140,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const { activeSizes } = active;
const { activeColors } = active;
const { activeHighlights } = active;
- const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc();
+ const refDoc = DocumentView.Selected().lastElement()?.dataDoc ?? Doc.UserDoc();
const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey);
const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt));
this._activeListType = this.getActiveListStyle();
this._activeAlignment = this.getActiveAlignment();
+ this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0];
this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
@@ -181,7 +175,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
toggleMark(mark.type, mark.attrs)(state, dispatch);
}
}
- // this.updateMenu(this.view, undefined, undefined, this.layoutDoc);
}
};
@@ -195,8 +188,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
return node.attrs.align || 'left';
}
}
+ } else if (this.dataDoc) {
+ return StrCast(this.dataDoc.text_align) || 'left';
}
- return 'left';
+ return StrCast(Doc.UserDoc().textAlign) || 'left';
};
// finds font sizes and families in selection
@@ -285,7 +280,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this._noLinkActive = false;
this._boldActive = false;
- this._italicsActive = false;
+ this._italicActive = false;
this._underlineActive = false;
this._strikethroughActive = false;
this._subscriptActive = false;
@@ -295,7 +290,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
switch (mark.name) {
case 'noAutoLinkAnchor': this._noLinkActive = true; break;
case 'strong': this._boldActive = true; break;
- case 'em': this._italicsActive = true; break;
+ case 'em': this._italicActive = true; break;
case 'underline': this._underlineActive = true; break;
case 'strikethrough': this._strikethroughActive = true; break;
case 'subscript': this._subscriptActive = true; break;
@@ -330,6 +325,17 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.view.focus();
}
};
+ toggleFitBox = () => {
+ if (this.dataDoc) {
+ const doc = this.dataDoc;
+ (document.activeElement as HTMLElement)?.blur();
+ doc.text_fitBox = !doc.text_fitBox;
+ } else {
+ Doc.UserDoc().fitBox = !Doc.UserDoc().fitBox;
+ Doc.UserDoc().textAlign = Doc.UserDoc().fitBox ? 'center' : undefined;
+ }
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+ };
toggleBold = () => {
if (this.view) {
const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
@@ -346,7 +352,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
- toggleItalics = () => {
+ toggleItalic = () => {
if (this.view) {
const mark = this.view.state.schema.mark(this.view.state.schema.marks.em);
this.setMark(mark, this.view.state, this.view.dispatch, false);
@@ -354,8 +360,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
- setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
- if (this.TextView && this.view) {
+ setFontField = (value: string, fontField: 'fitBox' | 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
+ if (this.TextView && this.view && fontField !== 'fitBox') {
const { text, paragraph } = this.view.state.schema.nodes;
const selNode = this.view.state.selection.$anchor.node();
if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
@@ -367,9 +373,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs);
this.setMark(fmark, this.view.state, (tx: Transaction) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
this.view.focus();
+ } else if (this.dataDoc) {
+ this.dataDoc[`text_${fontField}`] = value;
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
} else {
Doc.UserDoc()[fontField] = value;
- // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
}
};
@@ -395,7 +404,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.view!.dispatch(tx3);
});
this.view.focus();
- // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
};
insertSummarizer(state: EditorState, dispatch: (tr: Transaction) => void) {
@@ -410,10 +418,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
vcenterToggle = () => {
- this.layoutDoc && (this.layoutDoc._layout_centered = !this.layoutDoc._layout_centered);
+ if (this.dataDoc) this.dataDoc._layout_centered = !this.dataDoc._layout_centered;
+ else Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered;
};
- align = (view: EditorView, dispatch: (tr: Transaction) => void, alignment: 'left' | 'right' | 'center') => {
- if (this.RootSelected) {
+ align = (view: EditorView | undefined, dispatch: undefined | ((tr: Transaction) => void), alignment: 'left' | 'right' | 'center') => {
+ if (view && dispatch && this.RootSelected) {
let { tr } = view.state;
view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos) => {
if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
@@ -425,6 +434,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
view.focus();
dispatch?.(tr);
+ } else {
+ if (this.dataDoc) {
+ this.dataDoc.text_align = alignment;
+ } else Doc.UserDoc().textAlign = alignment;
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
}
};
@@ -702,7 +716,7 @@ interface RichTextMenuPluginProps {
}
export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) {
- RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc);
+ RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.dataDoc);
}
render() {
return null;
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index f58434906..3d14ce4c0 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -121,7 +121,7 @@ export class RichTextRules {
annotationOn: textDoc,
_layout_fitWidth: true,
_layout_autoHeight: true,
- _text_fontSize: '9px',
+ text_fontSize: '9px',
title: 'inline comment',
});
textDocInline.title = inlineFieldKey; // give the annotation its own title
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7448fa898..06869717a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -14,7 +14,7 @@ import { StopEvent, lightOrDark, returnFalse, returnOne, setupMoveUpEvents } fro
import { emptyFunction, stringHash } from '../../../../Utils';
import { Doc, DocListCast, Field, FieldResult, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Animation, DocData, TransitionTimer } from '../../../../fields/DocSymbols';
-import { Copy } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
import { InkField } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { ObjectField } from '../../../../fields/ObjectField';
@@ -48,6 +48,7 @@ import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
import SlideEffect from './SlideEffect';
import { AnimationSettings, SpringSettings, SpringType, easeItems, effectItems, effectTimings, movementItems, presEffectDefaultTimings, springMappings, springPreviewColors } from './SpringUtils';
+import _ from 'lodash';
@observer
export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -381,7 +382,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (typeof res === 'string') {
const resObj = JSON.parse(res);
console.log('Parsed GPT Result ', resObj);
- // eslint-disable-next-line no-restricted-syntax
for (const key in resObj) {
if (resObj[key]) {
console.log('typeof property', typeof resObj[key]);
@@ -562,11 +562,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const datarange = [DocumentType.FUNCPLOT].includes(targetType);
const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG, DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
- const typeCollection = targetType === DocumentType.COL;
+ const collectionType = targetType === DocumentType.COL;
const filters = true;
const pivot = true;
const dataannos = false;
- return { scrollable, pannable, inkable, type_collection: typeCollection, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+ return { scrollable, pannable, inkable, collectionType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
}
@action
@@ -574,7 +574,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/* empty */
};
@action
- // eslint-disable-next-line default-param-last
static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: dataTypes, targetDoc?: Doc) {
const bestTarget = bestTargetView?.Document ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
if (!bestTarget) return undefined;
@@ -700,15 +699,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
changed = true;
}
}
- if ((pinDataTypes?.type_collection && activeItem.config_viewType !== undefined) || (!pinDataTypes && activeItem.config_viewType !== undefined)) {
- if (bestTarget._type_collection !== activeItem.config_viewType) {
- bestTarget._type_collection = activeItem.config_viewType;
+ if ((pinDataTypes?.collectionType && activeItem.config_card_curDoc !== undefined) || (!pinDataTypes && activeItem.config_card_curDoc !== undefined)) {
+ if (bestTarget._card_curDoc !== activeItem.config_card_curDoc) {
+ bestTarget._card_curDoc = activeItem.config_card_curDoc;
+ changed = true;
+ }
+ }
+ if ((pinDataTypes?.collectionType && activeItem.config_carousel_index !== undefined) || (!pinDataTypes && activeItem.config_carousel_index !== undefined)) {
+ if (bestTarget._carousel_index !== activeItem.config_carousel_index) {
+ bestTarget._carousel_index = activeItem.config_carousel_index;
+ changed = true;
+ }
+ }
+ if ((pinDataTypes?.collectionType && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
+ if (bestTarget._type_collection !== activeItem.config_type_collection) {
+ bestTarget._type_collection = activeItem.config_type_collection;
changed = true;
}
}
if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
- if (bestTarget.childFilters !== activeItem.config_docFilters) {
+ if (!_.isEqual(Array.from(StrListCast(bestTarget.childFilters)), Array.from(StrListCast(activeItem.config_docFilters)))) {
bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
changed = true;
}
@@ -1134,7 +1145,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return false;
}
} else if (doc.type !== DocumentType.PRES) {
- // eslint-disable-next-line operator-assignment
if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
doc.presentation_targetDoc = doc.createdFrom ?? doc; // dropped document will be a new embedding of an embedded document somewhere else.
doc.presentation_movement = PresMovement.Zoom;
@@ -1166,8 +1176,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
if (curDoc && curDoc === this.activeItem)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
<b>
{index + 1}. {StrCast(curDoc.title)})
</b>
@@ -1175,15 +1184,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
if (tagDoc)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
{index + 1}. {StrCast(curDoc.title)}
</div>
);
if (curDoc)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
{index + 1}. {StrCast(curDoc.title)}
</div>
);
@@ -1368,7 +1375,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const tagDoc = PresBox.targetRenderedDoc(doc);
const srcContext = Cast(tagDoc.embedContainer, Doc, null);
const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
- // eslint-disable-next-line react/no-array-index-key
<div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
<div className="pathOrder-frame">{index + 1}</div>
</div>
@@ -1619,7 +1625,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
- // eslint-disable-next-line camelcase
const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
array.forEach(curDoc => {
curDoc.presentation_transition = pt;
@@ -2017,7 +2022,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="presBox-effects">
{this.generatedAnimations.map((elem, i) => (
<div
- // eslint-disable-next-line react/no-array-index-key
key={i}
className="presBox-effect-container"
onClick={() => {
@@ -2612,13 +2616,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
createTemplate = (layout: string, input?: string) => {
const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
- const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _text_fontSize: '24pt' });
- const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _text_fontSize: '16pt' });
- const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _text_fontSize: '20pt' });
- const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _text_fontSize: '24pt' });
- const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _text_fontSize: '14pt' });
- const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _text_fontSize: '14pt' });
- const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _text_fontSize: '14pt' });
+ const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, text_fontSize: '24pt' });
+ const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, text_fontSize: '16pt' });
+ const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, text_fontSize: '20pt' });
+ const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, text_fontSize: '24pt' });
+ const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, text_fontSize: '14pt' });
+ const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, text_fontSize: '14pt' });
+ const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, text_fontSize: '14pt' });
// prettier-ignore
switch (layout) {
case 'blank': return Docs.Create.FreeformDocument([], { title: input || 'Blank slide', _width: 400, _height: 225, x, y });
@@ -3045,7 +3049,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="Slide">
{mode !== CollectionViewType.Invalid ? (
<CollectionView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
PanelWidth={this._props.PanelWidth}
PanelHeight={this.panelHeight}
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index d5f5f620c..a7e78bcea 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -572,18 +572,19 @@ export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
<div style={{ height: '80%' }}>
{this.heading(this.mode === GPTPopupMode.SORT ? 'SORTING' : 'QUIZ')}
<>
- {!this.cardsDoneLoading ? (
- <div className="content-wrapper">
- <div className="loading-spinner">
- <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
- {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+ {
+ !this.cardsDoneLoading ? (
+ <div className="content-wrapper">
+ <div className="loading-spinner">
+ <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
+ {this.loading ? <span>Loading...</span> : <span>Reading Cards...</span>}
+ </div>
</div>
- </div>
- ) : this.mode === GPTPopupMode.CARD ? (
- this.cardMenu()
- ) : (
- this.cardActual(this.mode)
- ) // Call the functions to render JSX
+ ) : this.mode === GPTPopupMode.CARD ? (
+ this.cardMenu()
+ ) : (
+ this.cardActual(this.mode)
+ ) // Call the functions to render JSX
}
</>
</div>
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 358557ad7..920c9ea8b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -377,7 +377,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) {
this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document);
}
- if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (!e.altKey && e.button === 0 && this._props.isContentActive() && Doc.ActiveTool !== InkTool.Ink) {
this._props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
this.isAnnotating = true;
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index b4635673c..d0f6566a5 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -21,7 +21,7 @@ import { SVGToBezier, SVGType } from '../../util/bezierFit';
import { InkingStroke } from '../InkingStroke';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { MarqueeView } from '../collections/collectionFreeForm';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
+import { ActiveInkArrowEnd, ActiveInkArrowStart, ActiveInkDash, ActiveInkFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
import './SmartDrawHandler.scss';
export interface DrawingOptions {
@@ -105,14 +105,14 @@ export class SmartDrawHandler extends ObservableReactComponent<object> {
y: bounds.top - inkWidth / 2,
_width: bounds.width + inkWidth,
_height: bounds.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ stroke_showLabel: !BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
inkWidth,
opts.autoColor ? stroke[1] : ActiveInkColor(),
ActiveInkBezierApprox(),
- stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
+ stroke[2] === 'none' ? ActiveInkFillColor() : stroke[2],
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkDash(),
ActiveIsInkMask()
);
drawing.push(inkDoc);
diff --git a/src/extensions/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts
index d6ffd3be3..fa8851bb3 100644
--- a/src/extensions/ExtensionsTypings.ts
+++ b/src/extensions/ExtensionsTypings.ts
@@ -1,6 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Array<T> {
+ /**
+ * returns the last element of the array or undefined
+ */
lastElement(): T;
+ /**
+ * if val is in the list, it returns its index, otherwise undefined;
+ * @param val
+ */
+ getIndex(val: T): number | undefined;
}
interface String {
diff --git a/src/extensions/Extensions_Array.ts b/src/extensions/Extensions_Array.ts
index a50fb330f..d61585e28 100644
--- a/src/extensions/Extensions_Array.ts
+++ b/src/extensions/Extensions_Array.ts
@@ -1,14 +1,13 @@
export default class ArrayExtension {
private readonly property: string;
- private readonly body: <T>(this: Array<T>) => any;
+ private readonly body: <T>(this: Array<T>, args: unknown) => unknown;
- constructor(property: string, body: <T>(this: Array<T>) => any) {
+ constructor(property: string, body: <T>(this: Array<T>, args: unknown) => unknown) {
this.property = property;
this.body = body;
}
assign() {
- // eslint-disable-next-line no-extend-native
Object.defineProperty(Array.prototype, this.property, {
value: this.body,
enumerable: false,
@@ -28,6 +27,11 @@ const extensions = [
}
return this[this.length - 1];
}),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ new ArrayExtension('getIndex', function (val: any) {
+ const index = this.indexOf(val);
+ return index === -1 ? undefined : index;
+ }),
];
function Assign() {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6ec195910..aef7bf330 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -14,7 +14,7 @@ import {
Initializing, Self, SelfProxy, TransitionTimer, UpdatingFromServer, Width
} from './DocSymbols'; // prettier-ignore
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
-import { InkTool } from './InkField';
+import { InkEraserTool, InkInkTool, InkTool } from './InkField';
import { List } from './List';
import { ObjectField, serverOpType } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
@@ -253,6 +253,10 @@ export class Doc extends RefField {
public static set ActivePage(val) { Doc.UserDoc().activePage = val; } // prettier-ignore
public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } // prettier-ignore
public static set ActiveTool(tool:InkTool){ Doc.UserDoc().activeTool = tool; } // prettier-ignore
+ public static get ActiveInk(): InkInkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkInkTool; } // prettier-ignore
+ public static set ActiveInk(tool:InkInkTool){ Doc.UserDoc().activeInkTool = tool; } // prettier-ignore
+ public static get ActiveEraser(): InkEraserTool { return StrCast(Doc.UserDoc().activeEraserTool, InkTool.None) as InkEraserTool; } // prettier-ignore
+ public static set ActiveEraser(tool:InkEraserTool){ Doc.UserDoc().activeEraserTool = tool; } // prettier-ignore
public static get ActivePresentation() { return DocCast(Doc.ActiveDashboard?.activePresentation) as Opt<Doc>; } // prettier-ignore
public static set ActivePresentation(val) { Doc.ActiveDashboard && (Doc.ActiveDashboard.activePresentation = val) } // prettier-ignore
public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); } // prettier-ignore
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 17b99b033..f3ff5f11f 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -8,17 +8,22 @@ import { ObjectField } from './ObjectField';
// Helps keep track of the current ink tool in use.
export enum InkTool {
- None = 'none',
- Pen = 'pen',
- Highlighter = 'highlighter',
- StrokeEraser = 'strokeeraser',
- SegmentEraser = 'segmenteraser',
- RadiusEraser = 'radiuseraser',
- Eraser = 'eraser', // not a real tool, but a class of tools
- Stamp = 'stamp',
- Write = 'write',
- PresentationPin = 'presentationpin',
- SmartDraw = 'smartdraw',
+ None = 'None',
+ Ink = 'Ink',
+ Eraser = 'Eraser', // not a real tool, but a class of tools
+ SmartDraw = 'Smartdraw',
+}
+
+export enum InkInkTool {
+ Pen = 'Pen',
+ Highlight = 'Highlight',
+ Write = 'Write',
+}
+
+export enum InkEraserTool {
+ Stroke = 'Stroke',
+ Segment = 'Segment',
+ Radius = 'Radius',
}
export type Segment = Array<Bezier>;
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 582c09f29..0252961d3 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -85,6 +85,7 @@ async function deserializeScript(scriptIn: ScriptField) {
}
@scriptingGlobal
+// eslint-disable-next-line no-use-before-define
@Deserializable('script', (obj: unknown) => deserializeScript(obj as ScriptField))
export class ScriptField extends ObjectField {
@serializable
@@ -137,7 +138,6 @@ export class ScriptField extends ObjectField {
[ToString]() {
return this.script.originalScript;
}
- // eslint-disable-next-line default-param-last
public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Doc | string | number | boolean }, transformer?: Transformer) {
return CompileScript(script, {
params: {
@@ -156,13 +156,11 @@ export class ScriptField extends ObjectField {
});
}
- // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, true, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
}
- // eslint-disable-next-line default-param-last
public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }) {
const compiled = ScriptField.CompileScript(script, params, false, capturedVariables);
return compiled.compiled ? new ScriptField(compiled) : undefined;
@@ -186,6 +184,7 @@ export class ScriptField extends ObjectField {
}
@scriptingGlobal
+// eslint-disable-next-line no-use-before-define
@Deserializable('computed', (obj: unknown) => deserializeScript(obj as ComputedField))
export class ComputedField extends ScriptField {
static undefined = '__undefined';
@@ -229,7 +228,6 @@ export class ComputedField extends ScriptField {
[ToValue](doc: Doc) { return ComputedField.useComputed ? { value: this.value(doc) } : undefined; } // prettier-ignore
[Copy](): ObjectField { return new ComputedField(this.script, this.setterscript, this.rawscript); } // prettier-ignore
- // eslint-disable-next-line default-param-last
public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Doc | string | number | boolean }, setterscript?: string) {
const compiled = ScriptField.CompileScript(script, params, true, { value: '', ...capturedVariables });
const compiledsetter = setterscript ? ScriptField.CompileScript(setterscript, { ...params, value: 'any' }, false, capturedVariables) : undefined;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 335683270..b27816f55 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -48,8 +48,8 @@ export const documentSchema = createSchema({
_columnsHideIfEmpty: 'boolean', // whether empty stacking view column headings should be hidden
// _columnHeaders: listSpec(SchemaHeaderField), // header descriptions for stacking/masonry
// _schemaHeaders: listSpec(SchemaHeaderField), // header descriptions for schema views
- _text_fontSize: 'string',
- _text_fontFamily: 'string',
+ text_fontSize: 'string',
+ text_fontFamily: 'string',
_layout_sidebarWidthPercent: 'string', // percent of text window width taken up by sidebar
// appearance properties on the data document
@@ -70,7 +70,7 @@ export const documentSchema = createSchema({
stroke_startMarker: 'string',
stroke_endMarker: 'string',
stroke_dash: 'string',
- textTransform: 'string',
+ text_transform: 'string',
treeView_Open: 'boolean', // flag denoting whether the documents sub-tree (contents) is visible or hidden
treeView_ExpandedView: 'string', // name of field whose contents are being displayed as the document's subtree
treeView_ExpandedViewLock: 'boolean', // whether the expanded view can be changed
@@ -112,5 +112,4 @@ export const collectionSchema = createSchema({
});
export type Document = makeInterface<[typeof documentSchema]>;
-// eslint-disable-next-line no-redeclare
export const Document = makeInterface(documentSchema);
diff --git a/src/pen-gestures/GestureTypes.ts b/src/pen-gestures/GestureTypes.ts
index 5a8e9bd97..10f9ba6d0 100644
--- a/src/pen-gestures/GestureTypes.ts
+++ b/src/pen-gestures/GestureTypes.ts
@@ -8,7 +8,6 @@ export enum Gestures {
Arrow = 'arrow',
RightAngle = 'rightangle',
}
-
// Defines a point in an ink as a pair of x- and y-coordinates.
export interface PointData {
X: number;
diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts
index 04262b61f..f6e1c87fa 100644
--- a/src/pen-gestures/ndollar.ts
+++ b/src/pen-gestures/ndollar.ts
@@ -1,4 +1,5 @@
/* eslint-disable no-use-before-define */
+import { numberRange } from '../Utils';
import { Gestures } from './GestureTypes';
/**
@@ -193,77 +194,38 @@ export class NDollarRecognizer {
constructor(
useBoundedRotationInvariance: boolean // constructor
) {
+ const rectMaker = (width: number, height1: number, height2: number) => [
+ new Point(0, 0), //
+ new Point(0, height1),
+ new Point(width, height2),
+ new Point(width, 0),
+ new Point(0, 0),
+ ];
+
+ const arect = rectMaker(100, 100, 50);
+ const aorect = rectMaker(300, 100, 50);
+ const brect = rectMaker(100, 100, 200);
+ const borect = rectMaker(300, 100, 200);
+ const rect = rectMaker(100, 100, 100);
+ const orect = rectMaker(300, 100, 100);
+ const equilateral = [new Point(50, 100), new Point(100, 0), new Point(0, 0), new Point(50, 100)];
+ const aequilateral = [new Point(20, 100), new Point(200, 0), new Point(0, 0), new Point(20, 100)];
+ const bequilateral = [new Point(180, 100), new Point(200, 0), new Point(0, 0), new Point(180, 100)];
+ const circle = numberRange(11).map(i => new Point(100 + 100 * Math.cos((i / 10) * Math.PI * 2), 100 + 100 * Math.sin((i / 10) * Math.PI * 2)));
+ const rightAngle = [new Point(0, 0), new Point(0, 100), new Point(200, 100)];
//
- // one predefined multistroke for each multistroke type
+ // one predefined multistroke (plus its counterclockwise reversal for closed shapes) for each multistroke type
//
this.Multistrokes.push(
- new Multistroke(
- Gestures.Rectangle,
- useBoundedRotationInvariance,
- new Array([
- new Point(30, 146), // new Point(29, 160), new Point(30, 180), new Point(31, 200),
- new Point(30, 222), // new Point(50, 219), new Point(70, 225), new Point(90, 230),
- new Point(106, 225), // new Point(100, 200), new Point(106, 180), new Point(110, 160),
- new Point(106, 146), // new Point(80, 150), new Point(50, 146),
- new Point(30, 143),
- ])
- )
- );
- this.Multistrokes.push(new Multistroke(Gestures.Rectangle, useBoundedRotationInvariance, new Array([new Point(30, 143), new Point(106, 146), new Point(106, 225), new Point(30, 222), new Point(30, 146)])));
- this.Multistrokes.push(new Multistroke(Gestures.Line, useBoundedRotationInvariance, [[new Point(12, 347), new Point(119, 347)]]));
- this.Multistrokes.push(
- new Multistroke(
- Gestures.Triangle, // equilateral
- useBoundedRotationInvariance,
- new Array([new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100)])
- )
- );
- this.Multistrokes.push(
- new Multistroke(
- Gestures.Triangle, // equilateral
- useBoundedRotationInvariance,
- new Array([new Point(42, 100), new Point(140, 102), new Point(100, 200), new Point(40, 100)])
- )
- );
- this.Multistrokes.push(
- new Multistroke(
- Gestures.Circle,
- useBoundedRotationInvariance,
- new Array([
- new Point(200, 250),
- new Point(240, 230),
- new Point(248, 210),
- new Point(248, 190),
- new Point(240, 170),
- new Point(200, 150),
- new Point(160, 170),
- new Point(151, 190),
- new Point(151, 210),
- new Point(160, 230),
- new Point(201, 250),
- ])
- )
- );
- this.Multistrokes.push(
- new Multistroke(
- Gestures.Circle,
- useBoundedRotationInvariance,
- new Array([
- new Point(201, 250),
- new Point(160, 230),
- new Point(151, 210),
- new Point(151, 190),
- new Point(160, 170),
- new Point(200, 150),
- new Point(240, 170),
- new Point(248, 190),
- new Point(248, 210),
- new Point(240, 230),
- new Point(200, 250),
- ])
- )
+ ...[arect, aorect, brect, borect, rect, orect].map(s => new Multistroke(Gestures.Rectangle, useBoundedRotationInvariance, [s])),
+ ...[arect, aorect, brect, borect, rect, orect].map(s => new Multistroke(Gestures.Rectangle, useBoundedRotationInvariance, [s.reverse()])),
+ ...[aequilateral, bequilateral, equilateral].map(s => new Multistroke(Gestures.Triangle, useBoundedRotationInvariance, [s])),
+ ...[aequilateral, bequilateral, equilateral].map(s => new Multistroke(Gestures.Triangle, useBoundedRotationInvariance, [s.reverse()])),
+ new Multistroke(Gestures.Circle, useBoundedRotationInvariance, [circle]),
+ new Multistroke(Gestures.Circle, useBoundedRotationInvariance, [circle.reverse()]),
+ new Multistroke(Gestures.RightAngle, useBoundedRotationInvariance, [rightAngle]),
+ new Multistroke(Gestures.Line, useBoundedRotationInvariance, [[new Point(12, 347), new Point(119, 347)]])
);
- this.Multistrokes.push(new Multistroke(Gestures.RightAngle, useBoundedRotationInvariance, new Array([new Point(0, 0), new Point(0, 100), new Point(200, 100)])));
NumMultistrokes = this.Multistrokes.length; // NumMultistrokes flags the end of the non user-defined gstures strokes
//
// PREDEFINED STROKES