aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DictationManager.ts5
-rw-r--r--src/client/util/DocumentManager.ts48
-rw-r--r--src/client/util/DragManager.ts34
-rw-r--r--src/client/util/DropConverter.ts46
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx2
-rw-r--r--src/client/util/InteractionUtils.ts111
-rw-r--r--src/client/util/LinkManager.ts21
-rw-r--r--src/client/util/RichTextSchema.tsx199
-rw-r--r--src/client/util/SearchUtil.ts7
-rw-r--r--src/client/util/SelectionManager.ts15
-rw-r--r--src/client/util/TooltipTextMenu.scss571
-rw-r--r--src/client/util/TooltipTextMenu.tsx1397
12 files changed, 1505 insertions, 951 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 182cfb70a..6bbd3d0ed 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -6,7 +6,7 @@ import { DocumentType } from "../documents/DocumentTypes";
import { Doc, Opt } from "../../new_fields/Doc";
import { List } from "../../new_fields/List";
import { Docs } from "../documents/Documents";
-import { CollectionViewType } from "../views/collections/CollectionBaseView";
+import { CollectionViewType } from "../views/collections/CollectionView";
import { Cast, CastCtor } from "../../new_fields/Types";
import { listSpec } from "../../new_fields/Schema";
import { AudioField, ImageField } from "../../new_fields/URLField";
@@ -64,7 +64,7 @@ export namespace DictationManager {
const intraSession = ". ";
const interSession = " ... ";
- let isListening = false;
+ export let isListening = false;
let isManuallyStopped = false;
let current: string | undefined = undefined;
@@ -200,6 +200,7 @@ export namespace DictationManager {
if (!isListening || !recognizer) {
return;
}
+ isListening = false;
isManuallyStopped = true;
salvageSession ? recognizer.stop() : recognizer.abort();
// let main = MainView.Instance;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 00de39671..346e88f40 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,5 +1,5 @@
import { action, computed, observable } from 'mobx';
-import { Doc, DocListCastAsync } from '../../new_fields/Doc';
+import { Doc, DocListCastAsync, DocListCast } from '../../new_fields/Doc';
import { Id } from '../../new_fields/FieldSymbols';
import { List } from '../../new_fields/List';
import { Cast, NumCast, StrCast } from '../../new_fields/Types';
@@ -9,6 +9,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
import { SelectionManager } from './SelectionManager';
+import { DocumentType } from '../documents/DocumentTypes';
export class DocumentManager {
@@ -41,8 +42,8 @@ export class DocumentManager {
if (toReturn.length === 0) {
DocumentManager.Instance.DocumentViews.map(view => {
let doc = view.props.Document.proto;
- if (doc && doc[Id]) {
- if (doc[Id] === id) { toReturn.push(view); }
+ if (doc && doc[Id] && doc[Id] === id) {
+ toReturn.push(view);
}
});
}
@@ -72,6 +73,8 @@ export class DocumentManager {
toReturn = view;
}
});
+ } else {
+ break;
}
}
@@ -87,49 +90,40 @@ export class DocumentManager {
return views.length ? views[0] : undefined;
}
public getDocumentViews(toFind: Doc): DocumentView[] {
-
let toReturn: DocumentView[] = [];
- //gets document view that is in a freeform canvas collection
- DocumentManager.Instance.DocumentViews.map(view => {
- let doc = view.props.Document;
-
- if (doc === toFind) {
- toReturn.push(view);
- } else {
- if (Doc.AreProtosEqual(doc, toFind)) {
- toReturn.push(view);
- }
- }
- });
+ DocumentManager.Instance.DocumentViews.map(view =>
+ Doc.AreProtosEqual(view.props.Document, toFind) && toReturn.push(view));
return toReturn;
}
@computed
public get LinkedDocumentViews() {
- let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || Doc.IsBrushed(dv.props.Document)).reduce((pairs, dv) => {
+ let pairs = DocumentManager.Instance.DocumentViews.filter(dv =>
+ (dv.isSelected() || Doc.IsBrushed(dv.props.Document)) // draw links from DocumentViews that are selected or brushed OR
+ || DocumentManager.Instance.DocumentViews.some(dv2 => { // Documentviews which
+ let rest = DocListCast(dv2.props.Document.links).some(l => Doc.AreProtosEqual(l, dv.props.Document));// are link doc anchors
+ let init = (dv2.isSelected() || Doc.IsBrushed(dv2.props.Document)) && dv2.Document.type !== DocumentType.AUDIO; // on a view that is selected or brushed
+ return init && rest;
+ })
+ ).reduce((pairs, dv) => {
let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
pairs.push(...linksList.reduce((pairs, link) => {
- if (link) {
- let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
- if (linkToDoc) {
- DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
- pairs.push({ a: dv, b: docView1, l: link });
- });
+ let linkToDoc = link && LinkManager.Instance.getOppositeAnchor(link, dv.props.Document);
+ linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
+ if (dv.props.Document.type !== DocumentType.LINK || dv.props.layoutKey !== docView1.props.layoutKey) {
+ pairs.push({ a: dv, b: docView1, l: link });
}
- }
+ });
return pairs;
}, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
- // }
return pairs;
}, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
return pairs;
}
-
-
public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, linkId?: string, closeContextIfNotFound: boolean = false): Promise<void> => {
let highlight = () => {
const finalDocView = DocumentManager.Instance.getFirstDocumentView(targetDoc);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 2c316ccdf..bbc29585c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -14,6 +14,8 @@ import { ScriptField } from "../../new_fields/ScriptField";
import { List } from "../../new_fields/List";
import { PrefetchProxy } from "../../new_fields/Proxy";
import { listSpec } from "../../new_fields/Schema";
+import { Scripting } from "./Scripting";
+import { convertDropDataToButtons } from "./DropConverter";
export type dropActionType = "alias" | "copy" | undefined;
export function SetupDrag(
@@ -87,12 +89,12 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num
}
}
-export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) {
+export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc, singleLink?: Doc) {
let srcTarg = sourceDoc.proto;
let draggedDocs: Doc[] = [];
if (srcTarg) {
- let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg);
+ let linkDocs = singleLink ? [singleLink] : LinkManager.Instance.getAllRelatedLinks(srcTarg);
if (linkDocs) {
draggedDocs = linkDocs.map(link => {
let opp = LinkManager.Instance.getOppositeAnchor(link, sourceDoc);
@@ -211,7 +213,9 @@ export namespace DragManager {
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
+ embedDoc?: boolean;
moveDocument?: MoveFunction;
+ isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts
applyAsTemplate?: boolean;
[id: string]: any;
}
@@ -234,13 +238,13 @@ export namespace DragManager {
export let StartDragFunctions: (() => void)[] = [];
- export async function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
runInAction(() => StartDragFunctions.map(func => func()));
- await dragData.draggedDocuments.map(d => d.dragFactory);
+ dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded
StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
(dropData: { [id: string]: any }) => {
(dropData.droppedDocuments =
- dragData.draggedDocuments.map(d => ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
+ dragData.draggedDocuments.map(d => !dragData.isSelectionMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
);
@@ -304,16 +308,6 @@ export namespace DragManager {
[id: string]: any;
}
- export class EmbedDragData {
- constructor(embeddableSourceDoc: Doc) {
- this.embeddableSourceDoc = embeddableSourceDoc;
- this.urlField = embeddableSourceDoc.data instanceof URLField ? embeddableSourceDoc.data : undefined;
- }
- embeddableSourceDoc: Doc;
- urlField?: URLField;
- [id: string]: any;
- }
-
// for column dragging in schema view
export class ColumnDragData {
constructor(colKey: SchemaHeaderField) {
@@ -327,10 +321,6 @@ export namespace DragManager {
StartDrag([ele], dragData, downX, downY, options);
}
- export function StartEmbedDrag(ele: HTMLElement, dragData: EmbedDragData, downX: number, downY: number, options?: DragOptions) {
- StartDrag([ele], dragData, downX, downY, options);
- }
-
export function StartColumnDrag(ele: HTMLElement, dragData: ColumnDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag([ele], dragData, downX, downY, options);
}
@@ -426,16 +416,17 @@ export namespace DragManager {
const moveHandler = (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
if (dragData instanceof DocumentDragData) {
- dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined;
+ dragData.userDropAction = e.ctrlKey ? "alias" : undefined;
}
if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) {
AbortDrag();
+ finishDrag && finishDrag(dragData);
CollectionDockingView.Instance.StartOtherDrag({
pageX: e.pageX,
pageY: e.pageY,
preventDefault: emptyFunction,
button: 0
- }, docs);
+ }, dragData.droppedDocuments);
}
//TODO: Why can't we use e.movementX and e.movementY?
let moveX = e.pageX - lastX;
@@ -508,3 +499,4 @@ export namespace DragManager {
}
}
}
+Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); });
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
new file mode 100644
index 000000000..6b53333d7
--- /dev/null
+++ b/src/client/util/DropConverter.ts
@@ -0,0 +1,46 @@
+import { DragManager } from "./DragManager";
+import { CollectionViewType } from "../views/collections/CollectionView";
+import { Doc, DocListCast } from "../../new_fields/Doc";
+import { DocumentType } from "../documents/DocumentTypes";
+import { ObjectField } from "../../new_fields/ObjectField";
+import { StrCast } from "../../new_fields/Types";
+import { Docs } from "../documents/Documents";
+import { ScriptField } from "../../new_fields/ScriptField";
+
+
+function makeTemplate(doc: Doc): boolean {
+ let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+ let layout = StrCast(layoutDoc.layout).match(/fieldKey={"[^"]*"}/)![0];
+ let fieldKey = layout.replace('fieldKey={"', "").replace(/"}$/, "");
+ let docs = DocListCast(layoutDoc[fieldKey]);
+ let any = false;
+ docs.map(d => {
+ if (!StrCast(d.title).startsWith("-")) {
+ any = true;
+ return Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc));
+ }
+ if (d.type === DocumentType.COL) return makeTemplate(d);
+ return false;
+ });
+ return any;
+}
+export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
+ data && data.draggedDocuments.map((doc, i) => {
+ let dbox = doc;
+ if (!doc.onDragStart && !doc.onClick && doc.viewType !== CollectionViewType.Linear) {
+ let layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc;
+ if (layoutDoc.type === DocumentType.COL) {
+ layoutDoc.isTemplateDoc = makeTemplate(layoutDoc);
+ } else {
+ layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && !data.userDropAction;
+ }
+ dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" });
+ dbox.dragFactory = layoutDoc;
+ dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+ dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
+ } else if (doc.viewType === CollectionViewType.Linear) {
+ dbox.ignoreClick = true;
+ }
+ data.droppedDocuments[i] = dbox;
+ });
+}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 437e7766b..f0880f193 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -51,7 +51,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
@observable private uploading = false;
@observable private removeHover = false;
- public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DirectoryImportBox, fieldKey); }
constructor(props: FieldViewProps) {
super(props);
diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts
new file mode 100644
index 000000000..e58635a6f
--- /dev/null
+++ b/src/client/util/InteractionUtils.ts
@@ -0,0 +1,111 @@
+export namespace InteractionUtils {
+ export const MOUSE = "mouse";
+ export const TOUCH = "touch";
+
+ export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean {
+ return e.pointerType === type;
+ }
+
+ export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number {
+ return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2));
+ }
+
+ /**
+ * Returns the centroid of an n-arbitrary long list of points (takes the average the x and y components of each point)
+ * @param pts - n-arbitrary long list of points
+ */
+ export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } {
+ let centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length;
+ let centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length;
+ return { X: centerX, Y: centerY };
+ }
+
+ /**
+ * Returns -1 if pinching out, 0 if not pinching, and 1 if pinching in
+ * @param pt1 - new point that corresponds to oldPoint1
+ * @param pt2 - new point that corresponds to oldPoint2
+ * @param oldPoint1 - previous point 1
+ * @param oldPoint2 - previous point 2
+ */
+ export function Pinching(pt1: React.Touch, pt2: React.Touch, oldPoint1: React.Touch, oldPoint2: React.Touch): number {
+ let threshold = 4;
+ let oldDist = TwoPointEuclidist(oldPoint1, oldPoint2);
+ let newDist = TwoPointEuclidist(pt1, pt2);
+
+ /** if they have the same sign, then we are either pinching in or out.
+ * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch)
+ * so that it can still pan without freaking out
+ */
+ if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) {
+ return Math.sign(oldDist - newDist);
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the type of Touch Interaction from a list of points.
+ * Also returns any data that is associated with a Touch Interaction
+ * @param pts - List of points
+ */
+ // export function InterpretPointers(pts: React.Touch[]): { type: Opt<TouchInteraction>, data?: any } {
+ // const leniency = 200;
+ // switch (pts.length) {
+ // case 1:
+ // return { type: OneFinger };
+ // case 2:
+ // return { type: TwoSeperateFingers };
+ // case 3:
+ // let pt1 = pts[0];
+ // let pt2 = pts[1];
+ // let pt3 = pts[2];
+ // if (pt1 && pt2 && pt3) {
+ // let dist12 = TwoPointEuclidist(pt1, pt2);
+ // let dist23 = TwoPointEuclidist(pt2, pt3);
+ // let dist13 = TwoPointEuclidist(pt1, pt3);
+ // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`);
+ // let dist12close = dist12 < leniency;
+ // let dist23close = dist23 < leniency;
+ // let dist13close = dist13 < leniency;
+ // let xor2313 = dist23close ? !dist13close : dist13close;
+ // let xor = dist12close ? !xor2313 : xor2313;
+ // // three input xor because javascript doesn't have logical xor's
+ // if (xor) {
+ // let points: number[] = [];
+ // let min = Math.min(dist12, dist23, dist13);
+ // switch (min) {
+ // case dist12:
+ // points = [0, 1, 2];
+ // break;
+ // case dist23:
+ // points = [1, 2, 0];
+ // break;
+ // case dist13:
+ // points = [0, 2, 1];
+ // break;
+ // }
+ // return { type: TwoToOneFingers, data: points };
+ // }
+ // else {
+ // return { type: ThreeSeperateFingers, data: null };
+ // }
+ // }
+ // default:
+ // return { type: undefined };
+ // }
+ // }
+
+ export function IsDragging(oldTouches: Map<number, React.Touch>, newTouches: TouchList, leniency: number): boolean {
+ for (let i = 0; i < newTouches.length; i++) {
+ let touch = newTouches.item(i);
+ if (touch) {
+ let oldTouch = oldTouches.get(touch.identifier);
+ if (oldTouch) {
+ if (TwoPointEuclidist(touch, oldTouch) >= leniency) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 8a668e8d8..eedc4967d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,10 +1,7 @@
-import { observable, action } from "mobx";
-import { StrCast, Cast, FieldValue } from "../../new_fields/Types";
import { Doc, DocListCast } from "../../new_fields/Doc";
-import { listSpec } from "../../new_fields/Schema";
import { List } from "../../new_fields/List";
-import { Id } from "../../new_fields/FieldSymbols";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { listSpec } from "../../new_fields/Schema";
+import { Cast, StrCast } from "../../new_fields/Types";
import { Docs } from "../documents/Documents";
import { Scripting } from "./Scripting";
@@ -37,8 +34,6 @@ export class LinkManager {
// the linkmanagerdoc stores a list of docs representing all linkdocs in 'allLinks' and a list of strings representing all group types in 'allGroupTypes'
// lists of strings representing the metadata keys for each group type is stored under a key that is the same as the group type
public get LinkManagerDoc(): Doc | undefined {
- // return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc));
-
return Docs.Prototypes.MainLinkDocument();
}
@@ -79,7 +74,7 @@ export class LinkManager {
let related = LinkManager.Instance.getAllLinks().filter(link => {
let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, null));
let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, null));
- return protomatch1 || protomatch2;
+ return protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor);
});
return related;
}
@@ -242,11 +237,11 @@ export class LinkManager {
//TODO This should probably return undefined if there isn't an opposite anchor
//TODO This should also await the return value of the anchor so we don't filter out promises
public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
- if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) {
- return Cast(linkDoc.anchor2, Doc, null);
- } else {
- return Cast(linkDoc.anchor1, Doc, null);
- }
+ let a1 = Cast(linkDoc.anchor1, Doc, null);
+ let a2 = Cast(linkDoc.anchor2, Doc, null);
+ if (Doc.AreProtosEqual(anchor, a1)) return a2;
+ if (Doc.AreProtosEqual(anchor, a2)) return a1;
+ if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
}
Scripting.addGlobal(function links(doc: any) {
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index cc3548e1a..0a717dff1 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -18,7 +18,6 @@ import { Transform } from "./Transform";
import React = require("react");
import { BoolCast, NumCast } from "../../new_fields/Types";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
-import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -130,6 +129,7 @@ export const nodes: { [index: string]: NodeSpec } = {
// }
// }]
},
+
// :: NodeSpec An inline image (`<img>`) node. Supports `src`,
// `alt`, and `href` attributes. The latter two default to the empty
// string.
@@ -241,6 +241,7 @@ export const nodes: { [index: string]: NodeSpec } = {
},
toDOM(node: Node<any>) {
const bs = node.attrs.bulletStyle;
+ if (node.attrs.mapStyle === "bullet") return ['ul', 0];
const decMap = bs ? "decimal" + bs : "";
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;
@@ -273,7 +274,7 @@ export const nodes: { [index: string]: NodeSpec } = {
const bs = node.attrs.bulletStyle;
const decMap = bs ? "decimal" + bs : "";
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;
+ let map = node.attrs.mapStyle === "decimal" ? decMap : node.attrs.mapStyle === "multi" ? multiMap : "";
return node.attrs.visibility ? ["li", { class: `${map}` }, 0] : ["li", { class: `${map}` }, "..."];
//return ["li", { class: `${map}` }, 0];
}
@@ -304,8 +305,39 @@ export const marks: { [index: string]: MarkSpec } = {
}],
toDOM(node: any) {
return node.attrs.docref && node.attrs.title ?
- ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
- ["a", { ...node.attrs }, 0];
+ ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] :
+ ["a", { ...node.attrs, title: `${node.attrs.title}` }, 0];
+ }
+ },
+
+ // :: MarkSpec Coloring on text. Has `color` attribute that defined the color of the marked text.
+ color: {
+ attrs: {
+ color: { default: "#000" }
+ },
+ inclusive: false,
+ 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', { style: 'color: black' }];
+ }
+ },
+
+ marker: {
+ attrs: {
+ highlight: { default: "transparent" }
+ },
+ inclusive: false,
+ parseDOM: [{
+ tag: "span", getAttrs(dom: any) {
+ return { highlight: dom.getAttribute("backgroundColor") };
+ }
+ }],
+ toDOM(node: any) {
+ return node.attrs.highlight ? ['span', { style: 'background-color:' + node.attrs.highlight }] : ['span', { style: 'background-color: transparent' }];
}
},
@@ -482,72 +514,26 @@ export const marks: { [index: string]: MarkSpec } = {
toDOM() { return codeDOM; }
},
- // pFontFamily: {
- // attrs: {
- // style: { default: 'font-family: "Times New Roman", Times, serif;' },
- // },
- // parseDOM: [{
- // tag: "span", getAttrs(dom: any) {
- // if (getComputedStyle(dom).font === "Times New Roman") return { style: `font-family: "Times New Roman", Times, serif;` };
- // if (getComputedStyle(dom).font === "Arial, Helvetica") return { style: `font-family: Arial, Helvetica, sans-serif;` };
- // if (getComputedStyle(dom).font === "Georgia") return { style: `font-family: Georgia, serif;` };
- // if (getComputedStyle(dom).font === "Comic Sans") return { style: `font-family: "Comic Sans MS", cursive, sans-serif;` };
- // if (getComputedStyle(dom).font === "Tahoma, Geneva") return { style: `font-family: Tahoma, Geneva, sans-serif;` };
- // }
- // }],
- // toDOM: (node: any) => ['span', {
- // style: node.attrs.style
- // }]
- // },
-
-
/* FONTS */
- timesNewRoman: {
- parseDOM: [{ style: 'font-family: "Times New Roman", Times, serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: "Times New Roman", Times, serif;'
- }]
- },
-
- arial: {
- parseDOM: [{ style: 'font-family: Arial, Helvetica, sans-serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: Arial, Helvetica, sans-serif;'
- }]
- },
-
- georgia: {
- parseDOM: [{ style: 'font-family: Georgia, serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: Georgia, serif;'
- }]
- },
-
- comicSans: {
- parseDOM: [{ style: 'font-family: "Comic Sans MS", cursive, sans-serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: "Comic Sans MS", cursive, sans-serif;'
- }]
- },
-
- tahoma: {
- parseDOM: [{ style: 'font-family: Tahoma, Geneva, sans-serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: Tahoma, Geneva, sans-serif;'
- }]
- },
-
- impact: {
- parseDOM: [{ style: 'font-family: Impact, Charcoal, sans-serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: Impact, Charcoal, sans-serif;'
- }]
- },
-
- crimson: {
- parseDOM: [{ style: 'font-family: "Crimson Text", sans-serif;' }],
- toDOM: () => ['span', {
- style: 'font-family: "Crimson Text", sans-serif;'
+ pFontFamily: {
+ attrs: {
+ family: { default: "Crimson Text" },
+ },
+ parseDOM: [{
+ tag: "span", getAttrs(dom: any) {
+ let 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}";`
}]
},
@@ -573,76 +559,6 @@ export const marks: { [index: string]: MarkSpec } = {
style: `font-size: ${node.attrs.fontSize}px;`
}]
},
-
- p10: {
- parseDOM: [{ style: 'font-size: 10px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 10px;'
- }]
- },
-
- p12: {
- parseDOM: [{ style: 'font-size: 12px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 12px;'
- }]
- },
-
- p14: {
- parseDOM: [{ style: 'font-size: 14px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 14px;'
- }]
- },
-
- p16: {
- parseDOM: [{ style: 'font-size: 16px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 16px;'
- }]
- },
-
- p18: {
- parseDOM: [{ style: 'font-size: 18px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 18px;'
- }]
- },
-
- p20: {
- parseDOM: [{ style: 'font-size: 20px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 20px;'
- }]
- },
-
- p24: {
- parseDOM: [{ style: 'font-size: 24px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 24px;'
- }]
- },
-
- p32: {
- parseDOM: [{ style: 'font-size: 32px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 32px;'
- }]
- },
-
- p48: {
- parseDOM: [{ style: 'font-size: 48px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 48px;'
- }]
- },
-
- p72: {
- parseDOM: [{ style: 'font-size: 72px;' }],
- toDOM: () => ['span', {
- style: 'font-size: 72px;'
- }]
- },
};
export class ImageResizeView {
@@ -691,8 +607,8 @@ export class ImageResizeView {
this._handle.onpointerdown = function (e: any) {
e.preventDefault();
e.stopPropagation();
- let wid = Number(getComputedStyle(self._img).width!.replace(/px/, ""));
- let hgt = Number(getComputedStyle(self._img).height!.replace(/px/, ""));
+ let wid = Number(getComputedStyle(self._img).width.replace(/px/, ""));
+ let hgt = Number(getComputedStyle(self._img).height.replace(/px/, ""));
const startX = e.pageX;
const startWidth = parseFloat(node.attrs.width);
const onpointermove = (e: any) => {
@@ -795,6 +711,7 @@ export class DashDocView {
bringToFront={emptyFunction}
zoomToScale={emptyFunction}
getScale={returnOne}
+ dontRegisterView={true}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
ContentScaling={this.contentScaling}
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 6706dcb89..2cf13680a 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -28,6 +28,7 @@ export namespace SearchUtil {
start?: number;
rows?: number;
fq?: string;
+ allowAliases?: boolean;
}
export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
@@ -73,7 +74,7 @@ export namespace SearchUtil {
const docs = ids.map((id: string) => docMap[id]).map(doc => doc as Doc);
for (let i = 0; i < ids.length; i++) {
let testDoc = docs[i];
- if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1) {
+ if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
theDocs.push(testDoc);
theLines.push([]);
}
@@ -88,9 +89,9 @@ export namespace SearchUtil {
const proto = Doc.GetProto(doc);
const protoId = proto[Id];
if (returnDocs) {
- return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).docs;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).docs;
} else {
- return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).ids;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"`, allowAliases: true })).ids;
}
// return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 398c90ddb..ca61f9014 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -3,7 +3,6 @@ import { Doc, Opt } from "../../new_fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { NumCast, StrCast } from "../../new_fields/Types";
-import { InkingControl } from "../views/InkingControl";
export namespace SelectionManager {
@@ -45,20 +44,6 @@ export namespace SelectionManager {
}
const manager = new Manager();
- reaction(() => manager.SelectedDocuments, sel => {
- let targetColor = "#FFFFFF";
- if (sel.length > 0) {
- let firstView = sel[0];
- let doc = firstView.props.Document;
- let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc);
- let stored = StrCast(targetDoc.backgroundColor);
- stored.length > 0 && (targetColor = stored);
- }
- const { Instance } = InkingControl;
- if (Instance) {
- Instance.updateSelectedColor(targetColor);
- }
- }, { fireImmediately: true });
export function DeselectDoc(docView: DocumentView): void {
manager.DeselectDoc(docView);
diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss
index ebf833dbe..8310a0da6 100644
--- a/src/client/util/TooltipTextMenu.scss
+++ b/src/client/util/TooltipTextMenu.scss
@@ -1,100 +1,62 @@
@import "../views/globalCssVariables";
-
-.ProseMirror-textblock-dropdown {
- min-width: 3em;
- }
-
- .ProseMirror-menu {
- margin: 0 -4px;
- line-height: 1;
- }
-
- .ProseMirror-tooltip .ProseMirror-menu {
- width: -webkit-fit-content;
- width: fit-content;
- white-space: pre;
- }
-
- .ProseMirror-menuitem {
- margin-right: 3px;
+.ProseMirror-menu-dropdown-wrap {
display: inline-block;
- z-index: 50000;
position: relative;
- }
-
- .ProseMirror-menuseparator {
- // border-right: 1px solid #ddd;
- margin-right: 3px;
- }
-
- .ProseMirror-menu-dropdown, .ProseMirror-menu-dropdown-menu {
- font-size: 90%;
- white-space: nowrap;
- }
+}
- .ProseMirror-menu-dropdown {
+.ProseMirror-menu-dropdown {
vertical-align: 1px;
cursor: pointer;
position: relative;
- padding-right: 15px;
- margin: 3px;
+ padding: 0 15px 0 4px;
background: white;
- border-radius: 3px;
- text-align: center;
- }
-
- .ProseMirror-menu-dropdown-wrap {
- padding: 1px 0 1px 4px;
- display: inline-block;
+ border-radius: 2px;
+ text-align: left;
+ font-size: 12px;
+ white-space: nowrap;
+ margin-right: 4px;
+
+ &:after {
+ content: "";
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid currentColor;
+ opacity: .6;
+ position: absolute;
+ right: 4px;
+ top: calc(50% - 2px);
+ }
+}
+
+.ProseMirror-menu-submenu-wrap {
position: relative;
- }
-
- .ProseMirror-menu-dropdown:after {
- content: "";
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 4px solid currentColor;
- opacity: .6;
- position: absolute;
- right: 4px;
- top: calc(50% - 2px);
- }
-
- .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu {
- background: $dark-color;
- color:white;
+ margin-right: -4px;
+}
+
+.ProseMirror-menu-dropdown-menu,
+.ProseMirror-menu-submenu {
+ font-size: 12px;
+ background: white;
border: 1px solid rgb(223, 223, 223);
- padding: 2px;
- }
-
- .ProseMirror-menu-dropdown-menu {
+ min-width: 40px;
z-index: 50000;
- min-width: 6em;
- background: white;
position: absolute;
- }
-
- .linking {
- text-align: center;
- }
+ box-sizing: content-box;
- .ProseMirror-menu-dropdown-item {
- cursor: pointer;
- padding: 2px 8px 2px 4px;
- width: auto;
- z-index: 100000;
- }
-
- .ProseMirror-menu-dropdown-item:hover {
- background: white;
- }
-
- .ProseMirror-menu-submenu-wrap {
- position: relative;
- margin-right: -4px;
- }
-
- .ProseMirror-menu-submenu-label:after {
+ .ProseMirror-menu-dropdown-item {
+ cursor: pointer;
+ z-index: 100000;
+ text-align: left;
+ padding: 3px;
+
+ &:hover {
+ background-color: $light-color-secondary;
+ }
+ }
+}
+
+
+.ProseMirror-menu-submenu-label:after {
content: "";
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
@@ -103,153 +65,51 @@
position: absolute;
right: 4px;
top: calc(50% - 4px);
- }
-
- .ProseMirror-menu-submenu {
- display: none;
- min-width: 4em;
- left: 100%;
- top: -3px;
- }
-
- .ProseMirror-menu-active {
- background: #eee;
- border-radius: 4px;
- }
-
- .ProseMirror-menu-active {
- background: #eee;
- border-radius: 4px;
- }
-
- .ProseMirror-menu-disabled {
- opacity: .3;
- }
-
- .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu, .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
- display: block;
- }
-
- .ProseMirror-menubar {
- border-top-left-radius: inherit;
- border-top-right-radius: inherit;
- position: relative;
- min-height: 1em;
- color: white;
- padding: 10px 10px;
- top: 0; left: 0; right: 0;
- border-bottom: 1px solid silver;
- background:$dark-color;
- z-index: 10;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- overflow: visible;
- }
+}
.ProseMirror-icon {
display: inline-block;
- line-height: .8;
- vertical-align: -2px; /* Compensate for padding */
- padding: 2px 8px;
+ // line-height: .8;
+ // vertical-align: -2px; /* Compensate for padding */
+ // padding: 2px 8px;
cursor: pointer;
- }
-
- .ProseMirror-menu-disabled.ProseMirror-icon {
- cursor: default;
- }
-
- .ProseMirror-icon svg {
- fill:white;
- height: 1em;
- }
-
- .ProseMirror-icon span {
- vertical-align: text-top;
- }
-
- .ProseMirror ul, .ProseMirror ol {
- padding-left: 30px;
- }
-
- .ProseMirror blockquote {
- padding-left: 1em;
- border-left: 3px solid #eee;
- margin-left: 0; margin-right: 0;
- }
-
- .ProseMirror-example-setup-style img {
- cursor: default;
- }
-
- .ProseMirror-prompt {
- background: white;
- padding: 5px 10px 5px 15px;
- border: 1px solid silver;
- position: fixed;
- border-radius: 3px;
- z-index: 11;
- box-shadow: -.5px 2px 5px white(255, 255, 255, 0.2);
- }
-
- .ProseMirror-prompt h5 {
- margin: 0;
- font-weight: normal;
- font-size: 100%;
- color: #444;
- }
-
- .ProseMirror-prompt input[type="text"],
- .ProseMirror-prompt textarea {
- background: white;
- border: none;
- outline: none;
- }
-
- .ProseMirror-prompt input[type="text"] {
- padding: 0 4px;
- }
-
- .ProseMirror-prompt-close {
- position: absolute;
- left: 2px; top: 1px;
- color: #666;
- border: none; background: transparent; padding: 0;
- }
-
- .ProseMirror-prompt-close:after {
- content: "✕";
- font-size: 12px;
- }
-
- .ProseMirror-invalid {
- background: #ffc;
- border: 1px solid #cc7;
- border-radius: 4px;
- padding: 5px 10px;
- position: absolute;
- min-width: 10em;
- }
-
- .ProseMirror-prompt-buttons {
- margin-top: 5px;
- display: none;
+
+ &.ProseMirror-menu-disabled {
+ cursor: default;
+ }
+
+ svg {
+ fill:white;
+ height: 1em;
+ }
+
+ span {
+ vertical-align: text-top;
+ }
}
-.tooltipMenu {
+.wrapper {
position: absolute;
- z-index: 20000;
- background: #121721;
- border: 1px solid silver;
- border-radius: 15px;
- //height: 60px;
- //padding: 2px 10px;
- //margin-top: 100px;
- //-webkit-transform: translateX(-50%);
- //transform: translateX(-50%);
+ pointer-events: all;
+ display: flex;
+ align-items: center;
transform: translateY(-85px);
+
+ height: 35px;
+ background: #323232;
+ border-radius: 6px;
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+
+}
+
+.tooltipMenu, .basic-tools {
+ z-index: 20000;
pointer-events: all;
- height: fit-content;
- width:550px;
+ padding: 3px;
+ padding-bottom: 5px;
+ display: flex;
+ align-items: center;
+
.ProseMirror-example-setup-style hr {
padding: 2px 10px;
border: none;
@@ -265,60 +125,89 @@
}
}
-.tooltipExtras {
- position: absolute;
- z-index: 20000;
- background: #121721;
- border: 1px solid silver;
- border-radius: 15px;
- //height: 60px;
- //padding: 2px 10px;
- //margin-top: 100px;
- //-webkit-transform: translateX(-50%);
- //transform: translateX(-50%);
- transform: translateY(-115px);
- pointer-events: all;
+.menuicon {
+ width: 25px;
height: 25px;
- width:fit-content;
- .ProseMirror-example-setup-style hr {
- padding: 2px 10px;
- border: none;
- margin: 1em 0;
+ cursor: pointer;
+ text-align: center;
+ line-height: 25px;
+ margin: 0 2px;
+ border-radius: 3px;
+
+ &:hover {
+ background-color: black;
+
+ #link-drag {
+ background-color: black;
+ }
}
-
- .ProseMirror-example-setup-style hr:after {
- content: "";
- display: block;
- height: 1px;
- background-color: silver;
- line-height: 2px;
+
+ &> * {
+ margin-top: 50%;
+ margin-left: 50%;
+ transform: translate(-50%, -50%);
}
-}
-.wrapper {
- position: absolute;
- pointer-events: all;
+ svg {
+ fill: white;
+ width: 18px;
+ height: 18px;
+ }
}
- .menuicon {
- display: inline-block;
- border-right: 1px solid white(0, 0, 0, 0.2);
- //color: rgb(19, 18, 18);
- color: rgb(226, 21, 21);
- line-height: 1;
- padding: 0px 2px;
- margin: 1px;
+.menuicon-active {
+ width: 25px;
+ height: 25px;
cursor: pointer;
text-align: center;
- min-width: 10px;
-
- }
- .strong, .heading { font-weight: bold; }
- .em { font-style: italic; }
- .underline {text-decoration: underline}
- .superscript {vertical-align:super}
- .subscript { vertical-align:sub }
- .strikethrough {text-decoration-line:line-through}
+ line-height: 25px;
+ margin: 0 2px;
+ border-radius: 3px;
+
+ &:hover {
+ background-color: black;
+ }
+
+ &> * {
+ margin-top: 50%;
+ margin-left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ svg {
+ fill: greenyellow;
+ width: 18px;
+ height: 18px;
+ }
+}
+
+#colorPicker {
+ position: relative;
+
+ svg {
+ width: 18px;
+ height: 18px;
+ // margin-top: 11px;
+ }
+
+ .buttonColor {
+ position: absolute;
+ top: 24px;
+ left: 1px;
+ width: 24px;
+ height: 4px;
+ margin-top: 0;
+ }
+}
+
+#link-drag {
+ background-color: #323232;
+}
+
+.underline svg {
+ margin-top: 13px;
+}
+
.font-size-indicator {
font-size: 12px;
padding-right: 0px;
@@ -328,8 +217,9 @@
height: 20px;
text-align: center;
}
+
- .brush{
+.brush{
display: inline-block;
width: 1em;
height: 1em;
@@ -337,19 +227,146 @@
stroke: currentColor;
fill: currentColor;
margin-right: 15px;
- }
+}
- .brush-active{
+.brush-active{
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 3;
- stroke: greenyellow;
fill: greenyellow;
margin-right: 15px;
- }
+}
+
+.dragger-wrapper {
+ color: #eee;
+ height: 22px;
+ padding: 0 5px;
+ box-sizing: content-box;
+ cursor: grab;
- .dragger{
- color: #eee;
- margin-left: 5px;
- } \ No newline at end of file
+ .dragger {
+ width: 18px;
+ height: 100%;
+ display: flex;
+ justify-content: space-evenly;
+ }
+
+ .dragger-line {
+ width: 2px;
+ height: 100%;
+ background-color: black;
+ }
+}
+
+.button-dropdown-wrapper {
+ display: flex;
+ align-content: center;
+
+ &:hover {
+ background-color: black;
+ }
+}
+
+.buttonSettings-dropdown {
+
+ &.ProseMirror-menu-dropdown {
+ width: 10px;
+ height: 25px;
+ margin: 0;
+ padding: 0 2px;
+ background-color: #323232;
+ text-align: center;
+
+ &:after {
+ border-top: 4px solid white;
+ right: 2px;
+ }
+
+ &:hover {
+ background-color: black;
+ }
+ }
+
+ &.ProseMirror-menu-dropdown-menu {
+ min-width: 150px;
+ left: -27px;
+ top: 31px;
+ background-color: #323232;
+ border: 1px solid #4d4d4d;
+ color: $light-color-secondary;
+ // border: none;
+ // border: 1px solid $light-color-secondary;
+ border-radius: 0 6px 6px 6px;
+ padding: 3px;
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+
+ .ProseMirror-menu-dropdown-item{
+ cursor: default;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &:hover {
+ background-color: #323232;
+ }
+
+ .button-setting, .button-setting-disabled {
+ padding: 2px;
+ border-radius: 2px;
+ }
+
+ .button-setting:hover {
+ cursor: pointer;
+ background-color: black;
+ }
+
+ .separated-button {
+ border-top: 1px solid $light-color-secondary;
+ padding-top: 6px;
+ }
+
+ input {
+ color: black;
+ border: none;
+ border-radius: 1px;
+ padding: 3px;
+ }
+
+ button {
+ padding: 6px;
+ background-color: #323232;
+ border: 1px solid black;
+ border-radius: 1px;
+
+ &:hover {
+ background-color: black;
+ }
+ }
+ }
+
+
+ }
+}
+
+.colorPicker-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ margin-top: 3px;
+ margin-left: -3px;
+ width: calc(100% + 6px);
+}
+
+button.colorPicker {
+ width: 20px;
+ height: 20px;
+ border-radius: 15px !important;
+ margin: 3px;
+ border: none !important;
+
+ &.active {
+ border: 2px solid white !important;
+ }
+} \ No newline at end of file
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 31d98887f..5136089b3 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -1,6 +1,4 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faListUl } from '@fortawesome/free-solid-svg-icons';
-import { action, observable } from "mobx";
+import { action } from "mobx";
import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css
import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model";
import { wrapInList } from 'prosemirror-schema-list';
@@ -17,102 +15,103 @@ import { DragManager } from "./DragManager";
import { LinkManager } from "./LinkManager";
import { schema } from "./RichTextSchema";
import "./TooltipTextMenu.scss";
-import { Cast, NumCast } from '../../new_fields/Types';
+import { Cast, NumCast, StrCast } from '../../new_fields/Types';
import { updateBullets } from './ProsemirrorExampleTransfer';
import { DocumentDecorations } from '../views/DocumentDecorations';
+import { SelectionManager } from './SelectionManager';
+import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField';
const { toggleMark, setBlockType } = require("prosemirror-commands");
const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
- public tooltip: HTMLElement;
+ public static Toolbar: HTMLDivElement | undefined;
+
+ // editor state properties
private view: EditorView;
private editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
- private fontStyles: MarkType[];
- private fontSizes: MarkType[];
- private listTypes: (NodeType | any)[];
- private fontSizeToNum: Map<MarkType, number>;
- private fontStylesToName: Map<MarkType, string>;
- private listTypeToIcon: Map<NodeType | any, string>;
- //private link: HTMLAnchorElement;
- private wrapper: HTMLDivElement;
- private extras: HTMLDivElement;
+ private fontStyles: Mark[] = [];
+ private fontSizes: Mark[] = [];
+ private listTypes: (NodeType | any)[] = [];
+ private listTypeToIcon: Map<NodeType | any, string> = new Map();
+ private _activeMarks: Mark[] = [];
+ private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map();
+ private _collapsed: boolean = false;
+
+ // editor doms
+ public tooltip: HTMLElement = document.createElement("div");
+ private wrapper: HTMLDivElement = document.createElement("div");
+
+ // editor button doms
+ private colorDom?: Node;
+ private colorDropdownDom?: Node;
+ private highlightDom?: Node;
+ private highlightDropdownDom?: Node;
private linkEditor?: HTMLDivElement;
private linkText?: HTMLDivElement;
private linkDrag?: HTMLImageElement;
- //dropdown doms
+ private _linkDropdownDom?: Node;
+ private _brushdom?: Node;
+ private _brushDropdownDom?: Node;
private fontSizeDom?: Node;
private fontStyleDom?: Node;
private listTypeBtnDom?: Node;
+ private basicTools?: HTMLElement;
- private _activeMarks: Mark[] = [];
-
- private _collapseBtn?: MenuItem;
-
- private _brushMarks?: Set<Mark>;
- private _brushIsEmpty: boolean = true;
- private _brushdom?: Node;
-
- private _marksToDoms: Map<Mark, HTMLSpanElement> = new Map();
-
- private _collapsed: boolean = false;
constructor(view: EditorView) {
this.view = view;
- this.wrapper = document.createElement("div");
- this.tooltip = document.createElement("div");
- this.extras = document.createElement("div");
-
- this.wrapper.appendChild(this.extras);
- this.wrapper.appendChild(this.tooltip);
+ // initialize the tooltip -- sets this.tooltip
+ this.initTooltip(view);
- this.tooltip.className = "tooltipMenu";
- this.extras.className = "tooltipExtras";
+ // initialize the wrapper
+ this.wrapper = document.createElement("div");
this.wrapper.className = "wrapper";
+ this.wrapper.appendChild(this.tooltip);
- const dragger = document.createElement("span");
- dragger.className = "dragger";
- dragger.textContent = ">>>";
- this.extras.appendChild(dragger);
+ // initialize the dragger -- appends it to the wrapper
+ this.createDragger();
- this.dragElement(dragger);
+ TooltipTextMenu.Toolbar = this.wrapper;
+ }
- // this.createCollapse();
- // if (this._collapseBtn) {
- // this.tooltip.appendChild(this._collapseBtn.render(this.view).dom);
- // }
- //add the div which is the tooltip
- //view.dom.parentNode!.parentNode!.appendChild(this.tooltip);
+ private async initTooltip(view: EditorView) {
+ // initialize tooltip dom
+ this.tooltip = document.createElement("div");
+ this.tooltip.className = "tooltipMenu";
+ this.basicTools = document.createElement("div");
+ this.basicTools.className = "basic-tools";
- //add additional icons
- library.add(faListUl);
- //add the buttons to the tooltip
+ // init buttons to the tooltip -- paths to svgs are obtained from fontawesome
let items = [
- { command: toggleMark(schema.marks.strong), dom: this.icon("B", "strong", "Bold") },
- { command: toggleMark(schema.marks.em), dom: this.icon("i", "em", "Italic") },
- { command: toggleMark(schema.marks.underline), dom: this.icon("U", "underline", "Underline") },
- { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough", "Strikethrough") },
- { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript", "Superscript") },
- { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript", "Subscript") },
- { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') }
+ { command: toggleMark(schema.marks.strong), dom: this.svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") },
+ { command: toggleMark(schema.marks.em), dom: this.svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") },
+ { command: toggleMark(schema.marks.underline), dom: this.svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") },
+ { command: toggleMark(schema.marks.strikethrough), dom: this.svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") },
+ { command: toggleMark(schema.marks.superscript), dom: this.svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
+ { command: toggleMark(schema.marks.subscript), dom: this.svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") },
+ // { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') }
];
+ // add menu items
this._marksToDoms = new Map();
- //add menu items
items.forEach(({ dom, command }) => {
this.tooltip.appendChild(dom);
switch (dom.title) {
case "Bold":
this._marksToDoms.set(schema.mark(schema.marks.strong), dom);
+ this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
break;
case "Italic":
this._marksToDoms.set(schema.mark(schema.marks.em), dom);
+ this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
break;
case "Underline":
this._marksToDoms.set(schema.mark(schema.marks.underline), dom);
+ this.basicTools && this.basicTools.appendChild(dom.cloneNode(true));
break;
}
@@ -131,81 +130,177 @@ export class TooltipTextMenu {
});
});
+
+ // highlight menu
+ this.highlightDom = this.createHighlightTool().render(this.view).dom;
+ this.highlightDropdownDom = this.createHighlightDropdown().render(this.view).dom;
+ this.tooltip.appendChild(this.highlightDom);
+ this.tooltip.appendChild(this.highlightDropdownDom);
+
+ // color menu
+ this.colorDom = this.createColorTool().render(this.view).dom;
+ this.colorDropdownDom = this.createColorDropdown().render(this.view).dom;
+ this.tooltip.appendChild(this.colorDom);
+ this.tooltip.appendChild(this.colorDropdownDom);
+
+ // link menu
this.updateLinkMenu();
+ let dropdown = await this.createLinkDropdown();
+ this._linkDropdownDom = dropdown.render(this.view).dom;
+ this.tooltip.appendChild(this._linkDropdownDom);
+ // list of font styles
+ this.initFontStyles();
- //list of font styles
- this.fontStylesToName = new Map();
- this.fontStylesToName.set(schema.marks.timesNewRoman, "Times New Roman");
- this.fontStylesToName.set(schema.marks.arial, "Arial");
- this.fontStylesToName.set(schema.marks.georgia, "Georgia");
- this.fontStylesToName.set(schema.marks.comicSans, "Comic Sans MS");
- this.fontStylesToName.set(schema.marks.tahoma, "Tahoma");
- this.fontStylesToName.set(schema.marks.impact, "Impact");
- this.fontStylesToName.set(schema.marks.crimson, "Crimson Text");
- this.fontStyles = Array.from(this.fontStylesToName.keys());
-
- //font sizes
- this.fontSizeToNum = new Map();
- this.fontSizeToNum.set(schema.marks.p10, 10);
- this.fontSizeToNum.set(schema.marks.p12, 12);
- this.fontSizeToNum.set(schema.marks.p14, 14);
- this.fontSizeToNum.set(schema.marks.p16, 16);
- this.fontSizeToNum.set(schema.marks.p18, 18);
- this.fontSizeToNum.set(schema.marks.p20, 20);
- this.fontSizeToNum.set(schema.marks.p24, 24);
- this.fontSizeToNum.set(schema.marks.p32, 32);
- this.fontSizeToNum.set(schema.marks.p48, 48);
- this.fontSizeToNum.set(schema.marks.p72, 72);
- this.fontSizeToNum.set(schema.marks.pFontSize, 10);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 12);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 14);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 16);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 18);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 20);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 24);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 32);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 48);
- // this.fontSizeToNum.set(schema.marks.pFontSize, 72);
- this.fontSizes = Array.from(this.fontSizeToNum.keys());
-
- //list types
- this.listTypeToIcon = new Map();
- this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
- this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1");
- this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A");
- // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜");
- this.listTypes = Array.from(this.listTypeToIcon.keys());
+ // font sizes
+ this.initFontSizes();
- //custom tools
- // this.tooltip.appendChild(this.createLink().render(this.view).dom);
+ // list types
+ this.initListTypes();
+ // init brush tool
this._brushdom = this.createBrush().render(this.view).dom;
this.tooltip.appendChild(this._brushdom);
- this.tooltip.appendChild(this.createLink().render(this.view).dom);
+ this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom;
+ this.tooltip.appendChild(this._brushDropdownDom);
+
+ // star
this.tooltip.appendChild(this.createStar().render(this.view).dom);
+ // list types dropdown
this.updateListItemDropdown(":", this.listTypeBtnDom);
- this.updateFromDash(view, undefined, undefined);
- TooltipTextMenu.Toolbar = this.wrapper;
+ await this.updateFromDash(view, undefined, undefined);
+ }
+
+ initFontStyles() {
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" }));
+ this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" }));
+ }
+
+ initFontSizes() {
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 16 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 18 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 20 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 24 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 32 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 48 }));
+ this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 72 }));
+ }
+
+ initListTypes() {
+ this.listTypeToIcon = new Map();
+ //this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
+ this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "bullet" }), ":");
+ this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1)");
+ this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A)");
+ // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜");
+ this.listTypes = Array.from(this.listTypeToIcon.keys());
+ }
+
+ // creates dragger element that allows dragging and collapsing (on double click)
+ // of editor and appends it to the wrapper
+ createDragger() {
+ let draggerWrapper = document.createElement("div");
+ draggerWrapper.className = "dragger-wrapper";
+
+ let dragger = document.createElement("div");
+ dragger.className = "dragger";
+
+ let line1 = document.createElement("span");
+ line1.className = "dragger-line";
+ let line2 = document.createElement("span");
+ line2.className = "dragger-line";
+ let line3 = document.createElement("span");
+ line3.className = "dragger-line";
+
+ dragger.appendChild(line1);
+ dragger.appendChild(line2);
+ dragger.appendChild(line3);
+
+ draggerWrapper.appendChild(dragger);
+
+ this.wrapper.appendChild(draggerWrapper);
+ this.dragElement(draggerWrapper);
+ }
+
+ dragElement(elmnt: HTMLElement) {
+ var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
+ if (elmnt) {
+ // if present, the header is where you move the DIV from:
+ elmnt.onpointerdown = dragMouseDown;
+ elmnt.ondblclick = onClick;
+ }
+ const self = this;
+
+ function dragMouseDown(e: PointerEvent) {
+ e = e || window.event;
+ //e.preventDefault();
+ // get the mouse cursor position at startup:
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ document.onpointerup = closeDragElement;
+ // call a function whenever the cursor moves:
+ document.onpointermove = elementDrag;
+ }
+
+ function onClick(e: MouseEvent) {
+ self._collapsed = !self._collapsed;
+ const children = self.wrapper.childNodes;
+ if (self._collapsed && children.length > 0) {
+ self.wrapper.removeChild(self.tooltip);
+ self.basicTools && self.wrapper.prepend(self.basicTools);
+ }
+ else {
+ self.wrapper.prepend(self.tooltip);
+ self.basicTools && self.wrapper.removeChild(self.basicTools);
+ }
+ }
+
+ function elementDrag(e: PointerEvent) {
+ e = e || window.event;
+ //e.preventDefault();
+ // calculate the new cursor position:
+ pos1 = pos3 - e.clientX;
+ pos2 = pos4 - e.clientY;
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ // set the element's new position:
+ // elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
+ // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
+
+ self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px";
+ self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px";
+ }
+
+ function closeDragElement() {
+ // stop moving when mouse button is released:
+ document.onpointerup = null;
+ document.onpointermove = null;
+ //self.highlightSearchTerms(self.state, ["hello"]);
+ //FormattedTextBox.Instance.unhighlightSearchTerms();
+ }
}
- public static Toolbar: HTMLDivElement | undefined;
//label of dropdown will change to given label
updateFontSizeDropdown(label: string) {
- //filtering function - might be unecessary
- let cut = (arr: MenuItem[]) => arr.filter(x => x);
-
//font SIZES
let fontSizeBtns: MenuItem[] = [];
- this.fontSizeToNum.forEach((number, mark) => {
- fontSizeBtns.push(this.dropdownMarkBtn(String(number), "color: black; width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
+ this.fontSizes.forEach(mark => {
+ fontSizeBtns.push(this.dropdownFontSizeBtn(String(mark.attrs.fontSize), "color: black; width: 50px;", mark, this.view, this.changeToFontSize));
});
- let newfontSizeDom = (new Dropdown(cut(fontSizeBtns), {
+ let newfontSizeDom = (new Dropdown(fontSizeBtns, {
label: label,
- css: "color:black; min-width: 60px; padding-left: 5px; margin-right: 0;"
+ css: "color:black; min-width: 60px;"
}) as MenuItem).render(this.view).dom;
if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); }
else {
@@ -214,40 +309,30 @@ export class TooltipTextMenu {
this.fontSizeDom = newfontSizeDom;
}
- // Make the DIV element draggable
-
//label of dropdown will change to given label
updateFontStyleDropdown(label: string) {
- //filtering function - might be unecessary
- let cut = (arr: MenuItem[]) => arr.filter(x => x);
-
//font STYLES
let fontBtns: MenuItem[] = [];
- this.fontStylesToName.forEach((name, mark) => {
- fontBtns.push(this.dropdownMarkBtn(name, "color: black; font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
+ this.fontStyles.forEach((mark) => {
+ fontBtns.push(this.dropdownFontFamilyBtn(mark.attrs.family, "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", mark, this.view, this.changeToFontFamily));
});
- let newfontStyleDom = (new Dropdown(cut(fontBtns), {
+ let newfontStyleDom = (new Dropdown(fontBtns, {
label: label,
- css: "color:black; width: 125px; margin-left: -3px; padding-left: 2px;"
+ css: "color:black; width: 125px;"
}) as MenuItem).render(this.view).dom;
if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); }
else {
this.tooltip.appendChild(newfontStyleDom);
}
this.fontStyleDom = newfontStyleDom;
-
}
updateLinkMenu() {
if (!this.linkEditor || !this.linkText) {
this.linkEditor = document.createElement("div");
this.linkEditor.className = "ProseMirror-icon menuicon";
- this.linkEditor.style.color = "black";
this.linkText = document.createElement("div");
- this.linkText.style.cssFloat = "left";
- this.linkText.style.marginRight = "5px";
- this.linkText.style.marginLeft = "5px";
this.linkText.setAttribute("contenteditable", "true");
this.linkText.style.whiteSpace = "nowrap";
this.linkText.style.width = "150px";
@@ -286,9 +371,7 @@ export class TooltipTextMenu {
this.linkDrag.style.width = "15px";
this.linkDrag.style.height = "15px";
this.linkDrag.title = "Drag to create link";
- this.linkDrag.style.color = "black";
- this.linkDrag.style.background = "black";
- this.linkDrag.style.cssFloat = "left";
+ this.linkDrag.id = "link-drag";
this.linkDrag.onpointerdown = (e: PointerEvent) => {
if (!this.editorProps) return;
let dragData = new DragManager.LinkDragData(this.editorProps.Document);
@@ -307,7 +390,7 @@ export class TooltipTextMenu {
if (proto && docView) {
proto.sourceContext = docView.props.ContainingCollectionDoc;
}
- let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab");
+ let text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab");
if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
}
@@ -334,88 +417,153 @@ export class TooltipTextMenu {
e.preventDefault();
}
};
- // this.tooltip.appendChild(this.linkEditor);
}
- dragElement(elmnt: HTMLElement) {
- var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
- if (elmnt) {
- // if present, the header is where you move the DIV from:
- elmnt.onpointerdown = dragMouseDown;
- elmnt.ondblclick = onClick;
+ async getTextLinkTargetTitle() {
+ let node = this.view.state.selection.$from.nodeAfter;
+ let link = node && node.marks.find(m => m.type.name === "link");
+ if (link) {
+ let href = link.attrs.href;
+ if (href) {
+ if (href.indexOf(Utils.prepend("/doc/")) === 0) {
+ const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ if (linkclicked) {
+ let linkDoc = await DocServer.GetRefField(linkclicked);
+ if (linkDoc instanceof Doc) {
+ let anchor1 = await Cast(linkDoc.anchor1, Doc);
+ let anchor2 = await Cast(linkDoc.anchor2, Doc);
+ let currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ if (currentDoc && anchor1 && anchor2) {
+ if (Doc.AreProtosEqual(currentDoc, anchor1)) {
+ return StrCast(anchor2.title);
+ }
+ if (Doc.AreProtosEqual(currentDoc, anchor2)) {
+ return StrCast(anchor1.title);
+ }
+ }
+ }
+ }
+ } else {
+ return href;
+ }
+ } else {
+ return link.attrs.title;
+ }
}
- const self = this;
+ }
- function dragMouseDown(e: PointerEvent) {
- e = e || window.event;
- //e.preventDefault();
- // get the mouse cursor position at startup:
- pos3 = e.clientX;
- pos4 = e.clientY;
- document.onpointerup = closeDragElement;
- // call a function whenever the cursor moves:
- document.onpointermove = elementDrag;
- }
+ async createLinkDropdown() {
+ let targetTitle = await this.getTextLinkTargetTitle();
+ let input = document.createElement("input");
- function onClick(e: MouseEvent) {
- self._collapsed = !self._collapsed;
- const children = self.wrapper.childNodes;
- if (self._collapsed && children.length > 1) {
- self.wrapper.removeChild(self.tooltip);
+ // menu item for input for hyperlink url
+ // TODO: integrate search to allow users to search for a doc to link to
+ let linkInfo = new MenuItem({
+ title: "",
+ execEvent: "",
+ class: "button-setting-disabled",
+ css: "",
+ render() {
+ let p = document.createElement("p");
+ p.textContent = "Linked to:";
+
+ input.type = "text";
+ input.placeholder = "Enter URL";
+ if (targetTitle) input.value = targetTitle;
+ input.onclick = (e: MouseEvent) => {
+ input.select();
+ input.focus();
+ };
+
+ let div = document.createElement("div");
+ div.appendChild(p);
+ div.appendChild(input);
+ return div;
+ },
+ enable() { return false; },
+ run(p1, p2, p3, event) {
+ event.stopPropagation();
}
- else {
- self.wrapper.appendChild(self.tooltip);
+ });
+
+ // menu item to update/apply the hyperlink to the selected text
+ let linkApply = new MenuItem({
+ title: "",
+ execEvent: "",
+ class: "",
+ css: "",
+ render() {
+ let button = document.createElement("button");
+ button.className = "link-url-button";
+ button.textContent = "Apply hyperlink";
+ return button;
+ },
+ enable() { return false; },
+ run: (state, dispatch, view, event) => {
+ event.stopPropagation();
+ this.makeLinkToURL(input.value, "onRight");
}
- }
+ });
- function elementDrag(e: PointerEvent) {
- e = e || window.event;
- //e.preventDefault();
- // calculate the new cursor position:
- pos1 = pos3 - e.clientX;
- pos2 = pos4 - e.clientY;
- pos3 = e.clientX;
- pos4 = e.clientY;
- // set the element's new position:
- // elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
- // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
+ // menu item to remove the link
+ // TODO: allow this to be undoable
+ let self = this;
+ let deleteLink = new MenuItem({
+ title: "Delete link",
+ execEvent: "",
+ class: "separated-button",
+ css: "",
+ render() {
+ let button = document.createElement("button");
+ button.textContent = "Remove link";
+
+ let wrapper = document.createElement("div");
+ wrapper.appendChild(button);
+ return wrapper;
+ },
+ enable() { return true; },
+ async run() {
+ self.deleteLink();
+ // update link dropdown
+ let dropdown = await self.createLinkDropdown();
+ let newLinkDropdowndom = dropdown.render(self.view).dom;
+ self._linkDropdownDom && self.tooltip.replaceChild(newLinkDropdowndom, self._linkDropdownDom);
+ self._linkDropdownDom = newLinkDropdowndom;
+ }
+ });
- self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px";
- self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px";
- }
- function closeDragElement() {
- // stop moving when mouse button is released:
- document.onpointerup = null;
- document.onpointermove = null;
- //self.highlightSearchTerms(self.state, ["hello"]);
- //FormattedTextBox.Instance.unhighlightSearchTerms();
- }
+ let linkDropdown = new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem;
+ return linkDropdown;
}
// makeLinkWithState = (state: EditorState, target: string, location: string) => {
// let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
// }
- makeLink = (targetDoc: Doc, location: string): string => {
- let target = Utils.prepend("/doc/" + targetDoc[Id]);
+ makeLink = (targetDoc: Doc, title: string, location: string): string => {
+ let link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location });
+ this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link).
+ addMark(this.view.state.selection.from, this.view.state.selection.to, link));
let node = this.view.state.selection.$from.nodeAfter;
- let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: targetDoc[Id] });
+ if (node && node.text) {
+ return node.text;
+ }
+ return "";
+ }
+
+ makeLinkToURL = (target: String, lcoation: string) => {
+ let node = this.view.state.selection.$from.nodeAfter;
+ let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location });
this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
node = this.view.state.selection.$from.nodeAfter;
link = node && node.marks.find(m => m.type.name === "link");
- if (node) {
- if (node.text) {
- return node.text;
- }
- }
- return "";
}
deleteLink = () => {
let node = this.view.state.selection.$from.nodeAfter;
- let link = node && node.marks.find(m => m.type.name === "link");
+ let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link);
let href = link!.attrs.href;
if (href) {
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
@@ -430,19 +578,69 @@ export class TooltipTextMenu {
}
}
}
+ }
-
+ deleteLinkItem() {
+ const icon = {
+ height: 16, width: 16,
+ path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
+ };
+ return new MenuItem({
+ title: "Delete Link",
+ label: "X",
+ icon: icon,
+ css: "color: red",
+ class: "summarize",
+ execEvent: "",
+ run: (state, dispatch) => {
+ this.deleteLink();
+ }
+ });
}
- public static insertStar(state: EditorState<any>, dispatch: any) {
- 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 = state.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;
+ createLink() {
+ let markType = schema.marks.link;
+ return new MenuItem({
+ title: "Add or remove link",
+ label: "Add or remove link",
+ execEvent: "",
+ icon: icons.link,
+ css: "color:white;",
+ class: "menuicon",
+ enable(state) { return !state.selection.empty; },
+ run: (state, dispatch, view) => {
+ // to remove link
+ let curLink = "";
+ if (this.markActive(state, markType)) {
+
+ let { from, $from, to, empty } = state.selection;
+ let node = state.doc.nodeAt(from);
+ node && node.marks.map(m => {
+ m.type === markType && (curLink = m.attrs.href);
+ });
+ //toggleMark(markType)(state, dispatch);
+ //return true;
+ }
+ // to create link
+ openPrompt({
+ title: "Create a link",
+ fields: {
+ href: new TextField({
+ value: curLink,
+ label: "Link Target",
+ required: true
+ }),
+ title: new TextField({ label: "Title" })
+ },
+ callback(attrs: any) {
+ toggleMark(markType, attrs)(view.state, view.dispatch);
+ view.focus();
+ },
+ flyout_top: 0,
+ flyout_left: 0
+ });
+ }
+ });
}
//will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
@@ -468,139 +666,244 @@ export class TooltipTextMenu {
return listTypeBtn;
}
- //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text
- changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
- let { $cursor, ranges } = view.state.selection as TextSelection;
- let state = view.state;
- let dispatch = view.dispatch;
-
- //remove all other active font marks
- fontMarks.forEach((type) => {
- if (dispatch) {
- if ($cursor) {
- if (type.isInSet(state.storedMarks || $cursor.marks())) {
- dispatch(state.tr.removeStoredMark(type));
- }
- } else {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- has = state.doc.rangeHasMark($from.pos, $to.pos, type);
- }
- for (let i of ranges) {
- if (has) {
- toggleMark(type)(view.state, view.dispatch, view);
- }
- }
- }
+ createStar() {
+ return new MenuItem({
+ title: "Summarize",
+ label: "Summarize",
+ icon: icons.join,
+ css: "color:white;",
+ class: "menuicon",
+ execEvent: "",
+ run: (state, dispatch) => {
+ TooltipTextMenu.insertStar(this.view.state, this.view.dispatch);
}
- });
- if (markType) {
- // fontsize
- if (markType.name[0] === 'p') {
- let size = this.fontSizeToNum.get(markType);
- if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleSize_" + heading] = size;
- }
- }
- }
- else {
- let fontName = this.fontStylesToName.get(markType);
- if (fontName) { this.updateFontStyleDropdown(fontName); }
- if (this.editorProps) {
- let ruleProvider = this.editorProps.ruleProvider;
- let heading = NumCast(this.editorProps.Document.heading);
- if (ruleProvider && heading) {
- ruleProvider["ruleFont_" + heading] = fontName;
- }
- }
- }
- //actually apply font
- if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
- let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
- { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
- view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
- }
- else toggleMark(markType)(view.state, view.dispatch, view);
- }
+ });
}
- //remove all node typeand apply the passed-in one to the selected text
- changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => {
- //remove oldif (nodeType) { //add new
- if (nodeType === schema.nodes.bullet_list) {
- wrapInList(nodeType)(view.state, view.dispatch);
- } else {
- var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
- if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
- let tx3 = updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle);
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
+ public static insertStar(state: EditorState<any>, dispatch: any) {
+ 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 = state.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;
+ }
- view.dispatch(tx2);
- })) {
- let tx2 = view.state.tr;
- let tx3 = nodeType ? updateBullets(tx2, schema, (nodeType as any).attrs.mapStyle) : tx2;
- marks && tx3.ensureMarks([...marks]);
- marks && tx3.setStoredMarks([...marks]);
+ public static insertComment(state: EditorState<any>, dispatch: any) {
+ 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 = state.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;
+ }
- view.dispatch(tx3);
+ createHighlightTool() {
+ return new MenuItem({
+ title: "Highlight",
+ css: "color:white;",
+ class: "menuicon",
+ execEvent: "",
+ render() {
+ let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttribute("viewBox", "-100 -100 650 650");
+ let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z");
+ svg.appendChild(path);
+
+ let color = document.createElement("div");
+ color.className = "buttonColor";
+ color.style.backgroundColor = TooltipTextMenuManager.Instance.highlight.toString();
+
+ let wrapper = document.createElement("div");
+ wrapper.id = "colorPicker";
+ wrapper.appendChild(svg);
+ wrapper.appendChild(color);
+ return wrapper;
+ },
+ run: (state, dispatch) => {
+ TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, this.view.state, this.view.dispatch);
}
- }
+ });
}
- //makes a button for the drop down FOR MARKS
- //css is the style you want applied to the button
- dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
- return new MenuItem({
+ public static insertHighlight(color: String, state: EditorState<any>, dispatch: any) {
+ if (state.selection.empty) return false;
+
+ let highlightMark = state.schema.mark(state.schema.marks.marker, { highlight: color });
+ dispatch(state.tr.addMark(state.selection.from, state.selection.to, highlightMark));
+ }
+
+ createHighlightDropdown() {
+ // menu item for color picker
+ let self = this;
+ let colors = new MenuItem({
title: "",
- label: label,
execEvent: "",
- class: "menuicon",
- css: css,
- enable() { return true; },
- run() {
- changeToMarkInGroup(markType, view, groupMarks);
+ class: "button-setting-disabled",
+ css: "",
+ render() {
+ let p = document.createElement("p");
+ p.textContent = "Change highlight:";
+
+ let colorsWrapper = document.createElement("div");
+ colorsWrapper.className = "colorPicker-wrapper";
+
+ let colors = [
+ PastelSchemaPalette.get("pink2"),
+ PastelSchemaPalette.get("purple4"),
+ PastelSchemaPalette.get("bluegreen1"),
+ PastelSchemaPalette.get("yellow4"),
+ PastelSchemaPalette.get("red2"),
+ PastelSchemaPalette.get("bluegreen7"),
+ PastelSchemaPalette.get("bluegreen5"),
+ PastelSchemaPalette.get("orange1"),
+ "white",
+ "transparent"
+ ];
+
+ colors.forEach(color => {
+ let button = document.createElement("button");
+ button.className = color === TooltipTextMenuManager.Instance.highlight ? "colorPicker active" : "colorPicker";
+ if (color) {
+ button.style.backgroundColor = color;
+ button.textContent = color === "transparent" ? "X" : "";
+ button.onclick = e => {
+ TooltipTextMenuManager.Instance.highlight = color;
+
+ TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlight, self.view.state, self.view.dispatch);
+
+ // update color menu
+ let highlightDom = self.createHighlightTool().render(self.view).dom;
+ let highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom;
+ self.highlightDom && self.tooltip.replaceChild(highlightDom, self.highlightDom);
+ self.highlightDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlightDropdownDom);
+ self.highlightDom = highlightDom;
+ self.highlightDropdownDom = highlightDropdownDom;
+ };
+ }
+ colorsWrapper.appendChild(button);
+ });
+
+ let div = document.createElement("div");
+ div.appendChild(p);
+ div.appendChild(colorsWrapper);
+ return div;
+ },
+ enable() { return false; },
+ run(p1, p2, p3, event) {
+ event.stopPropagation();
}
});
+
+ let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
+ return colorDropdown;
}
- createStar() {
+ createColorTool() {
return new MenuItem({
- title: "Summarize",
- label: "Summarize",
- icon: icons.join,
+ title: "Color",
css: "color:white;",
- class: "summarize",
+ class: "menuicon",
execEvent: "",
+ render() {
+ let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttribute("viewBox", "-100 -100 650 650");
+ let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z");
+ svg.appendChild(path);
+
+ let color = document.createElement("div");
+ color.className = "buttonColor";
+ color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString();
+
+ let wrapper = document.createElement("div");
+ wrapper.id = "colorPicker";
+ wrapper.appendChild(svg);
+ wrapper.appendChild(color);
+ return wrapper;
+ },
run: (state, dispatch) => {
- TooltipTextMenu.insertStar(this.view.state, this.view.dispatch);
+ TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, this.view.state, this.view.dispatch);
}
-
});
}
- deleteLinkItem() {
- const icon = {
- height: 16, width: 16,
- path: "M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"
- };
- return new MenuItem({
- title: "Delete Link",
- label: "X",
- icon: icon,
- css: "color: red",
- class: "summarize",
+ public static insertColor(color: String, state: EditorState<any>, dispatch: any) {
+ if (state.selection.empty) return false;
+
+ let colorMark = state.schema.mark(state.schema.marks.color, { color: color });
+ dispatch(state.tr.addMark(state.selection.from, state.selection.to, colorMark));
+ }
+
+ createColorDropdown() {
+ // menu item for color picker
+ let self = this;
+ let colors = new MenuItem({
+ title: "",
execEvent: "",
- run: (state, dispatch) => {
- this.deleteLink();
+ class: "button-setting-disabled",
+ css: "",
+ render() {
+ let p = document.createElement("p");
+ p.textContent = "Change color:";
+
+ let colorsWrapper = document.createElement("div");
+ colorsWrapper.className = "colorPicker-wrapper";
+
+ let colors = [
+ DarkPastelSchemaPalette.get("pink2"),
+ DarkPastelSchemaPalette.get("purple4"),
+ DarkPastelSchemaPalette.get("bluegreen1"),
+ DarkPastelSchemaPalette.get("yellow4"),
+ DarkPastelSchemaPalette.get("red2"),
+ DarkPastelSchemaPalette.get("bluegreen7"),
+ DarkPastelSchemaPalette.get("bluegreen5"),
+ DarkPastelSchemaPalette.get("orange1"),
+ "#757472",
+ "#000"
+ ];
+
+ colors.forEach(color => {
+ let button = document.createElement("button");
+ button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker";
+ if (color) {
+ button.style.backgroundColor = color;
+ button.onclick = e => {
+ TooltipTextMenuManager.Instance.color = color;
+
+ TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch);
+
+ // update color menu
+ let colorDom = self.createColorTool().render(self.view).dom;
+ let colorDropdownDom = self.createColorDropdown().render(self.view).dom;
+ self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom);
+ self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom);
+ self.colorDom = colorDom;
+ self.colorDropdownDom = colorDropdownDom;
+ };
+ }
+ colorsWrapper.appendChild(button);
+ });
+
+ let div = document.createElement("div");
+ div.appendChild(p);
+ div.appendChild(colorsWrapper);
+ return div;
+ },
+ enable() { return false; },
+ run(p1, p2, p3, event) {
+ event.stopPropagation();
}
});
+
+ let colorDropdown = new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem;
+ return colorDropdown;
}
createBrush(active: boolean = false) {
@@ -608,15 +911,21 @@ export class TooltipTextMenu {
height: 32, width: 32,
path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z"
};
+ let self = this;
return new MenuItem({
title: "Brush tool",
label: "Brush tool",
icon: icon,
css: "color:white;",
- class: active ? "brush-active" : "brush",
+ class: active ? "menuicon-active" : "menuicon",
execEvent: "",
run: (state, dispatch) => {
this.brush_function(state, dispatch);
+
+ // update dropdown with marks
+ let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
+ self._brushDropdownDom = newBrushDropdowndom;
},
active: (state) => {
return true;
@@ -624,18 +933,16 @@ export class TooltipTextMenu {
});
}
- // selectionchanged event handler
-
brush_function(state: EditorState<any>, dispatch: any) {
- if (this._brushIsEmpty) {
+ if (TooltipTextMenuManager.Instance._brushIsEmpty) {
const selected_marks = this.getMarksInSelection(this.view.state);
if (this._brushdom) {
if (selected_marks.size >= 0) {
- this._brushMarks = selected_marks;
+ TooltipTextMenuManager.Instance._brushMarks = selected_marks;
const newbrush = this.createBrush(true).render(this.view).dom;
this.tooltip.replaceChild(newbrush, this._brushdom);
this._brushdom = newbrush;
- this._brushIsEmpty = !this._brushIsEmpty;
+ TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty;
}
}
}
@@ -643,9 +950,9 @@ export class TooltipTextMenu {
let { from, to, $from } = this.view.state.selection;
if (this._brushdom) {
if (!this.view.state.selection.empty && $from && $from.nodeAfter) {
- if (this._brushMarks && to - from > 0) {
+ if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) {
this.view.dispatch(this.view.state.tr.removeMark(from, to));
- Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
+ Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => {
const markType = mark.type;
this.changeToMarkInGroup(markType, this.view, []);
});
@@ -655,96 +962,247 @@ export class TooltipTextMenu {
const newbrush = this.createBrush(false).render(this.view).dom;
this.tooltip.replaceChild(newbrush, this._brushdom);
this._brushdom = newbrush;
- this._brushIsEmpty = !this._brushIsEmpty;
+ TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty;
}
}
}
+ }
+ createBrushDropdown(active: boolean = false) {
+ let label = "Stored marks: ";
+ if (TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0) {
+ TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => {
+ const markType = mark.type;
+ label += markType.name;
+ label += ", ";
+ });
+ label = label.substring(0, label.length - 2);
+ } else {
+ label = "No marks are currently stored";
+ }
- }
- createCollapse() {
- this._collapseBtn = new MenuItem({
- title: "Collapse",
- //label: "Collapse",
- icon: icons.join,
+ let brushInfo = new MenuItem({
+ title: "",
+ label: label,
execEvent: "",
- css: "color:white;",
- class: "summarize",
- run: () => {
- this.collapseToolTip();
+ class: "button-setting-disabled",
+ css: "",
+ enable() { return false; },
+ run(p1, p2, p3, event) {
+ event.stopPropagation();
}
});
+
+ let self = this;
+ let clearBrush = new MenuItem({
+ title: "Clear brush",
+ execEvent: "",
+ class: "separated-button",
+ css: "",
+ render() {
+ let button = document.createElement("button");
+ button.textContent = "Clear brush";
+
+ let wrapper = document.createElement("div");
+ wrapper.appendChild(button);
+ return wrapper;
+ },
+ enable() { return true; },
+ run() {
+ TooltipTextMenuManager.Instance._brushIsEmpty = true;
+ TooltipTextMenuManager.Instance._brushMarks = new Set();
+
+ // update brush tool
+ // TODO: this probably isn't very clean
+ let newBrushdom = self.createBrush().render(self.view).dom;
+ self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom);
+ self._brushdom = newBrushdom;
+ let newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom;
+ self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom);
+ self._brushDropdownDom = newBrushDropdowndom;
+ }
+ });
+
+ let hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0;
+ let brushDom = new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem;
+ return brushDom;
}
+ //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected textchangeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
+ changeToMarkInGroup = (markType: MarkType | undefined, view: EditorView, fontMarks: MarkType[]) => {
+ let { $cursor, ranges } = view.state.selection as TextSelection;
+ let state = view.state;
+ let dispatch = view.dispatch;
- collapseToolTip() {
- if (this._collapseBtn) {
- if (this._collapseBtn.spec.title === "Collapse") {
- // const newcollapseBtn = new MenuItem({
- // title: "Expand",
- // icon: icons.join,
- // execEvent: "",
- // css: "color:white;",
- // class: "summarize",
- // run: (state, dispatch, view) => {
- // this.collapseToolTip();
- // }
- // });
- // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom);
- // this._collapseBtn = newcollapseBtn;
- this.tooltip.style.width = "30px";
- this._collapseBtn.spec.title = "Expand";
- this._collapseBtn.render(this.view);
+ //remove all other active font marks
+ fontMarks.forEach((type) => {
+ if (dispatch) {
+ if ($cursor) {
+ if (type.isInSet(state.storedMarks || $cursor.marks())) {
+ dispatch(state.tr.removeStoredMark(type));
+ }
+ } else {
+ let has = false;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ has = state.doc.rangeHasMark($from.pos, $to.pos, type);
+ }
+ for (let i of ranges) {
+ if (has) {
+ toggleMark(type)(view.state, view.dispatch, view);
+ }
+ }
+ }
}
- else {
- this._collapseBtn.spec.title = "Collapse";
- this.tooltip.style.width = "550px";
- this._collapseBtn.render(this.view);
+ });
+
+ if (markType) {
+ //actually apply font
+ if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+ let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+ { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: markType.name, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema);
+ view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
}
+ else toggleMark(markType)(view.state, view.dispatch, view);
}
}
- createLink() {
- let markType = schema.marks.link;
- return new MenuItem({
- title: "Add or remove link",
- label: "Add or remove link",
- execEvent: "",
- icon: icons.link,
- css: "color:white;",
- class: "menuicon",
- enable(state) { return !state.selection.empty; },
- run: (state, dispatch, view) => {
- // to remove link
- let curLink = "";
- if (this.markActive(state, markType)) {
+ changeToFontFamily = (mark: Mark, view: EditorView) => {
+ let { $cursor, ranges } = view.state.selection as TextSelection;
+ let state = view.state;
+ let dispatch = view.dispatch;
- let { from, $from, to, empty } = state.selection;
- let node = state.doc.nodeAt(from);
- node && node.marks.map(m => {
- m.type === markType && (curLink = m.attrs.href);
- });
- //toggleMark(markType)(state, dispatch);
- //return true;
+ //remove all other active font marks
+ if ($cursor) {
+ if (view.state.schema.marks.pFontFamily.isInSet(state.storedMarks || $cursor.marks())) {
+ dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontFamily));
+ }
+ } else {
+ let has = false;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontFamily);
+ }
+ for (let i of ranges) {
+ if (has) {
+ toggleMark(view.state.schema.marks.pFontFamily)(view.state, view.dispatch, view);
}
- // to create link
- openPrompt({
- title: "Create a link",
- fields: {
- href: new TextField({
- value: curLink,
- label: "Link Target",
- required: true
- }),
- title: new TextField({ label: "Title" })
- },
- callback(attrs: any) {
- toggleMark(markType, attrs)(view.state, view.dispatch);
- view.focus();
- },
- flyout_top: 0,
- flyout_left: 0
- });
+ }
+ }
+
+ let fontName = mark.attrs.family;
+ if (fontName) { this.updateFontStyleDropdown(fontName); }
+ if (this.editorProps) {
+ let ruleProvider = this.editorProps.ruleProvider;
+ let heading = NumCast(this.editorProps.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleFont_" + heading] = fontName;
+ }
+ }
+ //actually apply font
+ if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+ let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+ { ...(view.state.selection as NodeSelection).node.attrs, setFontFamily: fontName }), view.state.schema);
+ view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+ }
+ else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontFamily.create({ family: fontName })));
+ view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontFamily.create({ family: fontName })];
+ }
+
+ changeToFontSize = (mark: Mark, view: EditorView) => {
+ let { $cursor, ranges } = view.state.selection as TextSelection;
+ let state = view.state;
+ let dispatch = view.dispatch;
+
+ //remove all other active font marks
+ if ($cursor) {
+ if (view.state.schema.marks.pFontSize.isInSet(state.storedMarks || $cursor.marks())) {
+ dispatch(state.tr.removeStoredMark(view.state.schema.marks.pFontSize));
+ }
+ } else {
+ let has = false;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ has = state.doc.rangeHasMark($from.pos, $to.pos, view.state.schema.marks.pFontSize);
+ }
+ for (let i of ranges) {
+ if (has) {
+ toggleMark(view.state.schema.marks.pFontSize)(view.state, view.dispatch, view);
+ }
+ }
+ }
+
+ let size = mark.attrs.fontSize;
+ if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
+ if (this.editorProps) {
+ let ruleProvider = this.editorProps.ruleProvider;
+ let heading = NumCast(this.editorProps.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleSize_" + heading] = size;
+ }
+ }
+ //actually apply font
+ if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) {
+ let status = updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type,
+ { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: size }), view.state.schema);
+ view.dispatch(status.setSelection(new NodeSelection(status.doc.resolve(view.state.selection.from))));
+ }
+ else view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, view.state.schema.marks.pFontSize.create({ fontSize: size })));
+ view.state.storedMarks = [...(view.state.storedMarks || []), view.state.schema.marks.pFontSize.create({ fontSize: size })];
+ }
+
+ //remove all node typeand apply the passed-in one to the selected text
+ changeToNodeType = (nodeType: NodeType | undefined) => {
+ //remove oldif (nodeType) { //add new
+ let view = this.view;
+ if (nodeType === schema.nodes.bullet_list) {
+ wrapInList(nodeType)(view.state, view.dispatch);
+ } else {
+ var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks());
+ if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => {
+ let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ view.dispatch(tx2);
+ })) {
+ let tx2 = view.state.tr;
+ let tx3 = updateBullets(tx2, schema, nodeType && (nodeType as any).attrs.mapStyle);
+ marks && tx3.ensureMarks([...marks]);
+ marks && tx3.setStoredMarks([...marks]);
+
+ view.dispatch(tx3);
+ }
+ }
+ }
+
+ //makes a button for the drop down FOR MARKS
+ //css is the style you want applied to the button
+ dropdownFontFamilyBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontFamily: (mark: Mark<any>, view: EditorView) => any) {
+ return new MenuItem({
+ title: "",
+ label: label,
+ execEvent: "",
+ class: "dropdown-item",
+ css: css,
+ enable() { return true; },
+ run() {
+ changeFontFamily(mark, view);
+ }
+ });
+ }
+ //makes a button for the drop down FOR MARKS
+ //css is the style you want applied to the button
+ dropdownFontSizeBtn(label: string, css: string, mark: Mark, view: EditorView, changeFontSize: (markType: Mark<any>, view: EditorView) => any) {
+ return new MenuItem({
+ title: "",
+ label: label,
+ execEvent: "",
+ class: "dropdown-item",
+ css: css,
+ enable() { return true; },
+ run() {
+ changeFontSize(mark, view);
}
});
}
@@ -756,7 +1214,7 @@ export class TooltipTextMenu {
title: "",
label: label,
execEvent: "",
- class: "menuicon",
+ class: "dropdown-item",
css: css,
enable() { return true; },
run() {
@@ -765,7 +1223,7 @@ export class TooltipTextMenu {
});
}
- markActive = function (state: EditorState<any>, type: MarkType<Schema<string, string>>) {
+ markActive = function(state: EditorState<any>, type: MarkType<Schema<string, string>>) {
let { from, $from, to, empty } = state.selection;
if (empty) return type.isInSet(state.storedMarks || $from.marks());
else return state.doc.rangeHasMark(from, to, type);
@@ -781,6 +1239,21 @@ export class TooltipTextMenu {
return span;
}
+ svgIcon(name: string, title: string = name, dpath: string) {
+ let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttribute("viewBox", "-100 -100 650 650");
+ let path = document.createElementNS('http://www.w3.org/2000/svg', "path");
+ path.setAttributeNS(null, "d", dpath);
+ svg.appendChild(path);
+
+ let span = document.createElement("span");
+ span.className = name + " menuicon";
+ span.title = title;
+ span.appendChild(svg);
+
+ return span;
+ }
+
//method for checking whether node can be inserted
canInsert(state: EditorState, nodeType: NodeType<Schema<string, string>>) {
let $from = state.selection.$from;
@@ -843,7 +1316,7 @@ export class TooltipTextMenu {
update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); }
//updates the tooltip menu when the selection changes
- public updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
+ public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) {
if (!view) {
console.log("no editor? why?");
return;
@@ -863,87 +1336,76 @@ export class TooltipTextMenu {
//this.tooltip.style.display = "none";
//return;
}
- //UPDATE LIST ITEM DROPDOWN
+
+ // update link dropdown
+ let linkDropdown = await this.createLinkDropdown();
+ let newLinkDropdowndom = linkDropdown.render(this.view).dom;
+ this._linkDropdownDom && this.tooltip.replaceChild(newLinkDropdowndom, this._linkDropdownDom);
+ this._linkDropdownDom = newLinkDropdowndom;
//UPDATE FONT STYLE DROPDOWN
- let activeStyles = this.activeMarksOnSelection(this.fontStyles);
+ let activeStyles = this.activeFontFamilyOnSelection();
if (activeStyles !== undefined) {
- // activeStyles.forEach((markType) => {
- // this._activeMarks.push(this.view.state.schema.mark(markType));
- // });
if (activeStyles.length === 1) {
- // if we want to update something somewhere with active font name
- let fontName = this.fontStylesToName.get(activeStyles[0]);
- if (fontName) { this.updateFontStyleDropdown(fontName); }
- } else if (activeStyles.length === 0) {
- //crimson on default
- this.updateFontStyleDropdown("Crimson Text");
- } else {
- this.updateFontStyleDropdown("Various");
- }
+ console.log("updating font style dropdown", activeStyles[0]);
+ activeStyles[0] && this.updateFontStyleDropdown(activeStyles[0]);
+ } else this.updateFontStyleDropdown(activeStyles.length ? "various" : "default");
}
//UPDATE FONT SIZE DROPDOWN
- let activeSizes = this.activeMarksOnSelection(this.fontSizes);
+ let activeSizes = this.activeFontSizeOnSelection();
if (activeSizes !== undefined) {
if (activeSizes.length === 1) { //if there's only one active font size
- // activeSizes.forEach((markType) => {
- // this._activeMarks.push(this.view.state.schema.mark(markType));
- // });
- let size = this.fontSizeToNum.get(activeSizes[0]);
- if (size) { this.updateFontSizeDropdown(String(size) + " pt"); }
- } else if (activeSizes.length === 0) {
- //should be 14 on default
- this.updateFontSizeDropdown("14 pt");
- } else { //multiple font sizes selected
- this.updateFontSizeDropdown("Various");
- }
+ activeSizes[0] && this.updateFontSizeDropdown(String(activeSizes[0]) + " pt");
+ } else this.updateFontSizeDropdown(activeSizes.length ? "various" : "default");
}
this.update_mark_doms();
}
-
- public mark_key_pressed(marks: Mark<any>[]) {
- if (this.view.state.selection.empty) {
- if (marks) this._activeMarks = marks;
- this.update_mark_doms();
- }
- }
-
update_mark_doms() {
this.reset_mark_doms();
- let foundlink = false;
- let children = this.extras.childNodes;
this._activeMarks.forEach((mark) => {
if (this._marksToDoms.has(mark)) {
let dom = this._marksToDoms.get(mark);
if (dom) dom.style.color = "greenyellow";
}
- if (children.length > 1) {
- foundlink = true;
- }
- if (mark.type.name === "link" && children.length === 1) {
- // let del = document.createElement("button");
- // del.textContent = "X";
- // del.style.color = "red";
- // del.style.height = "10px";
- // del.style.width = "10px";
- // del.style.marginLeft = "5px";
- // del.onclick = this.deleteLink;
- // this.extras.appendChild(del);
- let del = this.deleteLinkItem().render(this.view).dom;
- this.extras.appendChild(del);
- foundlink = true;
- }
});
- if (!foundlink) {
- if (children.length > 1) {
- this.extras.removeChild(children[1]);
+
+ // keeps brush tool highlighted if active when switching between textboxes
+ if (!TooltipTextMenuManager.Instance._brushIsEmpty) {
+ if (this._brushdom) {
+ const newbrush = this.createBrush(true).render(this.view).dom;
+ this.tooltip.replaceChild(newbrush, this._brushdom);
+ this._brushdom = newbrush;
}
}
}
+ //finds fontSize at start of selection
+ activeFontSizeOnSelection() {
+ //current selection
+ let state = this.view.state;
+ let activeSizes: number[] = [];
+ const pos = this.view.state.selection.$from;
+ const ref_node: ProsNode = this.reference_node(pos);
+ if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+ ref_node.marks.forEach(m => m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize));
+ }
+ return activeSizes;
+ }
+ //finds fontSize at start of selection
+ activeFontFamilyOnSelection() {
+ //current selection
+ let state = this.view.state;
+ let activeFamilies: string[] = [];
+ const pos = this.view.state.selection.$from;
+ const ref_node: ProsNode = this.reference_node(pos);
+ if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) {
+ ref_node.marks.forEach(m => m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family));
+ }
+ return activeFamilies;
+ }
//finds all active marks on selection in given group
activeMarksOnSelection(markGroup: MarkType[]) {
//current selection
@@ -953,12 +1415,10 @@ export class TooltipTextMenu {
let activeMarks: MarkType[];
if (!empty) {
activeMarks = markGroup.filter(mark => {
- if (dispatch) {
- let has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- let { $from, $to } = ranges[i];
- return state.doc.rangeHasMark($from.pos, $to.pos, mark);
- }
+ let has = false;
+ for (let i = 0; !has && i < ranges.length; i++) {
+ let { $from, $to } = ranges[i];
+ return state.doc.rangeHasMark($from.pos, $to.pos, mark);
}
return false;
});
@@ -977,10 +1437,11 @@ export class TooltipTextMenu {
}
this._activeMarks = ref_node.marks;
activeMarks = markGroup.filter(mark_type => {
- if (dispatch) {
- let mark = state.schema.mark(mark_type);
- return ref_node.marks.includes(mark);
+ if (mark_type === state.schema.marks.pFontSize) {
+ return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name);
}
+ let mark = state.schema.mark(mark_type);
+ return ref_node.marks.includes(mark);
return false;
});
}
@@ -1019,6 +1480,40 @@ export class TooltipTextMenu {
}
destroy() {
- this.wrapper.remove();
+ // this.wrapper.remove();
+ }
+}
+
+
+class TooltipTextMenuManager {
+ private static _instance: TooltipTextMenuManager;
+
+ public pinnedX: number = 0;
+ public pinnedY: number = 0;
+ public unpinnedX: number = 0;
+ public unpinnedY: number = 0;
+ private _isPinned: boolean = false;
+
+ public _brushMarks: Set<Mark> | undefined;
+ public _brushIsEmpty: boolean = true;
+
+ public color: String = "#000";
+ public highlight: String = "transparent";
+
+ public activeMenu: TooltipTextMenu | undefined;
+
+ static get Instance() {
+ if (!TooltipTextMenuManager._instance) {
+ TooltipTextMenuManager._instance = new TooltipTextMenuManager();
+ }
+ return TooltipTextMenuManager._instance;
+ }
+
+ public get isPinned() {
+ return this._isPinned;
+ }
+
+ public toggleIsPinned() {
+ this._isPinned = !this._isPinned;
}
}