From aea4acf334aa62b2cbaa060a1dfbcbc1489d800a Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 20 Jan 2020 14:12:31 -0500 Subject: scraper support for multicolumn --- src/client/documents/Documents.ts | 14 +++++++------- src/client/views/collections/CollectionTreeView.tsx | 9 ++++----- src/scraping/buxton/scraper.py | 4 +++- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4fe13fa78..9f5c1dafe 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -666,28 +666,28 @@ export namespace Docs { const field = target[fieldKey]; const resolved = options || {}; if (field instanceof ImageField) { - created = Docs.Create.ImageDocument((field as ImageField).url.href, resolved); + created = Docs.Create.ImageDocument((field).url.href, resolved); layout = ImageBox.LayoutString; } else if (field instanceof Doc) { created = field; } else if (field instanceof VideoField) { - created = Docs.Create.VideoDocument((field as VideoField).url.href, resolved); + created = Docs.Create.VideoDocument((field).url.href, resolved); layout = VideoBox.LayoutString; } else if (field instanceof PdfField) { - created = Docs.Create.PdfDocument((field as PdfField).url.href, resolved); + created = Docs.Create.PdfDocument((field).url.href, resolved); layout = PDFBox.LayoutString; } else if (field instanceof IconField) { - created = Docs.Create.IconDocument((field as IconField).icon, resolved); + created = Docs.Create.IconDocument((field).icon, resolved); layout = IconBox.LayoutString; } else if (field instanceof AudioField) { - created = Docs.Create.AudioDocument((field as AudioField).url.href, resolved); + created = Docs.Create.AudioDocument((field).url.href, resolved); layout = AudioBox.LayoutString; } else if (field instanceof HistogramField) { - created = Docs.Create.HistogramDocument((field as HistogramField).HistoOp, resolved); + created = Docs.Create.HistogramDocument((field).HistoOp, resolved); layout = HistogramBox.LayoutString; } else if (field instanceof InkField) { const { selectedColor, selectedWidth, selectedTool } = InkingControl.Instance; - created = Docs.Create.InkDocument(selectedColor, selectedTool, Number(selectedWidth), (field as InkField).inkData, resolved); + created = Docs.Create.InkDocument(selectedColor, selectedTool, Number(selectedWidth), (field).inkData, resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index a48208bd9..a458aa0af 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,6 +32,7 @@ import { ScriptBox } from '../ScriptBox'; import { ImageBox } from '../nodes/ImageBox'; import { makeTemplate } from '../../util/DropConverter'; import { CollectionDockingView } from './CollectionDockingView'; +import { CollectionViewType } from './CollectionView'; export interface TreeViewProps { @@ -633,13 +634,11 @@ export class CollectionTreeView extends CollectionSubView(Document) { } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { - const { TextDocument, ImageDocument } = Docs.Create; + const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; const wrapper = Docs.Create.StackingDocument([ ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "hero" }), - TextDocument({ title: "year" }), - TextDocument({ title: "degrees_of_freedom" }), - TextDocument({ title: "company" }), - TextDocument({ title: "short_description" }), + // MulticolumnDocument([], { title: "data", height: 100 }), + ...["year", "company", "degrees_of_freedom", "short_description"].map(key => TextDocument({ title: key })) ], { autoHeight: true, chromeStatus: "disabled" }); wrapper.disableLOD = true; makeTemplate(wrapper, true); diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 2d1a5ca32..e80979616 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -191,7 +191,9 @@ def write_image(folder, name): "x": 10, "y": 10, "width": min(800, native_width), - "zIndex": 2 + "zIndex": 2, + "widthUnit": "*", + "widthMagnitude": 1 }, "__type": "Doc" } -- cgit v1.2.3-70-g09d2 From cc2cbf44ba5c30a70bad2ffd7a57d2c6d17d0e4e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 20 Jan 2020 16:09:43 -0500 Subject: finalLayoutKey changes and sorted facet values --- src/client/views/collections/CollectionPivotView.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 5 ++--- src/client/views/nodes/DocumentView.tsx | 10 +++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 53ad433b3..ad2e05908 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -76,7 +76,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { facetValues.add(child[facet]?.toString() || "(null)"); }); - const newFacetVals = facetValues.toArray().map(val => Docs.Create.TextDocument({ title: val.toString() })); + const newFacetVals = facetValues.toArray().sort().map(val => Docs.Create.TextDocument({ title: val.toString() })); const newFacet = Docs.Create.FreeformDocument(newFacetVals, { title: facet, treeViewOpen: true, isFacetFilter: true }); Doc.AddDocToList(facetCollection, "data", newFacet); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index a458aa0af..3a208038c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -634,11 +634,10 @@ export class CollectionTreeView extends CollectionSubView(Document) { } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { - const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; + const { TextDocument, ImageDocument } = Docs.Create; const wrapper = Docs.Create.StackingDocument([ ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "hero" }), - // MulticolumnDocument([], { title: "data", height: 100 }), - ...["year", "company", "degrees_of_freedom", "short_description"].map(key => TextDocument({ title: key })) + ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) ], { autoHeight: true, chromeStatus: "disabled" }); wrapper.disableLOD = true; makeTemplate(wrapper, true); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 29f658eca..c58362f6c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -759,7 +759,15 @@ export class DocumentView extends DocComponent(Docu return (showTitle && !showTitleHover ? 0 : 0) + 1; } - @computed get finalLayoutKey() { return this.props.layoutKey || "layout"; } + @computed get finalLayoutKey() { + const { layoutKey } = this.props; + if (typeof layoutKey === "string") { + return layoutKey; + } + // return "layout"; + const fallback = Cast(this.props.Document.layoutKey, "string"); + return typeof fallback === "string" ? fallback : "layout"; + } childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); @computed get contents() { TraceMobx(); -- cgit v1.2.3-70-g09d2 From 778d79a3236c623a5985ef5bd9b5ac5c195ff8a9 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 20 Jan 2020 18:18:25 -0500 Subject: layout key fix --- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 66886165e..867be287f 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -61,7 +61,7 @@ export class DocumentContentsView extends React.Componentawaiting layout

"; - const layout = Cast(this.layoutDoc[this.props.layoutKey], "string"); + const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")], "string"); if (layout === undefined) { return this.props.Document.data ? "" : diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c58362f6c..13aa049db 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -764,7 +764,6 @@ export class DocumentView extends DocComponent(Docu if (typeof layoutKey === "string") { return layoutKey; } - // return "layout"; const fallback = Cast(this.props.Document.layoutKey, "string"); return typeof fallback === "string" ? fallback : "layout"; } -- cgit v1.2.3-70-g09d2 From d528b9d19a685d8447a9d54715dc94cd66034852 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Jan 2020 21:18:10 -0500 Subject: fixed linking to text region. fixed link displays. removed old tooltiptextmenu. fixed brushes in richTextMenu. --- src/client/util/ProsemirrorExampleTransfer.ts | 2 - src/client/util/RichTextMenu.tsx | 25 +- src/client/util/RichTextRules.ts | 4 +- src/client/util/RichTextSchema.tsx | 3 +- src/client/util/TooltipLinkingMenu.tsx | 68 -- src/client/util/TooltipTextMenu.scss | 372 ------- src/client/util/TooltipTextMenu.tsx | 1042 -------------------- src/client/views/DocumentButtonBar.tsx | 8 +- src/client/views/DocumentDecorations.tsx | 8 - src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 22 +- .../authentication/models/current_user_utils.ts | 4 +- 12 files changed, 37 insertions(+), 1523 deletions(-) delete mode 100644 src/client/util/TooltipLinkingMenu.tsx delete mode 100644 src/client/util/TooltipTextMenu.scss delete mode 100644 src/client/util/TooltipTextMenu.tsx (limited to 'src') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index c028dbf8b..da3815181 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -5,9 +5,7 @@ import { Schema } from "prosemirror-model"; import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; -import { TooltipTextMenu } from "./TooltipTextMenu"; import { SelectionManager } from "./SelectionManager"; -import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx index 419d7caf9..f070589ae 100644 --- a/src/client/util/RichTextMenu.tsx +++ b/src/client/util/RichTextMenu.tsx @@ -32,8 +32,9 @@ export default class RichTextMenu extends AntimodeMenu { public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable private view?: EditorView; - private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; + public editorProps: FieldViewProps & FormattedTextBoxProps | undefined; + public _brushMap: Map> = new Map(); private fontSizeOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; private fontFamilyOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; private listTypeOptions: { node: NodeType | any | null, title: string, label: string, command: any, style?: {} }[]; @@ -142,6 +143,17 @@ export default class RichTextMenu extends AntimodeMenu { this.updateFromDash(view, lastState, this.editorProps); } + + public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { + if (this.view) { + const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId }); + this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). + addMark(this.view.state.selection.from, this.view.state.selection.to, link)); + return this.view.state.selection.$from.nodeAfter?.text || ""; + } + return ""; + } + @action public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { if (!view) { @@ -417,6 +429,15 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleBrushDropdown() { this.showBrushDropdown = !this.showBrushDropdown; } + // todo: add brushes to brushMap to save with a style name + onBrushNameKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + RichTextMenu.Instance.brushMarks && RichTextMenu.Instance._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks); + this._brushNameRef.current!.style.background = "lightGray"; + } + } + _brushNameRef = React.createRef(); + createBrushButton() { const self = this; function onBrushClick(e: React.PointerEvent) { @@ -447,7 +468,7 @@ export default class RichTextMenu extends AntimodeMenu {

{label}

- {/* */} +
; return ( diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 29b378299..e20abb04c 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -5,11 +5,11 @@ import { NodeSelection, TextSelection } from "prosemirror-state"; import { NumCast, Cast } from "../../new_fields/Types"; import { Doc } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; -import { TooltipTextMenuManager } from "../util/TooltipTextMenu"; import { Docs, DocUtils } from "../documents/Documents"; import { Id } from "../../new_fields/FieldSymbols"; import { DocServer } from "../DocServer"; import { returnFalse, Utils } from "../../Utils"; +import RichTextMenu from "./RichTextMenu"; export const inpRules = { rules: [ @@ -264,7 +264,7 @@ export const inpRules = { new RegExp(/%[a-z]+$/), (state, match, start, end) => { const color = match[0].substring(1, match[0].length); - const marks = TooltipTextMenuManager.Instance._brushMap.get(color); + const marks = RichTextMenu.Instance._brushMap.get(color); if (marks) { const tr = state.tr.deleteRange(start, end); return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index ef90a7294..1f70cafc4 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -855,7 +855,8 @@ export class FootnoteView { }), new Plugin({ view(newView) { - return FormattedTextBox.getToolTip(newView); + // TODO -- make this work with RichTextMenu + // return FormattedTextBox.getToolTip(newView); } }) ], diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx deleted file mode 100644 index b46675a04..000000000 --- a/src/client/util/TooltipLinkingMenu.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { EditorState } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import { FieldViewProps } from "../views/nodes/FieldView"; -import "./TooltipTextMenu.scss"; - -//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. -export class TooltipLinkingMenu { - - private tooltip: HTMLElement; - private view: EditorView; - private editorProps: FieldViewProps; - - constructor(view: EditorView, editorProps: FieldViewProps) { - this.view = view; - this.editorProps = editorProps; - this.tooltip = document.createElement("div"); - this.tooltip.className = "tooltipMenu linking"; - - //add the div which is the tooltip - view.dom.parentNode!.parentNode!.appendChild(this.tooltip); - - const target = "https://www.google.com"; - - const link = document.createElement("a"); - link.href = target; - link.textContent = target; - link.target = "_blank"; - link.style.color = "white"; - this.tooltip.appendChild(link); - - this.update(view, undefined); - } - - //updates the tooltip menu when the selection changes - update(view: EditorView, lastState: EditorState | undefined) { - const state = view.state; - // Don't do anything if the document/selection didn't change - if (lastState && lastState.doc.eq(state.doc) && - lastState.selection.eq(state.selection)) return; - - // Hide the tooltip if the selection is empty - if (state.selection.empty) { - this.tooltip.style.display = "none"; - return; - } - - console.log("STORED:"); - console.log(state.doc.content.firstChild!.content); - - // Otherwise, reposition it and update its content - this.tooltip.style.display = ""; - const { from, to } = state.selection; - const start = view.coordsAtPos(from), end = view.coordsAtPos(to); - // The box in which the tooltip is positioned, to use as base - const box = this.tooltip.offsetParent!.getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when - // crossing lines, end may be more to the left) - const left = Math.max((start.left + end.left) / 2, start.left + 3); - this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - const width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; - const mid = Math.min(start.left, end.left) + width; - - this.tooltip.style.width = "auto"; - this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - } - - destroy() { this.tooltip.remove(); } -} diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss deleted file mode 100644 index 2a38fe118..000000000 --- a/src/client/util/TooltipTextMenu.scss +++ /dev/null @@ -1,372 +0,0 @@ -@import "../views/globalCssVariables"; -.ProseMirror-menu-dropdown-wrap { - display: inline-block; - position: relative; -} - -.ProseMirror-menu-dropdown { - vertical-align: 1px; - cursor: pointer; - position: relative; - padding: 0 15px 0 4px; - background: white; - border-radius: 2px; - text-align: left; - font-size: 12px; - white-space: nowrap; - margin-right: 4px; - - &:after { - content: ""; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid currentColor; - opacity: .6; - position: absolute; - right: 4px; - top: calc(50% - 2px); - } -} - -.ProseMirror-menu-submenu-wrap { - position: relative; - margin-right: -4px; -} - -.ProseMirror-menu-dropdown-menu, -.ProseMirror-menu-submenu { - font-size: 12px; - background: white; - border: 1px solid rgb(223, 223, 223); - min-width: 40px; - z-index: 50000; - position: absolute; - box-sizing: content-box; - - .ProseMirror-menu-dropdown-item { - cursor: pointer; - z-index: 100000; - text-align: left; - padding: 3px; - - &:hover { - background-color: $light-color-secondary; - } - } -} - - -.ProseMirror-menu-submenu-label:after { - content: ""; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - border-left: 4px solid currentColor; - opacity: .6; - position: absolute; - right: 4px; - top: calc(50% - 4px); -} - - .ProseMirror-icon { - display: inline-block; - // line-height: .8; - // vertical-align: -2px; /* Compensate for padding */ - // padding: 2px 8px; - cursor: pointer; - - &.ProseMirror-menu-disabled { - cursor: default; - } - - svg { - fill:white; - height: 1em; - } - - span { - vertical-align: text-top; - } - } - -.wrapper { - position: absolute; - pointer-events: all; - display: flex; - align-items: center; - transform: translateY(-85px); - - height: 35px; - background: #323232; - border-radius: 6px; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - -} - -.tooltipMenu, .basic-tools { - z-index: 20000; - pointer-events: all; - padding: 3px; - padding-bottom: 5px; - display: flex; - align-items: center; - - .ProseMirror-example-setup-style hr { - padding: 2px 10px; - border: none; - margin: 1em 0; - } - - .ProseMirror-example-setup-style hr:after { - content: ""; - display: block; - height: 1px; - background-color: silver; - line-height: 2px; - } -} - -.menuicon { - width: 25px; - height: 25px; - cursor: pointer; - text-align: center; - line-height: 25px; - margin: 0 2px; - border-radius: 3px; - - &:hover { - background-color: black; - - #link-drag { - background-color: black; - } - } - - &> * { - margin-top: 50%; - margin-left: 50%; - transform: translate(-50%, -50%); - } - - svg { - fill: inherit; - width: 18px; - height: 18px; - } -} - -.menuicon-active { - width: 25px; - height: 25px; - cursor: pointer; - text-align: center; - line-height: 25px; - margin: 0 2px; - border-radius: 3px; - - &:hover { - background-color: black; - } - - &> * { - margin-top: 50%; - margin-left: 50%; - transform: translate(-50%, -50%); - } - - svg { - fill: greenyellow; - width: 18px; - height: 18px; - } -} - -.colorPicker { - position: relative; - - svg { - width: 18px; - height: 18px; - // margin-top: 11px; - } - - .buttonColor { - position: absolute; - top: 24px; - left: 1px; - width: 24px; - height: 4px; - margin-top: 0; - } -} - -#link-drag { - background-color: #323232; -} - -.underline svg { - margin-top: 13px; -} - - .font-size-indicator { - font-size: 12px; - padding-right: 0px; - } - .summarize{ - color: white; - height: 20px; - text-align: center; - } - - -.brush{ - display: inline-block; - width: 1em; - height: 1em; - stroke-width: 0; - stroke: currentColor; - fill: currentColor; - margin-right: 15px; -} - -.brush-active{ - display: inline-block; - width: 1em; - height: 1em; - stroke-width: 3; - fill: greenyellow; - margin-right: 15px; -} - -.dragger-wrapper { - color: #eee; - height: 22px; - padding: 0 5px; - box-sizing: content-box; - cursor: grab; - - .dragger { - width: 18px; - height: 100%; - display: flex; - justify-content: space-evenly; - } - - .dragger-line { - width: 2px; - height: 100%; - background-color: black; - } -} - -.button-dropdown-wrapper { - display: flex; - align-content: center; - - &:hover { - background-color: black; - } -} - -.buttonSettings-dropdown { - - &.ProseMirror-menu-dropdown { - width: 10px; - height: 25px; - margin: 0; - padding: 0 2px; - background-color: #323232; - text-align: center; - - &:after { - border-top: 4px solid white; - right: 2px; - } - - &:hover { - background-color: black; - } - } - - &.ProseMirror-menu-dropdown-menu { - min-width: 150px; - left: -27px; - top: 31px; - background-color: #323232; - border: 1px solid #4d4d4d; - color: $light-color-secondary; - // border: none; - // border: 1px solid $light-color-secondary; - border-radius: 0 6px 6px 6px; - padding: 3px; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - - .ProseMirror-menu-dropdown-item{ - cursor: default; - - &:last-child { - border-bottom: none; - } - - &:hover { - background-color: #323232; - } - - .button-setting, .button-setting-disabled { - padding: 2px; - border-radius: 2px; - } - - .button-setting:hover { - cursor: pointer; - background-color: black; - } - - .separated-button { - border-top: 1px solid $light-color-secondary; - padding-top: 6px; - } - - input { - color: black; - border: none; - border-radius: 1px; - padding: 3px; - } - - button { - padding: 6px; - background-color: #323232; - border: 1px solid black; - border-radius: 1px; - - &:hover { - background-color: black; - } - } - } - - - } -} - -.colorPicker-wrapper { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-top: 3px; - margin-left: -3px; - width: calc(100% + 6px); -} - -button.colorPicker { - width: 20px; - height: 20px; - border-radius: 15px !important; - margin: 3px; - border: none !important; - - &.active { - border: 2px solid white !important; - } -} \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx deleted file mode 100644 index 1c15dca7f..000000000 --- a/src/client/util/TooltipTextMenu.tsx +++ /dev/null @@ -1,1042 +0,0 @@ -import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css -import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model"; -import { wrapInList } from 'prosemirror-schema-list'; -import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import { Doc, Field, Opt } from "../../new_fields/Doc"; -import { Utils } from "../../Utils"; -import { DocServer } from "../DocServer"; -import { FieldViewProps } from "../views/nodes/FieldView"; -import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; -import { LinkManager } from "./LinkManager"; -import { schema } from "./RichTextSchema"; -import "./TooltipTextMenu.scss"; -import { Cast, NumCast, StrCast } from '../../new_fields/Types'; -import { updateBullets } from './ProsemirrorExampleTransfer'; -import { DocumentDecorations } from '../views/DocumentDecorations'; -import { SelectionManager } from './SelectionManager'; -import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; -const { toggleMark } = require("prosemirror-commands"); - -//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. -export class TooltipTextMenu { - public static Toolbar: HTMLDivElement | undefined; - - // editor state properties - private view: EditorView; - private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; - - private fontStyles: Mark[] = []; - private fontSizes: Mark[] = []; - private _marksToDoms: Map = new Map(); - private _collapsed: boolean = false; - - // editor doms - public tooltip: HTMLElement = document.createElement("div"); - private wrapper: HTMLDivElement = document.createElement("div"); - - // editor button doms - private colorDom?: Node; - private colorDropdownDom?: Node; - private linkDom?: Node; - private highighterDom?: Node; - private highlighterDropdownDom?: Node; - private linkDropdownDom?: Node; - private _brushdom?: Node; - private _brushDropdownDom?: Node; - private fontSizeDom?: Node; - private fontStyleDom?: Node; - private basicTools?: HTMLElement; - - static createDiv(className: string) { const div = document.createElement("div"); div.className = className; return div; } - static createSpan(className: string) { const div = document.createElement("span"); div.className = className; return div; } - constructor(view: EditorView) { - this.view = view; - - // initialize the tooltip -- sets this.tooltip - this.initTooltip(view); - - // initialize the wrapper - this.wrapper = TooltipTextMenu.createDiv("wrapper"); - this.wrapper.appendChild(this.tooltip); - - TooltipTextMenu.Toolbar = this.wrapper; - } - - private async initTooltip(view: EditorView) { - const self = this; - this.tooltip = TooltipTextMenu.createDiv("tooltipMenu"); - this.basicTools = TooltipTextMenu.createDiv("basic-tools"); - - const svgIcon = (name: string, title: string = name, dpath: string) => { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", dpath); - svg.appendChild(path); - - const span = TooltipTextMenu.createSpan(name + " menuicon"); - span.title = title; - span.appendChild(svg); - - return span; - }; - - const basicItems = [ // init basicItems in minimized toolbar -- paths to svgs are obtained from fontawesome - { mark: schema.marks.strong, dom: svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") }, - { mark: schema.marks.em, dom: svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") }, - { mark: schema.marks.underline, dom: svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") }, - ]; - const items = [ // init items in full size toolbar - { mark: schema.marks.strikethrough, dom: svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") }, - { mark: schema.marks.superscript, dom: svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - { mark: schema.marks.subscript, dom: svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - ]; - - basicItems.map(({ dom, mark }) => this.basicTools ?.appendChild(dom.cloneNode(true))); - basicItems.concat(items).forEach(({ dom, mark }) => { - this.tooltip.appendChild(dom); - this._marksToDoms.set(mark, dom); - - //pointer down handler to activate button effects - dom.addEventListener("pointerdown", e => { - this.view.focus(); - if (dom.contains(e.target as Node)) { - e.preventDefault(); - e.stopPropagation(); - toggleMark(mark)(this.view.state, this.view.dispatch, this.view); - this.updateHighlightStateOfButtons(); - } - }); - }); - - // summarize menu - this.highighterDom = this.createHighlightTool().render(this.view).dom; - this.highlighterDropdownDom = this.createHighlightDropdown().render(this.view).dom; - this.tooltip.appendChild(this.highighterDom); - this.tooltip.appendChild(this.highlighterDropdownDom); - - // color menu - this.colorDom = this.createColorTool().render(this.view).dom; - this.colorDropdownDom = this.createColorDropdown().render(this.view).dom; - this.tooltip.appendChild(this.colorDom); - this.tooltip.appendChild(this.colorDropdownDom); - - // link menu - this.linkDom = this.createLinkTool().render(this.view).dom; - this.linkDropdownDom = this.createLinkDropdown("").render(this.view).dom; - this.tooltip.appendChild(this.linkDom); - this.tooltip.appendChild(this.linkDropdownDom); - - // list of font styles - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 7 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 8 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 9 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 16 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 18 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 20 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 24 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 32 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 48 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 72 })); - - // font sizes - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" })); - - - // init brush tool - this._brushdom = this.createBrushTool().render(this.view).dom; - this.tooltip.appendChild(this._brushdom); - this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom; - this.tooltip.appendChild(this._brushDropdownDom); - - // summarizer tool - const summarizer = new MenuItem({ - title: "Summarize", - label: "Summarize", - icon: icons.join, - css: "fill:white;", - class: "menuicon", - execEvent: "", - run: (state, dispatch) => TooltipTextMenu.insertSummarizer(state, dispatch) - }); - this.tooltip.appendChild(summarizer.render(this.view).dom); - - // list types dropdown - const listDropdownTypes = [{ mapStyle: "bullet", label: ":" }, { mapStyle: "decimal", label: "1.1" }, { mapStyle: "multi", label: "A.1" }, { label: "X" }]; - const listTypes = new Dropdown(listDropdownTypes.map(({ mapStyle, label }) => - new MenuItem({ - title: "Set Bullet Style", - label: label, - execEvent: "", - class: "dropdown-item", - css: "color: black; width: 40px;", - enable() { return true; }, - run() { - const marks = self.view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks()); - if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { - const tx3 = updateBullets(tx2, schema, mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx2); - })) { - const tx2 = view.state.tr; - const tx3 = updateBullets(tx2, schema, mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx3); - } - } - })), { label: ":", css: "color:black; width: 40px;" }); - this.tooltip.appendChild(listTypes.render(this.view).dom); - - await this.updateFromDash(view, undefined, undefined); - - const draggerWrapper = TooltipTextMenu.createDiv("dragger-wrapper"); - const dragger = TooltipTextMenu.createDiv("dragger"); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - draggerWrapper.appendChild(dragger); - this.wrapper.appendChild(draggerWrapper); - this.setupDragElementInteractions(draggerWrapper); - } - - setupDragElementInteractions(elmnt: HTMLElement) { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - if (elmnt) { - // if present, the header is where you move the DIV from: - elmnt.onpointerdown = dragPointerDown; - elmnt.ondblclick = onClick; - } - const self = this; - - function dragPointerDown(e: PointerEvent) { - e = e || window.event; - e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - document.onpointerup = closeDragElement; - // call a function whenever the cursor moves: - document.onpointermove = elementDrag; - } - - function onClick(e: MouseEvent) { - self._collapsed = !self._collapsed; - const children = self.wrapper.childNodes; - if (self._collapsed && children.length > 0) { - self.wrapper.removeChild(self.tooltip); - self.basicTools && self.wrapper.prepend(self.basicTools); - } - else { - self.wrapper.prepend(self.tooltip); - self.basicTools && self.wrapper.removeChild(self.basicTools); - } - } - - function elementDrag(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - // elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; - // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; - - self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px"; - self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px"; - } - - function closeDragElement() { - // stop moving when mouse button is released: - document.onpointerup = null; - document.onpointermove = null; - } - } - - //label of dropdown will change to given label - updateFontSizeDropdown(label: string) { - //font SIZES - const fontSizeBtns: MenuItem[] = []; - const self = this; - this.fontSizes.forEach(mark => - fontSizeBtns.push(new MenuItem({ - title: "Set Font Size", - label: String(mark.attrs.fontSize), - execEvent: "", - class: "dropdown-item", - css: "color: black; width: 50px;", - enable() { return true; }, - run() { - const size = mark.attrs.fontSize; - if (size) { self.updateFontSizeDropdown(String(size) + " pt"); } - if (self.editorProps) { - const ruleProvider = self.editorProps.ruleProvider; - const heading = NumCast(self.editorProps.Document.heading); - if (ruleProvider && heading) { - ruleProvider["ruleSize_" + heading] = size; - } - } - TooltipTextMenu.setMark(self.view.state.schema.marks.pFontSize.create({ fontSize: size }), self.view.state, self.view.dispatch); - } - }))); - - const newfontSizeDom = (new Dropdown(fontSizeBtns, { label: label, css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom; - if (this.fontSizeDom) { - this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); - } - else { - this.tooltip.appendChild(newfontSizeDom); - } - this.fontSizeDom = newfontSizeDom; - } - - //label of dropdown will change to given label - updateFontStyleDropdown(label: string) { - //font STYLES - const fontBtns: MenuItem[] = []; - const self = this; - this.fontStyles.forEach(mark => - fontBtns.push(new MenuItem({ - title: "Set Font Family", - label: mark.attrs.family, - execEvent: "", - class: "dropdown-item", - css: "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", - enable() { return true; }, - run() { - const fontName = mark.attrs.family; - if (fontName) { self.updateFontStyleDropdown(fontName); } - if (self.editorProps) { - const ruleProvider = self.editorProps.ruleProvider; - const heading = NumCast(self.editorProps.Document.heading); - if (ruleProvider && heading) { - ruleProvider["ruleFont_" + heading] = fontName; - } - } - TooltipTextMenu.setMark(self.view.state.schema.marks.pFontFamily.create({ family: fontName }), self.view.state, self.view.dispatch); - } - }))); - - const newfontStyleDom = (new Dropdown(fontBtns, { label: label, css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom; - if (this.fontStyleDom) { - this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); - } - else { - this.tooltip.appendChild(newfontStyleDom); - } - this.fontStyleDom = newfontStyleDom; - } - async getTextLinkTargetTitle() { - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type.name === "link"); - if (link) { - const href = link.attrs.href; - if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - if (linkclicked) { - const linkDoc = await DocServer.GetRefField(linkclicked); - if (linkDoc instanceof Doc) { - const anchor1 = await Cast(linkDoc.anchor1, Doc); - const anchor2 = await Cast(linkDoc.anchor2, Doc); - const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document; - if (currentDoc && anchor1 && anchor2) { - if (Doc.AreProtosEqual(currentDoc, anchor1)) { - return StrCast(anchor2.title); - } - if (Doc.AreProtosEqual(currentDoc, anchor2)) { - return StrCast(anchor1.title); - } - } - } - } - } else { - return href; - } - } else { - return link.attrs.title; - } - } - } - - // LINK TOOL - createLinkTool(active: boolean = false) { - return new MenuItem({ - title: "Link tool", - label: "Link tool", - icon: icons.link, - css: "fill:white;", - class: active ? "menuicon-active" : "menuicon", - execEvent: "", - run: async (state, dispatch) => { }, - active: (state) => true - }); - } - - createLinkDropdown(targetTitle: string) { - const input = document.createElement("input"); - - // menu item for input for hyperlink url - // TODO: integrate search to allow users to search for a doc to link to - const linkInfo = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Linked to:"; - - input.type = "text"; - input.placeholder = "Enter URL"; - if (targetTitle) input.value = targetTitle; - input.onclick = (e: MouseEvent) => { - input.select(); - input.focus(); - }; - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(input); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - // menu item to update/apply the hyperlink to the selected text - const linkApply = new MenuItem({ - title: "", - execEvent: "", - class: "", - css: "", - render() { - const button = document.createElement("button"); - button.className = "link-url-button"; - button.textContent = "Apply hyperlink"; - return button; - }, - enable() { return false; }, - run: async (state, dispatch, view, event) => { - event.stopPropagation(); - let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: input.value, location: "onRight" }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); - this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - node = this.view.state.selection.$from.nodeAfter; - link = node && node.marks.find(m => m.type.name === "link"); - - // update link menu - const linkDom = self.createLinkTool(true).render(self.view).dom; - const linkDropdownDom = self.createLinkDropdown(await self.getTextLinkTargetTitle()).render(self.view).dom; - self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); - self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); - self.linkDom = linkDom; - self.linkDropdownDom = linkDropdownDom; - } - }); - - // menu item to remove the link - // TODO: allow this to be undoable - const self = this; - const deleteLink = new MenuItem({ - title: "Delete link", - execEvent: "", - class: "separated-button", - css: "", - render() { - const button = document.createElement("button"); - button.textContent = "Remove link"; - - const wrapper = document.createElement("div"); - wrapper.appendChild(button); - return wrapper; - }, - enable() { return true; }, - async run() { - // delete the link - const node = self.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type === self.view.state.schema.marks.link); - const href = link!.attrs.href; - if (href ?.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - linkclicked && DocServer.GetRefField(linkclicked).then(async linkDoc => { - if (linkDoc instanceof Doc) { - LinkManager.Instance.deleteLink(linkDoc); - self.view.dispatch(self.view.state.tr.removeMark(self.view.state.selection.from, self.view.state.selection.to, self.view.state.schema.marks.link)); - } - }); - } - // update link menu - const linkDom = self.createLinkTool(false).render(self.view).dom; - const linkDropdownDom = self.createLinkDropdown("").render(self.view).dom; - self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); - self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); - self.linkDom = linkDom; - self.linkDropdownDom = linkDropdownDom; - } - }); - - return new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { - const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). - addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - return this.view.state.selection.$from.nodeAfter ?.text || ""; - } - - // SUMMARIZER TOOL - static insertSummarizer(state: EditorState, dispatch: any) { - if (!state.selection.empty) { - const mark = state.schema.marks.summarize.create(); - const tr = state.tr.addMark(state.selection.from, state.selection.to, mark); - const content = tr.selection.content(); - const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }); - dispatch ?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); - } - } - - // HIGHLIGHTER TOOL - createHighlightTool() { - return new MenuItem({ - title: "Highlight", - css: "fill:white;", - class: "menuicon", - execEvent: "", - render() { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z"); - svg.appendChild(path); - - const color = TooltipTextMenu.createDiv("buttonColor"); - color.style.backgroundColor = TooltipTextMenuManager.Instance.highlighter.toString(); - - const wrapper = TooltipTextMenu.createDiv("colorPicker"); - wrapper.appendChild(svg); - wrapper.appendChild(color); - return wrapper; - }, - run: (state, dispatch) => TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, state, dispatch) - }); - } - - static insertHighlight(color: String, state: EditorState, dispatch: any) { - if (!state.selection.empty) { - toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch); - } - } - - createHighlightDropdown() { - // menu item for color picker - const self = this; - const colors = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Change highlight:"; - - const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); - - const colors = [ - PastelSchemaPalette.get("pink2"), - PastelSchemaPalette.get("purple4"), - PastelSchemaPalette.get("bluegreen1"), - PastelSchemaPalette.get("yellow4"), - PastelSchemaPalette.get("red2"), - PastelSchemaPalette.get("bluegreen7"), - PastelSchemaPalette.get("bluegreen5"), - PastelSchemaPalette.get("orange1"), - "white", - "transparent" - ]; - - colors.forEach(color => { - const button = document.createElement("button"); - button.className = color === TooltipTextMenuManager.Instance.highlighter ? "colorPicker active" : "colorPicker"; - if (color) { - button.style.backgroundColor = color; - button.textContent = color === "transparent" ? "X" : ""; - button.onclick = e => { - TooltipTextMenuManager.Instance.highlighter = color; - - TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, self.view.state, self.view.dispatch); - - // update color menu - const highlightDom = self.createHighlightTool().render(self.view).dom; - const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom; - self.highighterDom && self.tooltip.replaceChild(highlightDom, self.highighterDom); - self.highlighterDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlighterDropdownDom); - self.highighterDom = highlightDom; - self.highlighterDropdownDom = highlightDropdownDom; - }; - } - colorsWrapper.appendChild(button); - }); - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(colorsWrapper); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } - }); - - return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - // COLOR TOOL - createColorTool() { - return new MenuItem({ - title: "Color", - css: "fill:white;", - class: "menuicon", - execEvent: "", - render() { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"); - svg.appendChild(path); - - const color = TooltipTextMenu.createDiv("buttonColor"); - color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString(); - - const wrapper = TooltipTextMenu.createDiv("colorPicker"); - wrapper.appendChild(svg); - wrapper.appendChild(color); - return wrapper; - }, - run: (state, dispatch) => TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, state, dispatch) - }); - } - - static insertColor(color: String, state: EditorState, dispatch: any) { - const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color }); - if (state.selection.empty) { - dispatch(state.tr.addStoredMark(colorMark)); - } else { - this.setMark(colorMark, state, dispatch); - } - } - - createColorDropdown() { - // menu item for color picker - const self = this; - const colors = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Change color:"; - - const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); - - const colors = [ - DarkPastelSchemaPalette.get("pink2"), - DarkPastelSchemaPalette.get("purple4"), - DarkPastelSchemaPalette.get("bluegreen1"), - DarkPastelSchemaPalette.get("yellow4"), - DarkPastelSchemaPalette.get("red2"), - DarkPastelSchemaPalette.get("bluegreen7"), - DarkPastelSchemaPalette.get("bluegreen5"), - DarkPastelSchemaPalette.get("orange1"), - "#757472", - "#000" - ]; - - colors.forEach(color => { - const button = document.createElement("button"); - button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker"; - if (color) { - button.style.backgroundColor = color; - button.onclick = e => { - TooltipTextMenuManager.Instance.color = color; - - TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch); - - // update color menu - const colorDom = self.createColorTool().render(self.view).dom; - const colorDropdownDom = self.createColorDropdown().render(self.view).dom; - self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom); - self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom); - self.colorDom = colorDom; - self.colorDropdownDom = colorDropdownDom; - }; - } - colorsWrapper.appendChild(button); - }); - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(colorsWrapper); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - // BRUSH TOOL - createBrushTool(active: boolean = false) { - const icon = { - height: 32, width: 32, - path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z" - }; - const self = this; - return new MenuItem({ - title: "Brush tool", - label: "Brush tool", - icon: icon, - css: "fill:white;", - class: active ? "menuicon-active" : "menuicon", - execEvent: "", - run: (state, dispatch) => { - this.brush_function(state, dispatch); - - // update dropdown with marks - const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom; - self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); - self._brushDropdownDom = newBrushDropdowndom; - }, - active: (state) => true - }); - } - - brush_function(state: EditorState, dispatch: any) { - if (TooltipTextMenuManager.Instance._brushIsEmpty) { - // get marks in the selection - const selected_marks = new Set(); - const { from, to } = state.selection as TextSelection; - state.doc.nodesBetween(from, to, (node) => node.marks ?.forEach(m => selected_marks.add(m))); - - if (this._brushdom && selected_marks.size >= 0) { - TooltipTextMenuManager.Instance._brushMarks = selected_marks; - const newbrush = this.createBrushTool(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; - } - } - else { - const { from, to, $from } = this.view.state.selection; - if (this._brushdom) { - if (!this.view.state.selection.empty && $from && $from.nodeAfter) { - if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) { - this.view.dispatch(this.view.state.tr.removeMark(from, to)); - Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { - TooltipTextMenu.setMark(mark, this.view.state, this.view.dispatch); - }); - } - } - else { - const newbrush = this.createBrushTool(false).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; - } - } - } - } - - createBrushDropdown(active: boolean = false) { - let label = "Stored marks: "; - if (TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0) { - TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => label += mark.type.name + ", "); - label = label.substring(0, label.length - 2); - } else { - label = "No marks are currently stored"; - } - - const brushInfo = new MenuItem({ - title: "", - label: label, - execEvent: "", - class: "button-setting-disabled", - css: "", - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - const self = this; - const input = document.createElement("input"); - const clearBrush = new MenuItem({ - title: "Clear brush", - execEvent: "", - class: "separated-button", - css: "", - render() { - const button = document.createElement("button"); - button.textContent = "Clear brush"; - - input.textContent = "editme"; - input.style.width = "75px"; - input.style.height = "30px"; - input.style.background = "white"; - input.setAttribute("contenteditable", "true"); - input.style.whiteSpace = "nowrap"; - input.type = "text"; - input.placeholder = "Enter URL"; - input.onpointerdown = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - }; - input.onclick = (e: MouseEvent) => { - input.select(); - input.focus(); - }; - input.onkeypress = (e: KeyboardEvent) => { - if (e.key === "Enter") { - TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMap.set(input.value, TooltipTextMenuManager.Instance._brushMarks); - input.style.background = "lightGray"; - } - }; - - const wrapper = document.createElement("div"); - wrapper.appendChild(input); - wrapper.appendChild(button); - return wrapper; - }, - enable() { return true; }, - run() { - TooltipTextMenuManager.Instance._brushIsEmpty = true; - TooltipTextMenuManager.Instance._brushMarks = new Set(); - - // update brush tool - // TODO: this probably isn't very clean - const newBrushdom = self.createBrushTool().render(self.view).dom; - self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom); - self._brushdom = newBrushdom; - const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom; - self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); - self._brushDropdownDom = newBrushDropdowndom; - } - }); - - const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0; - return new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - static setMark = (mark: Mark, state: EditorState, dispatch: any) => { - if (mark) { - const node = (state.selection as NodeSelection).node; - if (node ?.type === schema.nodes.ordered_list) { - let attrs = node.attrs; - if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family }; - if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize }; - if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color }; - const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema); - dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from)))); - } else { - toggleMark(mark.type, mark.attrs)(state, (tx: any) => { - const { from, $from, to, empty } = tx.selection; - if (!tx.doc.rangeHasMark(from, to, mark.type)) { - toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch); - } else dispatch(tx); - }); - } - } - } - - // called by Prosemirror - update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); } - //updates the tooltip menu when the selection changes - public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { - if (!view) { - console.log("no editor? why?"); - return; - } - this.view = view; - DocumentDecorations.Instance.showTextBar(); - props && (this.editorProps = props); - - // Don't do anything if the document/selection didn't change - if (!lastState || !lastState.doc.eq(view.state.doc) || !lastState.selection.eq(view.state.selection)) { - - // UPDATE LINK DROPDOWN - const linkTarget = await this.getTextLinkTargetTitle(); - const linkDom = this.createLinkTool(linkTarget ? true : false).render(this.view).dom; - const linkDropdownDom = this.createLinkDropdown(linkTarget).render(this.view).dom; - this.linkDom && this.tooltip.replaceChild(linkDom, this.linkDom); - this.linkDropdownDom && this.tooltip.replaceChild(linkDropdownDom, this.linkDropdownDom); - this.linkDom = linkDom; - this.linkDropdownDom = linkDropdownDom; - - //UPDATE FONT STYLE DROPDOWN - const activeStyles = this.activeFontFamilyOnSelection(); - this.updateFontStyleDropdown(activeStyles.length === 1 ? activeStyles[0] : activeStyles.length ? "various" : "default"); - - //UPDATE FONT SIZE DROPDOWN - const activeSizes = this.activeFontSizeOnSelection(); - this.updateFontSizeDropdown(activeSizes.length === 1 ? String(activeSizes[0]) + " pt" : activeSizes.length ? "various" : "default"); - - //UPDATE ALL OTHER BUTTONS - this.updateHighlightStateOfButtons(); - } - } - - updateHighlightStateOfButtons() { - Array.from(this._marksToDoms.values()).forEach(val => val.style.fill = "white"); - this.activeMarksOnSelection().filter(mark => this._marksToDoms.has(mark)).forEach(mark => - this._marksToDoms.get(mark)!.style.fill = "greenyellow"); - - // keeps brush tool highlighted if active when switching between textboxes - if (!TooltipTextMenuManager.Instance._brushIsEmpty && this._brushdom) { - const newbrush = this.createBrushTool(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - } - } - - //finds fontSize at start of selection - activeFontSizeOnSelection() { - //current selection - const state = this.view.state; - const activeSizes: number[] = []; - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - ref_node.marks.forEach(m => m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize)); - } - return activeSizes; - } - //finds fontSize at start of selection - activeFontFamilyOnSelection() { - //current selection - const state = this.view.state; - const activeFamilies: string[] = []; - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - ref_node.marks.forEach(m => m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family)); - } - return activeFamilies; - } - //finds all active marks on selection in given group - activeMarksOnSelection() { - const markGroup = Array.from(this._marksToDoms.keys()); - if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); - //current selection - const { empty, ranges, $to } = this.view.state.selection as TextSelection; - const state = this.view.state; - let activeMarks: MarkType[] = []; - if (!empty) { - activeMarks = markGroup.filter(mark => { - const has = false; - for (let i = 0; !has && i < ranges.length; i++) { - return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark); - } - return false; - }); - } - else { - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node !== null && ref_node !== this.view.state.doc) { - if (ref_node.isText) { - } - else { - return []; - } - activeMarks = markGroup.filter(mark_type => { - if (mark_type === state.schema.marks.pFontSize) { - return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); - } - const mark = state.schema.mark(mark_type); - return ref_node.marks.includes(mark); - }); - } - } - return activeMarks; - } - - reference_node(pos: ResolvedPos): ProsNode { - let ref_node: ProsNode = this.view.state.doc; - if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { - ref_node = pos.nodeBefore; - } - else if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { - ref_node = pos.nodeAfter; - } - else if (pos.pos > 0) { - let skip = false; - for (let i: number = pos.pos - 1; i > 0; i--) { - this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => { - if (node.isLeaf && !skip) { - ref_node = node; - skip = true; - } - - }); - } - } - if (!ref_node.isLeaf && ref_node.childCount > 0) { - ref_node = ref_node.child(0); - } - return ref_node; - } - - destroy() { - // this.wrapper.remove(); - } -} - - -export class TooltipTextMenuManager { - private static _instance: TooltipTextMenuManager; - private _isPinned: boolean = false; - - public pinnedX: number = 0; - public pinnedY: number = 0; - public unpinnedX: number = 0; - public unpinnedY: number = 0; - - public _brushMarks: Set | undefined; - public _brushMap: Map> = new Map(); - public _brushIsEmpty: boolean = true; - - public color: String = "#000"; - public highlighter: String = "transparent"; - - public activeMenu: TooltipTextMenu | undefined; - - static get Instance() { - if (!TooltipTextMenuManager._instance) { - TooltipTextMenuManager._instance = new TooltipTextMenuManager(); - } - return TooltipTextMenuManager._instance; - } - - public get isPinned() { return this._isPinned; } - - public toggleIsPinned() { this._isPinned = !this._isPinned; } -} diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 202bfe400..4441356dc 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -20,6 +20,7 @@ import React = require("react"); import { DocumentView } from './nodes/DocumentView'; import { ParentDocSelector } from './collections/ParentDocumentSelector'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import RichTextMenu from '../util/RichTextMenu'; import { Id } from '../../new_fields/FieldSymbols'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -112,14 +113,15 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const linkDrag = UndoManager.StartBatch("Drag Link"); this.view0 && DragManager.StartLinkDrag(this._linkButton.current, this.view0.props.Document, e.pageX, e.pageY, { dragComplete: dropEv => { - const linkDoc = dropEv.linkDragData?.linkDocument; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop - if (this.view0 && linkDoc && FormattedTextBox.ToolTipTextMenu) { + const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop + if (this.view0 && linkDoc) { const proto = Doc.GetProto(linkDoc); proto.sourceContext = this.view0.props.ContainingCollectionDoc; const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; + const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; + const text = RichTextMenu.Instance.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", anchor2Id); if (linkDoc.anchor2 instanceof Doc) { - const text = FormattedTextBox.ToolTipTextMenu.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", linkDoc.anchor2[Id]); proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODO open to more descriptive descriptions of following in text link } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 799b3695c..32fea15fb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -14,7 +14,6 @@ import { Docs, DocUtils } from "../documents/Documents"; import { DocumentManager } from "../util/DocumentManager"; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; -import { TooltipTextMenu } from '../util/TooltipTextMenu'; import { undoBatch, UndoManager } from "../util/UndoManager"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { CollectionView } from "./collections/CollectionView"; @@ -26,8 +25,6 @@ import { IconBox } from "./nodes/IconBox"; import React = require("react"); import { DocumentType } from '../documents/DocumentTypes'; import { ScriptField } from '../../new_fields/ScriptField'; -import { render } from 'react-dom'; -import RichTextMenu from '../util/RichTextMenu'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -546,11 +543,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this.TextBar = ele; } } - public showTextBar = () => { - if (this.TextBar && TooltipTextMenu.Toolbar && Array.from(this.TextBar.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1) { - this.TextBar.appendChild(TooltipTextMenu.Toolbar); - } - } render() { const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 867be287f..b9f601a9a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -61,7 +61,7 @@ export class DocumentContentsView extends React.Componentawaiting layout

"; - const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")], "string"); + const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); if (layout === undefined) { return this.props.Document.data ? "" : diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 60842bcb0..1cd5cdcea 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -29,8 +29,6 @@ import buildKeymap from "../../util/ProsemirrorExampleTransfer"; import { inpRules } from "../../util/RichTextRules"; import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummaryView } from "../../util/RichTextSchema"; import { SelectionManager } from "../../util/SelectionManager"; -import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu"; -import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocAnnotatableComponent } from "../DocComponent"; import { DocumentButtonBar } from '../DocumentButtonBar'; @@ -77,7 +75,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; - public static ToolTipTextMenu: TooltipTextMenu | undefined = undefined; public ProseRef?: HTMLDivElement; private _ref: React.RefObject = React.createRef(); private _scrollRef: React.RefObject = React.createRef(); @@ -127,10 +124,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & return ""; } - public static getToolTip(ev: EditorView) { - return this.ToolTipTextMenu ? this.ToolTipTextMenu : this.ToolTipTextMenu = new TooltipTextMenu(ev); - } - @undoBatch public setFontColor(color: string) { const view = this._editorView!; @@ -485,11 +478,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & schema, plugins: [ inputRules(inpRules), - this.tooltipTextMenuPlugin(), + this.richTextMenuPlugin(), history(), keymap(this._keymap), keymap(baseKeymap), - // this.tooltipLinkingMenuPlugin(), new Plugin({ props: { attributes: { class: "ProseMirror-example-setup-style" } @@ -1038,25 +1030,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } } - tooltipTextMenuPlugin() { + richTextMenuPlugin() { const self = FormattedTextBox; return new Plugin({ view(newView) { - // return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView); RichTextMenu.Instance.changeView(newView); return RichTextMenu.Instance; } }); } - tooltipLinkingMenuPlugin() { - const myprops = this.props; - return new Plugin({ - view(_editorView) { - return new TooltipLinkingMenu(_editorView, myprops); - } - }); - } onBlur = (e: any) => { //DictationManager.Controls.stop(false); if (this._undoTyping) { @@ -1138,7 +1121,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & if (this.props.isSelected()) { // TODO: ftong --> update from dash in richtextmenu RichTextMenu.Instance.updateFromDash(this._editorView!, undefined, this.props); - // FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props); } else if (FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5375c8ac8..e502a06b8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -151,14 +151,14 @@ export class CurrentUserUtils { }); // setup a color picker const color = Docs.Create.ColorDocument({ - title: "color picker", width: 400, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) + title: "color picker", width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) }); return Docs.Create.ButtonDocument({ width: 35, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.StackingDocument([dragCreators, color], { - width: 500, height: 800, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" + width: 500, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" }), onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), }); -- cgit v1.2.3-70-g09d2 From 69e068d77731c25d9f1dbafb8c7d279e343a4e55 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 00:11:41 -0500 Subject: fixed up creating template fields in stacking views (+ after the template is made) --- .../views/collections/CollectionPivotView.tsx | 37 +++++++++++++++------- .../CollectionStackingViewFieldColumn.tsx | 6 ++++ src/client/views/collections/CollectionView.tsx | 6 ++++ .../views/collections/CollectionViewChromes.tsx | 10 ++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 -- src/client/views/nodes/DocumentView.tsx | 3 +- src/new_fields/Doc.ts | 5 +++ src/new_fields/documentSchemas.ts | 7 ++++ src/new_fields/util.ts | 4 +-- 9 files changed, 61 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index ad2e05908..07ad5683c 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -2,7 +2,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx"; import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Field, DocCastAsync } from "../../../new_fields/Doc"; import "./CollectionPivotView.scss"; import { observer } from "mobx-react"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; @@ -15,10 +15,15 @@ import { anchorPoints, Flyout } from "../TemplateMenu"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { List } from "../../../new_fields/List"; import { Set } from "typescript-collections"; +import { PrefetchProxy } from "../../../new_fields/Proxy"; @observer export class CollectionPivotView extends CollectionSubView(doc => doc) { - componentDidMount = () => { + private _narrativeDisposer: IReactionDisposer | undefined; + componentWillUnmount() { + this._narrativeDisposer?.(); + } + componentDidMount() { this.props.Document.freeformLayoutEngine = "pivot"; if (true || !this.props.Document.facetCollection) { const facetCollection = Docs.Create.FreeformDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); @@ -34,16 +39,24 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { facetCollection.onCheckedClick = new ScriptField(script); } - const openDocText = "const alias = getAlias(this); alias.layoutKey = 'layout_detailed'; useRightSplit(alias); "; - const openDocScript = CompileScript(openDocText, { - params: { this: Doc.name, heading: "boolean", checked: "boolean", context: Doc.name }, - typecheck: false, - editable: true, - }); - if (openDocScript.compiled) { - this.props.Document.onChildClick = new ScriptField(openDocScript); - } - + this._narrativeDisposer = reaction(() => this.props.Document.childDetailed, + (childDetailed) => + DocCastAsync(childDetailed).then(childDetailed => { + if (childDetailed instanceof Doc) { + let captured: { [name: string]: Field } = {}; + captured["childDetailed"] = new PrefetchProxy(childDetailed); + const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; + const openDocScript = CompileScript(openDocText, { + params: { this: Doc.name, heading: "boolean", context: Doc.name }, + typecheck: false, + editable: true, + capturedVariables: captured + }); + if (openDocScript.compiled) { + this.props.Document.onChildClick = new ScriptField(openDocScript); + } + } + }), { fireImmediately: true }); this.props.Document.facetCollection = facetCollection; this.props.Document.fitToBox = true; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 23a664359..229a23294 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -142,6 +142,12 @@ export class CollectionStackingViewFieldColumn extends React.Component { const existing = ContextMenu.Instance.findByDescription("Layout..."); const layoutItems = existing && "subitems" in existing ? existing.subitems : []; layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); + if (this.props.Document.childLayout instanceof Doc) { + layoutItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, undefined, "onRight"), icon: "project-diagram" }); + } + if (this.props.Document.childDetailed instanceof Doc) { + layoutItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childDetailed as Doc, undefined, "onRight"), icon: "project-diagram" }); + } !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); const more = ContextMenu.Instance.findByDescription("More..."); diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 996c7671e..075c48134 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -39,10 +39,15 @@ export class CollectionViewBaseChrome extends React.Component Doc.setChildLayout(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined) }; + _narrativeCommand = { + title: "set detailed template", script: "setChildLayout(this.target, this.source?.[0])", params: ["target", "source"], + initialize: emptyFunction, + immediate: (draggedDocs: Doc[]) => Doc.setChildDetailed(this.props.CollectionView.props.Document, draggedDocs.length ? draggedDocs[0] : undefined) + }; _contentCommand = { // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"], // bcz: doesn't look like we can do async stuff in scripting... title: "set content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"], @@ -54,7 +59,7 @@ export class CollectionViewBaseChrome extends React.Component { this.props.CollectionView.props.Document.panX = 0; this.props.CollectionView.props.Document.panY = 0; this.props.CollectionView.props.Document.scale = 1; }, initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document.panX; button.restoredPanY = this.props.CollectionView.props.Document.panY; button.restoredScale = this.props.CollectionView.props.Document.scale; } }; - _freeform_commands = [this._contentCommand, this._templateCommand, this._viewCommand]; + _freeform_commands = [this._contentCommand, this._templateCommand, this._narrativeCommand, this._viewCommand]; _stacking_commands = [this._contentCommand, this._templateCommand]; _masonry_commands = [this._contentCommand, this._templateCommand]; _tree_commands = []; @@ -64,6 +69,7 @@ export class CollectionViewBaseChrome extends React.Component { const layoutItems: ContextMenuProps[] = []; - if (this.childDocs.some(d => BoolCast(d.isTemplateDoc))) { - layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); - } layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); layoutItems.push({ description: `${this.Document.LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document.LODdisable = !this.Document.LODdisable, icon: "table" }); layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document.fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 63bb6b18c..e91013be4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -659,7 +659,8 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action - setCustomView = (custom: boolean): void => { + setCustomView = + (custom: boolean): void => { if (this.props.ContainingCollectionView?.props.DataDoc || this.props.ContainingCollectionView?.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.Document); } else { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index e0ab5d97c..29834256a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -746,6 +746,11 @@ export namespace Doc { source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory : source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined; } + export function setChildDetailed(target: Doc, source?: Doc) { + target.childDetailed = source && source.isTemplateDoc ? source : source && + source.dragFactory instanceof Doc && source.dragFactory.isTemplateDoc ? source.dragFactory : + source && source.layout instanceof Doc && source.layout.isTemplateDoc ? source.layout : undefined; + } export function MakeDocFilter(docFilters: string[]) { let docFilterText = ""; diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index d5c34e128..3683e5820 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -68,6 +68,13 @@ export const positionSchema = createSchema({ z: "number", }); +export const collectionSchema = createSchema({ + childLayout: Doc, // layout template for children of a collecion + childDetailed: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) + onChildClick: ScriptField, // script to run for each child when its clicked + onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view +}); + export type Document = makeInterface<[typeof documentSchema]>; export const Document = makeInterface(documentSchema); diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 4147be278..3255c6172 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -1,7 +1,7 @@ import { UndoManager } from "../client/util/UndoManager"; import { Doc, Field, FieldResult, UpdatingFromServer } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; -import { ProxyField } from "./Proxy"; +import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action, trace } from "mobx"; @@ -52,7 +52,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number value = new ProxyField(value); } if (value instanceof ObjectField) { - if (value[Parent] && value[Parent] !== receiver) { + if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) { throw new Error("Can't put the same object in multiple documents at the same time"); } value[Parent] = receiver; -- cgit v1.2.3-70-g09d2 From 00a44ed4b0011ca6967f5d1512aa9fd1f6a1bdfe Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 00:39:21 -0500 Subject: fix to avoid crash when rendering a non-string in an EditableView (eg when the document's title is displayed as a richtextBox and as an EditableView header on a DocumentView) --- src/client/views/EditableView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index faf02b946..0d677b8ce 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -5,6 +5,7 @@ import "./EditableView.scss"; import * as Autosuggest from 'react-autosuggest'; import { undoBatch } from '../util/UndoManager'; import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField'; +import { ObjectField } from '../../new_fields/ObjectField'; export interface EditableProps { /** @@ -152,7 +153,7 @@ export class EditableView extends React.Component { />; } else { if (this.props.autosuggestProps) this.props.autosuggestProps.resetValue(); - return ( + return (this.props.contents instanceof ObjectField ? (null) :
-- cgit v1.2.3-70-g09d2 From 9090808d81028fb7ed8796339524b5a0fa0893d5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 08:27:50 -0500 Subject: fixed some hangs when the title is a rich text field --- src/client/views/collections/ParentDocumentSelector.tsx | 6 +++--- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 24aa6ddfa..1cc05f92c 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -6,7 +6,7 @@ import { observable, action, runInAction } from "mobx"; import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; -import { NumCast } from "../../../new_fields/Types"; +import { NumCast, StrCast } from "../../../new_fields/Types"; import { CollectionViewType } from "./CollectionView"; import { DocumentButtonBar } from "../DocumentButtonBar"; import { DocumentManager } from "../../util/DocumentManager"; @@ -71,9 +71,9 @@ export class SelectorContextMenu extends React.Component { return
Metadata: {this.metadataMenu}

Contexts:

- {this._docs.map(doc =>

{doc.col.title}

)} + {this._docs.map(doc =>

{doc.col.title?.toString()}

)} {this._otherDocs.length ?
: null} - {this._otherDocs.map(doc =>

{doc.col.title}

)} + {this._otherDocs.map(doc =>

{doc.col.title?.toString()}

)}
; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7ed36df69..db5259bdc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -78,7 +78,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _layoutComputeReaction: IReactionDisposer | undefined; private _layoutPoolData = new ObservableMap(); - public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive + public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _clusterSets: (Doc[])[] = []; @@ -973,7 +973,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @computed get placeholder() { return
- {this.props.Document.title} + {this.props.Document.title?.toString()}
; } @computed get marqueeView() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e91013be4..2ccfad448 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -930,9 +930,9 @@ export class DocumentView extends DocComponent(Docu pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all", }}> StrCast((this.props.DataDoc || this.props.Document)[showTitle])} + GetValue={() => (this.props.DataDoc || this.props.Document)[showTitle]?.toString()} SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)} />
); -- cgit v1.2.3-70-g09d2 From 4d1c1286f311de9d69158adccb02b60638a30e83 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 09:12:35 -0500 Subject: added a ToString() method to fields so that rendering them as RichTextField won't make them unreadable --- src/client/northstar/dash-fields/HistogramField.ts | 5 ++++- .../CollectionFreeFormLayoutEngines.tsx | 11 +++++------ src/new_fields/CursorField.ts | 5 ++++- src/new_fields/DateField.ts | 5 ++++- src/new_fields/Doc.ts | 17 ++++++++++++++++- src/new_fields/FieldSymbols.ts | 3 ++- src/new_fields/HtmlField.ts | 5 ++++- src/new_fields/IconField.ts | 5 ++++- src/new_fields/InkField.ts | 5 ++++- src/new_fields/List.ts | 5 ++++- src/new_fields/ObjectField.ts | 3 ++- src/new_fields/Proxy.ts | 5 ++++- src/new_fields/RefField.ts | 3 ++- src/new_fields/RichTextField.ts | 5 ++++- src/new_fields/SchemaHeaderField.ts | 5 ++++- src/new_fields/ScriptField.ts | 5 ++++- src/new_fields/URLField.ts | 5 ++++- 17 files changed, 75 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index f3365e73d..076516977 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -7,7 +7,7 @@ import { ObjectField } from "../../../new_fields/ObjectField"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { OmitKeys } from "../../../Utils"; import { Deserializable } from "../../util/SerializationHelper"; -import { Copy, ToScriptString } from "../../../new_fields/FieldSymbols"; +import { Copy, ToScriptString, ToString } from "../../../new_fields/FieldSymbols"; function serialize(field: HistogramField) { const obj = OmitKeys(field, ['Links', 'BrushLinks', 'Result', 'BrushColors', 'FilterModels', 'FilterOperand']).omit; @@ -60,4 +60,7 @@ export class HistogramField extends ObjectField { [ToScriptString]() { return this.toString(); } + [ToString]() { + return this.toString(); + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 8c8da63cc..5924c3afb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -7,8 +7,10 @@ import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { emptyFunction } from "../../../../Utils"; import React = require("react"); import { ObservableMap, runInAction } from "mobx"; -import { Id } from "../../../../new_fields/FieldSymbols"; +import { Id, ToString } from "../../../../new_fields/FieldSymbols"; import { DateField } from "../../../../new_fields/DateField"; +import { ObjectField } from "../../../../new_fields/ObjectField"; +import { RefField } from "../../../../new_fields/RefField"; interface PivotData { type: string; @@ -35,11 +37,8 @@ export interface ViewDefResult { } function toLabel(target: FieldResult) { - if (target instanceof DateField) { - const date = DateCast(target).date; - if (date) { - return `${date.toDateString()} ${date.toTimeString()}`; - } + if (target instanceof ObjectField || target instanceof RefField) { + return target[ToString](); } return String(target); } diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts index fd86031a8..28467377b 100644 --- a/src/new_fields/CursorField.ts +++ b/src/new_fields/CursorField.ts @@ -2,7 +2,7 @@ import { ObjectField } from "./ObjectField"; import { observable } from "mobx"; import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, createSimpleSchema, object, date } from "serializr"; -import { OnUpdate, ToScriptString, Copy } from "./FieldSymbols"; +import { OnUpdate, ToScriptString, ToString, Copy } from "./FieldSymbols"; export type CursorPosition = { x: number, @@ -60,4 +60,7 @@ export default class CursorField extends ObjectField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return "invalid"; + } } \ No newline at end of file diff --git a/src/new_fields/DateField.ts b/src/new_fields/DateField.ts index 4f999e5e8..a925148c2 100644 --- a/src/new_fields/DateField.ts +++ b/src/new_fields/DateField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, date } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; import { scriptingGlobal, Scripting } from "../client/util/Scripting"; @scriptingGlobal @@ -26,6 +26,9 @@ export class DateField extends ObjectField { [ToScriptString]() { return `new DateField(new Date(${this.date.toISOString()}))`; } + [ToString]() { + return this.date.toISOString(); + } } Scripting.addGlobal(function d(...dateArgs: any[]) { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 29834256a..fa5ca1489 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -4,7 +4,7 @@ import { DocServer } from "../client/DocServer"; import { DocumentType } from "../client/documents/DocumentTypes"; import { Scripting, scriptingGlobal } from "../client/util/Scripting"; import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper"; -import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, Update } from "./FieldSymbols"; +import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols"; import { List } from "./List"; import { ObjectField } from "./ObjectField"; import { PrefetchProxy, ProxyField } from "./Proxy"; @@ -36,6 +36,18 @@ export namespace Field { return field[ToScriptString](); } } + export function toString(field: Field): string { + if (typeof field === "string") { + return `"${field}"`; + } else if (typeof field === "number" || typeof field === "boolean") { + return String(field); + } else if (field instanceof ObjectField) { + return field[ToString](); + } else if (field instanceof RefField) { + return field[ToString](); + } + return "-invalid field-"; + } export function IsField(field: any): field is Field; export function IsField(field: any, includeUndefined: true): field is Field | undefined; export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined { @@ -156,6 +168,9 @@ export class Doc extends RefField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return "Doc"; + } private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; public static CurrentUserEmail: string = ""; diff --git a/src/new_fields/FieldSymbols.ts b/src/new_fields/FieldSymbols.ts index b5b3aa588..4aadb81a2 100644 --- a/src/new_fields/FieldSymbols.ts +++ b/src/new_fields/FieldSymbols.ts @@ -7,4 +7,5 @@ export const Id = Symbol("Id"); export const OnUpdate = Symbol("OnUpdate"); export const Parent = Symbol("Parent"); export const Copy = Symbol("Copy"); -export const ToScriptString = Symbol("ToScriptString"); \ No newline at end of file +export const ToScriptString = Symbol("ToScriptString"); +export const ToString = Symbol("ToString"); \ No newline at end of file diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts index f952acff9..6e8bba977 100644 --- a/src/new_fields/HtmlField.ts +++ b/src/new_fields/HtmlField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString} from "./FieldSymbols"; @Deserializable("html") export class HtmlField extends ObjectField { @@ -20,4 +20,7 @@ export class HtmlField extends ObjectField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return this.html; + } } diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts index 62b2cd254..76c4ddf1b 100644 --- a/src/new_fields/IconField.ts +++ b/src/new_fields/IconField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; @Deserializable("icon") export class IconField extends ObjectField { @@ -20,4 +20,7 @@ export class IconField extends ObjectField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return "ICONfield"; + } } diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 01493bcc2..4a44b4f55 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; export enum InkTool { None, @@ -45,4 +45,7 @@ export class InkField extends ObjectField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return "InkField"; + } } diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index bb48b1bb3..a43f11e82 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -6,7 +6,7 @@ import { observable, action } from "mobx"; import { ObjectField } from "./ObjectField"; import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; -import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, Copy } from "./FieldSymbols"; +import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy } from "./FieldSymbols"; import { Scripting } from "../client/util/Scripting"; const listHandlers: any = { @@ -292,6 +292,9 @@ class ListImpl extends ObjectField { [ToScriptString]() { return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; } + [ToString]() { + return "List"; + } } export type List = ListImpl & (T | (T extends RefField ? Promise : never))[]; export const List: { new (fields?: T[]): List } = ListImpl as any; diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts index 65ada91c0..b693c8c98 100644 --- a/src/new_fields/ObjectField.ts +++ b/src/new_fields/ObjectField.ts @@ -1,6 +1,6 @@ import { Doc } from "./Doc"; import { RefField } from "./RefField"; -import { OnUpdate, Parent, Copy, ToScriptString } from "./FieldSymbols"; +import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols"; import { Scripting } from "../client/util/Scripting"; export abstract class ObjectField { @@ -9,6 +9,7 @@ export abstract class ObjectField { abstract [Copy](): ObjectField; abstract [ToScriptString](): string; + abstract [ToString](): string; } export namespace ObjectField { diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index c6292e37c..d50c0f14e 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -5,7 +5,7 @@ import { observable, action } from "mobx"; import { DocServer } from "../client/DocServer"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; -import { Id, Copy, ToScriptString } from "./FieldSymbols"; +import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; import { Plugins } from "./util"; @@ -32,6 +32,9 @@ export class ProxyField extends ObjectField { [ToScriptString]() { return "invalid"; } + [ToString]() { + return "ProxyField"; + } @serializable(primitive()) readonly fieldId: string = ""; diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts index f7bea8c94..b6ef69750 100644 --- a/src/new_fields/RefField.ts +++ b/src/new_fields/RefField.ts @@ -1,6 +1,6 @@ import { serializable, primitive, alias } from "serializr"; import { Utils } from "../Utils"; -import { Id, HandleUpdate, ToScriptString } from "./FieldSymbols"; +import { Id, HandleUpdate, ToScriptString, ToString } from "./FieldSymbols"; import { afterDocDeserialize } from "../client/util/SerializationHelper"; export type FieldId = string; @@ -17,4 +17,5 @@ export abstract class RefField { protected [HandleUpdate]?(diff: any): void | Promise; abstract [ToScriptString](): string; + abstract [ToString](): string; } diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index fd5459876..a0f21f45e 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -1,7 +1,7 @@ import { ObjectField } from "./ObjectField"; import { serializable } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; -import { Copy, ToScriptString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; @scriptingGlobal @@ -26,5 +26,8 @@ export class RichTextField extends ObjectField { [ToScriptString]() { return `new RichTextField("${this.Data}", "${this.Text}")`; } + [ToString]() { + return this.Text; + } } \ No newline at end of file diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index 42a8485ac..07c90f5a2 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, primitive } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, OnUpdate } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; import { ColumnType } from "../client/views/collections/CollectionSchemaView"; @@ -116,4 +116,7 @@ export class SchemaHeaderField extends ObjectField { [ToScriptString]() { return `invalid`; } + [ToString]() { + return `SchemaHeaderField`; + } } \ No newline at end of file diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index b5ad4a7f6..09a18c258 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -1,6 +1,6 @@ import { ObjectField } from "./ObjectField"; import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions } from "../client/util/Scripting"; -import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable, autoObject } from "../client/util/SerializationHelper"; import { Doc } from "../new_fields/Doc"; @@ -101,6 +101,9 @@ export class ScriptField extends ObjectField { [ToScriptString]() { return "script field"; } + [ToString]() { + return "script field"; + } public static CompileScript(script: string, params: object = {}, addReturn = false) { const compiled = CompileScript(script, { params: { this: Doc.name, _last_: "any", ...params }, diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index 35ef6dd02..cfab36906 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom } from "serializr"; import { ObjectField } from "./ObjectField"; -import { ToScriptString, Copy } from "./FieldSymbols"; +import { ToScriptString, ToString, Copy } from "./FieldSymbols"; import { Scripting, scriptingGlobal } from "../client/util/Scripting"; function url() { @@ -32,6 +32,9 @@ export abstract class URLField extends ObjectField { [ToScriptString]() { return `new ${this.constructor.name}("${this.url.href}")`; } + [ToString]() { + return this.url.href; + } [Copy](): this { return new (this.constructor as any)(this.url); -- cgit v1.2.3-70-g09d2 From 119f90cc5c4ef773133f1bfd18a813dde9487f94 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 21 Jan 2020 10:26:45 -0500 Subject: buxton templating fixes --- .../views/collections/CollectionPivotView.tsx | 5 ++-- .../views/collections/CollectionTreeView.tsx | 29 +++++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 07ad5683c..816939a41 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -43,8 +43,9 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { (childDetailed) => DocCastAsync(childDetailed).then(childDetailed => { if (childDetailed instanceof Doc) { - let captured: { [name: string]: Field } = {}; - captured["childDetailed"] = new PrefetchProxy(childDetailed); + const targetKey = "childDetailed"; + const captured: { [name: string]: Field } = {}; + captured[targetKey] = new PrefetchProxy(childDetailed); const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; const openDocScript = CompileScript(openDocText, { params: { this: Doc.name, heading: "boolean", context: Doc.name }, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3a208038c..4a6dffc1c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -634,22 +634,27 @@ export class CollectionTreeView extends CollectionSubView(Document) { } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { - const { TextDocument, ImageDocument } = Docs.Create; + const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; + const { Document } = this.props; + const fallback = "http://www.cs.brown.edu/~bcz/face.gif"; const wrapper = Docs.Create.StackingDocument([ - ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "hero" }), + ImageDocument(fallback, { title: "hero" }), + MulticolumnDocument([], { title: "data", height: 100 }), ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) ], { autoHeight: true, chromeStatus: "disabled" }); wrapper.disableLOD = true; - makeTemplate(wrapper, true); - const detailedLayout = Doc.MakeAlias(wrapper); - const cardLayout = ImageBox.LayoutString("hero"); - this.childLayoutPairs.forEach(({ layout }) => { - const proto = Doc.GetProto(layout); - proto.layout = cardLayout; - proto.layout_detailed = detailedLayout; - layout.showTitle = "title"; - layout.showTitleHover = "titlehover"; - }); + wrapper.isTemplateDoc = makeTemplate(wrapper, true); + + const cardLayout = ImageDocument(fallback); + const proto = Doc.GetProto(cardLayout); + proto.layout = ImageBox.LayoutString("hero"); + cardLayout.showTitle = "title"; + cardLayout.showTitleHover = "titlehover"; + cardLayout.isTemplateField = true; // make this document act like a template field + cardLayout.isTemplateDoc = true; // but it's also a template doc itself... a little weird + + Document.childLayout = cardLayout; + Document.childDetailed = wrapper; } }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); -- cgit v1.2.3-70-g09d2 From 74bad9d9deadeaf4959906e89457c16ed564fd01 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 21 Jan 2020 16:09:30 -0500 Subject: initial commit --- src/client/documents/Documents.ts | 1 + .../views/collections/CollectionPivotView.tsx | 18 +++++-------- .../views/collections/CollectionTreeView.tsx | 7 +++-- src/new_fields/Doc.ts | 30 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 91085cd0f..6cb3f604e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -129,6 +129,7 @@ export interface DocumentOptions { isExpanded?: boolean; // is linear view expanded textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded + treeViewChecked?: ScriptField; // computes whether or not the checkbox for this facet is checked } class EmptyBox { diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 816939a41..546fa3744 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -10,7 +10,7 @@ import { CollectionTreeView } from "./CollectionTreeView"; import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { CompileScript } from "../../util/Scripting"; +import { CompileScript, Scripting } from "../../util/Scripting"; import { anchorPoints, Flyout } from "../TemplateMenu"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { List } from "../../../new_fields/List"; @@ -43,9 +43,8 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { (childDetailed) => DocCastAsync(childDetailed).then(childDetailed => { if (childDetailed instanceof Doc) { - const targetKey = "childDetailed"; const captured: { [name: string]: Field } = {}; - captured[targetKey] = new PrefetchProxy(childDetailed); + captured.childDetailed = new PrefetchProxy(childDetailed); const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; const openDocScript = CompileScript(openDocText, { params: { this: Doc.name, heading: "boolean", context: Doc.name }, @@ -76,6 +75,9 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { return facets.toArray(); } + /** + * Responds to clicking the check box in the flyout menu + */ facetClick = (facet: string) => { const facetCollection = this.props.Document.facetCollection; if (facetCollection instanceof Doc) { @@ -84,15 +86,9 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { //Doc.RemoveDocFromList(facetCollection, "data", DocListCast(facetCollection.data)[found]); (facetCollection.data as List).splice(found, 1); } else { - const facetValues = new Set(); - this.childDocs.forEach(child => { - Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facet]?.toString() || "(null)")); - facetValues.add(child[facet]?.toString() || "(null)"); - }); - - const newFacetVals = facetValues.toArray().sort().map(val => Docs.Create.TextDocument({ title: val.toString() })); - const newFacet = Docs.Create.FreeformDocument(newFacetVals, { title: facet, treeViewOpen: true, isFacetFilter: true }); + const newFacet = Docs.Create.FreeformDocument([], { title: facet, treeViewOpen: true, isFacetFilter: true }); Doc.AddDocToList(facetCollection, "data", newFacet); + newFacet.data = ScriptField.MakeFunction("readFacetData()", { params: {} }); } } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 4a6dffc1c..2e9f0379c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -373,12 +373,12 @@ class TreeView extends React.Component { @action bulletClick = (e: React.MouseEvent) => { if (this.props.onCheckedClick && this.props.document.type !== DocumentType.COL) { - this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check"; + // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check"; ScriptCast(this.props.onCheckedClick).script.run({ this: this.props.document.isTemplateField && this.props.dataDoc ? this.props.dataDoc : this.props.document, heading: this.props.containingCollection.title, checked: this.props.document.treeViewChecked === "check" ? false : this.props.document.treeViewChecked === "x" ? "x" : "none", - context: this.props.treeViewId + context: this.props.treeViewId, }, console.log); } else { this.treeViewOpen = !this.treeViewOpen; @@ -634,12 +634,11 @@ export class CollectionTreeView extends CollectionSubView(Document) { } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { - const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; + const { TextDocument, ImageDocument } = Docs.Create; const { Document } = this.props; const fallback = "http://www.cs.brown.edu/~bcz/face.gif"; const wrapper = Docs.Create.StackingDocument([ ImageDocument(fallback, { title: "hero" }), - MulticolumnDocument([], { title: "data", height: 100 }), ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) ], { autoHeight: true, chromeStatus: "disabled" }); wrapper.disableLOD = true; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fa5ca1489..74b8df1a0 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -16,6 +16,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { intersectRect } from "../Utils"; import { UndoManager } from "../client/util/UndoManager"; import { computedFn } from "mobx-utils"; +import { Docs } from "../client/documents/Documents"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -823,4 +824,33 @@ Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: an } const docFilterText = Doc.MakeDocFilter(docFilters); container.viewSpecScript = docFilterText ? ScriptField.MakeFunction(docFilterText, { doc: Doc.name }) : undefined; +}); + +Scripting.addGlobal(function readFacetData(target: Doc, facet: string) { + const facetValues = new Set(); + DocListCast(target.dataField).forEach(child => { + Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facet]?.toString() || "(null)")); + facetValues.add(child[facet]?.toString() || "(null)"); + }); + return Array.from(facetValues).sort().map(val => { + const capturedVariables: { [name: string]: Field } = {}; + capturedVariables.facet = val; + capturedVariables.container = target; + return Docs.Create.TextDocument({ + title: val.toString(), + treeViewChecked: ScriptField.MakeFunction("readCheckedState(container, facetValue)", { capturedVariables }) + }); + }); +}); + +Scripting.addGlobal(function readCheckedState(container: Doc, facetValue: string) { + const docFilters = Cast(container.docFilter, listSpec("string"), []); + for (let i = 0; i < docFilters.length; i += 3) { + const key = docFilters[i]; + const value = docFilters[i + 1]; + if (key === facetValue) { + return value; + } + } + return false; }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 4eb5fde5f9b14d1b13c1995b9a1110347a57a582 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 21 Jan 2020 17:00:04 -0500 Subject: fixed templating with collection views set? --- src/client/views/collections/CollectionView.tsx | 2 +- src/new_fields/Doc.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index cc8ae2ac1..a99bd29d6 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -92,7 +92,7 @@ export class CollectionView extends Touchable { public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } + @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.Document, this.props.fieldKey); } get collectionViewType(): CollectionViewType | undefined { if (!this.extensionDoc) return CollectionViewType.Invalid; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fa5ca1489..6b0ec3fd3 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -608,6 +608,8 @@ export namespace Doc { if (fieldTemplate.layout instanceof Doc) { fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout); } + const fieldLayoutKey = StrCast(Doc.LayoutField(fieldLayoutDoc))?.split("'")[1] + const fieldLayoutExt = fieldLayoutKey && Doc.MakeDelegate(fieldTemplate[fieldLayoutKey + "_ext"] as Doc); fieldTemplate.templateField = metadataFieldName; fieldTemplate.title = metadataFieldName; @@ -629,6 +631,7 @@ export namespace Doc { !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data)); const layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldName}'}`); const layoutDelegate = Doc.Layout(fieldTemplate); + layoutDelegate[metadataFieldName + "_ext"] = fieldLayoutExt; layoutDelegate.layout = layout; fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor; @@ -672,7 +675,7 @@ export namespace Doc { // the document containing the view layout information - will be the Document itself unless the Document has // a layout field. In that case, all layout information comes from there unless overriden by Document - export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? doc[StrCast(doc.layoutKey, "layout")] as Doc : doc; } + export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? Doc.LayoutField(doc) as Doc : doc; } export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; } export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; } const manager = new DocData(); -- cgit v1.2.3-70-g09d2 From 7fffe13954ed0113bf1b2f9318518d015dc63844 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 21 Jan 2020 17:07:35 -0500 Subject: made onClick override default link follow behavior --- src/client/views/collections/CollectionView.tsx | 1 - src/client/views/nodes/DocuLinkBox.tsx | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a99bd29d6..430aecef4 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -91,7 +91,6 @@ export class CollectionView extends Touchable { @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); } @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.Document, this.props.fieldKey); } get collectionViewType(): CollectionViewType | undefined { diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index 0d4d50c59..a4a9a62aa 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -61,10 +61,12 @@ export class DocuLinkBox extends DocComponent(Doc } } onClick = (e: React.MouseEvent) => { - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { - DocumentManager.Instance.FollowLink(this.props.Document, this.props.Document[this.props.fieldKey] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false); + if (!this.props.Document.onClick) { + if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { + DocumentManager.Instance.FollowLink(this.props.Document, this.props.Document[this.props.fieldKey] as Doc, document => this.props.addDocTab(document, undefined, "inTab"), false); + } + e.stopPropagation(); } - e.stopPropagation(); } render() { -- cgit v1.2.3-70-g09d2 From 8a4a8212b024e2804596a08ea820be646c9a7c0f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 21 Jan 2020 19:02:43 -0500 Subject: functional but incomplete updating logic --- src/client/documents/Documents.ts | 2 - .../views/collections/CollectionPivotView.tsx | 30 ++++++++---- src/client/views/collections/CollectionSubView.tsx | 10 +++- .../views/collections/CollectionTreeView.tsx | 2 +- src/new_fields/Doc.ts | 55 +++++++++++----------- src/new_fields/ScriptField.ts | 13 ++--- 6 files changed, 67 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6cb3f604e..d7292837c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -129,7 +129,6 @@ export interface DocumentOptions { isExpanded?: boolean; // is linear view expanded textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded - treeViewChecked?: ScriptField; // computes whether or not the checkbox for this facet is checked } class EmptyBox { @@ -789,7 +788,6 @@ export namespace DocUtils { }); } - @undoBatch export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 546fa3744..f31f1aba6 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -9,7 +9,7 @@ import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormV import { CollectionTreeView } from "./CollectionTreeView"; import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; import { Docs } from "../../documents/Documents"; -import { ScriptField } from "../../../new_fields/ScriptField"; +import { ScriptField, ComputedField } from "../../../new_fields/ScriptField"; import { CompileScript, Scripting } from "../../util/Scripting"; import { anchorPoints, Flyout } from "../TemplateMenu"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -25,7 +25,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { } componentDidMount() { this.props.Document.freeformLayoutEngine = "pivot"; - if (true || !this.props.Document.facetCollection) { + if (!this.props.Document.facetCollection) { const facetCollection = Docs.Create.FreeformDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; @@ -38,7 +38,6 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { if (script.compiled) { facetCollection.onCheckedClick = new ScriptField(script); } - this._narrativeDisposer = reaction(() => this.props.Document.childDetailed, (childDetailed) => DocCastAsync(childDetailed).then(childDetailed => { @@ -78,17 +77,32 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { /** * Responds to clicking the check box in the flyout menu */ - facetClick = (facet: string) => { - const facetCollection = this.props.Document.facetCollection; + facetClick = (facetHeader: string) => { + const { Document, fieldKey } = this.props; + const facetCollection = Document.facetCollection; if (facetCollection instanceof Doc) { - const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facet); + const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); if (found !== -1) { //Doc.RemoveDocFromList(facetCollection, "data", DocListCast(facetCollection.data)[found]); (facetCollection.data as List).splice(found, 1); } else { - const newFacet = Docs.Create.FreeformDocument([], { title: facet, treeViewOpen: true, isFacetFilter: true }); + const newFacet = Docs.Create.FreeformDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); Doc.AddDocToList(facetCollection, "data", newFacet); - newFacet.data = ScriptField.MakeFunction("readFacetData()", { params: {} }); + const { dataDoc } = this; + const capturedVariables = { + layoutDoc: Document, + dataDoc, + dataKey: fieldKey, + facetHeader + }; + const params = { + layoutDoc: Doc.name, + dataDoc: Doc.name, + dataKey: "string", + facetHeader: "string" + }; + newFacet.container = dataDoc; + newFacet.data = ComputedField.MakeFunction("readFacetData(layoutDoc, dataDoc, dataKey, facetHeader)", params, capturedVariables); } } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5f4ee3669..9357b0507 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -90,7 +90,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { // to its children which may be templates. // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { - return this.props.annotationsKey ? (this.extensionDoc ? this.extensionDoc[this.props.annotationsKey] : undefined) : this.dataDoc[this.props.fieldKey]; + const { annotationsKey, fieldKey } = this.props; + const { extensionDoc, dataDoc } = this; + if (annotationsKey) { + if (extensionDoc) { + return extensionDoc[annotationsKey]; + } + return undefined; + } + return dataDoc[fieldKey]; } get childLayoutPairs(): { layout: Doc; data: Doc; }[] { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2e9f0379c..ffaad2ddd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -377,7 +377,7 @@ class TreeView extends React.Component { ScriptCast(this.props.onCheckedClick).script.run({ this: this.props.document.isTemplateField && this.props.dataDoc ? this.props.dataDoc : this.props.document, heading: this.props.containingCollection.title, - checked: this.props.document.treeViewChecked === "check" ? false : this.props.document.treeViewChecked === "x" ? "x" : "none", + checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check", context: this.props.treeViewId, }, console.log); } else { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 74b8df1a0..69d478a48 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -1,4 +1,4 @@ -import { observable, ObservableMap, runInAction, action, untracked } from "mobx"; +import { observable, ObservableMap, runInAction } from "mobx"; import { alias, map, serializable } from "serializr"; import { DocServer } from "../client/DocServer"; import { DocumentType } from "../client/documents/DocumentTypes"; @@ -11,7 +11,7 @@ import { PrefetchProxy, ProxyField } from "./Proxy"; import { FieldId, RefField } from "./RefField"; import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; -import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstructor } from "./Types"; +import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util"; import { intersectRect } from "../Utils"; import { UndoManager } from "../client/util/UndoManager"; @@ -807,16 +807,15 @@ Scripting.addGlobal(function matchFieldValue(doc: Doc, key: string, value: any) } return false; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: string) { +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: string) { const docFilters = Cast(container.docFilter, listSpec("string"), []); - let found = false; - for (let i = 0; i < docFilters.length && !found; i += 3) { + for (let i = 0; i < docFilters.length; i += 3) { if (docFilters[i] === key && docFilters[i + 1] === value) { - found = true; docFilters.splice(i, 3); + break; } } - if (!found || modifiers !== "none") { + if (modifiers !== undefined) { docFilters.push(key); docFilters.push(value); docFilters.push(modifiers); @@ -826,31 +825,33 @@ Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: an container.viewSpecScript = docFilterText ? ScriptField.MakeFunction(docFilterText, { doc: Doc.name }) : undefined; }); -Scripting.addGlobal(function readFacetData(target: Doc, facet: string) { +Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) { const facetValues = new Set(); - DocListCast(target.dataField).forEach(child => { - Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facet]?.toString() || "(null)")); - facetValues.add(child[facet]?.toString() || "(null)"); - }); - return Array.from(facetValues).sort().map(val => { - const capturedVariables: { [name: string]: Field } = {}; - capturedVariables.facet = val; - capturedVariables.container = target; - return Docs.Create.TextDocument({ - title: val.toString(), - treeViewChecked: ScriptField.MakeFunction("readCheckedState(container, facetValue)", { capturedVariables }) - }); + DocListCast(dataDoc[dataKey]).forEach(child => { + Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facetHeader]?.toString() || "(null)")); + facetValues.add(child[facetHeader]?.toString() || "(null)"); }); + const text = "determineCheckedState(layoutDoc, facetHeader, facetValue)"; + const params = { + layoutDoc: Doc.name, + facetHeader: "string", + facetValue: "string" + }; + const capturedVariables = { layoutDoc, facetHeader }; + return new List(Array.from(facetValues).sort().map(facetValue => { + const value = Docs.Create.TextDocument({ title: facetValue.toString() }); + value.treeViewChecked = ComputedField.MakeFunction(text, params, { ...capturedVariables, facetValue }); + return value; + })); }); -Scripting.addGlobal(function readCheckedState(container: Doc, facetValue: string) { - const docFilters = Cast(container.docFilter, listSpec("string"), []); +Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { + const docFilters = Cast(layoutDoc.docFilter, listSpec("string"), []); for (let i = 0; i < docFilters.length; i += 3) { - const key = docFilters[i]; - const value = docFilters[i + 1]; - if (key === facetValue) { - return value; + const [header, value, state] = docFilters.slice(i, i + 3); + if (header === facetHeader && value === facetValue) { + return state; } } - return false; + return undefined; }); \ No newline at end of file diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index 09a18c258..f8a8d1226 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -3,7 +3,7 @@ import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions } from ". import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable, autoObject } from "../client/util/SerializationHelper"; -import { Doc } from "../new_fields/Doc"; +import { Doc, Field } from "../new_fields/Doc"; import { Plugins } from "./util"; import { computedFn } from "mobx-utils"; import { ProxyField } from "./Proxy"; @@ -104,12 +104,13 @@ export class ScriptField extends ObjectField { [ToString]() { return "script field"; } - public static CompileScript(script: string, params: object = {}, addReturn = false) { + public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) { const compiled = CompileScript(script, { params: { this: Doc.name, _last_: "any", ...params }, typecheck: false, editable: true, - addReturn: addReturn + addReturn: addReturn, + capturedVariables }); return compiled; } @@ -130,12 +131,12 @@ export class ComputedField extends ScriptField { _lastComputedResult: any; //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc value = computedFn((doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, _last_: this._lastComputedResult }, console.log).result); - public static MakeScript(script: string, params: object = {}, ) { + public static MakeScript(script: string, params: object = {}) { const compiled = ScriptField.CompileScript(script, params, false); return compiled.compiled ? new ComputedField(compiled) : undefined; } - public static MakeFunction(script: string, params: object = {}) { - const compiled = ScriptField.CompileScript(script, params, true); + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) { + const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); return compiled.compiled ? new ComputedField(compiled) : undefined; } } -- cgit v1.2.3-70-g09d2 From e1bfca0cc529c8ae954f7b1f678966e7f34f3887 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 21 Jan 2020 19:13:00 -0500 Subject: semicolon --- src/new_fields/Doc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 48ff431bc..d57a94e9f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -609,7 +609,7 @@ export namespace Doc { if (fieldTemplate.layout instanceof Doc) { fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout); } - const fieldLayoutKey = StrCast(Doc.LayoutField(fieldLayoutDoc))?.split("'")[1] + const fieldLayoutKey = StrCast(Doc.LayoutField(fieldLayoutDoc))?.split("'")[1]; const fieldLayoutExt = fieldLayoutKey && Doc.MakeDelegate(fieldTemplate[fieldLayoutKey + "_ext"] as Doc); fieldTemplate.templateField = metadataFieldName; -- cgit v1.2.3-70-g09d2 From 377837d63289c5e1a155e6ab40f008cc0507b088 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 21 Jan 2020 19:21:45 -0500 Subject: fixed error with dependencies --- .../views/collections/CollectionTreeView.tsx | 52 +++++++++++++++++----- src/new_fields/Doc.ts | 32 ------------- 2 files changed, 41 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ffaad2ddd..48b5d91e9 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,19 +1,22 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faExpand, faMinus, faPlus, faTrash, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked, runInAction } from "mobx"; +import { action, computed, observable, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, Field, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types'; -import { emptyFunction, Utils, returnFalse, emptyPath } from '../../../Utils'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; +import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; +import { emptyFunction, emptyPath, returnFalse, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { makeTemplate } from '../../util/DropConverter'; +import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; @@ -21,18 +24,14 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; +import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { ImageBox } from '../nodes/ImageBox'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { ScriptBox } from '../ScriptBox'; import { Templates } from '../Templates'; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; -import { ScriptBox } from '../ScriptBox'; -import { ImageBox } from '../nodes/ImageBox'; -import { makeTemplate } from '../../util/DropConverter'; -import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionViewType } from './CollectionView'; export interface TreeViewProps { @@ -713,4 +712,35 @@ export class CollectionTreeView extends CollectionSubView(Document) { ); } -} \ No newline at end of file +} + +Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) { + const facetValues = new Set(); + DocListCast(dataDoc[dataKey]).forEach(child => { + Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facetHeader]?.toString() || "(null)")); + facetValues.add(child[facetHeader]?.toString() || "(null)"); + }); + const text = "determineCheckedState(layoutDoc, facetHeader, facetValue)"; + const params = { + layoutDoc: Doc.name, + facetHeader: "string", + facetValue: "string" + }; + const capturedVariables = { layoutDoc, facetHeader }; + return new List(Array.from(facetValues).sort().map(facetValue => { + const value = Docs.Create.TextDocument({ title: facetValue.toString() }); + value.treeViewChecked = ComputedField.MakeFunction(text, params, { ...capturedVariables, facetValue }); + return value; + })); +}); + +Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { + const docFilters = Cast(layoutDoc.docFilter, listSpec("string"), []); + for (let i = 0; i < docFilters.length; i += 3) { + const [header, value, state] = docFilters.slice(i, i + 3); + if (header === facetHeader && value === facetValue) { + return state; + } + } + return undefined; +}); \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index d57a94e9f..50dc02e57 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -16,7 +16,6 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { intersectRect } from "../Utils"; import { UndoManager } from "../client/util/UndoManager"; import { computedFn } from "mobx-utils"; -import { Docs } from "../client/documents/Documents"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -826,35 +825,4 @@ Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: an } const docFilterText = Doc.MakeDocFilter(docFilters); container.viewSpecScript = docFilterText ? ScriptField.MakeFunction(docFilterText, { doc: Doc.name }) : undefined; -}); - -Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) { - const facetValues = new Set(); - DocListCast(dataDoc[dataKey]).forEach(child => { - Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facetHeader]?.toString() || "(null)")); - facetValues.add(child[facetHeader]?.toString() || "(null)"); - }); - const text = "determineCheckedState(layoutDoc, facetHeader, facetValue)"; - const params = { - layoutDoc: Doc.name, - facetHeader: "string", - facetValue: "string" - }; - const capturedVariables = { layoutDoc, facetHeader }; - return new List(Array.from(facetValues).sort().map(facetValue => { - const value = Docs.Create.TextDocument({ title: facetValue.toString() }); - value.treeViewChecked = ComputedField.MakeFunction(text, params, { ...capturedVariables, facetValue }); - return value; - })); -}); - -Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { - const docFilters = Cast(layoutDoc.docFilter, listSpec("string"), []); - for (let i = 0; i < docFilters.length; i += 3) { - const [header, value, state] = docFilters.slice(i, i + 3); - if (header === facetHeader && value === facetValue) { - return state; - } - } - return undefined; }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 613c2c8de636927fb38a7a32ec6b0427b238db2b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 21:33:42 -0500 Subject: added filter resizer for pivot viewer. fixed click/drag confusion with mainview divider. --- src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionPivotView.scss | 13 +++- .../views/collections/CollectionPivotView.tsx | 90 +++++++++++++--------- 3 files changed, 70 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d7656aba4..0958c434a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -307,8 +307,10 @@ export class MainView extends React.Component { ; } + _canClick = false; onPointerDown = (e: React.PointerEvent) => { if (this._flyoutTranslate) { + this._canClick = true; this._flyoutSizeOnDown = e.clientX; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -339,11 +341,12 @@ export class MainView extends React.Component { @action onPointerMove = (e: PointerEvent) => { this.flyoutWidth = Math.max(e.clientX, 0); + Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false); this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30; } @action onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) { + if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) { this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30); } diff --git a/src/client/views/collections/CollectionPivotView.scss b/src/client/views/collections/CollectionPivotView.scss index bd3d6c77b..2ddcc97ea 100644 --- a/src/client/views/collections/CollectionPivotView.scss +++ b/src/client/views/collections/CollectionPivotView.scss @@ -45,7 +45,7 @@ } .collectionPivotView-tree { display:inline-block; - width: 200px; + width: 100%; height: calc(100% - 30px); } } @@ -54,4 +54,15 @@ width: calc(100% - 200px); height: 100%; } + .collectionPivotView-dragger { + background-color: lightgray; + height: 40px; + width: 20px; + position: absolute; + border-radius: 10px; + top: 55%; + border: 1px black solid; + z-index: 2; + left:-10px; + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index f31f1aba6..8e712e370 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -65,7 +65,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey); } - bodyPanelWidth = () => this.props.PanelWidth() - 200; + bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth; getTransform = () => this.props.ScreenToLocalTransform().translate(-200, 0); @computed get _allFacets() { @@ -78,39 +78,53 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { * Responds to clicking the check box in the flyout menu */ facetClick = (facetHeader: string) => { - const { Document, fieldKey } = this.props; - const facetCollection = Document.facetCollection; + const facetCollection = this.props.Document.facetCollection; if (facetCollection instanceof Doc) { const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); if (found !== -1) { - //Doc.RemoveDocFromList(facetCollection, "data", DocListCast(facetCollection.data)[found]); (facetCollection.data as List).splice(found, 1); } else { const newFacet = Docs.Create.FreeformDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; + const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); Doc.AddDocToList(facetCollection, "data", newFacet); - const { dataDoc } = this; - const capturedVariables = { - layoutDoc: Document, - dataDoc, - dataKey: fieldKey, - facetHeader - }; - const params = { - layoutDoc: Doc.name, - dataDoc: Doc.name, - dataKey: "string", - facetHeader: "string" - }; - newFacet.container = dataDoc; - newFacet.data = ComputedField.MakeFunction("readFacetData(layoutDoc, dataDoc, dataKey, facetHeader)", params, capturedVariables); } } } + _canClick = false; + _facetWidthOnDown = 0; + @observable _facetWidth = 200; + onPointerDown = (e: React.PointerEvent) => { + this._canClick = true; + this._facetWidthOnDown = e.screenX; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + } + + + @action + onPointerMove = (e: PointerEvent) => { + this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); + Math.abs(e.movementX) > 6 && (this._canClick = false); + } + @action + onPointerUp = (e: PointerEvent) => { + if (Math.abs(e.screenX - this._facetWidthOnDown) < 6 && this._canClick) { + this._facetWidth = this._facetWidth < 15 ? 200 : 0; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } render() { const facetCollection = Cast(this.props.Document?.facetCollection, Doc, null); const flyout = ( -
+
{this._allFacets.map(facet => )}
); - return !facetCollection ? (null) :
-
-
e.stopPropagation()}> - -
- Facet Filters - -
-
+ return !facetCollection ? (null) : +
+
+
-
- +
+
e.stopPropagation()}> + +
+ Facet Filters + +
+
+
+
+ +
-
-
- -
-
; +
+ +
+
; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From cc6b30d2824ad5ff686fe728587d89d17aa3c8b2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Jan 2020 01:00:09 -0500 Subject: fixed pivot viewer to not have multiple columns with the same value. fixed imagebox to show fade views. cleaned up makeMetadataFieldTemplate --- .../CollectionFreeFormLayoutEngines.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/new_fields/Doc.ts | 76 +++++++++++----------- 3 files changed, 42 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 5924c3afb..5c6336248 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -8,7 +8,6 @@ import { emptyFunction } from "../../../../Utils"; import React = require("react"); import { ObservableMap, runInAction } from "mobx"; import { Id, ToString } from "../../../../new_fields/FieldSymbols"; -import { DateField } from "../../../../new_fields/DateField"; import { ObjectField } from "../../../../new_fields/ObjectField"; import { RefField } from "../../../../new_fields/RefField"; @@ -47,8 +46,9 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo const pivotAxisWidth = NumCast(pivotDoc.pivotWidth, 200); const pivotColumnGroups = new Map, Doc[]>(); + const pivotFieldKey = toLabel(pivotDoc.pivotField); for (const doc of childDocs) { - const val = doc[StrCast(pivotDoc.pivotField, "title")]; + const val = Field.toString(doc[pivotFieldKey] as Field); if (val) { !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, []); pivotColumnGroups.get(val)!.push(doc); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 634555012..4b2a1c1cb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -319,7 +319,7 @@ export class ImageBox extends DocAnnotatableComponent - {fadepath === srcpath ? (null) :
+ {fadepath === srcpath ? (null) :
{ - !templateDataDoc[metadataFieldName] && data instanceof ObjectField && (Doc.GetProto(templateDataDoc)[metadataFieldName] = ObjectField.MakeCopy(data)); - const layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldName}'}`); - const layoutDelegate = Doc.Layout(fieldTemplate); - layoutDelegate[metadataFieldName + "_ext"] = fieldLayoutExt; - layoutDelegate.layout = layout; - fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; - if (fieldTemplate.backgroundColor !== templateDataDoc.defaultBackgroundColor) fieldTemplate.defaultBackgroundColor = fieldTemplate.backgroundColor; - fieldTemplate.proto = templateDataDoc; - // }), 0); + // + // This function converts a generic field layout display into a field layout that displays a specific + // metadata field indicated by the title of the template field (not the default field that it was rendering) + // + export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Doc, suppressTitle: boolean = false): boolean { + + // find the metadata field key that this template field doc will display (indicated by its title) + const metadataFieldKey = StrCast(templateField.title).replace(/^-/, ""); + + // update the original template to mark it as a template + templateField.templateField = metadataFieldKey; + templateField.isTemplateField = true; + templateField.title = metadataFieldKey; + templateField.showTitle = suppressTitle ? undefined : "title"; + + // move any data that the template field had been rendering over to the template doc so that things will + // appear the same after the conversion to a template has completed. (otherwise, there would be no data for the template to render) + // note: this will not overwrite any field that already exists on the template doc at the field key + if (!templateDoc[metadataFieldKey] && templateField.data instanceof ObjectField) { + (Doc.GetProto(templateDoc)[metadataFieldKey] = ObjectField.MakeCopy(templateField.data)); + } + + // get the layout string that the template uses to specify its layout + const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField))); + + // change itto render the target metadata field instead of what it was rendering before and assign it to the template field layout document. + Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`); + + // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino) + Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc); + + if (templateField.backgroundColor !== templateDoc.defaultBackgroundColor) { + templateField.defaultBackgroundColor = templateField.backgroundColor; + } + + // finally, make the templateField be a delegate of the templateDoc so that it can find all the fields needed to render itselt + // (note that this is only useful to see the template doc itself which may not be necessary for many use cases) + templateField.proto = templateDoc; return true; } -- cgit v1.2.3-70-g09d2 From 17c4ca6f517ab786f3c6b269c95a9b5ae4b0ff9a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 22 Jan 2020 01:20:56 -0500 Subject: cleaned up imageBox alternate paths. --- src/client/views/nodes/ImageBox.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 4b2a1c1cb..5f407fd3a 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -223,7 +223,7 @@ export class ImageBox extends DocAnnotatableComponent 0.1)) { setTimeout(action(() => { - if (this.pathInfos.srcpath === imgPath && (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc)) { + if (this.paths[NumCast(this.props.Document.curPage)] === imgPath && (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc)) { this._resized = imgPath; this.Document.height = this.Document[WidthSym]() * aspect; this.Document.nativeHeight = realsize.height; @@ -278,24 +278,20 @@ export class ImageBox extends DocAnnotatableComponent 20) { const alts = DocListCast(extensionDoc.Alternates); - const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => [this.choosePath((doc.data as ImageField).url), doc[WidthSym]() / doc[HeightSym]()]); + const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); const field = this.dataDoc[this.props.fieldKey]; // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; - if (field instanceof ImageField) paths = [[this.choosePath(field.url), nativeWidth / nativeHeight]]; + if (field instanceof ImageField) paths = [this.choosePath(field.url)]; paths.push(...altpaths); - const srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][0] as string; - const srcaspect = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))][1] as number; - const fadepath = paths[Math.min(paths.length - 1, 1)][0] as string; - return { srcpath, srcaspect, fadepath }; + return paths; } @computed get content() { @@ -303,7 +299,8 @@ export class ImageBox extends DocAnnotatableComponent Date: Wed, 22 Jan 2020 12:08:20 -0500 Subject: fixed template/scripting problem. added collectionDoc parameter for onClick scripts. made buxton layout update hero image on click. --- src/client/documents/Documents.ts | 3 ++- src/client/util/DropConverter.ts | 4 ++-- .../collections/CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 13 +++++++------ .../CollectionMulticolumnView.tsx | 6 +++++- src/client/views/nodes/DocumentView.tsx | 19 ++++++++++--------- src/new_fields/Doc.ts | 5 +---- 7 files changed, 28 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d7292837c..634c223a7 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,6 +88,7 @@ export interface DocumentOptions { defaultBackgroundColor?: string; dropAction?: dropActionType; chromeStatus?: string; + disableLOD?: boolean; columnWidth?: number; fontSize?: number; curPage?: number; @@ -330,7 +331,7 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "annotationOn", "forceActive", "fitWidth"]; + const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "annotationOn", "forceActive", "fitWidth", "diableLOD"]; /** * This function receives the relevant document prototype and uses diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index ff0e19347..3e2cc6a2e 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -7,7 +7,7 @@ import { StrCast } from "../../new_fields/Types"; import { Docs } from "../documents/Documents"; import { ScriptField } from "../../new_fields/ScriptField"; -export function makeTemplate(doc: Doc, suppressTitle = false): boolean { +export function makeTemplate(doc: Doc): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateField ? doc.layout : doc; const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0]; const fieldKey = layout.replace("fieldKey={'", "").replace(/'}$/, ""); @@ -16,7 +16,7 @@ export function makeTemplate(doc: Doc, suppressTitle = false): boolean { docs.forEach(d => { if (!StrCast(d.title).startsWith("-")) { any = true; - Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc), suppressTitle); + Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); } else if (d.type === DocumentType.COL) { any = makeTemplate(d) || any; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 229a23294..f0dcadb5b 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -145,7 +145,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { - const { TextDocument, ImageDocument } = Docs.Create; + const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; const { Document } = this.props; const fallback = "http://www.cs.brown.edu/~bcz/face.gif"; + const imageRack = MulticolumnDocument([], { title: "data", height: 100 }); const wrapper = Docs.Create.StackingDocument([ ImageDocument(fallback, { title: "hero" }), + imageRack, ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) - ], { autoHeight: true, chromeStatus: "disabled" }); - wrapper.disableLOD = true; - wrapper.isTemplateDoc = makeTemplate(wrapper, true); + ], { autoHeight: true, chromeStatus: "disabled", disableLOD: true, title: "detailed layout stack" }); + wrapper.isTemplateDoc = makeTemplate(wrapper); + imageRack.onChildClick = ScriptField.MakeFunction(`containingCollection.resolvedDataDoc.hero = copyField(this.data)`, { containingCollection: Doc.name }); const cardLayout = ImageDocument(fallback); - const proto = Doc.GetProto(cardLayout); - proto.layout = ImageBox.LayoutString("hero"); + cardLayout.proto!.layout = ImageBox.LayoutString("hero"); cardLayout.showTitle = "title"; cardLayout.showTitleHover = "titlehover"; cardLayout.isTemplateField = true; // make this document act like a template field diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 70e56183c..b9b367106 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -4,7 +4,7 @@ import { documentSchema } from '../../../../new_fields/documentSchemas'; import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import * as React from "react"; import { Doc } from '../../../../new_fields/Doc'; -import { NumCast, StrCast, BoolCast } from '../../../../new_fields/Types'; +import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types'; import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; import { Utils } from '../../../../Utils'; import "./collectionMulticolumnView.scss"; @@ -186,6 +186,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu return Transform.Identity(); // type coersion, this case should never be hit } + @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + /** * @returns the resolved list of rendered child documents, displayed * at their resolved pixel widths, each separated by a resizer. @@ -206,9 +208,11 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu {...this.props} Document={layout} DataDocument={layout.resolvedDataDoc as Doc} + CollectionDoc={this.props.Document} PanelWidth={() => this.lookupPixels(layout)} PanelHeight={() => PanelHeight() - (BoolCast(Document.showWidthLabels) ? 20 : 0)} getTransform={() => this.lookupIndividualTransform(layout)} + onClick={this.onChildClickHandler} /> (Docu SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } else if (this.onClickHandler && this.onClickHandler.script) { - this.onClickHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + this.onClickHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, containingCollection: this.props.ContainingCollectionDoc }, console.log); } else if (this.Document.type === DocumentType.BUTTON) { ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY); } else if (this.props.Document.isButton === "Selector") { // this should be moved to an OnClick script @@ -552,7 +552,7 @@ export class DocumentView extends DocComponent(Docu const docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) }); - Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate), true); + Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined); } else { doc.layoutKey = "layoutCustom"; @@ -659,14 +659,14 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action - setCustomView = - (custom: boolean): void => { - if (this.props.ContainingCollectionView?.props.DataDoc || this.props.ContainingCollectionView?.props.Document.isTemplateDoc) { - Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.Document); - } else { - custom ? DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc) : DocumentView.makeNativeViewClicked(this.props.Document); + setCustomView = + (custom: boolean): void => { + if (this.props.ContainingCollectionView?.props.DataDoc || this.props.ContainingCollectionView?.props.Document.isTemplateDoc) { + Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.Document); + } else { + custom ? DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc) : DocumentView.makeNativeViewClicked(this.props.Document); + } } - } @undoBatch @action @@ -996,6 +996,7 @@ export class DocumentView extends DocComponent(Docu color: StrCast(this.Document.color), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, + boxShadow: this.props.Document.isTemplateField ? "black 0.2vw 0.2vw 0.8vw" : undefined, background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc.viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 674c7cc25..9620f753f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -614,7 +614,7 @@ export namespace Doc { templateField.templateField = metadataFieldKey; templateField.isTemplateField = true; templateField.title = metadataFieldKey; - templateField.showTitle = suppressTitle ? undefined : "title"; + // templateField.showTitle = suppressTitle ? undefined : "title"; // move any data that the template field had been rendering over to the template doc so that things will // appear the same after the conversion to a template has completed. (otherwise, there would be no data for the template to render) @@ -636,9 +636,6 @@ export namespace Doc { templateField.defaultBackgroundColor = templateField.backgroundColor; } - // finally, make the templateField be a delegate of the templateDoc so that it can find all the fields needed to render itselt - // (note that this is only useful to see the template doc itself which may not be necessary for many use cases) - templateField.proto = templateDoc; return true; } -- cgit v1.2.3-70-g09d2 From 6b1e9b68c3d409df6ea2a8909b99d5d8d75226c5 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Jan 2020 12:53:40 -0500 Subject: various fixes to pivot view related things. --- src/client/documents/Documents.ts | 3 +- .../views/collections/CollectionPivotView.tsx | 39 ++++++++++------------ .../views/collections/CollectionTreeView.tsx | 33 +++++++++--------- .../CollectionFreeFormLayoutEngines.tsx | 6 +--- .../collections/collectionFreeForm/MarqueeView.tsx | 1 + src/scraping/buxton/scraper.py | 6 +++- .../authentication/models/current_user_utils.ts | 2 +- 7 files changed, 45 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 634c223a7..e86ed52f3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,7 +88,7 @@ export interface DocumentOptions { defaultBackgroundColor?: string; dropAction?: dropActionType; chromeStatus?: string; - disableLOD?: boolean; + LODdisable?: boolean; columnWidth?: number; fontSize?: number; curPage?: number; @@ -107,6 +107,7 @@ export interface DocumentOptions { ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked onPointerDown?: ScriptField; onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 8e712e370..f23540529 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -26,36 +26,33 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { componentDidMount() { this.props.Document.freeformLayoutEngine = "pivot"; if (!this.props.Document.facetCollection) { - const facetCollection = Docs.Create.FreeformDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); + const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; - const scriptText = "setDocFilter(context.target, heading, this.title, checked)"; + const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; const script = CompileScript(scriptText, { - params: { this: Doc.name, heading: "boolean", checked: "boolean", context: Doc.name }, + params: { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name }, typecheck: false, editable: true, }); if (script.compiled) { facetCollection.onCheckedClick = new ScriptField(script); } - this._narrativeDisposer = reaction(() => this.props.Document.childDetailed, - (childDetailed) => - DocCastAsync(childDetailed).then(childDetailed => { - if (childDetailed instanceof Doc) { - const captured: { [name: string]: Field } = {}; - captured.childDetailed = new PrefetchProxy(childDetailed); - const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; - const openDocScript = CompileScript(openDocText, { - params: { this: Doc.name, heading: "boolean", context: Doc.name }, - typecheck: false, - editable: true, - capturedVariables: captured - }); - if (openDocScript.compiled) { - this.props.Document.onChildClick = new ScriptField(openDocScript); - } + const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; + this._narrativeDisposer = reaction(() => DocCastAsync(this.props.Document.childDetailed), + (childDetailedPromise) => childDetailedPromise.then(childDetailed => { + if (childDetailed) { + const openDocScript = CompileScript(openDocText, { + params: { this: Doc.name, heading: "boolean", containingTreeView: Doc.name }, + capturedVariables: { childDetailed: new PrefetchProxy(childDetailed) }, + typecheck: false, + editable: true, + }); + if (openDocScript.compiled) { + this.props.Document.onChildClick = new ScriptField(openDocScript); } - }), { fireImmediately: true }); + } + }), { fireImmediately: true }); this.props.Document.facetCollection = facetCollection; this.props.Document.fitToBox = true; } @@ -84,7 +81,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { if (found !== -1) { (facetCollection.data as List).splice(found, 1); } else { - const newFacet = Docs.Create.FreeformDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2433cc40e..bef587786 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,6 +32,7 @@ import { Templates } from '../Templates'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); +import { CollectionViewType } from './CollectionView'; export interface TreeViewProps { @@ -377,7 +378,7 @@ class TreeView extends React.Component { this: this.props.document.isTemplateField && this.props.dataDoc ? this.props.dataDoc : this.props.document, heading: this.props.containingCollection.title, checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check", - context: this.props.treeViewId, + containingTreeView: this.props.treeViewId, }, console.log); } else { this.treeViewOpen = !this.treeViewOpen; @@ -635,32 +636,32 @@ export class CollectionTreeView extends CollectionSubView(Document) { description: "Buxton Layout", icon: "eye", event: () => { const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; const { Document } = this.props; - const fallback = "http://www.cs.brown.edu/~bcz/face.gif"; - const imageRack = MulticolumnDocument([], { title: "data", height: 100 }); - const wrapper = Docs.Create.StackingDocument([ - ImageDocument(fallback, { title: "hero" }), - imageRack, - ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) - ], { autoHeight: true, chromeStatus: "disabled", disableLOD: true, title: "detailed layout stack" }); - wrapper.isTemplateDoc = makeTemplate(wrapper); - imageRack.onChildClick = ScriptField.MakeFunction(`containingCollection.resolvedDataDoc.hero = copyField(this.data)`, { containingCollection: Doc.name }); - - const cardLayout = ImageDocument(fallback); + const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; + + const detailedLayout = Docs.Create.StackingDocument([ + ImageDocument(fallbackImg, { title: "activeHero" }), + MulticolumnDocument([], { title: "data", height: 100, onChildClick: ScriptField.MakeFunction(`containingCollection.resolvedDataDoc.activeHero = copyField(this.data)`, { containingCollection: Doc.name }) }), + TextDocument({ title: "short_description", autoHeight: true }), + ...["year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key, height: 30 })) + ], { autoHeight: true, chromeStatus: "disabled", title: "detailed layout stack" }); + detailedLayout.isTemplateDoc = makeTemplate(detailedLayout); + + const cardLayout = ImageDocument(fallbackImg, { isTemplateDoc: true, isTemplateField: true }); // this acts like a template doc and a template field ... a little weird, but seems to work? cardLayout.proto!.layout = ImageBox.LayoutString("hero"); cardLayout.showTitle = "title"; cardLayout.showTitleHover = "titlehover"; - cardLayout.isTemplateField = true; // make this document act like a template field - cardLayout.isTemplateDoc = true; // but it's also a template doc itself... a little weird Document.childLayout = cardLayout; - Document.childDetailed = wrapper; + Document.childDetailed = detailedLayout; + Document.viewType = CollectionViewType.Pivot; + Document.pivotField = "company"; } }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document, - "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", context: Doc.name }) + "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", treeViewContainer: Doc.name }) }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 5c6336248..32097816d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -59,11 +59,7 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo let numCols = NumCast(pivotDoc.pivotNumColumns, Math.ceil(Math.sqrt(minSize))); const docMap = new Map(); const groupNames: PivotData[] = []; - if (panelDim[0] < 2500) numCols = Math.min(5, numCols); - if (panelDim[0] < 2000) numCols = Math.min(4, numCols); - if (panelDim[0] < 1400) numCols = Math.min(3, numCols); - if (panelDim[0] < 1000) numCols = Math.min(2, numCols); - if (panelDim[0] < 600) numCols = 1; + numCols = Math.min(panelDim[0] / pivotAxisWidth, numCols) const expander = 1.05; const gap = .15; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 523edb918..584d18372 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -331,6 +331,7 @@ export class MarqueeView extends React.Component Date: Wed, 22 Jan 2020 15:41:04 -0500 Subject: basic cleanup for warnings --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- src/client/util/ProseMirrorEditorView.tsx | 74 ---------------------- src/client/util/RichTextMenu.tsx | 68 ++++++++++---------- src/client/util/RichTextSchema.tsx | 2 +- src/client/views/EditableView.tsx | 2 +- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionPivotView.tsx | 11 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/CollectionViewChromes.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../collectionFreeForm/MarqueeOptionsMenu.tsx | 3 + src/client/views/linking/LinkMenu.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 1 - src/new_fields/Doc.ts | 10 +-- src/new_fields/documentSchemas.ts | 1 + src/server/Websocket/Websocket.ts | 2 - 17 files changed, 59 insertions(+), 132 deletions(-) delete mode 100644 src/client/util/ProseMirrorEditorView.tsx (limited to 'src') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index fd3d9e2f1..d654e2530 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -46,7 +46,7 @@ export class YoutubeBox extends React.Component { * When component mounts, last search's results are laoded in based on the back up stored * in the document of the props. */ - async componentWillMount() { + async componentDidMount() { //DocServer.getYoutubeChannels(); const castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); const awaitedBackUp = await castedSearchBackUp; diff --git a/src/client/util/ProseMirrorEditorView.tsx b/src/client/util/ProseMirrorEditorView.tsx deleted file mode 100644 index b42adfbb4..000000000 --- a/src/client/util/ProseMirrorEditorView.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import { EditorView } from "prosemirror-view"; -import { EditorState } from "prosemirror-state"; - -export interface ProseMirrorEditorViewProps { - /* EditorState instance to use. */ - editorState: EditorState; - /* Called when EditorView produces new EditorState. */ - onEditorState: (editorState: EditorState) => any; -} - -/** - * This wraps ProseMirror's EditorView into React component. - * This code was found on https://discuss.prosemirror.net/t/using-with-react/904 - */ -export class ProseMirrorEditorView extends React.Component { - - private _editorView?: EditorView; - - _createEditorView = (element: HTMLDivElement | null) => { - if (element !== null) { - this._editorView = new EditorView(element, { - state: this.props.editorState, - dispatchTransaction: this.dispatchTransaction, - }); - } - } - - dispatchTransaction = (tx: any) => { - // In case EditorView makes any modification to a state we funnel those - // modifications up to the parent and apply to the EditorView itself. - const editorState = this.props.editorState.apply(tx); - if (this._editorView) { - this._editorView.updateState(editorState); - } - this.props.onEditorState(editorState); - } - - focus() { - if (this._editorView) { - this._editorView.focus(); - } - } - - componentWillReceiveProps(nextProps: { editorState: EditorState; }) { - // In case we receive new EditorState through props — we apply it to the - // EditorView instance. - if (this._editorView) { - if (nextProps.editorState !== this.props.editorState) { - this._editorView.updateState(nextProps.editorState); - } - } - } - - componentWillUnmount() { - if (this._editorView) { - this._editorView.destroy(); - } - } - - shouldComponentUpdate() { - // Note that EditorView manages its DOM itself so we'd ratrher don't mess - // with it. - return false; - } - - render() { - // Render just an empty div which is then used as a container for an - // EditorView instance. - return ( -
- ); - } -} \ No newline at end of file diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx index f070589ae..eb5c90654 100644 --- a/src/client/util/RichTextMenu.tsx +++ b/src/client/util/RichTextMenu.tsx @@ -73,7 +73,7 @@ export default class RichTextMenu extends AntimodeMenu { this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "8pt", command: this.changeFontSize }, + { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize }, @@ -312,22 +312,22 @@ export default class RichTextMenu extends AntimodeMenu { } return ( - ); } - createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[]): JSX.Element { + createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element { const items = options.map(({ title, label, hidden, style }) => { if (hidden) { return label === activeOption ? - : - ; + : + ; } return label === activeOption ? - : - ; + : + ; }); const self = this; @@ -340,19 +340,19 @@ export default class RichTextMenu extends AntimodeMenu { } }); } - return ; + return ; } - createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[]): JSX.Element { + createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element { const items = options.map(({ title, label, hidden, style }) => { if (hidden) { return label === activeOption ? - : - ; + : + ; } return label === activeOption ? - : - ; + : + ; }); const self = this; @@ -363,7 +363,7 @@ export default class RichTextMenu extends AntimodeMenu { } }); } - return ; + return ; } changeFontSize = (mark: Mark, view: EditorView) => { @@ -472,7 +472,7 @@ export default class RichTextMenu extends AntimodeMenu {
; return ( - + ); } @@ -535,21 +535,21 @@ export default class RichTextMenu extends AntimodeMenu { ; const dropdownContent = -
+

Change font color:

{this.fontColors.map(color => { if (color) { return this.activeFontColor === color ? - : - ; + : + ; } })}
; return ( - + ); } @@ -582,7 +582,7 @@ export default class RichTextMenu extends AntimodeMenu { } const button = - ; @@ -594,15 +594,15 @@ export default class RichTextMenu extends AntimodeMenu { {this.highlightColors.map(color => { if (color) { return this.activeHighlightColor === color ? - : - ; + : + ; } })}
; return ( - + ); } @@ -635,7 +635,7 @@ export default class RichTextMenu extends AntimodeMenu {
; return ( - + ); } @@ -773,7 +773,7 @@ export default class RichTextMenu extends AntimodeMenu { render() { - const row1 =
{[ + const row1 =
{[ this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), @@ -787,14 +787,14 @@ export default class RichTextMenu extends AntimodeMenu { this.createButton("indent", "Summarize", undefined, this.insertSummarizer), ]}
; - const row2 =
-
- {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions), - this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions), - this.createNodesDropdown(this.activeListType, this.listTypeOptions)]} + const row2 =
+
+ {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"), + this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"), + this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
-
- {this.getDragger()} @@ -864,12 +864,12 @@ class ButtonDropdown extends React.Component { : <> {this.props.button} - } - {this.showDropdown ? this.props.dropdownContent : <>} + {this.showDropdown ? this.props.dropdownContent : (null)}
); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 1f70cafc4..5751c8c7e 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -306,7 +306,7 @@ export const marks: { [index: string]: MarkSpec } = { } }], toDOM(node: any) { - return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }]; + return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0]; } }, diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 0d677b8ce..394caba72 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -66,7 +66,7 @@ export class EditableView extends React.Component { } @action - componentWillReceiveProps(nextProps: EditableProps) { + componentDidUpdate(nextProps: EditableProps) { // this is done because when autosuggest is turned on, the suggestions are passed in as a prop, // so when the suggestions are passed in, and no editing prop is passed in, it used to set it // to false. this will no longer do so -syip diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0958c434a..79e0cfea5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -64,7 +64,7 @@ export class MainView extends React.Component { public isPointerDown = false; - componentWillMount() { + componentDidMount() { const tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index f23540529..aa68cc18f 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -28,6 +28,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { if (!this.props.Document.facetCollection) { const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; + facetCollection.dontCopyOnAlias = true; const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; const script = CompileScript(scriptText, { @@ -122,8 +123,8 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { const facetCollection = Cast(this.props.Document?.facetCollection, Doc, null); const flyout = (
- {this._allFacets.map(facet =>