aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DictationManager.ts2
-rw-r--r--src/client/util/SelectionManager.ts6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx24
-rw-r--r--src/client/views/nodes/DocumentView.tsx4
-rw-r--r--src/client/views/nodes/ImageBox.tsx17
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx12
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss7
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx52
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts10
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx36
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts70
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts85
-rw-r--r--src/fields/RichTextUtils.ts2
14 files changed, 186 insertions, 143 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index d8a5657c3..28b1ca6cf 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -335,7 +335,7 @@ export namespace DictationManager {
const prompt = "Press alt + r to start dictating here...";
const head = 3;
const anchor = head + prompt.length;
- const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
+ const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
proto.data = new RichTextField(proseMirrorState);
proto.backgroundColor = "#eeffff";
target.props.addDocTab(newBox, "onRight");
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 05515e502..eb905d237 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -10,13 +10,17 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
-
+ clearSelection() {
+ if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
+ else if (document.getSelection()) { document.getSelection()?.empty(); }
+ }
@action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
if (!manager.SelectedDocuments.get(docView)) {
if (!ctrlPressed) {
this.DeselectAll();
+ this.clearSelection();
}
manager.SelectedDocuments.set(docView, true);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 6cac39f77..b65a2486c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -56,27 +56,27 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const targetAhyperlink = linkEles.find((ele: any) => ele.getAttribute("targetids")?.includes(AanchorId));
const targetBhyperlink = linkEles.find((ele: any) => ele.getAttribute("targetids")?.includes(BanchorId));
if (!targetBhyperlink) {
- this.props.A.props.Document[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
- this.props.A.props.Document[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
+ this.props.A.rootDoc[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100;
+ this.props.A.rootDoc[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100;
} else {
setTimeout(() => {
- (this.props.A.props.Document[(this.props.A.props as any).fieldKey] as Doc);
+ (this.props.A.rootDoc[(this.props.A.props as any).fieldKey] as Doc);
const m = targetBhyperlink.getBoundingClientRect();
- const mp = this.props.A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- this.props.A.props.Document[afield + "_x"] = mp[0] / this.props.A.props.PanelWidth() * 100;
- this.props.A.props.Document[afield + "_y"] = mp[1] / this.props.A.props.PanelHeight() * 100;
+ const mp = this.props.A.props.ContainingCollectionView?.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ this.props.A.rootDoc[afield + "_x"] = Math.min(1, mp![0] / this.props.A.props.PanelWidth()) * 100;
+ this.props.A.rootDoc[afield + "_y"] = Math.min(1, mp![1] / this.props.A.props.PanelHeight()) * 100;
}, 0);
}
if (!targetAhyperlink) {
- this.props.A.props.Document[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
- this.props.A.props.Document[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
+ this.props.A.rootDoc[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100;
+ this.props.A.rootDoc[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100;
} else {
setTimeout(() => {
- (this.props.B.props.Document[(this.props.B.props as any).fieldKey] as Doc);
+ (this.props.B.rootDoc[(this.props.B.props as any).fieldKey] as Doc);
const m = targetAhyperlink.getBoundingClientRect();
- const mp = this.props.B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- this.props.B.props.Document[bfield + "_x"] = mp[0] / this.props.B.props.PanelWidth() * 100;
- this.props.B.props.Document[bfield + "_y"] = mp[1] / this.props.B.props.PanelHeight() * 100;
+ const mp = this.props.B.props.ContainingCollectionView?.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
+ this.props.B.rootDoc[bfield + "_x"] = Math.min(1, mp![0] / this.props.B.props.PanelWidth()) * 100;
+ this.props.B.rootDoc[bfield + "_y"] = Math.min(1, mp![1] / this.props.B.props.PanelHeight()) * 100;
}, 0);
}
})
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3205e3050..345ca479f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -995,7 +995,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
@computed get contents() {
TraceMobx();
- return (<>
+ return (<div style={{ position: "absolute", width: "100%", height: "100%" }}>
<DocumentContentsView key={1}
docFilters={this.props.docFilters}
ContainingCollectionView={this.props.ContainingCollectionView}
@@ -1033,7 +1033,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClick={this.onClickHandler}
layoutKey={this.finalLayoutKey} />
{this.anchors}
- </>
+ </div>
);
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 84d49681c..f0818c7b4 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -396,12 +396,23 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
const aspect = (rotation % 180) ? nativeHeight / nativeWidth : 1;
const shift = (rotation % 180) ? (nativeHeight - nativeWidth) * (1 - 1 / aspect) : 0;
this.resize(srcpath);
+ let transformOrigin = "center center";
+ let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`
+ if (rotation === 90 || rotation === -270) {
+ transformOrigin = "top left";
+ transform = `translate(100%, 0%) rotate(${rotation}deg) scale(${aspect})`
+ } else if (rotation === 180) {
+ transform = `rotate(${rotation}deg) scale(${aspect})`
+ } else if (rotation === 270 || rotation === -90) {
+ transformOrigin = "right top";
+ transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`
+ }
return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget}>
<div className="imageBox-fader" >
<img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
- style={{ transform: `scale(${aspect}) translate(0px, ${shift}px) rotate(${rotation}deg)` }}
+ style={{ transform, transformOrigin }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} />
@@ -409,7 +420,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
<img className="imageBox-fadeaway"
key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={fadepath}
- style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})`, }}
+ style={{ transform, transformOrigin }}
width={nativeWidth}
ref={this._imgRef}
onError={this.onError} /></div>}
@@ -447,7 +458,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
TraceMobx();
return (<div className={`imageBox`} onContextMenu={this.specificContextMenu}
style={{
- transform: this.props.PanelWidth() ? `translate(0px, ${this.ycenter}px)` : `scale(${this.props.ContentScaling()})`,
+ transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
pointerEvents: this.layoutDoc.isBackground ? "none" : undefined,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 3c232eff8..233acc481 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -17,6 +17,7 @@ import { LinkEditor } from "../linking/LinkEditor";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SelectionManager } from "../../util/SelectionManager";
import { TraceMobx } from "../../../fields/util";
+import { Id } from "../../../fields/FieldSymbols";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -55,8 +56,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
DragManager.StartDocumentDrag([this._ref.current!], dragData, down[0], down[1]);
return true;
} else if (dragdist > separation) {
- this.layoutDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
- this.layoutDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
+ this.rootDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
+ this.rootDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
}
}
return false;
@@ -113,8 +114,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
render() {
TraceMobx();
- const x = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_x"], 100) : 0;
- const y = this.props.PanelWidth() > 1 ? NumCast(this.layoutDoc[this.fieldKey + "_y"], 100) : 0;
+ const x = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_x"], 100) : 0;
+ const y = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_y"], 100) : 0;
const c = StrCast(this.layoutDoc.backgroundColor, "lightblue");
const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
@@ -130,7 +131,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
</div>
);
const small = this.props.PanelWidth() <= 1;
- return <div className={`linkAnchorBox-cont${small ? "-small" : ""}`} onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
+ return <div className={`linkAnchorBox-cont${small ? "-small" : ""} ${this.rootDoc[Id]}`}
+ onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c,
left: !small ? `calc(${x}% - 7.5px)` : undefined,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 17421b1e3..348ed4ba5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -295,6 +295,9 @@ footnote::after {
margin-left: 1em;
font-family: inherit;
}
+ .bullet { p {display: inline; font-family: inherit} margin-left: 0; }
+ .bullet1 { p {display: inline; font-family: inherit} }
+ .bullet2,.bullet3,.bullet4,.bullet5,.bullet6 { p {display: inline; font-family: inherit} font-size: smaller; }
.decimal1-ol { counter-reset: deci1; p {display: inline; font-family: inherit} margin-left: 0; }
.decimal2-ol { counter-reset: deci2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1em;}
@@ -308,6 +311,8 @@ footnote::after {
.multi2-ol { counter-reset: multi2; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 1.4em;}
.multi3-ol { counter-reset: multi3; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 2em;}
.multi4-ol { counter-reset: multi4; p {display: inline; font-family: inherit} font-size: smaller; padding-left: 3.4em;}
+
+ .bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; margin-left: -1em; width: 1em; content:" " }
.decimal1:before { transition: 0.5s;counter-increment: deci1; display: inline-block; margin-left: -1em; width: 1em; content: counter(deci1) ". "; }
.decimal2:before { transition: 0.5s;counter-increment: deci2; display: inline-block; margin-left: -2.1em; width: 2.1em; content: counter(deci1) "."counter(deci2) ". "; }
@@ -320,7 +325,7 @@ footnote::after {
.multi1:before { transition: 0.5s;counter-increment: multi1; display: inline-block; margin-left: -1em; width: 1.2em; content: counter(multi1, upper-alpha) ". "; }
.multi2:before { transition: 0.5s;counter-increment: multi2; display: inline-block; margin-left: -2em; width: 2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) ". "; }
.multi3:before { transition: 0.5s;counter-increment: multi3; display: inline-block; margin-left: -2.85em; width:2.85em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) ". "; }
- .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
+ .multi4:before { transition: 0.5s;counter-increment: multi4; display: inline-block; margin-left: -4.2em; width: 4.2em; content: counter(multi1, upper-alpha) "."counter(multi2, decimal) "."counter(multi3, lower-alpha) "."counter(multi4, lower-roman) ". "; }
}
.formattedTextBox-inner-rounded-selected,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c23ecb8ac..82334688b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -15,6 +15,7 @@ import { EditorView } from "prosemirror-view";
import { DateField } from '../../../../fields/DateField';
import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym } from "../../../../fields/Doc";
import { documentSchema } from '../../../../fields/documentSchemas';
+import applyDevTools = require("prosemirror-dev-tools");
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -57,6 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView";
import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment';
import React = require("react");
+import requestPromise = require('request-promise');
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -488,6 +490,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
});
changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
!change && cm.addItem({ description: "Change Perspective...", subitems: changeItems, icon: "external-link-alt" });
+ this._downX = this._downY = Number.NaN;
}
recordDictation = () => {
@@ -920,6 +923,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
+ // applyDevTools.applyDevTools(this._editorView);
const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field);
if (startupText) {
const { state: { tr }, dispatch } = this._editorView;
@@ -1008,7 +1012,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) {
e.stopPropagation();
}
- this._downX = this._downY = Number.NaN;
}
@action
@@ -1073,38 +1076,35 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
// this hackiness handles clicking on the list item bullets to do expand/collapse. the bullets are ::before pseudo elements so there's no real way to hit test against them.
- hitBulletTargets(x: number, y: number, select: boolean, highlightOnly: boolean) {
+ hitBulletTargets(x: number, y: number, collapse: boolean, highlightOnly: boolean) {
clearStyleSheetRules(FormattedTextBox._bulletStyleSheet);
- const pos = this._editorView!.posAtCoords({ left: x, top: y });
- if (pos && this.props.isSelected(true)) {
- // let beforeEle = document.querySelector("." + hit.className) as Element; // const before = hit ? window.getComputedStyle(hit, ':before') : undefined;
- //const node = this._editorView!.state.doc.nodeAt(pos.pos);
- const $pos = this._editorView!.state.doc.resolve(pos.pos);
- let list_node = $pos.node().type === schema.nodes.list_item ? $pos.node() : undefined;
- if ($pos.node().type === schema.nodes.ordered_list) {
- for (let off = 1; off < 100; off++) {
- const pos = this._editorView!.posAtCoords({ left: x + off, top: y });
- const node = pos && this._editorView!.state.doc.nodeAt(pos.pos);
- if (node?.type === schema.nodes.list_item) {
- list_node = node;
- break;
- }
+ const clickPos = this._editorView!.posAtCoords({ left: x, top: y });
+ let olistPos = clickPos?.pos;
+ if (clickPos && olistPos && this.props.isSelected(true)) {
+ const clickNode = this._editorView?.state.doc.nodeAt(olistPos);
+ let nodeBef = this._editorView?.state.doc.nodeAt(Math.max(0, olistPos - 1));
+ 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 as any).path.length > 3) {
+ olistNode = $olistPos.parent;
+ $olistPos = this._editorView?.state.doc.resolve(($olistPos as any).path[($olistPos as any).path.length - 4]);
}
}
- if (list_node && pos.inside >= 0 && this._editorView!.state.doc.nodeAt(pos.inside)!.attrs.bulletStyle === list_node.attrs.bulletStyle) {
- if (select) {
- const $olist_pos = this._editorView!.state.doc.resolve($pos.pos - $pos.parentOffset - 1);
+ const listNode = this._editorView?.state.doc.nodeAt(clickPos.pos!);
+ if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list) {
+ if (!collapse) {
if (!highlightOnly) {
- this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection($olist_pos)));
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection($olistPos!)));
}
- addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
- } else if (Math.abs(pos.pos - pos.inside) < 2) {
+ addStyleSheetRule(FormattedTextBox._bulletStyleSheet, olistNode.attrs.mapStyle + olistNode.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
+ } else if (listNode && listNode.type === this._editorView.state.schema.nodes.list_item) {
if (!highlightOnly) {
- const offset = this._editorView!.state.doc.nodeAt(pos.inside)?.type === schema.nodes.ordered_list ? 1 : 0;
- this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.inside + offset, list_node.type, { ...list_node.attrs, visibility: !list_node.attrs.visibility }));
- this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, pos.inside + offset)));
+ this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(clickPos.pos!, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility }));
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(TextSelection.create(this._editorView!.state.doc, clickPos.pos!)));
}
- addStyleSheetRule(FormattedTextBox._bulletStyleSheet, list_node.attrs.mapStyle + list_node.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
+ addStyleSheetRule(FormattedTextBox._bulletStyleSheet, olistNode.attrs.mapStyle + olistNode.attrs.bulletStyle + ":hover:before", { background: "lightgray" });
}
}
}
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 0a4c52ef9..75cfe6bd1 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -16,16 +16,13 @@ const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) :
export type KeyMap = { [key: string]: any };
-export let updateBullets = (tx2: Transaction, schema: Schema, mapStyle?: string) => {
- let fontSize: number | undefined = undefined;
+export let updateBullets = (tx2: Transaction, schema: Schema, mapStyle?: string, from?: number, to?: number) => {
tx2.doc.descendants((node: any, offset: any, index: any) => {
- if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) {
+ if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) {
const path = (tx2.doc.resolve(offset) as any).path;
let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0);
if (node.type === schema.nodes.ordered_list) depth++;
- fontSize = depth === 1 && node.attrs.setFontSize ? Number(node.attrs.setFontSize) : fontSize;
- const fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined;
- tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: mapStyle ? mapStyle : node.attrs.mapStyle, bulletStyle: depth, inheritedFontSize: fsize }, node.marks);
+ tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: mapStyle || node.attrs.mapStyle, bulletStyle: depth, }, node.marks);
}
});
return tx2;
@@ -62,7 +59,6 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, props: any
bind("Mod-U", toggleMark(schema.marks.underline));
//Commands for lists
- bind("Ctrl-.", wrapInList(schema.nodes.bullet_list));
bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 03d393cde..1e14b8237 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -104,7 +104,7 @@ export default class RichTextMenu extends AntimodeMenu {
{ node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType },
{ node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType },
{ node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "1.A", command: this.changeListType },
- { node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
+ //{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType },
];
this.fontColors = [
@@ -189,9 +189,9 @@ export default class RichTextMenu extends AntimodeMenu {
const node = (state.selection as NodeSelection).node;
if (node?.type === schema.nodes.ordered_list) {
let attrs = node.attrs;
- if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family };
- if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize };
- if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color };
+ if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family };
+ if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: `${mark.attrs.fontSize}px` };
+ if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema);
dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from))));
} else {
@@ -378,26 +378,24 @@ export default class RichTextMenu extends AntimodeMenu {
// TODO: remove doesn't work
//remove all node type and apply the passed-in one to the selected text
- changeListType = (nodeType: NodeType | undefined) => {
+ changeListType = (nodeType: Node | undefined) => {
if (!this.view) return;
- if (nodeType === schema.nodes.bullet_list) {
- wrapInList(nodeType)(this.view.state, this.view.dispatch);
- } else {
- const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
- if (!wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
- const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
-
- this.view!.dispatch(tx2);
- })) {
- const tx2 = this.view.state.tr;
- const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+ const marks = this.view.state.storedMarks || (this.view.state.selection.$to.parentOffset && this.view.state.selection.$from.marks());
+ if (!wrapInList(schema.nodes.ordered_list)(this.view.state, (tx2: any) => {
+ const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view!.state.selection.from - 1, this.view!.state.selection.to + 1);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ this.view!.dispatch(tx2);
+ })) {
+ const tx2 = this.view.state.tr;
+ if (nodeType && this.view.state.selection.$from.nodeAfter?.type === schema.nodes.ordered_list) {
+ const tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle, this.view.state.selection.from, this.view.state.selection.to);
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
- this.view.dispatch(tx3);
+ this.view.dispatch(tx3.setSelection(new NodeSelection(tx3.doc.resolve(this.view.state.selection.$from.pos))));
}
}
}
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 91187edf9..e442149d6 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -59,7 +59,7 @@ export class RichTextRules {
),
// * + - create bullet list
- wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list),
+ wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list),
// ``` create code block
textblockTypeInputRule(/^```$/, schema.nodes.code_block),
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 49d5c96a4..1de211f28 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -53,21 +53,45 @@ export const marks: { [index: string]: MarkSpec } = {
}
},
+ /** FONT SIZES */
+ pFontSize: {
+ attrs: { fontSize: { default: 10 } },
+ parseDOM: [{
+ tag: "span", getAttrs(dom: any) {
+ return { fontSize: dom.style.fontSize ? Number(dom.style.fontSize.replace("px", "")) : "" };
+ }
+ }],
+ toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize}px;` }] : ['span', 0]
+ },
+ /* FONTS */
+ pFontFamily: {
+ attrs: { family: { default: "" } },
+ parseDOM: [{
+ tag: "span", getAttrs(dom: any) {
+ const cstyle = getComputedStyle(dom);
+ if (cstyle.font) {
+ if (cstyle.font.indexOf("Times New Roman") !== -1) return { family: "Times New Roman" };
+ if (cstyle.font.indexOf("Arial") !== -1) return { family: "Arial" };
+ if (cstyle.font.indexOf("Georgia") !== -1) return { family: "Georgia" };
+ if (cstyle.font.indexOf("Comic Sans") !== -1) return { family: "Comic Sans MS" };
+ if (cstyle.font.indexOf("Tahoma") !== -1) return { family: "Tahoma" };
+ if (cstyle.font.indexOf("Crimson") !== -1) return { family: "Crimson Text" };
+ }
+ }
+ }],
+ toDOM: (node) => node.attrs.family ? ['span', { style: `font-family: "${node.attrs.family}";` }] : ['span', 0]
+ },
// :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text.
pFontColor: {
- attrs: {
- color: { default: "#000" }
- },
+ attrs: { color: { default: "" } },
inclusive: true,
parseDOM: [{
tag: "span", getAttrs(dom: any) {
return { color: dom.getAttribute("color") };
}
}],
- toDOM(node: any) {
- return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0];
- }
+ toDOM: (node) => node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0]
},
marker: {
@@ -277,38 +301,4 @@ export const marks: { [index: string]: MarkSpec } = {
parseDOM: [{ tag: "code" }],
toDOM() { return codeDOM; }
},
-
- /* FONTS */
- pFontFamily: {
- attrs: {
- family: { default: "Crimson Text" },
- },
- parseDOM: [{
- tag: "span", getAttrs(dom: any) {
- const cstyle = getComputedStyle(dom);
- if (cstyle.font) {
- if (cstyle.font.indexOf("Times New Roman") !== -1) return { family: "Times New Roman" };
- if (cstyle.font.indexOf("Arial") !== -1) return { family: "Arial" };
- if (cstyle.font.indexOf("Georgia") !== -1) return { family: "Georgia" };
- if (cstyle.font.indexOf("Comic Sans") !== -1) return { family: "Comic Sans MS" };
- if (cstyle.font.indexOf("Tahoma") !== -1) return { family: "Tahoma" };
- if (cstyle.font.indexOf("Crimson") !== -1) return { family: "Crimson Text" };
- }
- }
- }],
- toDOM: (node) => ['span', {
- style: `font-family: "${node.attrs.family}";`
- }]
- },
-
- /** FONT SIZES */
- pFontSize: {
- attrs: {
- fontSize: { default: 10 }
- },
- parseDOM: [{ style: 'font-size: 10px;' }],
- toDOM: (node) => ['span', {
- style: `font-size: ${node.attrs.fontSize}px;`
- }]
- },
};
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index af39ef9c7..b37e61d3f 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -218,48 +218,85 @@ export const nodes: { [index: string]: NodeSpec } = {
group: 'block',
attrs: {
bulletStyle: { default: 0 },
- mapStyle: { default: "decimal" },
- setFontSize: { default: undefined },
- setFontFamily: { default: "inherit" },
- setFontColor: { default: "inherit" },
- inheritedFontSize: { default: undefined },
+ mapStyle: { default: "decimal" },// "decimal", "multi", "bullet"
+ fontColor: { default: "inherit" },
+ fontSize: { default: undefined },
+ fontFamily: { default: undefined },
visibility: { default: true },
indent: { default: undefined }
},
+ parseDOM: [
+ {
+ tag: "ul", getAttrs(dom: any) {
+ return {
+ bulletStyle: dom.getAttribute("data-bulletStyle"),
+ mapStyle: dom.getAttribute("data-mapStyle"),
+ fontColor: dom.style.color,
+ fontSize: dom.style["font-size"],
+ fontFamily: dom.style["font-family"],
+ indent: dom.style["margin-left"]
+ };
+ }
+ },
+ {
+ style: 'list-style-type=disc', getAttrs(dom: any) {
+ return { mapStyle: "bullet" }
+ }
+ },
+ {
+ tag: "ol", getAttrs(dom: any) {
+ return {
+ bulletStyle: dom.getAttribute("data-bulletStyle"),
+ mapStyle: dom.getAttribute("data-mapStyle"),
+ fontColor: dom.style.color,
+ fontSize: dom.style["font-size"],
+ fontFamily: dom.style["font-family"],
+ indent: dom.style["margin-left"]
+ };
+ }
+ }],
toDOM(node: Node<any>) {
- if (node.attrs.mapStyle === "bullet") return ['ul', 0];
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
- const fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize;
- const ffam = node.attrs.setFontFamily;
- const color = node.attrs.setFontColor;
+ const fsize = node.attrs.fontSize ? `font-size: ${node.attrs.fontSize};` : "";
+ const ffam = node.attrs.fontFamily ? `font-family:${node.attrs.fontFamily};` : "";
+ const fcol = node.attrs.fontColor ? `color: ${node.attrs.fontColor};` : "";
+ const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : "";
+ if (node.attrs.mapStyle === "bullet") {
+ return ['ul', {
+ "data-mapStyle": node.attrs.mapStyle,
+ "data-bulletStyle": node.attrs.bulletStyle,
+ style: `${fsize} ${ffam} ${fcol} ${marg}`
+ }, 0];
+ }
return node.attrs.visibility ?
- ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}; font-family: ${ffam}; color:${color}; margin-left: ${node.attrs.indent}` }, 0] :
+ ['ol', {
+ class: `${map}-ol`,
+ "data-mapStyle": node.attrs.mapStyle,
+ "data-bulletStyle": node.attrs.bulletStyle,
+ style: `list-style: none; ${fsize} ${ffam} ${fcol} ${marg}`
+ }, 0] :
['ol', { class: `${map}-ol`, style: `list-style: none;` }];
}
},
- bullet_list: {
- ...bulletList,
- content: 'list_item+',
- group: 'block',
- // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }],
- toDOM(node: Node<any>) {
- return ['ul', 0];
- }
- },
-
list_item: {
+ ...listItem,
attrs: {
bulletStyle: { default: 0 },
- mapStyle: { default: "decimal" },
+ mapStyle: { default: "decimal" }, // "decimal", "multi", "bullet"
visibility: { default: true }
},
- ...listItem,
content: 'paragraph block*',
+ parseDOM: [{
+ tag: "li", getAttrs(dom: any) {
+ return { mapStyle: dom.getAttribute("data-mapStyle"), bulletStyle: dom.getAttribute("data-bulletStyle") };
+ }
+ }],
toDOM(node: any) {
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : "";
- return node.attrs.visibility ? ["li", { class: `${map}` }, 0] : ["li", { class: `${map}` }, "..."];
- //return ["li", { class: `${map}` }, 0];
+ return node.attrs.visibility ?
+ ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, 0] :
+ ["li", { class: `${map}`, "data-mapStyle": node.attrs.mapStyle, "data-bulletStyle": node.attrs.bulletStyle }, "..."];
}
},
}; \ No newline at end of file
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 66959882d..7c7bf3e12 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -256,7 +256,7 @@ export namespace RichTextUtils {
};
const list = (schema: any, items: Node[]): Node => {
- return schema.node("bullet_list", null, items);
+ return schema.node("ordered_list", { mapStyle: "bullet" }, items);
};
const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {