aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts17
-rw-r--r--src/client/northstar/core/filter/ValueComparision.ts6
-rw-r--r--src/client/northstar/dash-nodes/HistogramBox.tsx54
-rw-r--r--src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx8
-rw-r--r--src/client/northstar/operations/HistogramOperation.ts34
-rw-r--r--src/client/util/DragManager.ts32
-rw-r--r--src/client/util/TooltipTextMenu.tsx81
-rw-r--r--src/client/views/.DS_Storebin8196 -> 6148 bytes
-rw-r--r--src/client/views/DocumentDecorations.scss100
-rw-r--r--src/client/views/DocumentDecorations.tsx185
-rw-r--r--src/client/views/Main.scss6
-rw-r--r--src/client/views/Main.tsx4
-rw-r--r--src/client/views/MainOverlayTextBox.tsx13
-rw-r--r--src/client/views/PreviewCursor.tsx9
-rw-r--r--src/client/views/TemplateMenu.tsx75
-rw-r--r--src/client/views/Templates.tsx66
-rw-r--r--src/client/views/collections/CollectionBaseView.tsx5
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx1
-rw-r--r--src/client/views/collections/CollectionPDFView.scss26
-rw-r--r--src/client/views/collections/CollectionPDFView.tsx34
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx35
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx30
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx3
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.tsx52
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss11
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx48
-rw-r--r--src/client/views/nodes/KeyValueBox.scss1
-rw-r--r--src/client/views/nodes/KeyValuePair.scss1
-rw-r--r--src/client/views/nodes/PDFBox.scss18
-rw-r--r--src/client/views/nodes/PDFBox.tsx247
-rw-r--r--src/client/views/nodes/Sticky.tsx83
-rw-r--r--src/client/views/nodes/VideoBox.tsx21
-rw-r--r--src/client/views/nodes/WebBox.scss17
-rw-r--r--src/client/views/nodes/WebBox.tsx6
-rw-r--r--src/fields/TemplateField.ts43
-rw-r--r--src/server/Message.ts2
42 files changed, 865 insertions, 554 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2a9687bda..aa5277910 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -18,6 +18,8 @@ import { action } from "mobx";
import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel";
import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel";
import { AggregateFunction } from "../northstar/model/idea/idea";
+import { Template } from "../views/Templates";
+import { TemplateField } from "../../fields/TemplateField";
import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
import { IconBox } from "../views/nodes/IconBox";
import { Field, Doc, Opt } from "../../new_fields/Doc";
@@ -30,7 +32,11 @@ import { IconField } from "../../new_fields/IconField";
import { listSpec } from "../../new_fields/Schema";
import { DocServer } from "../DocServer";
import { StrokeData, InkField } from "../../new_fields/InkField";
+import { KeyStore } from "../../fields/KeyStore";
+// export class stringArray implements List<any> {
+// public Values: string[] = [];
+// }
export interface DocumentOptions {
x?: number;
y?: number;
@@ -44,7 +50,9 @@ export interface DocumentOptions {
panY?: number;
page?: number;
scale?: number;
+ baseLayout?: string;
layout?: string;
+ //templates?: Array<Template>;
viewType?: number;
backgroundColor?: string;
copyDraggedItems?: boolean;
@@ -52,6 +60,7 @@ export interface DocumentOptions {
curPage?: number;
documentText?: string;
borderRounding?: number;
+ //schemaColumns?: stringArray;
// [key: string]: Opt<Field>;
}
const delegateKeys = ["x", "y", "width", "height", "panX", "panY"];
@@ -94,7 +103,7 @@ export namespace Docs {
}
function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Doc {
- return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout });
+ return Doc.assign(new Doc(protoId, true), { ...options, title: title, layout: layout, baseLayout: layout });
}
function SetInstanceOptions<U extends Field>(doc: Doc, options: DocumentOptions, value: U) {
const deleg = Doc.MakeDelegate(doc);
@@ -234,13 +243,13 @@ export namespace Docs {
if (!makePrototype) {
return SetInstanceOptions(collProto, { ...options, viewType: CollectionViewType.Freeform }, new List(documents));
}
- return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Freeform });
+ return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns],*/...options, viewType: CollectionViewType.Freeform });
}
export function SchemaDocument(documents: Array<Doc>, options: DocumentOptions) {
- return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Schema });
+ return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns], */...options, viewType: CollectionViewType.Schema });
}
export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) {
- return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Tree });
+ return CreateInstance(collProto, new List(documents), { /*schemaColumns: [KeyStore.SchemaColumns], */...options, viewType: CollectionViewType.Tree });
}
export function DockDocument(config: string, options: DocumentOptions) {
return CreateInstance(collProto, config, { ...options, viewType: CollectionViewType.Docking });
diff --git a/src/client/northstar/core/filter/ValueComparision.ts b/src/client/northstar/core/filter/ValueComparision.ts
index 80b1242a9..65687a82b 100644
--- a/src/client/northstar/core/filter/ValueComparision.ts
+++ b/src/client/northstar/core/filter/ValueComparision.ts
@@ -62,13 +62,13 @@ export class ValueComparison {
var rawName = this.attributeModel.CodeName;
switch (this.Predicate) {
case Predicate.STARTS_WITH:
- ret += rawName + " !== null && " + rawName + ".StartsWith(" + val + ") ";
+ ret += rawName + " != null && " + rawName + ".StartsWith(" + val + ") ";
return ret;
case Predicate.ENDS_WITH:
- ret += rawName + " !== null && " + rawName + ".EndsWith(" + val + ") ";
+ ret += rawName + " != null && " + rawName + ".EndsWith(" + val + ") ";
return ret;
case Predicate.CONTAINS:
- ret += rawName + " !== null && " + rawName + ".Contains(" + val + ") ";
+ ret += rawName + " != null && " + rawName + ".Contains(" + val + ") ";
return ret;
default:
ret += rawName + " " + op + " " + val + " ";
diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx
index 19d108676..765ecf8f0 100644
--- a/src/client/northstar/dash-nodes/HistogramBox.tsx
+++ b/src/client/northstar/dash-nodes/HistogramBox.tsx
@@ -1,7 +1,6 @@
import React = require("react");
import { action, computed, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
-import Measure from "react-measure";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { ChartType, VisualBinRange } from '../../northstar/model/binRanges/VisualBinRange';
import { VisualBinRangeHelper } from "../../northstar/model/binRanges/VisualBinRangeHelper";
@@ -32,8 +31,6 @@ export class HistogramBox extends React.Component<FieldViewProps> {
private _dropXDisposer?: DragManager.DragDropDisposer;
private _dropYDisposer?: DragManager.DragDropDisposer;
- @observable public PanelWidth: number = 100;
- @observable public PanelHeight: number = 100;
@observable public HistoOp: HistogramOperation = HistogramOperation.Empty;
@observable public VisualBinRanges: VisualBinRange[] = [];
@observable public ValueRange: number[] = [];
@@ -89,7 +86,7 @@ export class HistogramBox extends React.Component<FieldViewProps> {
}
reaction(() => CurrentUserUtils.NorthstarDBCatalog, (catalog?: Catalog) => this.activateHistogramOperation(catalog), { fireImmediately: true });
reaction(() => [this.VisualBinRanges && this.VisualBinRanges.slice()], () => this.SizeConverter.SetVisualBinRanges(this.VisualBinRanges));
- reaction(() => [this.PanelHeight, this.PanelWidth], () => this.SizeConverter.SetIsSmall(this.PanelWidth < 40 && this.PanelHeight < 40));
+ reaction(() => [this.props.PanelWidth(), this.props.PanelHeight()], (size: number[]) => this.SizeConverter.SetIsSmall(size[0] < 40 && size[1] < 40));
reaction(() => this.HistogramResult ? this.HistogramResult.binRanges : undefined,
(binRanges: BinRange[] | undefined) => {
if (binRanges) {
@@ -138,38 +135,39 @@ export class HistogramBox extends React.Component<FieldViewProps> {
});
}
}
+
+ @action
+ private onScrollWheel = (e: React.WheelEvent) => {
+ this.HistoOp.DrillDown(e.deltaY > 0);
+ e.stopPropagation();
+ }
+
render() {
let labelY = this.HistoOp && this.HistoOp.Y ? this.HistoOp.Y.PresentedName : "<...>";
let labelX = this.HistoOp && this.HistoOp.X ? this.HistoOp.X.PresentedName : "<...>";
- var h = this.props.isTopMost ? this.PanelHeight : NumCast(this.props.Document.height);
- var w = this.props.isTopMost ? this.PanelWidth : NumCast(this.props.Document.width);
let loff = this.SizeConverter.LeftOffset;
let toff = this.SizeConverter.TopOffset;
let roff = this.SizeConverter.RightOffset;
let boff = this.SizeConverter.BottomOffset;
return (
- <Measure onResize={(r: any) => runInAction(() => { this.PanelWidth = r.entry.width; this.PanelHeight = r.entry.height; })}>
- {({ measureRef }) =>
- <div className="histogrambox-container" ref={measureRef}>
- <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
- <span className="histogrambox-yaxislabel-text">
- {labelY}
- </span>
- </div>
- <div className="histogrambox-primitives" style={{
- transform: `translate(${loff + 25}px, ${toff}px)`,
- width: `calc(100% - ${loff + roff + 25}px)`,
- height: `calc(100% - ${toff + boff}px)`,
- }}>
- <HistogramLabelPrimitives HistoBox={this} />
- <HistogramBoxPrimitives HistoBox={this} />
- </div>
- <div className="histogrambox-xaxislabel" onPointerDown={this.xLabelPointerDown} ref={this._dropXRef} >
- {labelX}
- </div>
- </div>
- }
- </Measure>
+ <div className="histogrambox-container" onWheel={this.onScrollWheel}>
+ <div className="histogrambox-yaxislabel" onPointerDown={this.yLabelPointerDown} ref={this._dropYRef} >
+ <span className="histogrambox-yaxislabel-text">
+ {labelY}
+ </span>
+ </div>
+ <div className="histogrambox-primitives" style={{
+ transform: `translate(${loff + 25}px, ${toff}px)`,
+ width: `calc(100% - ${loff + roff + 25}px)`,
+ height: `calc(100% - ${toff + boff}px)`,
+ }}>
+ <HistogramLabelPrimitives HistoBox={this} />
+ <HistogramBoxPrimitives HistoBox={this} />
+ </div>
+ <div className="histogrambox-xaxislabel" onPointerDown={this.xLabelPointerDown} ref={this._dropXRef} >
+ {labelX}
+ </div>
+ </div>
);
}
}
diff --git a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
index 5785fe838..62aebd3c6 100644
--- a/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
+++ b/src/client/northstar/dash-nodes/HistogramLabelPrimitives.tsx
@@ -12,7 +12,7 @@ import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives";
@observer
export class HistogramLabelPrimitives extends React.Component<HistogramPrimitivesProps> {
componentDidMount() {
- reaction(() => [this.props.HistoBox.PanelWidth, this.props.HistoBox.SizeConverter.LeftOffset, this.props.HistoBox.VisualBinRanges.length],
+ reaction(() => [this.props.HistoBox.props.PanelWidth(), this.props.HistoBox.SizeConverter.LeftOffset, this.props.HistoBox.VisualBinRanges.length],
(fields) => HistogramLabelPrimitives.computeLabelAngle(fields[0], fields[1], this.props.HistoBox), { fireImmediately: true });
}
@@ -35,7 +35,7 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
if (!vb.length || !sc.Initialized) {
return (null);
}
- let dim = (axis === 0 ? this.props.HistoBox.PanelWidth : this.props.HistoBox.PanelHeight) / ((axis === 0 && vb[axis] instanceof NominalVisualBinRange) ?
+ let dim = (axis === 0 ? this.props.HistoBox.props.PanelWidth() : this.props.HistoBox.props.PanelHeight()) / ((axis === 0 && vb[axis] instanceof NominalVisualBinRange) ?
(12 + 5) : // (<number>FontStyles.AxisLabel.fontSize + 5)));
sc.MaxLabelSizes[axis].coords[axis] + 5);
@@ -49,12 +49,12 @@ export class HistogramLabelPrimitives extends React.Component<HistogramPrimitive
let yStart = (axis === 1 ? r.yFrom - textHeight / 2 : r.yFrom);
if (axis === 0 && vb[axis] instanceof NominalVisualBinRange) {
- let space = (r.xTo - r.xFrom) / sc.RenderDimension * this.props.HistoBox.PanelWidth;
+ let space = (r.xTo - r.xFrom) / sc.RenderDimension * this.props.HistoBox.props.PanelWidth();
xStart += Math.max(textWidth / 2, (1 - textWidth / space) * textWidth / 2) - textHeight / 2;
}
let xPercent = axis === 1 ? `${xStart}px` : `${xStart / sc.RenderDimension * 100}%`;
- let yPercent = axis === 0 ? `${this.props.HistoBox.PanelHeight - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`;
+ let yPercent = axis === 0 ? `${this.props.HistoBox.props.PanelHeight() - sc.BottomOffset - textHeight}px` : `${yStart / sc.RenderDimension * 100}%`;
prims.push(
<div className="histogramLabelPrimitives-placer" key={DashUtils.GenerateGuid()} style={{ transform: `translate(${xPercent}, ${yPercent})` }}>
diff --git a/src/client/northstar/operations/HistogramOperation.ts b/src/client/northstar/operations/HistogramOperation.ts
index b6672eca3..5c9c832c0 100644
--- a/src/client/northstar/operations/HistogramOperation.ts
+++ b/src/client/northstar/operations/HistogramOperation.ts
@@ -22,7 +22,7 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
@observable public Links: Doc[] = [];
@observable public BrushLinks: { l: Doc, b: Doc }[] = [];
@observable public BrushColors: number[] = [];
- @observable public FilterModels: FilterModel[] = [];
+ @observable public BarFilterModels: FilterModel[] = [];
@observable public Normalization: number = -1;
@observable public X: AttributeTransformationModel;
@@ -49,17 +49,24 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
throw new Error("Method not implemented.");
}
+
+ @computed public get FilterModels() {
+ return this.BarFilterModels;
+ }
@action
public AddFilterModels(filterModels: FilterModel[]): void {
- filterModels.filter(f => f !== null).forEach(fm => this.FilterModels.push(fm));
+ filterModels.filter(f => f !== null).forEach(fm => this.BarFilterModels.push(fm));
}
@action
public RemoveFilterModels(filterModels: FilterModel[]): void {
- ArrayUtil.RemoveMany(this.FilterModels, filterModels);
+ ArrayUtil.RemoveMany(this.BarFilterModels, filterModels);
}
@computed
public get FilterString(): string {
+ if (this.OverridingFilters.length > 0) {
+ return "(" + this.OverridingFilters.filter(fm => fm != null).map(fm => fm.ToPythonString()).join(" || ") + ")";
+ }
let filterModels: FilterModel[] = [];
return FilterModel.GetFilterModelsRecursive(this, new Set<IBaseFilterProvider>(), filterModels, true);
}
@@ -78,6 +85,27 @@ export class HistogramOperation extends BaseOperation implements IBaseFilterCons
return brushes;
}
+ _stackedFilters: (FilterModel[])[] = [];
+ @action
+ public DrillDown(up: boolean) {
+ if (!up) {
+ if (!this.BarFilterModels.length)
+ return;
+ this._stackedFilters.push(this.BarFilterModels.map(f => f));
+ this.OverridingFilters.length = 0;
+ this.OverridingFilters.push(...this._stackedFilters[this._stackedFilters.length - 1]);
+ this.BarFilterModels.map(fm => fm).map(fm => this.RemoveFilterModels([fm]));
+ //this.updateHistogram();
+ } else {
+ this.OverridingFilters.length = 0;
+ if (this._stackedFilters.length) {
+ this.OverridingFilters.push(...this._stackedFilters.pop()!);
+ }
+ // else
+ // this.updateHistogram();
+ }
+ }
+
private getAggregateParameters(histoX: AttributeTransformationModel, histoY: AttributeTransformationModel, histoValue: AttributeTransformationModel) {
let allAttributes = new Array<AttributeTransformationModel>(histoX, histoY, histoValue);
allAttributes = ArrayUtil.Distinct(allAttributes.filter(a => a.AggregateFunction !== AggregateFunction.None));
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e65f2b9ed..a149da52f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -172,6 +172,8 @@ export namespace DragManager {
StartDrag([ele], dragData, downX, downY, options);
}
+ export let AbortDrag: () => void = emptyFunction;
+
function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
if (!dragDiv) {
dragDiv = document.createElement("div");
@@ -272,23 +274,32 @@ export namespace DragManager {
);
};
- AbortDrag = () => {
+ let hideDragElements = () => {
+ dragElements.map(dragElement => dragElement.parentNode == dragDiv && dragDiv.removeChild(dragElement));
+ eles.map(ele => (ele.hidden = false));
+ };
+ let endDrag = () => {
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
- dragElements.map(dragElement => { if (dragElement.parentNode === dragDiv) dragDiv.removeChild(dragElement); });
- eles.map(ele => (ele.hidden = false));
+ if (options) {
+ options.handlers.dragComplete({});
+ }
+ }
+
+ AbortDrag = () => {
+ hideDragElements();
+ endDrag();
};
const upHandler = (e: PointerEvent) => {
- AbortDrag();
- FinishDrag(eles, e, dragData, options, finishDrag);
+ hideDragElements();
+ dispatchDrag(eles, e, dragData, options, finishDrag);
+ endDrag();
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
}
- export let AbortDrag: () => void = emptyFunction;
-
- function FinishDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
+ function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any }, options?: DragOptions, finishDrag?: (dragData: { [index: string]: any }) => void) {
let removed = dragEles.map(dragEle => {
let parent = dragEle.parentElement;
if (parent) parent.removeChild(dragEle);
@@ -313,11 +324,6 @@ export namespace DragManager {
}
})
);
-
- if (options) {
- options.handlers.dragComplete({});
- }
}
- DocumentDecorations.Instance.Hidden = false;
}
}
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index 1b6647003..68a73375e 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -11,7 +11,8 @@ import React = require("react");
import "./TooltipTextMenu.scss";
const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands");
import { library } from '@fortawesome/fontawesome-svg-core';
-import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list';
+import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list';
+import { liftTarget } from 'prosemirror-transform';
import {
faListUl,
} from '@fortawesome/free-solid-svg-icons';
@@ -24,19 +25,22 @@ const SVG = "http://www.w3.org/2000/svg";
//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc.
export class TooltipTextMenu {
- private tooltip: HTMLElement;
+ public tooltip: HTMLElement;
private num_icons = 0;
private view: EditorView;
private fontStyles: MarkType[];
private fontSizes: MarkType[];
+ private listTypes: NodeType[];
private editorProps: FieldViewProps;
private state: EditorState;
private fontSizeToNum: Map<MarkType, number>;
private fontStylesToName: Map<MarkType, string>;
+ private listTypeToIcon: Map<NodeType, string>;
private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
//dropdown doms
private fontSizeDom?: Node;
private fontStyleDom?: Node;
+ private listTypeBtnDom?: Node;
constructor(view: EditorView, editorProps: FieldViewProps) {
this.view = view;
@@ -58,8 +62,9 @@ export class TooltipTextMenu {
{ command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough") },
{ command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript") },
{ command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript") },
- { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
- { command: lift, dom: this.icon("<", "lift") },
+ // { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") },
+ // { command: wrapInList(schema.nodes.ordered_list), dom: this.icon("1)", "bullets") },
+ // { command: lift, dom: this.icon("<", "lift") },
];
//add menu items
items.forEach(({ dom, command }) => {
@@ -96,7 +101,11 @@ export class TooltipTextMenu {
this.fontSizeToNum.set(schema.marks.p72, 72);
this.fontSizes = Array.from(this.fontSizeToNum.keys());
- //this.addFontDropdowns();
+ //list types
+ this.listTypeToIcon = new Map();
+ this.listTypeToIcon.set(schema.nodes.bullet_list, ":");
+ this.listTypeToIcon.set(schema.nodes.ordered_list, "1)");
+ this.listTypes = Array.from(this.listTypeToIcon.keys());
this.update(view, undefined);
}
@@ -109,7 +118,7 @@ export class TooltipTextMenu {
//font SIZES
let fontSizeBtns: MenuItem[] = [];
this.fontSizeToNum.forEach((number, mark) => {
- fontSizeBtns.push(this.dropdownBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
+ fontSizeBtns.push(this.dropdownMarkBtn(String(number), "width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes));
});
if (this.fontSizeDom) { this.tooltip.removeChild(this.fontSizeDom); }
@@ -128,7 +137,7 @@ export class TooltipTextMenu {
//font STYLES
let fontBtns: MenuItem[] = [];
this.fontStylesToName.forEach((name, mark) => {
- fontBtns.push(this.dropdownBtn(name, "font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
+ fontBtns.push(this.dropdownMarkBtn(name, "font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles));
});
if (this.fontStyleDom) { this.tooltip.removeChild(this.fontStyleDom); }
@@ -140,6 +149,29 @@ export class TooltipTextMenu {
this.tooltip.appendChild(this.fontStyleDom);
}
+ //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown
+ updateListItemDropdown(label: string, listTypeBtn: Node) {
+ //remove old btn
+ if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); }
+
+ //Make a dropdown of all list types
+ let toAdd: MenuItem[] = [];
+ this.listTypeToIcon.forEach((icon, type) => {
+ toAdd.push(this.dropdownNodeBtn(icon, "width: 40px;", type, this.view, this.listTypes, this.changeToNodeType));
+ });
+ //option to remove the list formatting
+ toAdd.push(this.dropdownNodeBtn("X", "width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType));
+
+ listTypeBtn = (new Dropdown(toAdd, {
+ label: label,
+ css: "color:white; width: 40px;"
+ }) as MenuItem).render(this.view).dom;
+
+ //add this new button and return it
+ this.tooltip.appendChild(listTypeBtn);
+ 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, view: EditorView, fontMarks: MarkType[]) {
let { empty, $cursor, ranges } = view.state.selection as TextSelection;
@@ -171,9 +203,18 @@ export class TooltipTextMenu {
return toggleMark(markType)(view.state, view.dispatch, view);
}
- //makes a button for the drop down
+ //remove all node typeand apply the passed-in one to the selected text
+ changeToNodeType(nodeType: NodeType | undefined, view: EditorView, allNodes: NodeType[]) {
+ //remove old
+ liftListItem(schema.nodes.list_item)(view.state, view.dispatch);
+ if (nodeType) { //add new
+ wrapInList(nodeType)(view.state, view.dispatch);
+ }
+ }
+
+ //makes a button for the drop down FOR MARKS
//css is the style you want applied to the button
- dropdownBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
+ dropdownMarkBtn(label: string, css: string, markType: MarkType, view: EditorView, changeToMarkInGroup: (markType: MarkType<any>, view: EditorView, groupMarks: MarkType[]) => any, groupMarks: MarkType[]) {
return new MenuItem({
title: "",
label: label,
@@ -186,6 +227,23 @@ export class TooltipTextMenu {
}
});
}
+
+ //makes a button for the drop down FOR NODE TYPES
+ //css is the style you want applied to the button
+ dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) {
+ return new MenuItem({
+ title: "",
+ label: label,
+ execEvent: "",
+ class: "menuicon",
+ css: css,
+ enable(state) { return true; },
+ run() {
+ changeToNodeInGroup(nodeType, view, groupNodes);
+ }
+ });
+ }
+
// Helper function to create menu icons
icon(text: string, name: string) {
let span = document.createElement("span");
@@ -262,6 +320,9 @@ export class TooltipTextMenu {
this.tooltip.style.width = 225 + "px";
this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px";
+ //UPDATE LIST ITEM DROPDOWN
+ this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!);
+
//UPDATE FONT STYLE DROPDOWN
let activeStyles = this.activeMarksOnSelection(this.fontStyles);
if (activeStyles.length === 1) {
@@ -288,7 +349,7 @@ export class TooltipTextMenu {
}
}
- //finds all active marks on selection
+ //finds all active marks on selection in given group
activeMarksOnSelection(markGroup: MarkType[]) {
//current selection
let { empty, $cursor, ranges } = this.view.state.selection as TextSelection;
diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index 0964d5ff3..5008ddfcf 100644
--- a/src/client/views/.DS_Store
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index f78bf9ff8..0dfa6f0ff 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -3,18 +3,19 @@
.documentDecorations {
position: absolute;
}
+
.documentDecorations-container {
z-index: $docDecorations-zindex;
position: absolute;
top: 0;
- left:0;
+ left: 0;
display: grid;
grid-template-rows: 20px 8px 1fr 8px;
grid-template-columns: 8px 16px 1fr 8px 8px;
pointer-events: none;
#documentDecorations-centerCont {
- grid-column:3;
+ grid-column: 3;
background: none;
}
@@ -39,8 +40,8 @@
#documentDecorations-bottomRightResizer,
#documentDecorations-topRightResizer,
#documentDecorations-rightResizer {
- grid-column-start:5;
- grid-column-end:7;
+ grid-column-start: 5;
+ grid-column-end: 7;
}
#documentDecorations-topLeftResizer,
@@ -63,16 +64,16 @@
cursor: ew-resize;
}
.title{
- width:100%;
background: lightblue;
- grid-column-start:3;
+ grid-column-start: 3;
grid-column-end: 4;
pointer-events: auto;
+ overflow: hidden;
}
}
.documentDecorations-closeButton {
- background:$alt-accent;
+ background: $alt-accent;
opacity: 0.8;
grid-column-start: 4;
grid-column-end: 6;
@@ -80,8 +81,9 @@
text-align: center;
cursor: pointer;
}
+
.documentDecorations-minimizeButton {
- background:$alt-accent;
+ background: $alt-accent;
opacity: 0.8;
grid-column-start: 1;
grid-column-end: 3;
@@ -94,6 +96,7 @@
width: $MINIMIZED_ICON_SIZE;
height: $MINIMIZED_ICON_SIZE;
}
+
.documentDecorations-background {
background: lightblue;
position: absolute;
@@ -105,21 +108,9 @@
margin-left: 25px;
}
-.linkButton-empty:hover {
- background: $main-accent;
- transform: scale(1.05);
- cursor: pointer;
-}
-
-.linkButton-nonempty:hover {
- background: $main-accent;
- transform: scale(1.05);
- cursor: pointer;
-}
-
.linkButton-linker {
- position:absolute;
- bottom:0px;
+ position: absolute;
+ bottom: 0px;
left: 0px;
height: 20px;
width: 20px;
@@ -139,13 +130,14 @@
justify-content: center;
align-items: center;
}
+
.linkButton-tray {
grid-column: 1/4;
}
-.linkButton-empty {
+
+.linkButton-empty, .linkButton-nonempty {
height: 20px;
width: 20px;
- margin-top: 10px;
border-radius: 50%;
opacity: 0.9;
pointer-events: auto;
@@ -159,23 +151,51 @@
display: flex;
justify-content: center;
align-items: center;
+
+ &:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ cursor: pointer;
+ }
}
-.linkButton-nonempty {
- height: 20px;
- width: 20px;
- margin-top: 10px;
- border-radius: 50%;
- opacity: 0.9;
+.templating-menu {
+ position: absolute;
+ bottom: 0;
+ left: 50px;
pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
- text-transform: uppercase;
- letter-spacing: 2px;
- font-size: 75%;
- transition: transform 0.2s;
- text-align: center;
- display: flex;
- justify-content: center;
- align-items: center;
+
+ .templating-button {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ opacity: 0.9;
+ background-color: $dark-color;
+ color: $light-color;
+ text-align: center;
+ cursor: pointer;
+
+ &:hover {
+ background: $main-accent;
+ transform: scale(1.05);
+ }
+ }
+
+ #template-list {
+ position: absolute;
+ top: 0;
+ left: 30px;
+ width: 150px;
+ line-height: 25px;
+ max-height: 175px;
+ font-family: $sans-serif;
+ font-size: 12px;
+ background-color: $light-color-secondary;
+ padding: 2px 12px;
+ list-style: none;
+
+ input {
+ margin-right: 10px;
+ }
+ }
} \ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6e361d76a..556117c54 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,4 +1,4 @@
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable, runInAction, untracked, reaction } from "mobx";
import { observer } from "mobx-react";
import { emptyFunction, Utils } from "../../Utils";
import { DragLinksAsDocuments, DragManager } from "../util/DragManager";
@@ -8,7 +8,9 @@ import './DocumentDecorations.scss';
import { MainOverlayTextBox } from "./MainOverlayTextBox";
import { DocumentView, PositionDocument } from "./nodes/DocumentView";
import { LinkMenu } from "./nodes/LinkMenu";
+import { TemplateMenu } from "./TemplateMenu";
import React = require("react");
+import { Template, Templates } from "./Templates";
import { CompileScript } from "../util/Scripting";
import { IconBox } from "./nodes/IconBox";
import { Cast, FieldValue, NumCast, StrCast } from "../../new_fields/Types";
@@ -16,6 +18,7 @@ import { Doc, FieldResult } from "../../new_fields/Doc";
import { listSpec } from "../../new_fields/Schema";
import { Docs } from "../documents/Documents";
import { List } from "../../new_fields/List";
+import { KeyStore } from "../../fields/KeyStore";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -26,9 +29,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _isPointerDown = false;
private _resizing = "";
private keyinput: React.RefObject<HTMLInputElement>;
- private _documents: DocumentView[] = SelectionManager.SelectedDocuments();
private _resizeBorderWidth = 16;
- private _linkBoxHeight = 30;
+ private _linkBoxHeight = 20;
private _titleHeight = 20;
private _linkButton = React.createRef<HTMLDivElement>();
private _linkerButton = React.createRef<HTMLDivElement>();
@@ -36,67 +38,76 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _downY = 0;
@observable private _minimizedX = 0;
@observable private _minimizedY = 0;
- //@observable private _title: string = this._documents[0].props.Document.Title;
- @observable private _title: string = this._documents.length > 0 ? Cast(this._documents[0].props.Document.title, "string", "") : "";
- @observable private _fieldKey: string = "title";
+ @observable private _title: string = "";
+ @observable private _edtingTitle = false;
+ @observable private _fieldKey = KeyStore.Title;
@observable private _hidden = false;
@observable private _opacity = 1;
- @observable private _dragging = false;
@observable private _iconifying = false;
@observable public Interacting = false;
-
constructor(props: Readonly<{}>) {
super(props);
DocumentDecorations.Instance = this;
- this.handleChange = this.handleChange.bind(this);
this.keyinput = React.createRef();
+ reaction(() => SelectionManager.SelectedDocuments().slice(), (docs) => docs.length === 0 && (this._edtingTitle = false));
}
- @action
- handleChange = (event: any) => {
- this._title = event.target.value;
- }
-
- @action
- enterPressed = (e: any) => {
+ @action titleChanged = (event: any) => { this._title = event.target.value; }
+ @action titleBlur = () => { this._edtingTitle = false; }
+ @action titleEntered = (e: any) => {
var key = e.keyCode || e.which;
// enter pressed
if (key === 13) {
var text = e.target.value;
if (text[0] === '#') {
- let command = text.slice(1, text.length);
- this._fieldKey = command;
- // if (command === "Title" || command === "title") {
- // this._fieldKey = KeyStore.Title;
- // }
- // else if (command === "Width" || command === "width") {
- // this._fieldKey = KeyStore.Width;
- // }
- this._title = "changed";
- // TODO: Change field with switch statement
+ this._fieldKey = text.slice(1, text.length);
+ this._title = this.selectionTitle;
}
else {
- if (this._documents.length > 0) {
- let field = this._documents[0].props.Document[this._fieldKey];
- if (typeof field === "string") {
- this._documents.forEach(d =>
- d.props.Document[this._fieldKey] = this._title);
- }
- else if (typeof field === "number") {
- this._documents.forEach(d =>
+ if (SelectionManager.SelectedDocuments().length > 0) {
+ let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
+ if (typeof field === "number") {
+ SelectionManager.SelectedDocuments().forEach(d =>
d.props.Document[this._fieldKey] = +this._title);
+ } else {
+ SelectionManager.SelectedDocuments().forEach(d =>
+ d.props.Document[this._fieldKey] = this._title);
}
- this._title = "changed";
}
}
e.target.blur();
}
}
+ @action onTitleDown = (e: React.PointerEvent): void => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ e.stopPropagation();
+ this.onBackgroundDown(e);
+ document.removeEventListener("pointermove", this.onTitleMove);
+ document.removeEventListener("pointerup", this.onTitleUp);
+ document.addEventListener("pointermove", this.onTitleMove);
+ document.addEventListener("pointerup", this.onTitleUp);
+ }
+ @action onTitleMove = (e: PointerEvent): void => {
+ if (Math.abs(e.clientX - this._downX) > 4 || Math.abs(e.clientY - this._downY) > 4) {
+ this.Interacting = true;
+ }
+ if (this.Interacting) this.onBackgroundMove(e);
+ e.stopPropagation();
+ }
+ @action onTitleUp = (e: PointerEvent): void => {
+ if (Math.abs(e.clientX - this._downX) < 4 || Math.abs(e.clientY - this._downY) < 4) {
+ this._title = this.selectionTitle;
+ this._edtingTitle = true;
+ }
+ document.removeEventListener("pointermove", this.onTitleMove);
+ document.removeEventListener("pointerup", this.onTitleUp);
+ this.onBackgroundUp(e);
+ }
@computed
get Bounds(): { x: number, y: number, b: number, r: number } {
- this._documents = SelectionManager.SelectedDocuments();
return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => {
if (documentView.props.isTopMost) {
return bounds;
@@ -111,47 +122,39 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE });
}
-
- @computed
- public get Hidden() { return this._hidden; }
- public set Hidden(value: boolean) { this._hidden = value; }
-
- _lastDrag: number[] = [0, 0];
onBackgroundDown = (e: React.PointerEvent): void => {
document.removeEventListener("pointermove", this.onBackgroundMove);
- document.addEventListener("pointermove", this.onBackgroundMove);
document.removeEventListener("pointerup", this.onBackgroundUp);
+ document.addEventListener("pointermove", this.onBackgroundMove);
document.addEventListener("pointerup", this.onBackgroundUp);
- this._lastDrag = [e.clientX, e.clientY];
e.stopPropagation();
- if (e.currentTarget.localName !== "input") {
- e.preventDefault();
- }
+ e.preventDefault();
}
@action
onBackgroundMove = (e: PointerEvent): void => {
let dragDocView = SelectionManager.SelectedDocuments()[0];
const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0);
- let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top);
+ let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document));
dragData.xOffset = xoff;
dragData.yOffset = yoff;
dragData.aliasOnDrop = false;
- let move = SelectionManager.SelectedDocuments()[0].props.moveDocument;
- dragData.moveDocument = move;
- this.Interacting = this._dragging = true;
+ dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument;
+ this.Interacting = true;
+ this._hidden = true;
document.removeEventListener("pointermove", this.onBackgroundMove);
document.removeEventListener("pointerup", this.onBackgroundUp);
+ document.removeEventListener("pointermove", this.onTitleMove);
+ document.removeEventListener("pointerup", this.onTitleUp);
DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(docView => docView.ContentDiv!), dragData, e.x, e.y, {
- handlers: {
- dragComplete: action(() => this.Interacting = this._dragging = false),
- },
+ handlers: { dragComplete: action(() => this._hidden = this.Interacting = false) },
hideSource: true
});
e.stopPropagation();
}
+ @action
onBackgroundUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onBackgroundMove);
document.removeEventListener("pointerup", this.onBackgroundUp);
@@ -238,17 +241,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
@action
public getIconDoc = async (docView: DocumentView): Promise<Doc | undefined> => {
let doc = docView.props.Document;
-
- const minDoc = await Cast(doc.minimizedDoc, Doc);
- if (minDoc) return minDoc;
-
- const field = StrCast(doc.backgroundLayout, undefined);
- if (field !== undefined) return this.createIcon(docView, field);
-
- const layout = StrCast(doc.layout, undefined);
- if (layout !== undefined) return this.createIcon(docView, field);
-
- return undefined;
+ let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc);
+ if (!iconDoc) {
+ const field = StrCast(doc.backgroundLayout, undefined);
+ if (field !== undefined) {
+ iconDoc = this.createIcon(docView, field);
+ } else {
+ const layout = StrCast(doc.layout, undefined);
+ if (layout !== undefined) {
+ iconDoc = this.createIcon(docView, field);
+ }
+ }
+ }
+ if (SelectionManager.SelectedDocuments()[0].props.addDocument !== undefined)
+ SelectionManager.SelectedDocuments()[0].props.addDocument!(iconDoc!);
+ return iconDoc;
}
@action
onMinimizeUp = (e: PointerEvent): void => {
@@ -450,17 +457,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
}
- getValue = (): string => {
- if (this._documents.length > 0) {
- let field = this._documents[0].props.Document[this._fieldKey];
+ @computed
+ get selectionTitle(): string {
+ if (SelectionManager.SelectedDocuments().length === 1) {
+ let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey];
if (typeof field === "string") {
return field;
}
else if (typeof field === "number") {
return field.toString();
}
+ } else if (SelectionManager.SelectedDocuments().length > 1) {
+ return "-multiple-";
}
- return this._title;
+ return "-unset-";
}
changeFlyoutContent = (): void => {
@@ -469,10 +479,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
// buttonOnPointerUp = (e: React.PointerEvent): void => {
// e.stopPropagation();
// }
+
render() {
var bounds = this.Bounds;
let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
- if (bounds.x === Number.MAX_VALUE || !seldoc) {
+ if (bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
return (null);
}
let minimizeIcon = (
@@ -480,14 +491,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{SelectionManager.SelectedDocuments().length === 1 ? IconBox.DocumentIcon(StrCast(SelectionManager.SelectedDocuments()[0].props.Document.layout, "...")) : "..."}
</div>);
- if (this.Hidden) {
- return (null);
- }
- if (isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
- console.log("DocumentDecorations: Bounds Error");
- return (null);
- }
-
let linkButton = null;
if (SelectionManager.SelectedDocuments().length > 0) {
let selFirst = SelectionManager.SelectedDocuments()[0];
@@ -498,17 +501,30 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
anchorPoint={anchorPoints.RIGHT_TOP}
content={<LinkMenu docView={selFirst}
changeFlyout={this.changeFlyoutContent} />}>
- {/* <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div> */}
- <div className={"linkButton-empty"} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
- </Flyout>);
+ <div className={"linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
+ </Flyout >);
}
+
+ let templates: Map<Template, boolean> = new Map();
+ let doc = SelectionManager.SelectedDocuments()[0];
+ Array.from(Object.values(Templates.TemplateList)).map(template => {
+ let docTemps = doc.templates;
+ let checked = false;
+ docTemps.forEach(temp => {
+ if (template.Name === temp.Name) {
+ checked = true;
+ }
+ });
+ templates.set(template, checked);
+ });
+
return (<div className="documentDecorations">
<div className="documentDecorations-background" style={{
width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
left: bounds.x - this._resizeBorderWidth / 2,
top: bounds.y - this._resizeBorderWidth / 2,
- pointerEvents: this._dragging ? "none" : "all",
+ pointerEvents: this.Interacting ? "none" : "all",
zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
</div>
@@ -521,7 +537,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}}>
{minimizeIcon}
- <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this.getValue()} onChange={this.handleChange} onPointerDown={this.onBackgroundDown} onKeyPress={this.enterPressed} />
+ {this._edtingTitle ?
+ <input ref={this.keyinput} className="title" type="text" name="dynbox" value={this._title} onBlur={this.titleBlur} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> :
+ <div className="title" onPointerDown={this.onTitleDown} ><span>{`${this.selectionTitle}`}</span></div>}
<div className="documentDecorations-closeButton" onPointerDown={this.onCloseDown}>X</div>
<div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
@@ -535,6 +553,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton} </div>
<div className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>∞</div>
+ <TemplateMenu doc={doc} templates={templates} />
</div >
</div>
);
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 2f899ff28..89fdf595a 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -12,6 +12,10 @@ body {
left:0;
}
+div {
+ user-select: none;
+}
+
#dash-title {
position: absolute;
right: 46.5%;
@@ -55,7 +59,7 @@ p {
::-webkit-scrollbar {
-webkit-appearance: none;
height: 5px;
- width: 5px;
+ width: 10px;
}
::-webkit-scrollbar-thumb {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 98c5a5306..3f8314d5f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -150,7 +150,7 @@ export class Main extends React.Component {
if (list) {
let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: "mini collection" });
var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] };
- let mainDoc = Docs.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.length + 1}` }, id);
+ let mainDoc = Docs.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.length + 1}` });
list.push(mainDoc);
CurrentUserUtils.MainDocId = mainDoc[Id];
// bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
@@ -216,7 +216,7 @@ export class Main extends React.Component {
let audiourl = "http://techslides.com/demos/samples/sample.mp3";
let videourl = "http://techslides.com/demos/sample-videos/small.mp4";
- let addTextNode = action(() => Docs.TextDocument({ width: 200, height: 200, title: "a text note" }));
+ let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" }));
let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" }));
let addSchemaNode = action(() => Docs.SchemaDocument([], { width: 200, height: 200, title: "a schema collection" }));
let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", copyDraggedItems: true }));
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index a7ced36bf..de8b3707d 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -20,7 +20,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
public static Instance: MainOverlayTextBox;
@observable public TextDoc?: Doc = undefined;
public TextScroll: number = 0;
- @observable _textRect: any;
@observable _textXf: () => Transform = () => Transform.Identity();
private _textFieldKey: string = "data";
private _textColor: string | null = null;
@@ -46,7 +45,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
if (div) {
this._textColor = div.style.color;
div.style.color = "transparent";
- this._textRect = div.getBoundingClientRect();
this.TextScroll = div.scrollTop;
}
}
@@ -87,13 +85,12 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
}
render() {
- if (this.TextDoc) {
- let toScreenXf = this._textXf().inverse();
- let pt = toScreenXf.transformPoint(0, 0);
- let s = 1 / this._textXf().Scale;
- return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${pt[0]}px, ${pt[1]}px) scale(${s},${s})`, width: "auto", height: "auto" }} >
+ if (this.TextDoc && this._textTargetDiv) {
+ let textRect = this._textTargetDiv.getBoundingClientRect();
+ let s = this._textXf().Scale;
+ return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.left}px, ${textRect.top}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
<div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
- style={{ width: `${NumCast(this.TextDoc.width)}px`, height: `${NumCast(this.TextDoc.height)}px` }}>
+ style={{ width: `${NumCast(this.TextDoc.width) * s}px`, height: `${NumCast(this.TextDoc.height) * s}px` }}>
<FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} />
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 744ec0535..4359ba093 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -26,12 +26,11 @@ export class PreviewCursor extends React.Component<{}> {
// DASHFormattedTextBoxHandled flag when a text box consumes a key press so that we can ignore
// the keyPress here.
//if not these keys, make a textbox if preview cursor is active!
- if (!e.ctrlKey && !e.altKey && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
- PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
- PreviewCursor.Visible = false;
- } else if (e.ctrlKey) {
- if (e.key == "v") {
+ if (e.key.startsWith("F") && !e.key.endsWith("F")) {
+ } else if (e.key != "Escape" && e.key != "Alt" && e.key != "Shift" && e.key != "Meta" && e.key != "Control" && !e.defaultPrevented && !(e as any).DASHFormattedTextBoxHandled) {
+ if ((!e.ctrlKey && !e.metaKey) || e.key === "v") {
PreviewCursor.Visible && PreviewCursor._onKeyPress && PreviewCursor._onKeyPress(e);
+ PreviewCursor.Visible = false;
}
}
}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
new file mode 100644
index 000000000..8eb2fc6c6
--- /dev/null
+++ b/src/client/views/TemplateMenu.tsx
@@ -0,0 +1,75 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { observer } from "mobx-react";
+import './DocumentDecorations.scss';
+import { Template } from "./Templates";
+import { DocumentView } from "./nodes/DocumentView";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
+ render() {
+ if (this.props.template) {
+ return (
+ <li>
+ <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+ {this.props.template.Name}
+ </li>
+ );
+ } else {
+ return (null);
+ }
+ }
+}
+
+export interface TemplateMenuProps {
+ doc: DocumentView;
+ templates: Map<Template, boolean>;
+}
+
+@observer
+export class TemplateMenu extends React.Component<TemplateMenuProps> {
+
+ @observable private _hidden: boolean = true;
+ @observable private _templates: Map<Template, boolean> = this.props.templates;
+
+
+ @action
+ toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
+ if (event.target.checked) {
+ this.props.doc.addTemplate(template);
+ this._templates.set(template, true);
+ } else {
+ this.props.doc.removeTemplate(template);
+ this._templates.set(template, false);
+ }
+ }
+
+ @action
+ componentWillReceiveProps(nextProps: TemplateMenuProps) {
+ this._templates = nextProps.templates;
+ }
+
+ @action
+ toggleTemplateActivity = (): void => {
+ this._hidden = !this._hidden;
+ }
+
+ render() {
+ let templateMenu: Array<JSX.Element> = [];
+ this._templates.forEach((checked, template) => {
+ templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />);
+ });
+
+ return (
+ <div className="templating-menu" >
+ <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
+ <ul id="template-list" style={{ display: this._hidden ? "none" : "block" }}>
+ {templateMenu}
+ </ul>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
new file mode 100644
index 000000000..fef69392d
--- /dev/null
+++ b/src/client/views/Templates.tsx
@@ -0,0 +1,66 @@
+import React = require("react");
+
+export enum TemplatePosition {
+ InnerTop,
+ InnerBottom,
+ InnerRight,
+ InnerLeft,
+ OutterTop,
+ OutterBottom,
+ OutterRight,
+ OutterLeft,
+}
+
+export class Template {
+ constructor(name: string, position: TemplatePosition, layout: string) {
+ this._name = name;
+ this._position = position;
+ this._layout = layout;
+ }
+
+ private _name: string;
+ private _position: TemplatePosition;
+ private _layout: string;
+
+ get Name(): string {
+ return this._name;
+ }
+
+ get Position(): TemplatePosition {
+ return this._position;
+ }
+
+ get Layout(): string {
+ return this._layout;
+ }
+}
+
+export namespace Templates {
+ // export const BasicLayout = new Template("Basic layout", "{layout}");
+
+ export const OuterCaption = new Template("Outer caption", TemplatePosition.OutterBottom,
+ `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey} /></div></div>`
+ );
+
+ export const InnerCaption = new Template("Inner caption", TemplatePosition.InnerBottom,
+ `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div></div>`
+ );
+
+ export const SideCaption = new Template("Side caption", TemplatePosition.OutterRight,
+ `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div> </div>`
+ );
+
+ export const Title = new Template("Title", TemplatePosition.InnerTop,
+ `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{Title}</div></div>`
+ );
+
+ export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
+
+ export function sortTemplates(a: Template, b: Template) {
+ if (a.Position < b.Position) { return -1; }
+ if (a.Position > b.Position) { return 1; }
+ return 0;
+ }
+
+}
+
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index ed761d3f3..76adfcdcd 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -8,6 +8,7 @@ import { Doc, FieldResult, Opt } from '../../../new_fields/Doc';
import { listSpec } from '../../../new_fields/Schema';
import { List } from '../../../new_fields/List';
import { Id } from '../../../new_fields/RefField';
+import { SelectionManager } from '../../util/SelectionManager';
export enum CollectionViewType {
Invalid,
@@ -104,8 +105,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
// set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) {
let zoom = NumCast(this.props.Document.scale, 1);
- let screen = this.props.ScreenToLocalTransform().inverse().Scale / (this.props as any).ContentScaling() * zoom;
- doc.zoomBasis = screen;
+ doc.zoomBasis = zoom;
}
}
return true;
@@ -145,6 +145,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
return true;
}
if (this.removeDocument(doc)) {
+ SelectionManager.DeselectAll();
return addDocument(doc);
}
return false;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1574562c6..958335425 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -241,6 +241,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this.props.Document.data = json;
if (this.undohack && !this.hack) {
this.undohack.end();
+ this.undohack = undefined;
}
this.hack = false;
}
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss
index 0eca3f1cd..f6fb79582 100644
--- a/src/client/views/collections/CollectionPDFView.scss
+++ b/src/client/views/collections/CollectionPDFView.scss
@@ -1,20 +1,39 @@
.collectionPdfView-buttonTray {
- top : 25px;
+ top : 15px;
left : 20px;
position: relative;
transform-origin: left top;
position: absolute;
}
+.collectionPdfView-thumb {
+ width:25px;
+ height:25px;
+ transform-origin: left top;
+ position: absolute;
+ background: darkgray;
+}
+.collectionPdfView-slider {
+ width:25px;
+ height:25px;
+ transform-origin: left top;
+ position: absolute;
+ background: lightgray;
+}
.collectionPdfView-cont{
width: 100%;
height: 100%;
position: absolute;
top: 0;
left:0;
-
+}
+.collectionPdfView-cont-dragging {
+ span {
+ user-select: none;
+ }
}
.collectionPdfView-backward {
color : white;
+ font-size: 24px;
top :0px;
left : 0px;
position: absolute;
@@ -22,8 +41,9 @@
}
.collectionPdfView-forward {
color : white;
+ font-size: 24px;
top :0px;
- left : 35px;
+ left : 45px;
position: absolute;
background-color: rgba(50, 50, 50, 0.2);
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx
index 5a1af354a..b3762206a 100644
--- a/src/client/views/collections/CollectionPDFView.tsx
+++ b/src/client/views/collections/CollectionPDFView.tsx
@@ -1,4 +1,4 @@
-import { action } from "mobx";
+import { action, observable } from "mobx";
import { observer } from "mobx-react";
import { ContextMenu } from "../ContextMenu";
import "./CollectionPDFView.scss";
@@ -17,18 +17,44 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
public static LayoutString(fieldKey: string = "data") {
return FieldView.LayoutString(CollectionPDFView, fieldKey);
}
+ @observable _inThumb = false;
+ private set curPage(value: number) { this.props.Document.curPage = value; }
private get curPage() { return NumCast(this.props.Document.curPage, -1); }
private get numPages() { return NumCast(this.props.Document.numPages); }
@action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1;
@action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1;
+ @action
+ onThumbDown = (e: React.PointerEvent) => {
+ document.addEventListener("pointermove", this.onThumbMove, false);
+ document.addEventListener("pointerup", this.onThumbUp, false);
+ e.stopPropagation();
+ this._inThumb = true;
+ }
+ @action
+ onThumbMove = (e: PointerEvent) => {
+ let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height;
+ this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1));
+ e.stopPropagation();
+ }
+ @action
+ onThumbUp = (e: PointerEvent) => {
+ this._inThumb = false;
+ document.removeEventListener("pointermove", this.onThumbMove);
+ document.removeEventListener("pointerup", this.onThumbUp);
+ }
+ nativeWidth = () => NumCast(this.props.Document.nativeWidth);
+ nativeHeight = () => NumCast(this.props.Document.nativeHeight);
private get uIButtons() {
- let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale);
+ let ratio = (this.curPage - 1) / this.numPages * 100;
return (
- <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}>
+ <div className="collectionPdfView-buttonTray" key="tray" style={{ height: "100%" }}>
<button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button>
<button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button>
+ <div className="collectionPdfView-slider" onPointerDown={this.onThumbDown} style={{ top: 60, left: -20, width: 50, height: `calc(100% - 80px)` }} >
+ <div className="collectionPdfView-thumb" onPointerDown={this.onThumbDown} style={{ top: `${ratio}%`, width: 50, height: 50 }} />
+ </div>
</div>
);
}
@@ -51,7 +77,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> {
render() {
return (
- <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}>
+ <CollectionBaseView {...this.props} className={`collectionPdfView-cont${this._inThumb ? "-dragging" : ""}`} onContextMenu={this.onContextMenu}>
{this.subView}
</CollectionBaseView>
);
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 58d20819b..6add6ada8 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked } from "mobx";
+import { action, computed, observable, untracked, runInAction } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
@@ -58,7 +58,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@observable _newKeyName: string = "";
@computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage); }
- @computed get columns() { return Cast(this.props.Document.columns, listSpec("string"), []); }
+ @computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); }
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
renderCell = (rowProps: CellInfo) => {
@@ -156,9 +156,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
@action
toggleKey = (key: string) => {
- let list = Cast(this.props.Document.columns, listSpec("string"));
+ let list = Cast(this.props.Document.schemaColumns, listSpec("string"));
if (list === undefined) {
- this.props.Document.columns = list = new List<string>([key]);
+ this.props.Document.schemaColumns = list = new List<string>([key]);
} else {
const index = list.indexOf(key);
if (index === -1) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
index 3b2f79be1..3e8a8a442 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss
@@ -3,4 +3,10 @@
stroke-width: 3;
transform: translate(10000px,10000px);
pointer-events: all;
+}
+.collectionfreeformlinkview-linkCircle {
+ stroke: black;
+ stroke-width: 3;
+ transform: translate(10000px,10000px);
+ pointer-events: all;
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index d4987fc18..3b700b053 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -4,20 +4,37 @@ import "./CollectionFreeFormLinkView.scss";
import React = require("react");
import v5 = require("uuid/v5");
import { StrCast, NumCast, BoolCast } from "../../../../new_fields/Types";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc";
+import { InkingControl } from "../../InkingControl";
export interface CollectionFreeFormLinkViewProps {
A: Doc;
B: Doc;
LinkDocs: Doc[];
+ addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+ removeDocument: (document: Doc) => boolean;
}
@observer
export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
onPointerDown = (e: React.PointerEvent) => {
- this.props.LinkDocs.map(l =>
- console.log("Link:" + StrCast(l.title)));
+ if (e.button === 0 && !InkingControl.Instance.selectedTool) {
+ let a = this.props.A;
+ let b = this.props.B;
+ let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2);
+ let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2);
+ let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2);
+ let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2);
+ this.props.LinkDocs.map(l => {
+ let width = l[WidthSym]();
+ l.x = (x1 + x2) / 2 - width / 2;
+ l.y = (y1 + y2) / 2 + 10;
+ if (!this.props.removeDocument(l)) this.props.addDocument(l, false);
+ });
+ e.stopPropagation();
+ e.preventDefault();
+ }
}
render() {
let l = this.props.LinkDocs;
@@ -28,10 +45,14 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2);
let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2);
return (
- <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown}
- style={{ strokeWidth: `${l.length * 5}` }}
- x1={`${x1}`} y1={`${y1}`}
- x2={`${x2}`} y2={`${y2}`} />
+ <>
+ <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine"
+ style={{ strokeWidth: `${l.length * 5}` }}
+ x1={`${x1}`} y1={`${y1}`}
+ x2={`${x2}`} y2={`${y2}`} />
+ <circle key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine"
+ cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={10} onPointerDown={this.onPointerDown} />
+ </>
);
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index f693d55e8..e5dcf8c29 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -84,6 +84,14 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
if (containerDoc) {
equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!);
}
+ if (view.props.ContainingCollectionView) {
+ let collid = view.props.ContainingCollectionView.props.Document.Id;
+ Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).
+ filter(child =>
+ child.Id === collid).map(view =>
+ DocumentManager.Instance.getDocumentViews(view).map(view =>
+ equalViews.push(view)));
+ }
return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document);
}
@@ -107,7 +115,8 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP
);
return drawnPairs;
}, [] as { a: Doc, b: Doc, l: Doc[] }[]);
- return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />);
+ return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l}
+ removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2689f48cd..f99800e21 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -24,6 +24,7 @@ import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types";
import { pageSchema } from "../../nodes/ImageBox";
import { List } from "../../../../new_fields/List";
import { Id } from "../../../../new_fields/RefField";
+import { KeyStore } from "../../../../fields/KeyStore";
export const panZoomSchema = createSchema({
panX: "number",
@@ -295,7 +296,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
}
- private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.getDocumentViewProps(this.props.Document)} />];
+ private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />];
render() {
trace();
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
@@ -308,7 +309,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
{this.props.Document.Title}
</text>
</svg> */}
- <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
+ <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected}
addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox}
getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}>
<CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY}
@@ -320,7 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
</CollectionFreeFormLinksView>
{/* <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" /> */}
</CollectionFreeFormViewPannableContents>
- <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} />
+ <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} />
</MarqueeView>
</div>
);
@@ -332,7 +333,7 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
@computed get overlayView() {
let overlayLayout = Cast(this.props.Document.overlayLayout, "string", "");
return !overlayLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"}
+ (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout}
isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
}
render() {
@@ -341,12 +342,12 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> {
}
@observer
-class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> {
+class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> {
@computed get backgroundView() {
let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", "");
return !backgroundLayout ? (null) :
- (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"}
- isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />);
+ (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout}
+ isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />);
}
render() {
return this.backgroundView;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index df1e7a90e..1d3850a72 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -23,6 +23,7 @@ interface MarqueeViewProps {
selectDocuments: (docs: Doc[]) => void;
removeDocument: (doc: Doc) => boolean;
addLiveTextDocument: (doc: Doc) => void;
+ isSelected: () => boolean;
}
@observer
@@ -33,12 +34,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@observable _downX: number = 0;
@observable _downY: number = 0;
@observable _visible: boolean = false;
+ _commandExecuted = false;
@action
cleanupInteractions = (all: boolean = false) => {
if (all) {
- document.removeEventListener("pointermove", this.onPointerMove, true);
document.removeEventListener("pointerup", this.onPointerUp, true);
+ document.removeEventListener("pointermove", this.onPointerMove, true);
}
document.removeEventListener("keydown", this.marqueeCommand, true);
this._visible = false;
@@ -56,6 +58,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
+ this._commandExecuted = false;
PreviewCursor.Visible = false;
if ((CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey && this.props.container.props.active()) ||
(!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && this.props.container.props.active())) {
@@ -75,7 +78,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
if (!e.cancelBubble) {
if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD ||
Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
- this._visible = true;
+ if (!this._commandExecuted) {
+ this._visible = true;
+ }
e.stopPropagation();
e.preventDefault();
}
@@ -104,7 +109,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
onClick = (e: React.MouseEvent): void => {
if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
+ if (this.props.isSelected()) {
+ PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress);
+ }
// let the DocumentView stopPropagation of this event when it selects this document
} else { // why do we get a click event when the cursor have moved a big distance?
// let's cut it off here so no one else has to deal with it.
@@ -129,16 +136,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@undoBatch
@action
marqueeCommand = (e: KeyboardEvent) => {
- if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
+ if (this._commandExecuted) {
+ return;
+ }
+ if (e.key === "Backspace" || e.key === "Delete" || e.key == "d") {
+ this._commandExecuted = true;
this.marqueeSelect().map(d => this.props.removeDocument(d));
let ink = Cast(this.props.container.props.Document.ink, InkField);
if (ink) {
this.marqueeInkDelete(ink.inkData);
}
- this.cleanupInteractions(true);
+ this.cleanupInteractions(false);
e.stopPropagation();
}
if (e.key === "c" || e.key === "r" || e.key === "e") {
+ this._commandExecuted = true;
e.stopPropagation();
let bounds = this.Bounds;
let selected = this.marqueeSelect().map(d => {
@@ -157,7 +169,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
panX: 0,
panY: 0,
borderRounding: e.key === "e" ? -1 : undefined,
- backgroundColor: selected.length ? "white" : "",
scale: zoomBasis,
width: bounds.width * zoomBasis,
height: bounds.height * zoomBasis,
@@ -166,7 +177,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
});
this.marqueeInkDelete(inkData);
- SelectionManager.DeselectAll();
+ // SelectionManager.DeselectAll();
if (e.key === "r") {
let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
Doc.MakeLink(summary.proto!, newCollection.proto!);
@@ -176,9 +187,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
else {
this.props.addDocument(newCollection, false);
}
- this.cleanupInteractions(true);
+ this.cleanupInteractions(false);
}
if (e.key === "s") {
+ this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
let bounds = this.Bounds;
@@ -188,7 +200,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this.props.addLiveTextDocument(summary);
selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!));
- this.cleanupInteractions(true);
+ this.cleanupInteractions(false);
}
}
@action
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 8766eb7ea..bd084bfe0 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -142,6 +142,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
onPointerDown = (e: React.PointerEvent): void => {
this._downX = e.clientX;
this._downY = e.clientY;
+ e.stopPropagation();
}
onClick = async (e: React.MouseEvent) => {
e.stopPropagation();
@@ -175,7 +176,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
const screenWidth = 1800;
let fadeUp = .75 * screenWidth;
let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth;
- zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
+ zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? fadeDown / w : w / fadeUp))) : 1;
return (
<div className="collectionFreeFormDocumentView-container" ref={this._mainCont}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index c90ca6d17..794442469 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -37,8 +37,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
@computed get layout(): string { return Cast(this.props.Document[this.props.layoutKey], "string", "<p>Error loading layout data</p>"); }
CreateBindings(): JsxBindings {
- let bindings: JsxBindings = { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
- return bindings;
+ return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
}
render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3814eeb9c..ac3dcde42 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,13 +12,14 @@ import { CollectionPDFView } from "../collections/CollectionPDFView";
import { CollectionVideoView } from "../collections/CollectionVideoView";
import { CollectionView } from "../collections/CollectionView";
import { ContextMenu } from "../ContextMenu";
+import { Template, Templates } from "./../Templates";
import { DocumentContentsView } from "./DocumentContentsView";
import "./DocumentView.scss";
import React = require("react");
import { Opt, Doc } from "../../../new_fields/Doc";
import { DocComponent } from "../DocComponent";
import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
-import { FieldValue, Cast, PromiseValue } from "../../../new_fields/Types";
+import { FieldValue, Cast, PromiseValue, StrCast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
@@ -87,6 +88,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
public get ContentDiv() { return this._mainCont.current; }
@computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
@computed get topMost(): boolean { return this.props.isTopMost; }
+ @computed get templates(): Array<Template> {
+ return new Array<Template>();
+ // let field = this.props.Document[KeyStore.Templates];
+ // return !field || field === FieldWaiting ? [] : field.Data;
+ }
+ set templates(templates: Array<Template>) { /* this.props.Document.templates = templates;*/ }
+ screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
@action
componentDidMount() {
@@ -151,7 +159,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && !this.isSelected()) {
return;
}
- if (e.shiftKey && e.buttons === 2) {
+ if (e.shiftKey && e.buttons === 1) {
if (this.props.isTopMost) {
this.startDragging(e.pageX, e.pageY, e.altKey || e.ctrlKey);
} else {
@@ -233,6 +241,46 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
}
+ updateLayout = async () => {
+ const baseLayout = await StrCast(this.props.Document.baseLayout);
+ if (baseLayout) {
+ let base = baseLayout;
+ let layout = baseLayout;
+
+ this.templates.forEach(template => {
+ let temp = template.Layout;
+ layout = temp.replace("{layout}", base);
+ base = layout;
+ });
+
+ this.props.Document.layout = layout;
+ }
+ }
+
+ @action
+ addTemplate = (template: Template) => {
+ let templates = this.templates;
+ templates.push(template);
+ templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
+ this.templates = templates;
+ this.updateLayout();
+ }
+
+ @action
+ removeTemplate = (template: Template) => {
+ let templates = this.templates;
+ for (let i = 0; i < templates.length; i++) {
+ let temp = templates[i];
+ if (temp.Name === template.Name) {
+ templates.splice(i, 1);
+ break;
+ }
+ }
+ templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
+ this.templates = templates;
+ this.updateLayout();
+ }
+
@action
onContextMenu = (e: React.MouseEvent): void => {
e.stopPropagation();
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index df76f7cea..c04b91c21 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -89,7 +89,7 @@ export class FieldView extends React.Component<FieldViewProps> {
isTopMost={true} //TODO Why is this top most?
selectOnLoad={false}
focus={emptyFunction}
- isSelected={returnFalse}
+ isSelected={this.props.isSelected}
select={returnFalse}
layoutKey={"layout"}
ContainingCollectionView={this.props.ContainingCollectionView}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index f4f37250f..727d3c0b2 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -10,7 +10,7 @@
outline: none !important;
}
-.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
+.formattedTextBox-cont-scroll, .formattedTextBox-cont-hidden {
background: $light-color-secondary;
padding: 0;
border-width: 0px;
@@ -24,10 +24,19 @@
height: 100%;
pointer-events: all;
}
+
.formattedTextBox-cont-hidden {
overflow: hidden;
pointer-events: none;
}
+.formattedTextBox-inner-rounded {
+ height: calc(100% - 40px);
+ width: calc(100% - 40px);
+ position: absolute;
+ overflow: scroll;
+ top: 20;
+ left: 20;
+}
.menuicon {
display: inline-block;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index c4c720ca9..455672472 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,4 +1,4 @@
-import { action, IReactionDisposer, reaction, trace, computed } from "mobx";
+import { action, IReactionDisposer, reaction, trace, computed, _allowStateChangesInsideComputed } from "mobx";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
@@ -20,7 +20,7 @@ import { createSchema, makeInterface } from "../../../new_fields/Schema";
import { Opt, Doc } from "../../../new_fields/Doc";
import { observer } from "mobx-react";
import { InkingControl } from "../InkingControl";
-import { StrCast, Cast } from "../../../new_fields/Types";
+import { StrCast, Cast, NumCast } from "../../../new_fields/Types";
import { RichTextField } from "../../../new_fields/RichTextField";
import { Id } from "../../../new_fields/RefField";
const { buildMenuItems } = require("prosemirror-example-setup");
@@ -60,6 +60,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
private _ref: React.RefObject<HTMLDivElement>;
+ private _proseRef: React.RefObject<HTMLDivElement>;
private _editorView: Opt<EditorView>;
private _gotDown: boolean = false;
private _reactionDisposer: Opt<IReactionDisposer>;
@@ -70,19 +71,26 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
super(props);
this._ref = React.createRef();
+ this._proseRef = React.createRef();
}
_applyingChange: boolean = false;
+ _lastState: any = undefined;
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
- const state = this._editorView.state.apply(tx);
+ const state = this._lastState = this._editorView.state.apply(tx);
this._editorView.updateState(state);
this._applyingChange = true;
Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new RichTextField(JSON.stringify(state.toJSON())));
Doc.SetOnPrototype(this.props.Document, "documentText", state.doc.textBetween(0, state.doc.content.size, "\n\n"));
this._applyingChange = false;
- // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+ let title = StrCast(this.props.Document.title);
+ if (title && title.startsWith("-") && this._editorView) {
+ let str = this._editorView.state.doc.textContent;
+ let titlestr = str.substr(0, Math.min(40, str.length));
+ this.props.Document.title = "-" + titlestr + (str.length > 40 ? "..." : "");
+ };
}
}
@@ -91,10 +99,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
schema,
inpRules, //these currently don't do anything, but could eventually be helpful
plugins: this.props.isOverlay ? [
+ this.tooltipTextMenuPlugin(),
history(),
keymap(buildKeymap(schema)),
keymap(baseKeymap),
- this.tooltipTextMenuPlugin(),
// this.tooltipLinkingMenuPlugin(),
new Plugin({
props: {
@@ -165,18 +173,19 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onPointerDown = (e: React.PointerEvent): void => {
- if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
- console.log("first");
+ if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
+ if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip)
+ this._toolTipTextMenu.tooltip.style.opacity = "0";
}
- if (e.button === 2) {
+ if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
this._gotDown = true;
- console.log("second");
e.preventDefault();
}
}
onPointerUp = (e: React.PointerEvent): void => {
- console.log("pointer up");
+ if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip)
+ this._toolTipTextMenu.tooltip.style.opacity = "1";
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
@@ -184,9 +193,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onFocused = (e: React.FocusEvent): void => {
if (!this.props.isOverlay) {
- if (MainOverlayTextBox.Instance.TextDoc !== this.props.Document) {
- MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
- }
+ MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
} else {
if (this._ref.current) {
this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
@@ -228,18 +235,20 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onClick = (e: React.MouseEvent): void => {
- this._ref.current!.focus();
+ this._proseRef.current!.focus();
}
tooltipTextMenuPlugin() {
let myprops = this.props;
+ let self = this;
return new Plugin({
view(_editorView) {
- return new TooltipTextMenu(_editorView, myprops);
+ return self._toolTipTextMenu = new TooltipTextMenu(_editorView, myprops);
}
});
}
+ _toolTipTextMenu: TooltipTextMenu | undefined = undefined;
tooltipLinkingMenuPlugin() {
let myprops = this.props;
return new Plugin({
@@ -254,7 +263,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
SelectionManager.DeselectAll();
}
e.stopPropagation();
- if (e.keyCode === 9) e.preventDefault();
+ if (e.key === "Tab") e.preventDefault();
// stop propagation doesn't seem to stop propagation of native keyboard events.
// so we set a flag on the native event that marks that the event's been handled.
(e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
@@ -266,10 +275,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
render() {
let style = this.props.isOverlay ? "scroll" : "hidden";
+ let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : "";
let color = StrCast(this.props.Document.backgroundColor);
let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
return (
- <div className={`formattedTextBox-cont-${style}`}
+ <div className={`formattedTextBox-cont${style}`} ref={this._ref}
style={{
pointerEvents: interactive ? "all" : "none",
background: color,
@@ -283,7 +293,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onContextMenu={this.specificContextMenu}
// tfs: do we need this event handler
onWheel={this.onPointerWheel}
- ref={this._ref} />
+ >
+ <div className={`formattedTextBox-inner${rounded}`} ref={this._proseRef} />
+ </div>
);
}
}
diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index 6ebd73f2c..20cae03d4 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -8,6 +8,7 @@
border-radius: $border-radius;
box-sizing: border-box;
display: inline-block;
+ pointer-events: all;
.imageBox-cont img {
width: auto;
}
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index 01701e02c..ff6885965 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -25,4 +25,5 @@
}
.keyValuePair-td-value {
display:inline-block;
+ overflow: scroll;
} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index 830dfe6c6..3760e378a 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -4,6 +4,9 @@
top: 0;
left:0;
}
+.react-pdf__Page__textContent span {
+ user-select: text;
+}
.react-pdf__Document {
position: absolute;
}
@@ -12,6 +15,21 @@
top: 0;
left:0;
z-index: 25;
+ pointer-events: all;
+}
+.pdfButton {
+ pointer-events: all;
+ width: 100px;
+ height:100px;
+}
+.pdfBox-cont {
+ pointer-events: none ;
+ span {
+ pointer-events: none !important;
+ }
+}
+.pdfBox-cont-interactive {
+ pointer-events: all;
}
.pdfBox-contentContainer {
position: absolute;
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 9f0849492..eb45ea273 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,5 +1,5 @@
import * as htmlToImage from "html-to-image";
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, Reaction, trace } from 'mobx';
import { observer } from "mobx-react";
import 'react-image-lightbox/style.css';
import Measure from "react-measure";
@@ -10,18 +10,17 @@ import { RouteStore } from "../../../server/RouteStore";
import { Utils } from '../../../Utils';
import { Annotation } from './Annotation';
import { FieldView, FieldViewProps } from './FieldView';
-import "./ImageBox.scss";
import "./PDFBox.scss";
-import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here
import React = require("react");
import { SelectionManager } from "../../util/SelectionManager";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
import { Opt } from "../../../new_fields/Doc";
import { DocComponent } from "../DocComponent";
import { makeInterface } from "../../../new_fields/Schema";
import { positionSchema } from "./DocumentView";
import { pageSchema } from "./ImageBox";
import { ImageField, PdfField } from "../../../new_fields/URLField";
+import { InkingControl } from "../InkingControl";
/** ALSO LOOK AT: Annotation.tsx, Sticky.tsx
* This method renders PDF and puts all kinds of functionalities such as annotation, highlighting,
@@ -43,15 +42,6 @@ import { ImageField, PdfField } from "../../../new_fields/URLField";
* 4) another method: click on highlight first and then drag on your desired text
* 5) To make another highlight, you need to reclick on the button
*
- * Draw:
- * 1) click draw and select color. then just draw like there's no tomorrow.
- * 2) once you finish drawing your masterpiece, just reclick on the draw button to end your drawing session.
- *
- * Pagination:
- * 1) click on arrows. You'll notice that stickies will stay in those page. But... highlights won't.
- * 2) to test this out, make few area/stickies and then click on next page then come back. You'll see that they are all saved.
- *
- *
* written by: Andrew Kim
*/
@@ -63,30 +53,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
public static LayoutString() { return FieldView.LayoutString(PDFBox); }
private _mainDiv = React.createRef<HTMLDivElement>();
- private _pdf = React.createRef<HTMLCanvasElement>();
@observable private _renderAsSvg = true;
- //very useful for keeping track of X and y position throughout the PDF Canvas
- private initX: number = 0;
- private initY: number = 0;
-
- //checks if tool is on
- private _toolOn: boolean = false; //checks if tool is on
- private _pdfContext: any = null; //gets pdf context
- private bool: Boolean = false; //general boolean debounce
- private currSpan: any;//keeps track of current span (for highlighting)
-
- private _currTool: any; //keeps track of current tool button reference
- private _drawToolOn: boolean = false; //boolean that keeps track of the drawing tool
- private _drawTool = React.createRef<HTMLButtonElement>();//drawing tool button reference
-
- private _colorTool = React.createRef<HTMLButtonElement>(); //color button reference
- private _currColor: string = "black"; //current color that user selected (for ink/pen)
-
- private _highlightTool = React.createRef<HTMLButtonElement>(); //highlighter button reference
- private _highlightToolOn: boolean = false;
- private _pdfCanvas: any;
private _reactionDisposer: Opt<IReactionDisposer>;
@observable private _perPageInfo: Object[] = []; //stores pageInfo
@@ -119,43 +88,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
/**
- * selection tool used for area highlighting (stickies). Kinda temporary
- */
- selectionTool = () => {
- this._toolOn = true;
- }
- /**
- * when user draws on the canvas. When mouse pointer is down
- */
- drawDown = (e: PointerEvent) => {
- this.initX = e.offsetX;
- this.initY = e.offsetY;
- this._pdfContext.beginPath();
- this._pdfContext.lineTo(this.initX, this.initY);
- this._pdfContext.strokeStyle = this._currColor;
- this._pdfCanvas.addEventListener("pointermove", this.drawMove);
- this._pdfCanvas.addEventListener("pointerup", this.drawUp);
-
- }
- //when user drags
- drawMove = (e: PointerEvent): void => {
- //x and y mouse movement
- let x = this.initX += e.movementX,
- y = this.initY += e.movementY;
- //connects the point
- this._pdfContext.lineTo(x, y);
- this._pdfContext.stroke();
- }
-
- drawUp = (e: PointerEvent) => {
- this._pdfContext.closePath();
- this._pdfCanvas.removeEventListener("pointermove", this.drawMove);
- this._pdfCanvas.removeEventListener("pointerdown", this.drawDown);
- this._pdfCanvas.addEventListener("pointerdown", this.drawDown);
- }
-
-
- /**
* highlighting helper function
*/
makeEditableAndHighlight = (colour: string) => {
@@ -190,7 +122,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
child.id = "highlighted";
//@ts-ignore
obj.spans.push(child);
- child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
+ // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
}
});
}
@@ -213,7 +145,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
child.id = "highlighted";
//@ts-ignore
temp.spans.push(child);
- child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
+ // child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler
}
});
@@ -279,11 +211,20 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
* controls the area highlighting (stickies) Kinda temporary
*/
onPointerDown = (e: React.PointerEvent) => {
- if (this._toolOn) {
- let mouse = e.nativeEvent;
- this.initX = mouse.offsetX;
- this.initY = mouse.offsetY;
-
+ if (this.props.isSelected() && !InkingControl.Instance.selectedTool && e.buttons === 1) {
+ if (e.altKey) {
+ this._alt = true;
+ } else {
+ if (e.metaKey)
+ e.stopPropagation();
+ }
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
+ }
+ if (this.props.isSelected() && e.buttons === 2) {
+ this._alt = true;
+ document.removeEventListener("pointerup", this.onPointerUp);
+ document.addEventListener("pointerup", this.onPointerUp);
}
}
@@ -291,96 +232,15 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
* controls area highlighting and partially highlighting. Kinda temporary
*/
@action
- onPointerUp = (e: React.PointerEvent) => {
- if (this._highlightToolOn) {
+ onPointerUp = (e: PointerEvent) => {
+ this._alt = false;
+ document.removeEventListener("pointerup", this.onPointerUp);
+ if (this.props.isSelected()) {
this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color.
- this._highlightToolOn = false;
- }
- if (this._toolOn) {
- let mouse = e.nativeEvent;
- let finalX = mouse.offsetX;
- let finalY = mouse.offsetY;
- let width = Math.abs(finalX - this.initX); //width
- let height = Math.abs(finalY - this.initY); //height
-
- //these two if statements are bidirectional dragging. You can drag from any point to another point and generate sticky
- if (finalX < this.initX) {
- this.initX = finalX;
- }
- if (finalY < this.initY) {
- this.initY = finalY;
- }
-
- if (this._mainDiv.current) {
- let sticky = <Sticky key={Utils.GenerateGuid()} Height={height} Width={width} X={this.initX} Y={this.initY} />;
- this._pageInfo.area.push(sticky);
- }
- this._toolOn = false;
}
this._interactive = true;
}
- /**
- * starts drawing the line when user presses down.
- */
- onDraw = () => {
- if (this._currTool !== null) {
- this._currTool.style.backgroundColor = "grey";
- }
-
- if (this._drawTool.current) {
- this._currTool = this._drawTool.current;
- if (this._drawToolOn) {
- this._drawToolOn = false;
- this._pdfCanvas.removeEventListener("pointerdown", this.drawDown);
- this._pdfCanvas.removeEventListener("pointerup", this.drawUp);
- this._pdfCanvas.removeEventListener("pointermove", this.drawMove);
- this._drawTool.current.style.backgroundColor = "grey";
- } else {
- this._drawToolOn = true;
- this._pdfCanvas.addEventListener("pointerdown", this.drawDown);
- this._drawTool.current.style.backgroundColor = "cyan";
- }
- }
- }
-
-
- /**
- * for changing color (for ink/pen)
- */
- onColorChange = (e: React.PointerEvent) => {
- if (e.currentTarget.innerHTML === "Red") {
- this._currColor = "red";
- } else if (e.currentTarget.innerHTML === "Blue") {
- this._currColor = "blue";
- } else if (e.currentTarget.innerHTML === "Green") {
- this._currColor = "green";
- } else if (e.currentTarget.innerHTML === "Black") {
- this._currColor = "black";
- }
-
- }
-
-
- /**
- * For highlighting (text drag highlighting)
- */
- onHighlight = () => {
- this._drawToolOn = false;
- if (this._currTool !== null) {
- this._currTool.style.backgroundColor = "grey";
- }
- if (this._highlightTool.current) {
- this._currTool = this._drawTool.current;
- if (this._highlightToolOn) {
- this._highlightToolOn = false;
- this._highlightTool.current.style.backgroundColor = "grey";
- } else {
- this._highlightToolOn = true;
- this._highlightTool.current.style.backgroundColor = "orange";
- }
- }
- }
@action
@@ -403,22 +263,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@action
onLoaded = (page: any) => {
- if (this._mainDiv.current) {
- this._mainDiv.current.childNodes.forEach((element) => {
- if (element.nodeName === "DIV") {
- element.childNodes[0].childNodes.forEach((e) => {
-
- if (e instanceof HTMLCanvasElement) {
- this._pdfCanvas = e;
- this._pdfContext = e.getContext("2d");
-
- }
-
- });
- }
- });
- }
-
// bcz: the number of pages should really be set when the document is imported.
this.props.Document.numPages = page._transport.numPages;
if (this._perPageInfo.length === 0) { //Makes sure it only runs once
@@ -430,7 +274,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@action
setScaling = (r: any) => {
// bcz: the nativeHeight should really be set when the document is imported.
- // also, the native dimensions could be different for different pages of the PDF
+ // also, the native dimensions could be different for different pages of the canvas
// so this design is flawed.
var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
if (!FieldValue(this.Document.nativeHeight, 0)) {
@@ -439,22 +283,29 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
this.props.Document.nativeHeight = nativeHeight;
}
}
-
+ renderHeight = 2400;
+ @computed
+ get pdfPage() {
+ return <Page height={this.renderHeight} pageNumber={this.curPage} onLoadSuccess={this.onLoaded} />
+ }
@computed
get pdfContent() {
let page = this.curPage;
const renderHeight = 2400;
let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField);
let xf = FieldValue(this.Document.nativeHeight, 0) / renderHeight;
+ let body = NumCast(this.props.Document.nativeHeight) ?
+ this.pdfPage :
+ <Measure onResize={this.setScaling}>
+ {({ measureRef }) =>
+ <div className="pdfBox-page" ref={measureRef}>
+ {this.pdfPage}
+ </div>
+ }
+ </Measure>;
return <div className="pdfBox-contentContainer" key="container" style={{ transform: `scale(${xf}, ${xf})` }}>
- <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl!.url.href}`} renderMode={this._renderAsSvg ? "svg" : ""}>
- <Measure onResize={this.setScaling}>
- {({ measureRef }) =>
- <div className="pdfBox-page" ref={measureRef}>
- <Page height={renderHeight} pageNumber={page} onLoadSuccess={this.onLoaded} />
- </div>
- }
- </Measure>
+ <Document file={window.origin + RouteStore.corsProxy + `/${pdfUrl}`} renderMode={this._renderAsSvg ? "svg" : "canvas"}>
+ {body}
</Document>
</div >;
}
@@ -484,10 +335,24 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
return (null);
}
-
+ @observable _alt = false;
+ @action
+ onKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Alt") {
+ this._alt = true;
+ }
+ }
+ @action
+ onKeyUp = (e: React.KeyboardEvent) => {
+ if (e.key === "Alt") {
+ this._alt = false;
+ }
+ }
render() {
+ trace();
+ let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
return (
- <div className="pdfBox-cont" ref={this._mainDiv} onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} >
+ <div className={classname} tabIndex={0} ref={this._mainDiv} onPointerDown={this.onPointerDown} onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} >
{this.pdfRenderer}
</div >
);
diff --git a/src/client/views/nodes/Sticky.tsx b/src/client/views/nodes/Sticky.tsx
deleted file mode 100644
index 11719831b..000000000
--- a/src/client/views/nodes/Sticky.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import "react-image-lightbox/style.css"; // This only needs to be imported once in your app
-import React = require("react");
-import { observer } from "mobx-react";
-import "react-pdf/dist/Page/AnnotationLayer.css";
-
-interface IProps {
- Height: number;
- Width: number;
- X: number;
- Y: number;
-}
-
-/**
- * Sticky, also known as area highlighting, is used to highlight large selection of the PDF file.
- * Improvements that could be made: maybe store line array and store that somewhere for future rerendering.
- *
- * Written By: Andrew Kim
- */
-@observer
-export class Sticky extends React.Component<IProps> {
- private initX: number = 0;
- private initY: number = 0;
-
- private _ref = React.createRef<HTMLCanvasElement>();
- private ctx: any; //context that keeps track of sticky canvas
-
- /**
- * drawing. Registers the first point that user clicks when mouse button is pressed down on canvas
- */
- drawDown = (e: React.PointerEvent) => {
- if (this._ref.current) {
- this.ctx = this._ref.current.getContext("2d");
- let mouse = e.nativeEvent;
- this.initX = mouse.offsetX;
- this.initY = mouse.offsetY;
- this.ctx.beginPath();
- this.ctx.lineTo(this.initX, this.initY);
- this.ctx.strokeStyle = "black";
- document.addEventListener("pointermove", this.drawMove);
- document.addEventListener("pointerup", this.drawUp);
- }
- }
-
- //when user drags
- drawMove = (e: PointerEvent): void => {
- //x and y mouse movement
- let x = (this.initX += e.movementX),
- y = (this.initY += e.movementY);
- //connects the point
- this.ctx.lineTo(x, y);
- this.ctx.stroke();
- }
-
- /**
- * when user lifts the mouse, the drawing ends
- */
- drawUp = (e: PointerEvent) => {
- this.ctx.closePath();
- console.log(this.ctx);
- document.removeEventListener("pointermove", this.drawMove);
- }
-
- render() {
- return (
- <div onPointerDown={this.drawDown}>
- <canvas
- ref={this._ref}
- height={this.props.Height}
- width={this.props.Width}
- style={{
- position: "absolute",
- top: "20px",
- left: "0px",
- zIndex: 1,
- background: "yellow",
- transform: `translate(${this.props.X}px, ${this.props.Y}px)`,
- opacity: 0.4
- }}
- />
- </div>
- );
- }
-}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 86276660a..184a89dbc 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -7,7 +7,7 @@ import { DocComponent } from "../DocComponent";
import { positionSchema } from "./DocumentView";
import { makeInterface } from "../../../new_fields/Schema";
import { pageSchema } from "./ImageBox";
-import { Cast, FieldValue } from "../../../new_fields/Types";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
import { VideoField } from "../../../new_fields/URLField";
import Measure from "react-measure";
import "./VideoBox.scss";
@@ -59,24 +59,27 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
(vref as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage;
}
}
+ videoContent(path: string) {
+ return <video className="videobox-cont" ref={this.setVideoRef}>
+ <source src={path} type="video/mp4" />
+ Not supported.
+ </video>;
+ }
render() {
let field = FieldValue(Cast(this.Document[this.props.fieldKey], VideoField));
if (!field) {
return <div>Loading</div>;
}
- let path = field.url.href;
- return (
+ let content = this.videoContent(field.url.href);
+ return NumCast(this.props.Document.nativeHeight) ?
+ content :
<Measure onResize={this.setScaling}>
{({ measureRef }) =>
<div style={{ width: "100%", height: "auto" }} ref={measureRef}>
- <video className="videobox-cont" ref={this.setVideoRef}>
- <source src={path} type="video/mp4" />
- Not supported.
- </video>
+ {content}
</div>
}
- </Measure>
- );
+ </Measure>;
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 2ad1129a4..eb09b0693 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -1,12 +1,19 @@
-.webBox-cont {
+.webBox-cont, .webBox-cont-interactive{
padding: 0vw;
position: absolute;
top: 0;
left:0;
width: 100%;
height: 100%;
- overflow: scroll;
+ overflow: auto;
+ pointer-events: none ;
+}
+.webBox-cont-interactive {
+ pointer-events: all;
+ span {
+ user-select: text !important;
+ }
}
#webBox-htmlSpan {
@@ -15,6 +22,12 @@
left:0;
}
+.webBox-overlay {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+}
+
.webBox-button {
padding : 0vw;
border: none;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index c12fd5655..2239a8e38 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -6,6 +6,7 @@ import { WebField } from "../../../new_fields/URLField";
import { observer } from "mobx-react";
import { computed, reaction, IReactionDisposer } from 'mobx';
import { DocumentDecorations } from "../DocumentDecorations";
+import { InkingControl } from "../InkingControl";
@observer
export class WebBox extends React.Component<FieldViewProps> {
@@ -46,12 +47,13 @@ export class WebBox extends React.Component<FieldViewProps> {
let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+ let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
return (
<>
- <div className="webBox-cont" >
+ <div className={classname} >
{content}
</div>
- {!frozen ? (null) : <div onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} style={{ width: "100%", height: "100%", position: "absolute" }} />}
+ {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
</>);
}
} \ No newline at end of file
diff --git a/src/fields/TemplateField.ts b/src/fields/TemplateField.ts
new file mode 100644
index 000000000..72ae13c2e
--- /dev/null
+++ b/src/fields/TemplateField.ts
@@ -0,0 +1,43 @@
+import { BasicField } from "./BasicField";
+import { Types } from "../server/Message";
+import { FieldId } from "./Field";
+import { Template, TemplatePosition } from "../client/views/Templates";
+
+
+export class TemplateField extends BasicField<Array<Template>> {
+ constructor(data: Array<Template> = [], id?: FieldId, save: boolean = true) {
+ super(data, save, id);
+ }
+
+ ToScriptString(): string {
+ return `new TemplateField("${this.Data}")`;
+ }
+
+ Copy() {
+ return new TemplateField(this.Data);
+ }
+
+ ToJson() {
+ let templates: Array<{ name: string, position: TemplatePosition, layout: string }> = [];
+ this.Data.forEach(template => {
+ templates.push({ name: template.Name, layout: template.Layout, position: template.Position });
+ });
+ return {
+ type: Types.Templates,
+ data: templates,
+ id: this.Id,
+ };
+ }
+
+ UpdateFromServer(data: any) {
+ this.data = new Array(data);
+ }
+
+ static FromJson(id: string, data: any): TemplateField {
+ let templates: Array<Template> = [];
+ data.forEach((template: { name: string, position: number, layout: string }) => {
+ templates.push(new Template(template.name, template.position, template.layout));
+ });
+ return new TemplateField(templates, id, false);
+ }
+} \ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 81da44f72..e9a8b0f0c 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -15,7 +15,7 @@ export class Message<T> {
export enum Types {
Number, List, Key, Image, Web, Document, Text, Icon, RichText, DocumentReference,
- Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script,
+ Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script, Templates
}
export interface Transferable {