aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts14
-rw-r--r--src/client/util/RichTextSchema.tsx157
-rw-r--r--src/client/util/TooltipTextMenu.tsx18
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss20
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx41
-rw-r--r--src/new_fields/Doc.ts2
6 files changed, 120 insertions, 132 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 959b89fe5..f805ae872 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -133,6 +133,20 @@ export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => vo
return dup;
}
+export function timenow() {
+ var now = new Date();
+ let ampm = 'am';
+ let h = now.getHours();
+ let m: any = now.getMinutes();
+ let s: any = now.getSeconds();
+ if (h >= 12) {
+ if (h > 12) h -= 12;
+ ampm = 'pm';
+ }
+ if (m < 10) m = '0' + m;
+ return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
+}
+
export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); }
export function returnTrue() { return true; }
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2b689b7ef..a8ba0a4be 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -1,4 +1,4 @@
-import { DOMOutputSpecArray, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model";
+import { DOMOutputSpecArray, MarkSpec, Node, NodeSpec, Schema, Slice, Fragment } from "prosemirror-model";
import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
import { TextSelection, EditorState } from "prosemirror-state";
import { Doc } from "../../new_fields/Doc";
@@ -7,6 +7,8 @@ import { EditorView } from "prosemirror-view";
import { keymap } from "prosemirror-keymap";
import { undo, redo } from "prosemirror-history";
import { toggleMark, splitBlock, selectAll, baseKeymap } from "prosemirror-commands";
+import { Domain } from "domain";
+import { DOM } from "@fortawesome/fontawesome-svg-core";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -42,14 +44,6 @@ export const nodes: { [index: string]: NodeSpec } = {
toDOM() { return pDOM; }
},
- // starmine: {
- // inline: true,
- // attrs: { oldtext: { default: "" } },
- // group: "inline",
- // toDOM() { return ["star", "㊉"]; },
- // parseDOM: [{ tag: "star" }]
- // },
-
// :: NodeSpec A blockquote (`<blockquote>`) wrapping one or more blocks.
blockquote: {
content: "block+",
@@ -107,10 +101,9 @@ export const nodes: { [index: string]: NodeSpec } = {
visibility: { default: false },
text: { default: undefined },
textslice: { default: undefined },
- textlen: { default: 0 }
-
},
group: "inline",
+ inclusive: false,
toDOM(node) {
const attrs = { style: `width: 40px` };
return ["span", { ...node.attrs, ...attrs }];
@@ -205,12 +198,10 @@ export const nodes: { [index: string]: NodeSpec } = {
const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : "";
let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap;
return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0] :
- ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
- //return node.attrs.bulletStyle < 2 ? ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0] :
- // ['ol', { class: `${node.attrs.bulletStyle}`, style: `list-style: ${node.attrs.bulletStyle}; font-size: 5px` }, "hello"];
+ ['ol', { class: `${map}-ol`, style: `list-style: none;` }];
}
},
- //this doesn't currently work for some reason
+
bullet_list: {
...bulletList,
content: 'list_item+',
@@ -221,14 +212,6 @@ export const nodes: { [index: string]: NodeSpec } = {
}
},
- //bullet_list: {
- // content: 'list_item+',
- // group: 'block',
- //active: blockActive(schema.nodes.bullet_list),
- //enable: wrapInList(schema.nodes.bullet_list),
- //run: wrapInList(schema.nodes.bullet_list),
- //select: state => true,
- // },
list_item: {
attrs: {
bulletStyle: { default: 0 },
@@ -251,7 +234,6 @@ export const nodes: { [index: string]: NodeSpec } = {
const emDOM: DOMOutputSpecArray = ["em", 0];
const strongDOM: DOMOutputSpecArray = ["strong", 0];
const codeDOM: DOMOutputSpecArray = ["code", 0];
-const underlineDOM: DOMOutputSpecArray = ["underline", 0];
// :: Object [Specs](#model.MarkSpec) for the marks in the schema.
export const marks: { [index: string]: MarkSpec } = {
@@ -289,16 +271,6 @@ export const marks: { [index: string]: MarkSpec } = {
toDOM() { return strongDOM; }
},
- underline: {
- parseDOM: [
- { tag: 'u' },
- { style: 'text-decoration=underline' }
- ],
- toDOM: () => ['span', {
- style: 'text-decoration:underline'
- }]
- },
-
strikethrough: {
parseDOM: [
{ tag: 'strike' },
@@ -340,14 +312,52 @@ export const marks: { [index: string]: MarkSpec } = {
},
highlight: {
- parseDOM: [{ style: 'text-decoration: underline' }],
+ parseDOM: [
+ {
+ tag: "span",
+ getAttrs: (p: any) => {
+ if (typeof (p) !== "string") {
+ let style = getComputedStyle(p);
+ if (style.textDecoration === "underline") return null;
+ if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 &&
+ p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1)
+ return null;
+ }
+ return false;
+ }
+ },
+ ],
+ inclusive: false,
+ priority: 100,
toDOM() {
return ['span', {
- style: 'text-decoration: underline; text-decoration-color: rgba(204, 206, 210, 0.92)'
+ style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)'
}];
}
},
+ underline: {
+ parseDOM: [
+ {
+ tag: "span",
+ getAttrs: (p: any) => {
+ if (typeof (p) !== "string") {
+ let style = getComputedStyle(p);
+ if (style.textDecoration === "underline")
+ return null;
+ if (p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1)
+ return null;
+ }
+ return false;
+ }
+ }
+ // { style: "text-decoration=underline" }
+ ],
+ toDOM: () => ['span', {
+ style: 'text-decoration:underline;text-decoration-style:line'
+ }]
+ },
+
search_highlight: {
parseDOM: [{ style: 'background: yellow' }],
toDOM() {
@@ -530,9 +540,6 @@ export const marks: { [index: string]: MarkSpec } = {
}]
},
};
-function getFontSize(element: any) {
- return parseFloat((getComputedStyle(element) as any).fontSize);
-}
export class ImageResizeView {
_handle: HTMLElement;
@@ -733,73 +740,52 @@ export class FootnoteView {
}
export class SummarizedView {
- // TODO: highlight text that is summarized. to find end of region, walk along mark
_collapsed: HTMLElement;
_view: any;
constructor(node: any, view: any, getPos: any) {
this._collapsed = document.createElement("span");
- this._collapsed.textContent = node.attrs.visibility ? "㊀" : "㊉";
- this._collapsed.style.opacity = "0.5";
- this._collapsed.style.position = "relative";
- this._collapsed.style.width = "40px";
- this._collapsed.style.height = "20px";
- let self = this;
+ this._collapsed.className = this.className(node.attrs.visibility);
this._view = view;
const js = node.toJSON;
node.toJSON = function () {
-
return js.apply(this, arguments);
};
- this._collapsed.onpointerdown = function (e: any) {
- if (node.attrs.visibility) {
- // node.attrs.visibility = !node.attrs.visibility;
- let y = getPos();
- const attrs = { ...node.attrs };
- attrs.visibility = !attrs.visibility;
- let { from, to } = self.updateSummarizedText(y + 1, view.state.schema.marks.highlight);
- let length = to - from;
- let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length);
- // update attrs of node
- attrs.text = newSelection.content();
- attrs.textslice = newSelection.content().toJSON();
- view.dispatch(view.state.tr.setNodeMarkup(y, undefined, attrs));
- view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { }));
- let marks = view.state.storedMarks.filter((m: any) => m.type !== view.state.schema.marks.highlight);
- view.state.storedMarks = marks;
- self._collapsed.textContent = "㊉";
- } else {
- // node.attrs.visibility = !node.attrs.visibility;
- let y = getPos();
- const attrs = { ...node.attrs };
- attrs.visibility = !attrs.visibility;
- view.dispatch(view.state.tr.setNodeMarkup(y, undefined, attrs));
- let mark = view.state.schema.mark(view.state.schema.marks.highlight);
- view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1)));
- const from = view.state.selection.from;
- let size = node.attrs.text.size;
- view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + size, mark).removeStoredMark(mark));
- self._collapsed.textContent = "㊀";
+
+ this._collapsed.onpointerdown = (e: any) => {
+ const visible = !node.attrs.visibility;
+ const attrs = { ...node.attrs, visibility: visible };
+ let textSelection = TextSelection.create(view.state.doc, getPos() + 1, getPos() + 1);
+ if (!visible) { // update summarized text and save in attrs
+ textSelection = this.updateSummarizedText(getPos() + 1);
+ attrs.text = textSelection.content();
+ attrs.textslice = attrs.text.toJSON();
}
+ view.dispatch(view.state.tr.
+ setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed)
+ replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it
+ setNodeMarkup(getPos(), undefined, attrs)); // update the attrs
e.preventDefault();
e.stopPropagation();
+ this._collapsed.className = this.className(visible);
};
(this as any).dom = this._collapsed;
-
- }
- selectNode() {
}
+ selectNode() { }
+
+ deselectNode() { }
- updateSummarizedText(start?: any, mark?: any) {
- let $start = this._view.state.doc.resolve(start);
+ className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed");
+
+ updateSummarizedText(start?: any) {
+ let mark = this._view.state.schema.marks.highlight.create();
let endPos = start;
- let _mark = this._view.state.schema.mark(this._view.state.schema.marks.highlight);
let visited = new Set();
for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) {
let skip = false;
this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => {
if (node.isLeaf && !visited.has(node) && !skip) {
- if (node.marks.find((m: any) => m.type === _mark.type)) {
+ if (node.marks.find((m: any) => m.type === mark.type)) {
visited.add(node);
endPos = i + node.nodeSize - 1;
}
@@ -807,10 +793,7 @@ export class SummarizedView {
}
});
}
- return { from: start, to: endPos };
- }
-
- deselectNode() {
+ return TextSelection.create(this._view.state.doc, start, endPos);
}
}
// :: Schema
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 6d375fc1d..020c51c36 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -307,10 +307,9 @@ export class TooltipTextMenu {
{
handlers: {
dragComplete: action(() => {
- // let m = dragData.droppedDocuments;
let linkDoc = dragData.linkDocument;
let proto = Doc.GetProto(linkDoc);
- if (docView && docView.props.ContainingCollectionView) {
+ if (proto && docView && docView.props.ContainingCollectionView) {
proto.sourceContext = docView.props.ContainingCollectionView.props.Document;
}
linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
@@ -322,8 +321,6 @@ export class TooltipTextMenu {
e.preventDefault();
};
this.linkEditor.appendChild(this.linkDrag);
- // this.linkEditor.appendChild(this.linkText);
- // this.linkEditor.appendChild(linkBtn);
this.tooltip.appendChild(this.linkEditor);
}
@@ -432,11 +429,13 @@ export class TooltipTextMenu {
}
public static insertStar(state: EditorState<any>, dispatch: any) {
- let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), textslice: state.selection.content().toJSON(), textlen: state.selection.to - state.selection.from });
- if (dispatch) {
- //console.log(newNode.attrs.text.toString());
- dispatch(state.tr.replaceSelectionWith(newNode));
- }
+ if (state.selection.empty) return false;
+ let mark = state.schema.marks.highlight.create();
+ let tr = state.tr;
+ tr.addMark(state.selection.from, state.selection.to, mark);
+ let content = tr.selection.content();
+ let newNode = schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() });
+ dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark));
return true;
}
@@ -637,7 +636,6 @@ export class TooltipTextMenu {
Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
const markType = mark.type;
this.changeToMarkInGroup(markType, this.view, []);
-
});
}
}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 8f47402c4..0de050e79 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -131,6 +131,26 @@ footnote::after {
width: 0;
}
+.formattedTextBox-summarizer {
+ opacity :0.5;
+ position: relative;
+ width:40px;
+ height:20px;
+}
+.formattedTextBox-summarizer::after{
+ content: "←" ;
+}
+
+.formattedTextBox-summarizer-collapsed {
+ opacity :0.5;
+ position: relative;
+ width:40px;
+ height:20px;
+}
+.formattedTextBox-summarizer-collapsed::after {
+ content: "...";
+}
+
ol { counter-reset: deci1 0;}
.decimal1-ol {counter-reset: deci1; p { display: inline }; font-size: 24 }
.decimal2-ol {counter-reset: deci2; p { display: inline }; font-size: 18 }
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index d811273e8..006a33011 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -15,7 +15,7 @@ import { List } from '../../../new_fields/List';
import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField";
import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Utils, numberRange } from '../../../Utils';
+import { Utils, numberRange, timenow } from '../../../Utils';
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
@@ -117,6 +117,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@undoBatch
public setFontColor(color: string) {
+ this._editorView!.state.storedMarks
if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) {
this.props.Document.color = color;
@@ -654,10 +655,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this.props.select(false);
}
else if (this.props.isOverlay) this._editorView!.focus();
- var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
- let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail })];
- this._editorView!.state.storedMarks = newMarks;
-
+ this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).
+ addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })));
}
componentWillUnmount() {
@@ -681,10 +680,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
- if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) {
- //this._toolTipTextMenu.tooltip.style.opacity = "0";
- }
}
+ this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).
+ addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }));
let ctrlKey = e.ctrlKey;
if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) {
let href = (e.target as any).href;
@@ -744,13 +742,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
- setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
- let view = this._editorView!;
- let mid = view.state.doc.resolve(Math.round((start + end) / 2));
- let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
- view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark).setSelection(new TextSelection(mid, mid)));
- }
-
@action
onFocused = (e: React.FocusEvent): void => {
document.removeEventListener("keypress", this.recordKeyHandler);
@@ -774,7 +765,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onClick = (e: React.MouseEvent): void => {
if (this.props.isSelected() && e.nativeEvent.offsetX < 40) {
let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
- if (pos) {
+ if (pos && pos.pos > 0) {
let node = this._editorView!.state.doc.nodeAt(pos.pos);
let node2 = node && node.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
if (node === this._nodeClicked && node2 && (node2.type === schema.nodes.ordered_list || node2.type === schema.nodes.list_item)) {
@@ -833,24 +824,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
// SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(this.props.Document, this.props.ContainingCollectionView)!, false);
// }, 0);
}
- function timenow() {
- var now = new Date();
- let ampm = 'am';
- let h = now.getHours();
- let m: any = now.getMinutes();
- let s: any = now.getSeconds();
- if (h >= 12) {
- if (h > 12) h -= 12;
- ampm = 'pm';
- }
-
- if (m < 10) m = '0' + m;
- return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
- }
- var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
- let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
- this._editorView!.state.storedMarks = newMarks;
-
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
(e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index a703f1cef..1462cdfad 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -308,7 +308,7 @@ export namespace Doc {
// gets the document's prototype or returns the document if it is a prototype
export function GetProto(doc: Doc) {
- return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc);
+ return doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc));
}
export function GetDataDoc(doc: Doc): Doc {
let proto = Doc.GetProto(doc);