aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts92
-rw-r--r--src/client/util/ProsemirrorExampleTransfer.ts5
-rw-r--r--src/client/util/RichTextRules.ts40
-rw-r--r--src/client/util/RichTextSchema.tsx19
-rw-r--r--src/client/views/ContextMenu.tsx1
-rw-r--r--src/client/views/ContextMenuItem.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx5
-rw-r--r--src/client/views/InkingControl.tsx32
-rw-r--r--src/client/views/ScriptBox.tsx15
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx27
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx26
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx11
-rw-r--r--src/client/views/nodes/DocumentView.tsx12
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx48
-rw-r--r--src/new_fields/Doc.ts3
-rw-r--r--src/server/authentication/models/current_user_utils.ts20
17 files changed, 306 insertions, 56 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index f805ae872..3921a49c3 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -53,6 +53,98 @@ export class Utils {
document.body.removeChild(textArea);
}
+ public static fromRGBAstr(rgba: string) {
+ let rm = rgba.match(/rgb[a]?\(([0-9]+)/);
+ let r = rm ? Number(rm[1]) : 0;
+ let gm = rgba.match(/rgb[a]?\([0-9]+,([0-9]+)/);
+ let g = gm ? Number(gm[1]) : 0;
+ let bm = rgba.match(/rgb[a]?\([0-9]+,[0-9]+,([0-9]+)/);
+ let b = bm ? Number(bm[1]) : 0;
+ let am = rgba.match(/rgba?\([0-9]+,[0-9]+,[0-9]+,([0-9]+)/);
+ let a = am ? Number(am[1]) : 0;
+ return { r: r, g: g, b: b, a: a };
+ }
+ public static toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
+ return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")";
+ }
+
+ public static HSLtoRGB(h: number, s: number, l: number) {
+ // Must be fractions of 1
+ // s /= 100;
+ // l /= 100;
+
+ let c = (1 - Math.abs(2 * l - 1)) * s,
+ x = c * (1 - Math.abs((h / 60) % 2 - 1)),
+ m = l - c / 2,
+ r = 0,
+ g = 0,
+ b = 0;
+ if (0 <= h && h < 60) {
+ r = c; g = x; b = 0;
+ } else if (60 <= h && h < 120) {
+ r = x; g = c; b = 0;
+ } else if (120 <= h && h < 180) {
+ r = 0; g = c; b = x;
+ } else if (180 <= h && h < 240) {
+ r = 0; g = x; b = c;
+ } else if (240 <= h && h < 300) {
+ r = x; g = 0; b = c;
+ } else if (300 <= h && h < 360) {
+ r = c; g = 0; b = x;
+ }
+ r = Math.round((r + m) * 255);
+ g = Math.round((g + m) * 255);
+ b = Math.round((b + m) * 255);
+ return { r: r, g: g, b: b };
+ }
+
+ public static RGBToHSL(r: number, g: number, b: number) {
+ // Make r, g, and b fractions of 1
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ // Find greatest and smallest channel values
+ let cmin = Math.min(r, g, b),
+ cmax = Math.max(r, g, b),
+ delta = cmax - cmin,
+ h = 0,
+ s = 0,
+ l = 0;
+ // Calculate hue
+
+ // No difference
+ if (delta == 0)
+ h = 0;
+ // Red is max
+ else if (cmax == r)
+ h = ((g - b) / delta) % 6;
+ // Green is max
+ else if (cmax == g)
+ h = (b - r) / delta + 2;
+ // Blue is max
+ else
+ h = (r - g) / delta + 4;
+
+ h = Math.round(h * 60);
+
+ // Make negative hues positive behind 360°
+ if (h < 0)
+ h += 360; // Calculate lightness
+
+ l = (cmax + cmin) / 2;
+
+ // Calculate saturation
+ s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
+
+ // Multiply l and s by 100
+ // s = +(s * 100).toFixed(1);
+ // l = +(l * 100).toFixed(1);
+
+ return { h: h, s: s, l: l };
+ }
+
+
public static GetClipboardText(): string {
var textArea = document.createElement("textarea");
document.body.appendChild(textArea);
diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index bac0177ad..1d2d33800 100644
--- a/src/client/util/ProsemirrorExampleTransfer.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -14,7 +14,6 @@ export type KeyMap = { [key: string]: any };
export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?: KeyMap): KeyMap {
let keys: { [key: string]: any } = {}, type;
- keys["ACTIVE"] = false;
function bind(key: string, cmd: any) {
if (mapKeys) {
let mapped = mapKeys[key];
@@ -148,10 +147,6 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
return tx;
}
bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
- if (!keys["ACTIVE"]) {// hack to ignore an initial carriage return when creating a textbox from the action menu
- dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from - 1, state.selection.from)).deleteSelection());
- return true;
- }
var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
if (!splitListItem(schema.nodes.list_item)(state, (tx3: Transaction) => dispatch(tx3))) {
if (!splitBlockKeepMarks(state, (tx3: Transaction) => {
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 5d1131410..00e671db9 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -2,6 +2,9 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from
import { schema } from "./RichTextSchema";
import { wrappingInputRule } from "./prosemirrorPatches";
import { NodeSelection } from "prosemirror-state";
+import { NumCast, Cast } from "../../new_fields/Types";
+import { Doc } from "../../new_fields/Doc";
+import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
export const inpRules = {
rules: [
@@ -57,6 +60,12 @@ export const inpRules = {
new InputRule(
new RegExp(/^#([0-9]+)\s$/),
(state, match, start, end) => {
+ let size = Number(match[1]);
+ let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+ let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleSize_" + heading] = size;
+ }
return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: Number(match[1]) }))
}),
new InputRule(
@@ -64,10 +73,41 @@ export const inpRules = {
(state, match, start, end) => {
let node = (state.doc.resolve(start) as any).nodeAfter;
let sm = state.storedMarks || undefined;
+ let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+ let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleAlign_" + heading] = "center";
+ }
return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "center" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
state.tr;
}),
new InputRule(
+ new RegExp(/^\[\[\s$/),
+ (state, match, start, end) => {
+ let node = (state.doc.resolve(start) as any).nodeAfter;
+ let sm = state.storedMarks || undefined;
+ let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+ let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleAlign_" + heading] = "left";
+ }
+ return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "left" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ state.tr;
+ }),
+ new InputRule(
+ new RegExp(/^\]\]\s$/),
+ (state, match, start, end) => {
+ let node = (state.doc.resolve(start) as any).nodeAfter;
+ let sm = state.storedMarks || undefined;
+ let ruleProvider = Cast(FormattedTextBox.InputBoxOverlay!.props.Document.ruleProvider, Doc) as Doc;
+ let heading = NumCast(FormattedTextBox.InputBoxOverlay!.props.Document.heading);
+ if (ruleProvider && heading) {
+ ruleProvider["ruleAlign_" + heading] = "right";
+ }
+ return node ? state.tr.replaceRangeWith(start, end, schema.nodes.paragraph.create({ align: "right" })).setStoredMarks([...node.marks, ...(sm ? sm : [])]) :
+ state.tr;
+ }),
+ new InputRule(
new RegExp(/\^f\s$/),
(state, match, start, end) => {
let newNode = schema.nodes.footnote.create({});
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 3def4a579..f027a4bf7 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -12,6 +12,7 @@ import { DocServer } from "../DocServer";
import { Cast, NumCast } from "../../new_fields/Types";
import { DocumentManager } from "./DocumentManager";
import ParagraphNodeSpec from "./ParagraphNodeSpec";
+import { times } from "async";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -422,6 +423,24 @@ 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: {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 890bfdfb7..68b97f2b6 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -250,6 +250,7 @@ export class ContextMenu extends React.Component {
const item = this.flatItems[this.selectedIndex];
item && item.event();
this.closeMenu();
+ e.preventDefault();
}
}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 1a0839060..0366a6a30 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -89,7 +89,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
);
} else if ("subitems" in this.props) {
let submenu = !this.overItem ? (null) :
- <div className="contextMenu-subMenu-cont" style={{ marginLeft: "10.5%", left: "0px" }}>
+ <div className="contextMenu-subMenu-cont" style={{ marginLeft: "25%", left: "0px" }}>
{this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
</div>;
return (
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7cdb16f52..94aab8b2f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -621,6 +621,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
doc.y = (doc.y || 0) + dY * (actualdH - height);
let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here...
let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight);
+ if (fixedAspect && e.ctrlKey && BoolCast(doc.ignoreAspect)) {
+ doc.ignoreAspect = false;
+ proto.nativeWidth = nwidth = doc.width || 0;
+ proto.nativeHeight = nheight = doc.height || 0;
+ }
if (fixedAspect && (!nwidth || !nheight)) {
proto.nativeWidth = nwidth = doc.width || 0;
proto.nativeHeight = nheight = doc.height || 0;
diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx
index eb6312e78..aa573f16b 100644
--- a/src/client/views/InkingControl.tsx
+++ b/src/client/views/InkingControl.tsx
@@ -10,8 +10,10 @@ import { InkTool } from "../../new_fields/InkField";
import { Doc } from "../../new_fields/Doc";
import { undoBatch, UndoManager } from "../util/UndoManager";
import { StrCast, NumCast, Cast } from "../../new_fields/Types";
-import { FormattedTextBox } from "./nodes/FormattedTextBox";
import { MainOverlayTextBox } from "./MainOverlayTextBox";
+import { listSpec } from "../../new_fields/Schema";
+import { List } from "../../new_fields/List";
+import { Utils } from "../../Utils";
library.add(faPen, faHighlighter, faEraser, faBan);
@@ -49,11 +51,34 @@ export class InkingControl extends React.Component {
let oldColors = selected.map(view => {
let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document);
let oldColor = StrCast(targetDoc.backgroundColor);
- targetDoc.backgroundColor = this._selectedColor;
+ if (view.props.ContainingCollectionView && view.props.ContainingCollectionView.props.Document.colorPalette) {
+ let cp = Cast(view.props.ContainingCollectionView.props.Document.colorPalette, listSpec("string")) as string[];
+ let closest = 0;
+ let dist = 10000000;
+ let ccol = Utils.fromRGBAstr(StrCast(targetDoc.backgroundColor));
+ for (let i = 0; i < cp.length; i++) {
+ let cpcol = Utils.fromRGBAstr(cp[i]);
+ let d = Math.sqrt((ccol.r - cpcol.r) * (ccol.r - cpcol.r) + (ccol.b - cpcol.b) * (ccol.b - cpcol.b) + (ccol.g - cpcol.g) * (ccol.g - cpcol.g));
+ if (d < dist) {
+ dist = d;
+ closest = i;
+ }
+ }
+ cp[closest] = "rgba(" + color.rgb.r + "," + color.rgb.g + "," + color.rgb.b + "," + color.rgb.a + ")";
+ view.props.ContainingCollectionView.props.Document.colorPalette = new List(cp);
+ targetDoc.backgroundColor = cp[closest];
+ } else
+ targetDoc.backgroundColor = this._selectedColor;
if (view.props.Document.heading) {
let cv = view.props.ContainingCollectionView;
let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc);
- cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = this._selectedColor);
+ let parback = cv && StrCast(cv.props.Document.backgroundColor);
+ cv && parback && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = Utils.toRGBAstr(color.rgb));
+ // if (parback && cv && parback.indexOf("rgb") !== -1) {
+ // let parcol = Utils.fromRGBAstr(parback);
+ // let hsl = Utils.RGBToHSL(parcol.r, parcol.g, parcol.b);
+ // cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = color.hsl.s - hsl.s);
+ // }
}
return {
target: targetDoc,
@@ -67,7 +92,6 @@ export class InkingControl extends React.Component {
});
}
});
-
@action
switchWidth = (width: string): void => {
this._selectedWidth = width;
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 2b862a81e..7afba5e01 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -66,13 +66,24 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
</div>
);
}
- public static EditClickScript(doc: Doc, fieldKey: string) {
+ public static EditClickScript(doc: Doc, fieldKey: string, prewrapper?: string, postwrapper?: string) {
let overlayDisposer: () => void = emptyFunction;
const script = ScriptCast(doc[fieldKey]);
let originalText: string | undefined = undefined;
- if (script) originalText = script.script.originalScript;
+ if (script) {
+ originalText = script.script.originalScript;
+ if (prewrapper && originalText.startsWith(prewrapper)) {
+ originalText = originalText.substr(prewrapper.length);
+ }
+ if (postwrapper && originalText.endsWith(postwrapper)) {
+ originalText = originalText.substr(0, originalText.length - postwrapper.length);
+ }
+ }
// tslint:disable-next-line: no-unnecessary-callback-wrapper
let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
+ if (prewrapper) {
+ text = prewrapper + text + (postwrapper ? postwrapper : "");
+ }
const script = CompileScript(text, {
params: { this: Doc.name },
typecheck: false,
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index b6ed6aaa0..bd8d56851 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -12,6 +12,7 @@ import { ContextMenu } from '../ContextMenu';
import { FieldViewProps } from '../nodes/FieldView';
import './CollectionBaseView.scss';
import { DateField } from '../../../new_fields/DateField';
+import { DocumentType } from '../../documents/DocumentTypes';
export enum CollectionViewType {
Invalid,
@@ -103,6 +104,9 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
if (this.props.fieldExt) { // bcz: fieldExt !== undefined means this is an overlay layer
Doc.GetProto(doc).annotationOn = this.props.Document;
}
+ if (doc.type === DocumentType.BUTTON) {
+ doc.collectionContext = this.props.Document; // used by docList() function in Doc.ts so that buttons can iterate over the documents in their collection
+ }
allowDuplicates = true;
let targetDataDoc = this.props.fieldExt || this.props.Document.isTemplate ? this.extensionDoc : this.props.Document;
let targetField = (this.props.fieldExt || this.props.Document.isTemplate) && this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3ebbe0046..21f119d57 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -8,7 +8,7 @@ import { Id } from "../../../../new_fields/FieldSymbols";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { createSchema, makeInterface } from "../../../../new_fields/Schema";
import { ScriptField } from "../../../../new_fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../../new_fields/Types";
import { emptyFunction, returnEmptyString, returnOne, Utils } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { Docs } from "../../../documents/Documents";
@@ -262,14 +262,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
newBox.heading = 2;
}
}
- let ruleProvider = Cast(this.props.Document.ruleProvider, Doc);
- if (!(ruleProvider instanceof Doc)) ruleProvider = this.props.Document;
- let col = StrCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]);
- let round = StrCast(ruleProvider["ruleRounding_" + NumCast(newBox.heading)]);
- round && (Doc.GetProto(newBox).borderRounding = round);
- col && (Doc.GetProto(newBox).backgroundColor = col);
- newBox.ruleProvider = ruleProvider;
- this.addDocument(newBox, false);
+ PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => {
+ if (!ruleProvider) ruleProvider = this.props.Document;
+ // saturation shift
+ // let col = NumCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]);
+ // let back = Utils.fromRGBAstr(StrCast(this.props.Document.backgroundColor));
+ // let hsl = Utils.RGBToHSL(back.r, back.g, back.b);
+ // let newcol = { h: hsl.h, s: hsl.s + col, l: hsl.l };
+ // col && (Doc.GetProto(newBox).backgroundColor = Utils.toRGBAstr(Utils.HSLtoRGB(newcol.h, newcol.s, newcol.l)));
+ // OR transparency set
+ let col = StrCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]);
+ col && (Doc.GetProto(newBox).backgroundColor = col);
+
+ let round = StrCast(ruleProvider["ruleRounding_" + NumCast(newBox.heading)]);
+ round && (Doc.GetProto(newBox).borderRounding = round);
+ newBox.ruleProvider = ruleProvider;
+ this.addDocument(newBox, false);
+ });
}
private addDocument = (newBox: Doc, allowDuplicates: boolean) => {
this.props.addDocument(newBox, false);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 100e6d817..56d8127e2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,11 +1,11 @@
import * as htmlToImage from "html-to-image";
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, FieldResult } from "../../../../new_fields/Doc";
+import { Doc, FieldResult, DocListCast } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
-import { Cast, NumCast } from "../../../../new_fields/Types";
+import { Cast, NumCast, StrCast } from "../../../../new_fields/Types";
import { Utils } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
@@ -20,6 +20,8 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
import { SchemaHeaderField, RandomPastel } from "../../../../new_fields/SchemaHeaderField";
+import { string } from "prop-types";
+import { listSpec } from "../../../../new_fields/Schema";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -272,14 +274,30 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
return d;
});
}
+ let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+ "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
+ let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string"));
+ if (!colorPalette) this.props.container.props.Document.colorPalette = new List<string>(defaultPalette);
+ let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
+ let usedPaletted = new Map<string, number>();
+ [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
+ let bg = StrCast(child.backgroundColor);
+ if (palette.indexOf(bg) !== -1) {
+ palette.splice(palette.indexOf(bg), 1);
+ if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
+ else usedPaletted.set(bg, 1);
+ }
+ });
+ let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
+ let chosenColor = usedPaletted.get("white") || usedPaletted.get("rgb(255,255,255)") && usedPaletted.size === 1 ? "white" : palette.length ? palette[0] : usedSequnce[0];
let inkData = this.ink ? this.ink.inkData : undefined;
let newCollection = Docs.Create.FreeformDocument(selected, {
x: bounds.left,
y: bounds.top,
panX: 0,
panY: 0,
- backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
- defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
+ backgroundColor: this.props.container.isAnnotationOverlay ? undefined : chosenColor,
+ defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : chosenColor,
width: bounds.width,
height: bounds.height,
title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection",
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c059ff50d..9692dd8a9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -99,10 +99,13 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transformOrigin: "left top",
position: "absolute",
backgroundColor: "transparent",
- boxShadow: this.props.Document.opacity === 0 ? undefined : this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` :
- this.clusterColor ? (
- this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :
- `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : undefined,
+ boxShadow:
+ this.props.Document.opacity === 0 ? undefined : // if it's not visible, then no shadow
+ this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
+ this.clusterColor ? (
+ this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
+ `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ undefined,
borderRadius: this.borderRounding(),
transform: txf,
transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a65f16e1c..633455f63 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -602,11 +602,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
+ makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") });
makes.push({
description: "Into Portal", event: () => {
- let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
- DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
- this.makeBtnClicked();
+ if (!DocListCast(this.props.Document.links).find(doc => {
+ if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
+ return false;
+ })) {
+ let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
+ DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
+ Doc.GetProto(this.props.Document).isButton = true;
+ }
}, icon: "window-restore"
});
makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" });
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 6a6000dc5..0ea36cdc2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc";
import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField";
-import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, DateCast, PromiseValue } from "../../../new_fields/Types";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
import { Utils, numberRange, timenow } from '../../../Utils';
import { DocServer } from "../../DocServer";
@@ -39,6 +39,7 @@ import { ReplaceStep } from 'prosemirror-transform';
import { DocumentType } from '../../documents/DocumentTypes';
import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { inputRules } from 'prosemirror-inputrules';
+import { select } from 'async';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -203,8 +204,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks);
}
- this._keymap["ACTIVE"] = true; // hack to ignore an initial carriage return when creating a textbox from the action menu
-
this._applyingChange = true;
this.extensionDoc && (this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"));
this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now())));
@@ -353,7 +352,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
_keymap: any = undefined;
@computed get config() {
this._keymap = buildKeymap(schema);
- this._keymap["ACTIVE"] = this.extensionDoc.text; // hack to ignore an initial carriage return only when creating a textbox from the action menu
return {
schema,
plugins: this.props.isOverlay ? [
@@ -659,7 +657,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
- if (this.props.Document[Id] === FormattedTextBox.SelectOnLoad) {
+ let selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
+ if (selectOnLoad) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
}
@@ -667,15 +666,38 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
// add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
let heading = this.props.Document.heading;
- if (heading) {
- let ruleProvider = Cast(this.props.Document.ruleProvider, Doc);
- if (ruleProvider instanceof Doc) {
- let font = StrCast(ruleProvider["ruleFont_" + heading]);
- let size = NumCast(ruleProvider["ruleSize_" + heading]);
- size && (this._editorView!.state.storedMarks = [...this._editorView!.state.storedMarks, schema.marks.pFontSize.create({ fontSize: size })]);
- font && (this._editorView!.state.storedMarks = [...this._editorView!.state.storedMarks, font === "Arial" ? schema.marks.arial.create() : schema.marks.comicSans.create()]);
- }
+ if (heading && selectOnLoad) {
+ PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => {
+ if (ruleProvider) {
+ let align = StrCast(ruleProvider["ruleAlign_" + heading]);
+ let font = StrCast(ruleProvider["ruleFont_" + heading]);
+ let size = NumCast(ruleProvider["ruleSize_" + heading]);
+ if (align) {
+ let tr = this._editorView!.state.tr;
+ tr = tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(2))).
+ replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: align }), true).
+ setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(0)));
+ this._editorView!.dispatch(tr);
+ }
+ let sm = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
+ size && (sm = [...sm, schema.marks.pFontSize.create({ fontSize: size })]);
+ font && (sm = [...sm, this.getFont(font)]);
+ this._editorView!.dispatch(this._editorView!.state.tr.setStoredMarks(sm));
+ }
+ });
+ }
+ }
+ getFont(font: string) {
+ switch (font) {
+ case "Arial": return schema.marks.arial.create();
+ case "Times New Roman": return schema.marks.timesNewRoman.create();
+ case "Georgia": return schema.marks.georgia.create();
+ case "Comic Sans MS": return schema.marks.comicSans.create();
+ case "Tahoma": return schema.marks.tahoma.create();
+ case "Impact": return schema.marks.impact.create();
+ case "ACrimson Textrial": return schema.marks.crimson.create();
}
+ return schema.marks.arial.create();
}
componentWillUnmount() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index e3d7cc9ed..ccb8f4aa2 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -685,4 +685,5 @@ export namespace Doc {
Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
-Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); }); \ No newline at end of file
+Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
+Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); \ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 9866e22eb..9d35d36d3 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -115,17 +115,17 @@ export class CurrentUserUtils {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
}
});
- try {
- const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" });
- NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
- await Gateway.Instance.ClearCatalog();
- const extraSchemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
- let extras = await Promise.all(extraSchemas.map(sc => Gateway.Instance.GetSchema("", sc)));
- let catprom = CurrentUserUtils.SetNorthstarCatalog(await Gateway.Instance.GetCatalog(), extras);
- // if (catprom) await Promise.all(catprom);
- } catch (e) {
+ // try {
+ // const getEnvironment = await fetch("/assets/env.json", { redirect: "follow", method: "GET", credentials: "include" });
+ // NorthstarSettings.Instance.UpdateEnvironment(await getEnvironment.json());
+ // await Gateway.Instance.ClearCatalog();
+ // const extraSchemas = Cast(CurrentUserUtils.UserDocument.DBSchemas, listSpec("string"), []);
+ // let extras = await Promise.all(extraSchemas.map(sc => Gateway.Instance.GetSchema("", sc)));
+ // let catprom = CurrentUserUtils.SetNorthstarCatalog(await Gateway.Instance.GetCatalog(), extras);
+ // // if (catprom) await Promise.all(catprom);
+ // } catch (e) {
- }
+ // }
}
/* Northstar catalog ... really just for testing so this should eventually go away */