aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2020-03-03 14:02:17 -0500
committerbob <bcz@cs.brown.edu>2020-03-03 14:02:17 -0500
commit8b71007c98f3c5f5092d15c6ce91142729bcec22 (patch)
treebd1a77582525bf99e2a19a2cbc46439e53ec5f0a /src
parent1e2f97d1dcd3abb3be701462a43d166e9826edc3 (diff)
fixed dragging from linear view because of screentransform. added [[key=value]] syntax. added button for seeing enumerated values. added step up button in DocDec for selecting parent collection
Diffstat (limited to 'src')
-rw-r--r--src/client/util/RichTextRules.ts8
-rw-r--r--src/client/util/RichTextSchema.tsx57
-rw-r--r--src/client/views/DocumentDecorations.scss11
-rw-r--r--src/client/views/DocumentDecorations.tsx16
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx2
-rw-r--r--src/new_fields/Doc.ts34
6 files changed, 89 insertions, 39 deletions
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 70a1a5154..7ffc2dd9c 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -81,10 +81,11 @@ export class RichTextRules {
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document [[ <fieldKey> : <Doc>]] // [[:Doc]] => hyperlink [[fieldKey]] => show field [[fieldKey:Doc]] => show field of doc
new InputRule(
- new RegExp(/\[\[([a-zA-Z_#@\? \-0-9]*)(:[a-zA-Z_#@\? \-0-9]+)?\]\]$/),
+ new RegExp(/\[\[([a-zA-Z_#@\? \-0-9]*)(=[a-zA-Z_#@\? \-0-9]*)?(:[a-zA-Z_#@\? \-0-9]+)?\]\]$/),
(state, match, start, end) => {
const fieldKey = match[1];
- const docid = match[2]?.substring(1);
+ const docid = match[3]?.substring(1);
+ const value = match[2]?.substring(1);
if (!fieldKey) {
if (docid) {
DocServer.GetRefField(docid).then(docx => {
@@ -97,6 +98,9 @@ export class RichTextRules {
}
return state.tr;
}
+ if (value !== "") {
+ this.Document[DataSym][fieldKey] = value;
+ }
const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
return state.tr.deleteRange(start, end).insert(start, fieldView);
}),
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2ef22b6a0..034f5d55a 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -8,28 +8,25 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
import { StepMap } from "prosemirror-transform";
import { EditorView } from "prosemirror-view";
import * as ReactDOM from 'react-dom';
-import { Doc, Field, HeightSym, WidthSym, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../new_fields/Doc";
import { Id } from "../../new_fields/FieldSymbols";
+import { List } from "../../new_fields/List";
import { ObjectField } from "../../new_fields/ObjectField";
+import { listSpec } from "../../new_fields/Schema";
+import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
import { ComputedField } from "../../new_fields/ScriptField";
-import { BoolCast, NumCast, StrCast, Cast } from "../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils";
import { DocServer } from "../DocServer";
+import { Docs } from "../documents/Documents";
+import { CollectionViewType } from "../views/collections/CollectionView";
+import { ContextMenu } from "../views/ContextMenu";
import { DocumentView } from "../views/nodes/DocumentView";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { DocumentManager } from "./DocumentManager";
import ParagraphNodeSpec from "./ParagraphNodeSpec";
import { Transform } from "./Transform";
import React = require("react");
-import { CollectionSchemaBooleanCell } from "../views/collections/CollectionSchemaCells";
-import { ContextMenu } from "../views/ContextMenu";
-import { ContextMenuProps } from "../views/ContextMenuItem";
-import { Docs } from "../documents/Documents";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { toBlob } from "html-to-image";
-import { listSpec } from "../../new_fields/Schema";
-import { List } from "../../new_fields/List";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -839,8 +836,8 @@ export class DashDocView {
zoomToScale={emptyFunction}
getScale={returnOne}
dontRegisterView={false}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
+ ContainingCollectionView={this._textBox.props.ContainingCollectionView}
+ ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
ContentScaling={this.contentScaling}
/>, this._dashSpan);
if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -863,6 +860,7 @@ export class DashFieldView {
_fieldWrapper: HTMLDivElement; // container for label and value
_labelSpan: HTMLSpanElement; // field label
_fieldSpan: HTMLDivElement; // field value
+ _enumerables: HTMLDivElement; // field value
_reactionDisposer: IReactionDisposer | undefined;
_textBoxDoc: Doc;
@observable _dashDoc: Doc | undefined;
@@ -879,6 +877,19 @@ export class DashFieldView {
this._fieldWrapper.style.display = "inline-block";
const self = this;
+ this._enumerables = document.createElement("div");
+ this._enumerables.style.width = "10px";
+ this._enumerables.style.height = "10px";
+ this._enumerables.style.position = "relative";
+ this._enumerables.style.display = "none";
+ this._enumerables.style.background = "dimGray";
+
+ this._enumerables.onpointerdown = async (e) => {
+ e.stopPropagation();
+ const collview = await Doc.addEnumerationToTextField(self._textBoxDoc, node.attrs.fieldKey, [Docs.Create.TextDocument(self._fieldSpan.innerText, { title: self._fieldSpan.innerText })]);
+ collview instanceof Doc && tbox.props.addDocTab(collview, "onRight");
+ }
+
this._fieldSpan = document.createElement("div");
this._fieldSpan.id = Utils.GenerateGuid();
this._fieldSpan.contentEditable = "true";
@@ -888,7 +899,10 @@ export class DashFieldView {
this._fieldSpan.style.backgroundColor = "rgba(155, 155, 155, 0.24)";
this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); };
this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); };
- this._fieldSpan.onmousedown = function (e: any) { e.stopPropagation(); };
+ this._fieldSpan.onmousedown = function (e: any) {
+ e.stopPropagation();
+ self._enumerables.style.display = "inline-block";
+ };
this._fieldSpan.oncontextmenu = function (e: any) {
ContextMenu.Instance.addItem({
description: "Show Enumeration Templates", event: () => {
@@ -898,15 +912,17 @@ export class DashFieldView {
});
};
this._fieldSpan.onblur = function (e: any) {
+ self._enumerables.style.display = "none";
let newText = self._fieldSpan.innerText.startsWith(":=") ? ":=-computed-" : self._fieldSpan.innerText;
+
// look for a document whose id === the fieldKey being displayed. If there's a match, then that document
// holds the different enumerated values for the field in the titles of its collected documents.
// if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
-
- // alternatively, if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
DocServer.GetRefField(node.attrs.fieldKey).then(options => {
(options instanceof Doc) && DocListCast(options.data).forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title)));
self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = newText;
+
+ // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
if (newText.startsWith(":=") && self._dashDoc && e.data === null && !e.inputType.includes("delete")) {
Doc.Layout(tbox.props.Document)[self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(2));
}
@@ -947,8 +963,12 @@ export class DashFieldView {
this._labelSpan.title = "click to see related tags";
this._labelSpan.onpointerdown = function (e: any) {
e.stopPropagation();
- if (tbox.props.ContainingCollectionDoc) {
- const alias = Doc.MakeAlias(tbox.props.ContainingCollectionDoc);
+ let container = tbox.props.ContainingCollectionView;
+ while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
+ container = container.props.ContainingCollectionView;
+ }
+ if (container) {
+ const alias = Doc.MakeAlias(container.props.Document);
alias.viewType = CollectionViewType.Time;
let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField));
if (!list) {
@@ -974,6 +994,7 @@ export class DashFieldView {
this._fieldWrapper.appendChild(this._labelSpan);
this._fieldWrapper.appendChild(this._fieldSpan);
+ this._fieldWrapper.appendChild(this._enumerables);
(this as any).dom = this._fieldWrapper;
}
destroy() {
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 1992c5efa..fdf7c3f42 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -27,6 +27,17 @@ $linkGap : 3px;
opacity: 1;
}
+ .documentDecorations-selector {
+ pointer-events: auto;
+ height: 15px;
+ width: 15px;
+ left: -20px;
+ top: 20px;
+ display: inline-block;
+ position: absolute;
+ opacity: 0.5;
+ }
+
.documentDecorations-radius {
pointer-events: auto;
background: black;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4922411e8..c98be0d4a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { PositionDocument } from '../../new_fields/documentSchemas';
import { ScriptField } from '../../new_fields/ScriptField';
import { Cast, StrCast, NumCast } from "../../new_fields/Types";
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Utils, setupMoveUpEvents } from "../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
import { DocUtils } from "../documents/Documents";
import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from "../util/DragManager";
@@ -245,6 +245,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
@action
+ onSelectorUp = (e: React.PointerEvent): void => {
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e) => {
+ const selDoc = SelectionManager.SelectedDocuments()?.[0];
+ if (selDoc) {
+ selDoc.props.ContainingCollectionView?.props.select(false);
+ }
+ }));
+ }
+
+ @action
onRadiusDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, this.onRadiusMove, (e) => this._resizeUndo?.end(), (e) => { });
if (e.button === 0) {
@@ -496,6 +506,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
<div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : <div id="documentDecorations-levelSelector" className="documentDecorations-selector" title="tap to select containing document"
+ onPointerDown={this.onSelectorUp} onContextMenu={(e) => e.preventDefault()}>
+ <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+ </div>}
<div id="documentDecorations-borderRadius" className="documentDecorations-radius"
onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 9384eb381..79ec6d518 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -77,7 +77,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
if (!ele.current) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
- return new Transform(-translateX, -translateY, 1 / scale);
+ return new Transform(-translateX, -translateY, 1);
}
render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0f3896055..6d94f050c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -865,25 +865,25 @@ export namespace Doc {
return (await DocServer.GetRefField(enumeratedFieldKey)) as Doc;
}
- export function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
- DocServer.GetRefField(enumeratedFieldKey).then(optionsCollection => {
- if (!(optionsCollection instanceof Doc)) {
- optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
- Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+ export async function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
+ let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
+ if (!(optionsCollection instanceof Doc)) {
+ optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
+ Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+ }
+ const options = optionsCollection as Doc;
+ doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
+ doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
+ enumeratedDocs.map(enumeratedDoc => {
+ const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
+ if (found) {
+ found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
+ found._color = enumeratedDoc._color || found._color;
+ } else {
+ Doc.AddDocToList(options, "data", enumeratedDoc);
}
- const options = optionsCollection as Doc;
- doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
- doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
- enumeratedDocs.map(enumeratedDoc => {
- const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
- if (found) {
- found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
- found._color = enumeratedDoc._color || found._color;
- } else {
- Doc.AddDocToList(options, "data", enumeratedDoc);
- }
- });
});
+ return optionsCollection;
}
}