aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/MainView.tsx3
-rw-r--r--src/client/views/PropertiesView.tsx2
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx4
-rw-r--r--src/client/views/collections/CollectionMenu.tsx10
-rw-r--r--src/client/views/collections/CollectionTreeView.scss3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx295
-rw-r--r--src/client/views/nodes/DocumentView.tsx13
-rw-r--r--src/client/views/nodes/PresBox.tsx28
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx15
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts76
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx36
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
12 files changed, 295 insertions, 192 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 379577a25..371ee3960 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
+import * as far from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
@@ -141,7 +142,7 @@ export class MainView extends React.Component {
fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper,
- fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle,
+ fa.faDesktop, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle,
fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, faBuffer, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 5a9402d70..53261d8de 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -45,8 +45,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
@computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; }
@computed get selectedDocumentView() {
+ if (PresBox.Instance?._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
- if (PresBox.Instance && PresBox.Instance._selectedArray.length) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
return undefined;
}
@computed get isPres(): boolean {
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 1131ec874..9269e6f33 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -382,10 +382,10 @@ export class Timeline extends React.Component<FieldViewProps> {
const ttime = `Total: ${this.toReadTime(this._time)}`;
return (
<div style={{ flexDirection: "column" }}>
- <div className="animation-text" style={{ fontSize: "10pt", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+ <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
{ctime}
</div>
- <div className="animation-text" style={{ fontSize: "10pt", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+ <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
{ttime}
</div>
</div>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index cb989d348..f3ed89c78 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -251,7 +251,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
}
@computed get subChrome() {
- switch (this.props.type) {
+ switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh! hack to get menu for tree view outline items
default: return this.otherSubChrome;
case CollectionViewType.Invalid:
case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />);
@@ -528,16 +528,14 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
}
@computed get selectedDocumentView() {
- if (SelectionManager.SelectedDocuments().length) {
- return SelectionManager.SelectedDocuments()[0];
- } else { return undefined; }
+ return SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
@computed get isText() {
if (this.selectedDoc) {
const layoutField = Doc.LayoutField(this.selectedDoc);
- return StrCast(layoutField).includes("FormattedText") ||
- (layoutField instanceof Doc && StrCast(layoutField.layout).includes("FormattedText"));
+ const layoutStr = this.selectedDocumentView?.props.LayoutTemplateString || StrCast(layoutField);
+ return layoutStr.includes("FormattedText") || StrCast((layoutField as Doc)?.layout).includes("FormattedText");
}
else return false;
}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 3f437b799..09a46c30f 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -34,8 +34,8 @@
position: relative;
width: 15px;
color: $intermediate-color;
- margin-top: 3px;
transform: scale(0.5);
+ display: inline-block;
}
.bullet {
@@ -127,6 +127,7 @@
.treeViewItem-header {
border: transparent 1px solid;
display: flex;
+ align-items: center;
.editableView-container-editing-oneLine {
min-width: 15px;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index d1a2fd33e..7765834f1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -4,7 +4,6 @@ import { observer } from "mobx-react";
import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
-import { PrefetchProxy } from '../../../fields/Proxy';
import { Document, listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -22,16 +21,17 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from "../EditableView";
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import { DocumentView } from '../nodes/DocumentView';
-import { ImageBox } from '../nodes/ImageBox';
import { KeyValueBox } from '../nodes/KeyValueBox';
import { Templates } from '../Templates';
import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
-import { CollectionViewType } from './CollectionView';
+import { CollectionViewType, CollectionView } from './CollectionView';
import React = require("react");
-import { makeTemplate } from '../../util/DropConverter';
import { TraceMobx } from '../../../fields/util';
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextField } from '../../../fields/RichTextField';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
export interface TreeViewProps {
document: Doc;
@@ -90,13 +90,14 @@ class TreeView extends React.Component<TreeViewProps> {
get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); }
get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive
get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; }
- get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); }
+ get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); }
get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); }
@observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
set treeViewOpen(c: boolean) {
if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c;
else this.doc.treeViewOpen = this._overrideTreeViewOpen = c;
}
+ @computed get outlineMode() { return this.props.treeView.doc.treeViewOutlineMode; }
@computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; }
@computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); }
@computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); }
@@ -122,12 +123,10 @@ class TreeView extends React.Component<TreeViewProps> {
return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc);
}
@undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
+ this.props.treeView.props.select(false);
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
}
- @undoBatch @action removeDoc = (doc: Doc | Doc[]) => {
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) =>
- flg && Doc.RemoveDocFromList(this.doc, Doc.LayoutFieldKey(this.doc), doc), true);
- }
+ @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc));
constructor(props: any) {
super(props);
@@ -144,10 +143,15 @@ class TreeView extends React.Component<TreeViewProps> {
ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
}
+ componentWillUnmount() {
+ document.removeEventListener("pointermove", this.onDragMove, true);
+ }
+
onPointerEnter = (e: React.PointerEvent): void => {
this.props.active(true) && Doc.BrushDoc(this.dataDoc);
if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
this._header!.current!.className = "treeViewItem-header";
+ document.removeEventListener("pointermove", this.onDragMove, true);
document.addEventListener("pointermove", this.onDragMove, true);
}
}
@@ -171,6 +175,24 @@ class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}
+ public static makeTextBullet() {
+ const bullet = Docs.Create.TextDocument("", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 2000, _height: 10, templates: new List<string>([Templates.Title.Layout]) });
+ Doc.GetProto(bullet).layout = CollectionView.LayoutString("data");
+ Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
+ Doc.GetProto(bullet).data = new List<Doc>([]);
+ Doc.SetInPlace(bullet, "editTitle", "*", false);
+ FormattedTextBox.SelectOnLoad = bullet[Id];
+ return bullet;
+ }
+
+ makeTextCollection = () => {
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+ const bullet = TreeView.makeTextBullet();
+ const added = this.props.addDocument(bullet);
+ bullet.context = this.props.treeView.Document;
+ return added;
+ }
+
editableView = (key: string, style?: string) => (<EditableView
oneLine={true}
display={"inline-block"}
@@ -182,27 +204,14 @@ class TreeView extends React.Component<TreeViewProps> {
fontSize={12}
GetValue={() => StrCast(this.doc[key])}
SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
- if (this.props.treeView.doc.treeViewOutlineMode && enterKey) {
+ if (this.outlineMode && enterKey) {
Doc.SetInPlace(this.doc, key, value, false);
- const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- Doc.SetInPlace(this.doc, "editTitle", undefined, false);
- Doc.SetInPlace(doc, "editTitle", "*", false);
- this.props.addDocument(doc);
- doc.context = this.props.treeView.Document;
+ this.makeTextCollection();
} else {
Doc.SetInPlace(this.doc, key, value, false) || true;
Doc.SetInPlace(this.doc, "editTitle", undefined, false);
}
})}
- OnFillDown={undoBatch((value: string) => {
- Doc.SetInPlace(this.doc, key, value, false);
- const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- Doc.SetInPlace(this.doc, "editTitle", undefined, false);
- Doc.SetInPlace(doc, "editTitle", "*", false);
- const added = this.props.addDocument(doc);
- doc.context = this.props.treeView.Document;
- return added;
- })}
onClick={() => {
SelectionManager.DeselectAll();
Doc.UserDoc().activeSelection = new List([this.doc]);
@@ -240,8 +249,13 @@ class TreeView extends React.Component<TreeViewProps> {
const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
let addDoc = parentAddDoc;
if (inside) {
+ const localAdd = (doc: Doc) => {
+ const added = Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
+ added && (doc.context = this.doc.context);
+ return added;
+ }
addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce(
- (flg: boolean, doc) => flg && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc), true) || parentAddDoc(doc);
+ (flg: boolean, doc) => flg && localAdd(doc), true) || parentAddDoc(doc);
}
const move = (!docDragData.dropAction || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument;
return docDragData.droppedDocuments.reduce((added, d) => (move ? docDragData.moveDocument?.(d, undefined, addDoc) : addDoc(d)) || added, false);
@@ -290,8 +304,12 @@ class TreeView extends React.Component<TreeViewProps> {
if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce(
- (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true);
+ const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+ const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
+ added && (doc.context = this.doc.context);
+ return added;
+ }
+ const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move,
this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active,
@@ -328,18 +346,23 @@ class TreeView extends React.Component<TreeViewProps> {
rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20);
rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
+ rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20);
@computed get renderContent() {
TraceMobx();
const expandKey = this.treeViewExpandedView;
if (["links", "annotations", this.fieldKey].includes(expandKey)) {
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey);
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) =>
- (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true);
+ const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
+ const added = Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true);
+ added && (doc.context = this.doc.context);
+ return added;
+ }
+ const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
const sortKey = `${this.fieldKey}-sortAscending`;
return <ul key={expandKey + "more"} className={this.doc.treeViewHideTitle ? "no-indent" : ""} onClick={(e) => {
- !this.props.treeView.Document.treeViewOutlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
+ !this.outlineMode && (this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)));
e.stopPropagation();
}}>
{!docs ? (null) :
@@ -416,7 +439,7 @@ class TreeView extends React.Component<TreeViewProps> {
title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
onClick={this.bulletClick}
style={{ opacity: NumCast(this.doc.opacity, 1) }}>
- {<FontAwesomeIcon icon={"circle"} />}
+ {(this.doc.text as RichTextField)?.Text ? <FontAwesomeIcon icon={this.childDocs?.length && !this.treeViewOpen ? ["fas", "circle"] : ["far", "circle"]} /> : (null)}
</div>;
}
@computed get renderBullet() {
@@ -442,7 +465,7 @@ class TreeView extends React.Component<TreeViewProps> {
return ["*", this._uniqueId, this.props.treeView._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "");
}
onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
- onChildDoubleClick = () => (!this.props.treeView.props.Document.treeViewOutlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+ onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
/**
* Renders the EditableView title element for placement into the tree.
*/
@@ -456,11 +479,11 @@ class TreeView extends React.Component<TreeViewProps> {
onPointerDown={action(() => {
if (this.treeViewOpen) {
this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView :
- this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") :
+ this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") :
this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" :
this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" :
(this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" :
- this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields");
+ this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields");
}
this.treeViewOpen = true;
})}>
@@ -488,7 +511,7 @@ class TreeView extends React.Component<TreeViewProps> {
PanelWidth={this.truncateTitleWidth}
PanelHeight={returnZero}
contextMenuItems={this.contextMenuItems}
- opacity={this.props.treeView.props.Document.treeViewOutlineMode ? undefined : returnOne}
+ opacity={this.outlineMode ? undefined : returnOne}
renderDepth={1}
focus={returnTrue}
parentActive={returnTrue}
@@ -529,7 +552,67 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
} else this._editMaxWidth = "";
- return this.doc.treeViewHideTitle || (this.props.treeView.props.Document.treeViewOutlineMode && this.doc.type !== DocumentType.COL) ? this.renderContent :
+ return this.doc.treeViewHideTitle || (this.outlineMode) ?
+ !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? this.renderContent :
+ <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}
+ onKeyDown={e => {
+ e.stopPropagation();
+ e.key === "Backspace" && this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete");
+ e.key === "Tab" && UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab");
+ e.key === "Enter" && UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet");
+ e.key === "Tab" && setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+ }}
+ >
+ <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }}
+ onPointerDown={e => {
+ if (this.props.active(true)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ }}
+ onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
+ {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
+ <div ref={this._dref} style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}>
+ <ContentFittingDocumentView
+ Document={this.doc}
+ DataDoc={undefined}
+ LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnTrue}
+ treeViewDoc={undefined}
+ backgroundColor={this.props.backgroundColor}
+ fitToBox={this.boundsOfCollectionDocument !== undefined}
+ PanelWidth={this.rtfWidth}
+ PanelHeight={this.rtfOutlineHeight}
+ focus={returnFalse}
+ ScreenToLocalTransform={this.docTransform}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containingCollection}
+ ContainingCollectionView={undefined}
+ addDocument={this.props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ />
+ </div>
+ </div>
+
+ <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen ? (null) : this.renderContent}
+ </div>
+ </div> :
<div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}>
<li className="collection-child">
<div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => {
@@ -546,11 +629,11 @@ class TreeView extends React.Component<TreeViewProps> {
}
}}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- {this.props.treeView.props.Document.treeViewOutlineMode ? this.renderOutlineBullet : this.renderBullet}
+ {this.outlineMode ? this.renderOutlineBullet : this.renderBullet}
{this.renderTitle}
</div>
- <div className={`treeViewItem-border${this.props.treeView.props.Document.treeViewOutlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
- {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
+ <div className={`treeViewItem-border${this.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting ? "crimson" : "blue" }}>
+ {!this.treeViewOpen ? (null) : this.renderContent}
</div>
</li>
</div>;
@@ -639,9 +722,10 @@ class TreeView extends React.Component<TreeViewProps> {
const fieldKeysub = StrCast(docs[i - 1].layout).split('fieldKey')[1];
const fieldKey = fieldKeysub.split("\'")[1];
if (fieldKey && Cast(docs[i - 1][fieldKey], listSpec(Doc)) !== undefined) {
+ remove(child);
+ FormattedTextBox.SelectOnLoad = child[Id];
Doc.AddDocToList(docs[i - 1], fieldKey, child);
docs[i - 1].treeViewOpen = true;
- remove(child);
child.context = treeView.Document;
}
}
@@ -650,9 +734,10 @@ class TreeView extends React.Component<TreeViewProps> {
if (remove && StrCast(parentCollectionDoc.layout).indexOf('fieldKey') !== -1) {
const fieldKeysub = StrCast(parentCollectionDoc.layout).split('fieldKey')[1];
const fieldKey = fieldKeysub.split("\'")[1];
+ remove(child);
+ FormattedTextBox.SelectOnLoad = child[Id];
Doc.AddDocToList(parentCollectionDoc, fieldKey, child, parentPrevSibling, false);
parentCollectionDoc.treeViewOpen = true;
- remove(child);
child.context = treeView.Document;
}
};
@@ -713,6 +798,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
private _mainEle?: HTMLDivElement;
public _uniqueId = Utils.GenerateGuid();
+ _isChildActive = false;
@computed get doc() { return this.props.Document; }
@computed get dataDoc() { return this.props.DataDoc || this.doc; }
@@ -743,6 +829,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
const targetDataDoc = this.doc[DataSym];
const value = DocListCast(targetDataDoc[this.props.fieldKey]);
const result = value.filter(v => !docs.includes(v));
+ SelectionManager.DeselectAll();
if (result.length !== value.length) {
targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
return true;
@@ -785,45 +872,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" });
ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
}
- !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({
- description: "Buxton Layout", icon: "eye", event: () => {
- const { ImageDocument, PdfDocument } = Docs.Create;
- const { Document } = this.props;
- const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif";
- const detailView = Cast(Cast(Doc.UserDoc()["template-button-detail"], Doc, null)?.dragFactory, Doc, null);
- const heroView = ImageDocument(fallbackImg, { title: "heroView", isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work?
- heroView.proto!.layout = ImageBox.LayoutString("hero");
- heroView._showTitle = "title";
- heroView._showTitleHover = "titlehover";
-
- const fallback = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target
- let pdfContent: string;
- this.childDocs?.map(d => {
- DocListCast(d.data).map((img, i) => {
- const caption = (d.captions as any)[i];
- if (caption) {
- Doc.GetProto(img).caption = caption;
- Doc.GetProto(img).doubleClickView = (pdfContent = StrCast(img.additionalMedia_pdfs)) ? PdfDocument(pdfContent, { title: pdfContent }) : fallback;
- }
- });
- Doc.GetProto(d).type = "buxton";
- Doc.GetProto(d).proto = heroView; // all devices "are" heroViews that share the same layout & defaults. Seems better than making them all be independent and copy a layout string // .layout = ImageBox.LayoutString("hero");
- });
-
- const iconBuxtonView = ImageDocument(fallbackImg, { title: "hero", _width: 60, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") });
- iconBuxtonView.isTemplateDoc = makeTemplate(iconBuxtonView, true, "icon_buxton");
- Doc.UserDoc()["template-icon-view-buxton"] = new PrefetchProxy(iconBuxtonView);
- const tempIcons = Doc.GetProto(Cast(Doc.UserDoc()["template-icons"], Doc, null));
- Doc.AddDocToList(tempIcons, "data", iconBuxtonView);
-
- Document.childLayoutTemplate = heroView;
- Document.childClickedOpenTemplateView = new PrefetchProxy(detailView);
- Document._viewType = CollectionViewType.Time;
- Document.forceActive = true;
- Document._pivotField = "company";
- Document.childDropAction = "alias";
- }
- });
const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
onClicks.push({
@@ -846,13 +894,74 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
</div >;
}
- onChildClick = () => {
- return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
+ @undoBatch
+ makeTextCollection = action((childDocs: Doc[]) => {
+ Doc.SetInPlace(this.doc, "editTitle", undefined, false);
+ const bullet = TreeView.makeTextBullet();
+ bullet.context = this.doc;
+ this.addDoc(bullet, childDocs.length ? childDocs[0] : undefined, true);
+ setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
+ })
+
+ editableTitle(childDocs: Doc[]) {
+ return <EditableView
+ contents={this.dataDoc.title}
+ editing={false}
+ display={"block"}
+ maxHeight={72}
+ height={"auto"}
+ GetValue={() => StrCast(this.dataDoc.title)}
+ SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+ if (this.props.Document.treeViewOutlineMode && enter) {
+ this.makeTextCollection(childDocs)
+ }
+ return Doc.SetInPlace(this.dataDoc, "title", value, false);
+ })} />;
}
- _isChildActive = false;
- whenActiveChanged = (isActive: boolean) => {
- this.props.whenActiveChanged(this._isChildActive = isActive);
+
+
+ rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.PanelWidth() - 20);
+ rtfOutlineHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2);
+ titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20));
+ documentTitle = (childDocs: Doc[]) => {
+ return <div style={{ display: "inline-block", height: this.rtfOutlineHeight() }} key={this.doc[Id]}
+ onKeyDown={e => {
+ e.stopPropagation();
+ e.key === "Enter" && this.makeTextCollection(childDocs)
+ }}>
+ <ContentFittingDocumentView
+ Document={this.doc}
+ DataDoc={undefined}
+ LayoutTemplateString={FormattedTextBox.LayoutString("text")}
+ LibraryPath={emptyPath}
+ renderDepth={this.props.renderDepth + 1}
+ rootSelected={returnTrue}
+ treeViewDoc={undefined}
+ dontRegisterView={true}
+ backgroundColor={this.props.backgroundColor}
+ PanelWidth={this.rtfWidth}
+ PanelHeight={this.rtfOutlineHeight}
+ focus={returnFalse}
+ ScreenToLocalTransform={this.titleTransform}
+ docFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.doc}
+ ContainingCollectionView={this.props.CollectionView}
+ addDocument={this.props.addDocument}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ parentActive={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ ContentScaling={returnOne}
+ />
+ </div>
}
+
+ onChildClick = () => { return this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); }
+ whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }
active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive;
render() {
TraceMobx();
@@ -882,21 +991,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={this.createTreeDropTarget}>
- {hideTitle ? (null) : <EditableView
- contents={this.dataDoc.title}
- editing={false}
- display={"block"}
- maxHeight={72}
- height={"auto"}
- GetValue={() => StrCast(this.dataDoc.title)}
- SetValue={undoBatch((value: string) => {
- if (this.props.Document.treeViewOutlineMode) {
- const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([Templates.Title.Layout]) });
- Doc.SetInPlace(doc, "editTitle", "*", false);
- this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true);
- }
- return Doc.SetInPlace(this.dataDoc, "title", value, false);
- })} />}
+ {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)}
{this.doc.allowClear ? this.renderClearButton : (null)}
<ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul>
</div >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7b7c3578b..4b740d3de 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -40,6 +40,7 @@ import { RadialMenu } from './RadialMenu';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require("react");
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { RichTextField } from '../../../fields/RichTextField';
export type DocFocusFunc = () => boolean;
@@ -269,6 +270,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onKeyDown = (e: React.KeyboardEvent) => {
+ if (this.rootDoc._singleLine && ((e.key === "Backspace" && this.dataDoc.text && !(this.dataDoc.text as RichTextField)?.Text) || ["Tab", "Enter"].includes(e.key))) {
+ return;
+ }
if (e.altKey && !(e.nativeEvent as any).StopPropagationForReal) {
(e.nativeEvent as any).StopPropagationForReal = true; // e.stopPropagation() doesn't seem to work...
e.stopPropagation();
@@ -624,6 +628,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
@undoBatch @action
drop = async (e: Event, de: DragManager.DropEvent) => {
+ if (this.props.LayoutTemplateString) return;
if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
if ((e.target as any)?.closest?.("*.lm_content")) {
alert("You can't perform this move most likely because you don't have permission to modify the destination.");
@@ -901,7 +906,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
{/* {this.allAnchors} */}
- {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) :
+ {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || (!this.isSelected() && this.layoutDoc.hideLinkButton) || this.props.dontRegisterView ? (null) :
<DocumentLinksButton View={this} links={this.allLinks} Offset={this.linkOffset} />}
</div>
);
@@ -1045,7 +1050,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor;
- const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
+ const fullDegree = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
const borderRounding = this.layoutDoc.borderRounding;
const localScale = fullDegree;
const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ?
@@ -1059,8 +1064,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
id={this.props.Document[Id]}
ref={this._mainCont} onKeyDown={this.onKeyDown}
onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={action(() => { Doc.BrushDoc(this.props.Document); })}
- onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => {
+ onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+ onPointerLeave={action(e => {
let entered = false;
const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
for (let child: any = target; child; child = child?.parentElement) {
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 79f1b7ddc..b23dffe80 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1129,13 +1129,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
}
let doc = undefined;
- const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", });
- const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" });
- const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" });
- const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" });
- const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" });
- const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" });
- const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" });
+ const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24px", });
+ const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16px" });
+ const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20px" });
+ const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24px" });
+ const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14px" });
+ const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14px" });
+ const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14px" });
switch (layout) {
case 'blank':
doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
@@ -1227,8 +1227,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
let type: string = '';
+ const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;// bcz: Argh .. .need a better way to identify a slide doc
if (activeItem) {
- switch (targetDoc.type) {
+ switch (effectiveType) {
case DocumentType.PDF: type = "PDF"; break;
case DocumentType.RTF: type = "Text node"; break;
case DocumentType.COL: type = "Collection"; break;
@@ -1248,8 +1249,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get progressivizeDropdown() {
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
+ const activeItem = this.activeItem;
+ const targetDoc = this.targetDoc;
+ const effectiveType = targetDoc.treeViewOutlineMode ? DocumentType.COL : targetDoc.type;
if (activeItem && targetDoc) {
const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
@@ -1258,7 +1260,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
{this.stringType} selected
- <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: effectiveType === DocumentType.COL ? "inline-flex" : "none" }}>
<div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
<div className="ribbon-button" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
@@ -1274,11 +1276,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
{this.viewedColorPicker}
- {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
+ {/* <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (effectiveType === DocumentType.COL && targetDoc._viewType === 'freeform') || effectiveType === DocumentType.IMG ? "inline-flex" : "none" }}>
<div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
<div className="ribbon-button" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div> */}
- <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || effectiveType === DocumentType.PDF || effectiveType === DocumentType.WEB || effectiveType === DocumentType.RTF ? "inline-flex" : "none" }}>
<div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
<div className="ribbon-button" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 97d023673..3be02aa92 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -988,7 +988,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (documentId) {
exportState = await RichTextUtils.GoogleDocs.Import(documentId, dataDoc);
}
- UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
+ exportState && UndoManager.RunInBatch(() => handler(exportState, dataDoc), Pulls);
}
updateState = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => {
@@ -1171,6 +1171,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
componentWillUnmount() {
+ this.endUndoTypingBatch();
Object.values(this._disposers).forEach(disposer => disposer?.());
this._editorView?.destroy();
}
@@ -1417,7 +1418,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
_lastTimedMark: Mark | undefined = undefined;
- onKeyPress = (e: React.KeyboardEvent) => {
+ onKeyDown = (e: React.KeyboardEvent) => {
+ // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container)
+ if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) {
+ return;
+ }
if (e.altKey) {
e.preventDefault();
return;
@@ -1502,8 +1507,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) {
setTimeout(() => FormattedTextBoxComment.Hide(), 0);
}
- const selPad = this.props.isSelected() ? -10 : 0;
- const selclass = this.props.isSelected() ? "-selected" : "";
+ const selPad = this.props.isSelected() && !this.layoutDoc._singleLine ? -10 : 0;
+ const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : "";
return (
<div className={"formattedTextBox-cont"}
style={{
@@ -1526,7 +1531,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"),
}}
onContextMenu={this.specificContextMenu}
- onKeyDown={this.onKeyPress}
+ onKeyDown={this.onKeyDown}
onFocus={this.onFocused}
onClick={this.onClick}
onPointerMove={e => this.hitBulletTargets(e.clientX, e.clientY, e.shiftKey, true)}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c6bacc1a8..674b63e77 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,11 +7,13 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { SelectionManager } from "../../../util/SelectionManager";
import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast } from "../../../../fields/Doc";
import { FormattedTextBox } from "./FormattedTextBox";
import { Id } from "../../../../fields/FieldSymbols";
import { Docs } from "../../../documents/Documents";
import { Utils } from "../../../../Utils";
+import { listSpec } from "../../../../fields/Schema";
+import { List } from "../../../../fields/List";
const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
@@ -45,6 +47,29 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
keys[key] = cmd;
}
+ /// bcz; Argh!! replace with an onEnter func that conditionally handles Enter
+ const addTextBox = (below: boolean, force?: boolean) => {
+ if (props.LayoutTemplateString) return true;
+ const layoutDoc = props.Document;
+ const originalDoc = layoutDoc.rootDocument || layoutDoc;
+ if (force || props.Document._singleLine) {
+ const layoutKey = StrCast(originalDoc.layoutKey);
+ const newDoc = Doc.MakeCopy(originalDoc, true);
+ const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)];
+ newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
+ else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
+ if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
+ newDoc[layoutKey] = originalDoc[layoutKey];
+ }
+ Doc.GetProto(newDoc).text = undefined;
+ FormattedTextBox.SelectOnLoad = newDoc[Id];
+ props.addDocument(newDoc);
+ return true;
+ }
+ return false;
+ }
+
//History commands
bind("Mod-z", undo);
bind("Shift-Mod-z", redo);
@@ -66,6 +91,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
+ if (props.Document._singleLine) {
+ if (!props.LayoutTemplateString) return addTextBox(false, true);
+ return true;
+ }
const ref = state.selection;
const range = ref.$from.blockRange(ref.$to);
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -89,6 +119,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
});
bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+ /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab);
+ if (props.Document._singleLine) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
@@ -136,47 +168,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
return tx;
};
- const addTextOnRight = (force: boolean) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (force || props.Document._singleLine) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- return true;
- }
- return false;
- };
-
//Command to create a text document to the right of the selected textbox
- bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- return addTextOnRight(true);
- });
+ bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => addTextBox(false, true));
//Command to create a text document to the bottom of the selected textbox
- bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- const layoutDoc = props.Document;
- const originalDoc = layoutDoc.rootDocument || layoutDoc;
- if (originalDoc instanceof Doc) {
- const layoutKey = StrCast(originalDoc.layoutKey);
- const newDoc = Doc.MakeCopy(originalDoc, true);
- newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = undefined;
- newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
- if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
- newDoc[layoutKey] = originalDoc[layoutKey];
- }
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
- props.addDocument(newDoc);
- }
- });
+ bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => addTextBox(true, true));
// backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
@@ -199,7 +195,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
//newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
//command to break line
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
- if (addTextOnRight(false)) return true;
+ if (addTextBox(true, false)) return true;
const trange = state.selection.$from.blockRange(state.selection.$to);
const path = (state.selection.$from as any).path;
const depth = trange ? liftTarget(trange) : undefined;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 307238ea1..da515aa5a 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -78,21 +78,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
runInAction(() => this.Pinned = true);
this.fontSizeOptions = [
- { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48pt", command: this.changeFontSize },
- { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72pt", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize },
+ { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize },
{ mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true },
- { mark: null, title: "", label: "13pt", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
+ { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option
];
this.fontFamilyOptions = [
@@ -177,7 +177,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeListType = this.getActiveListStyle();
this.activeAlignment = this.getActiveAlignment();
this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various";
- this.activeFontSize = !activeSizes.length ? "13pt" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
+ this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "...";
this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "...";
this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "...";
@@ -255,7 +255,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
marks.forEach(m => {
m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family);
m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color);
- m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "pt");
+ m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px");
m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight));
});
}
@@ -384,7 +384,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
if (!self.TextView.props.isSelected(true)) {
switch (mark.type) {
case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break;
- case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "pt"); break;
+ case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break;
}
}
else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown");
@@ -425,7 +425,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
changeFontSize = (mark: Mark, view: EditorView) => {
if ((this.view?.state.selection.$from.pos || 0) < 2) {
- this.TextView.layoutDoc._fontSize = mark.attrs.fontSize;
+ this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize;
}
const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize });
this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 5c0505909..02f9c6268 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -90,7 +90,7 @@ export class RichTextRules {
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to
const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation
- const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9pt", title: "inline comment" });
+ const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" });
textDocInline.title = inlineFieldKey; // give the annotation its own title
textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point