From 17cab0166753aee765585e1eb700fd85583e7cbb Mon Sep 17 00:00:00 2001 From: monikahedman Date: Mon, 5 Aug 2019 19:11:54 -0400 Subject: clickable text box appears --- src/client/views/nodes/FormattedTextBox.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e076efe18..a2e610fc2 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -8,7 +8,7 @@ import { keymap } from "prosemirror-keymap"; import { Node as ProsNode } from "prosemirror-model"; import { EditorState, Plugin, Transaction, Selection } from "prosemirror-state"; import { NodeType, Slice, Node, Fragment } from 'prosemirror-model'; -import { EditorView } from "prosemirror-view"; +import { EditorView, NodeView } from "prosemirror-view"; import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; @@ -21,7 +21,7 @@ import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from "../../util/DragManager"; import buildKeymap from "../../util/ProsemirrorExampleTransfer"; import { inpRules } from "../../util/RichTextRules"; -import { ImageResizeView, schema, SummarizedView } from "../../util/RichTextSchema"; +import { ImageResizeView, schema, SummarizedView, CheckboxView } from "../../util/RichTextSchema"; import { SelectionManager } from "../../util/SelectionManager"; import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu"; import { TooltipTextMenu } from "../../util/TooltipTextMenu"; @@ -38,6 +38,7 @@ import { For } from 'babel-types'; import { DateField } from '../../../new_fields/DateField'; import { Utils } from '../../../Utils'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; +import { CheckBox } from '../search/CheckBox'; library.add(faEdit); library.add(faSmile, faTextHeight); @@ -469,7 +470,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe dispatchTransaction: this.dispatchTransaction, nodeViews: { image(node, view, getPos) { return new ImageResizeView(node, view, getPos); }, - star(node, view, getPos) { return new SummarizedView(node, view, getPos); } + star(node, view, getPos) { return new SummarizedView(node, view, getPos); }, + checkbox(node, view, getPos) { return new CheckboxView(node, view, getPos) as NodeView; } }, clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, -- cgit v1.2.3-70-g09d2 From 18d02f8eb8cef6e0ae3bdb95a5d22958f0fda91e Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 8 Aug 2019 16:19:09 -0400 Subject: dragBoxes --- src/client/documents/Documents.ts | 11 +++ src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DragBox.scss | 13 ++++ src/client/views/nodes/DragBox.tsx | 80 ++++++++++++++++++++++ 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/client/views/nodes/DragBox.scss create mode 100644 src/client/views/nodes/DragBox.tsx (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7dd853156..9c8b6c129 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -17,6 +17,7 @@ export enum DocumentType { TEMPLATE = "template", EXTENSION = "extension", YOUTUBE = "youtube", + DRAGBOX = "dragbox", } import { HistogramField } from "../northstar/dash-fields/HistogramField"; @@ -60,6 +61,7 @@ import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting, CompileScript } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; +import { DragBox } from "../views/nodes/DragBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; import { ComputedField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; @@ -177,6 +179,10 @@ export namespace Docs { }], [DocumentType.BUTTON, { layout: { view: ButtonBox }, + }], + [DocumentType.DRAGBOX, { + layout: { view: DragBox }, + options: { width: 40, height: 40 }, }] ]); @@ -442,6 +448,11 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}) }); } + + export function DragboxDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.DRAGBOX), undefined, { ...(options || {}) }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ddb023aca..a4db753ab 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -446,7 +446,7 @@ export class MainView extends React.Component { //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); - let addTreeNode = action(() => CurrentUserUtils.UserDocument); + let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" })); let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })); let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); @@ -458,7 +458,8 @@ export class MainView extends React.Component { [React.createRef(), "bolt", "Add Button", addButtonDocument], // [React.createRef(), "clone", "Add Docking Frame", addDockingNode], [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], - [React.createRef(), "play", "Add Youtube Searcher", addYoutubeSearcher] + [React.createRef(), "play", "Add Youtube Searcher", addYoutubeSearcher], + [React.createRef(), "bolt", "Add Document Dragger", addDragboxNode] ]; if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4a751c84c..112d64e3d 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -120,7 +120,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { DataDocument={dataDoc} showOverlays={this.overlays} renderDepth={this.props.renderDepth} - fitToBox={true} + fitToBox={this.props.fitToBox} width={width} height={height} getTransform={finalDxf} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 396233551..6b7b239f0 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,6 +11,7 @@ import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; +import { DragBox } from "./DragBox"; import { ButtonBox } from "./ButtonBox"; import { IconBox } from "./IconBox"; import { KeyValueBox } from "./KeyValueBox"; @@ -99,7 +100,7 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return ; +const DragDocument = makeInterface(DragSchema); + +@observer +export class DragBox extends DocComponent(DragDocument) { + public static LayoutString() { return FieldView.LayoutString(DragBox); } + _mainCont = React.createRef(); + onDragStart = (e: React.PointerEvent) => { + if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected()) { + const onDragStart = this.Document.onDragStart; + e.stopPropagation(); + e.preventDefault(); + let res = onDragStart ? onDragStart.script.run({ this: this.props.Document }) : undefined; + let doc = res !== undefined && res.success ? + res.result as Doc : + Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" }); + doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY); + } + } + + onContextMenu = () => { + ContextMenu.Instance.addItem({ + description: "Edit OnClick script", icon: "edit", event: () => { + let overlayDisposer: () => void = emptyFunction; + const script = this.Document.onDragStart; + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false, + editable: true, + transformer: DocumentIconContainer.getTransformer() + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.Document.onClick = new ScriptField(script); + overlayDisposer(); + }} showDocumentIcons />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnDragStart` }); + } + }); + } + + render() { + return (
+ +
); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f75f6ed34625c4e5196a94d5b214e9eaf32a5b98 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 8 Aug 2019 17:50:58 -0400 Subject: fixed dragbox dragging. --- src/client/views/nodes/DragBox.tsx | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx index a1ad390f8..1f2c88086 100644 --- a/src/client/views/nodes/DragBox.tsx +++ b/src/client/views/nodes/DragBox.tsx @@ -27,13 +27,27 @@ const DragSchema = createSchema({ type DragDocument = makeInterface<[typeof DragSchema]>; const DragDocument = makeInterface(DragSchema); - @observer export class DragBox extends DocComponent(DragDocument) { + _downX: number = 0; + _downY: number = 0; public static LayoutString() { return FieldView.LayoutString(DragBox); } _mainCont = React.createRef(); onDragStart = (e: React.PointerEvent) => { - if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected()) { + if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected() && e.button === 0) { + document.removeEventListener("pointermove", this.onDragMove); + document.addEventListener("pointermove", this.onDragMove); + document.removeEventListener("pointerup", this.onDragUp); + document.addEventListener("pointerup", this.onDragUp); + e.stopPropagation(); + e.preventDefault(); + } + } + + onDragMove = (e: MouseEvent) => { + if (!e.cancelBubble && !this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) { + document.removeEventListener("pointermove", this.onDragMove); + document.removeEventListener("pointerup", this.onDragUp); const onDragStart = this.Document.onDragStart; e.stopPropagation(); e.preventDefault(); @@ -43,6 +57,13 @@ export class DragBox extends DocComponent(DragDocu Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" }); doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc], [undefined]), e.clientX, e.clientY); } + e.stopPropagation(); + e.preventDefault(); + } + + onDragUp = (e: MouseEvent) => { + document.removeEventListener("pointermove", this.onDragMove); + document.removeEventListener("pointerup", this.onDragUp); } onContextMenu = () => { -- cgit v1.2.3-70-g09d2 From 7aa0d076800c61b7545e2aa12f714eb7ea046eae Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 9 Aug 2019 10:31:40 -0400 Subject: changed applied templates to have a prototype. --- src/client/util/TooltipTextMenu.tsx | 6 ------ src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 16 ++-------------- src/client/views/nodes/ImageBox.tsx | 2 +- src/new_fields/Doc.ts | 9 +++++++-- 5 files changed, 11 insertions(+), 24 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index d33a52d7f..8dc5cdb2a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -190,12 +190,6 @@ export class TooltipTextMenu { this.updateListItemDropdown(":", this.listTypeBtnDom); this.update(view, undefined); - - //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); - - // add tooltip to outerdiv to circumvent scaling problem - const outer_div = this.editorProps.outer_div; - outer_div && outer_div(this.wrapper); } //label of dropdown will change to given label diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 72eb956e3..fccbeb16c 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -144,7 +144,7 @@ export class MainOverlayTextBox extends React.Component DataDoc={FormattedTextBox.InputBoxOverlay.props.DataDoc} isSelected={returnTrue} select={emptyFunction} renderDepth={0} selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue} ContentScaling={returnOne} - ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} /> + ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} /> diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 44b5d2c21..10f50c5a4 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -30,11 +30,9 @@ import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; -import { Templates } from '../Templates'; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); -import { For } from 'babel-types'; import { DateField } from '../../../new_fields/DateField'; import { Utils } from '../../../Utils'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; @@ -50,7 +48,6 @@ export interface FormattedTextBoxProps { hideOnLeave?: boolean; height?: string; color?: string; - outer_div?: (domminus: HTMLElement) => void; firstinstance?: boolean; } @@ -68,7 +65,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } public static Instance: FormattedTextBox; private _ref: React.RefObject; - private _outerdiv?: (dominus: HTMLElement) => void; private _proseRef?: HTMLDivElement; private _editorView: Opt; private static _toolTipTextMenu: TooltipTextMenu | undefined = undefined; @@ -124,13 +120,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe constructor(props: FieldViewProps) { super(props); - //if (this.props.firstinstance) { FormattedTextBox.Instance = this; - //} - if (this.props.outer_div) { - this._outerdiv = this.props.outer_div; - } - this._ref = React.createRef(); if (this.props.isOverlay) { DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); @@ -141,7 +131,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); } - @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } + @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? Doc.GetDataDoc(this.props.DataDoc) : Doc.GetProto(this.props.Document); } paste = (e: ClipboardEvent) => { @@ -206,9 +196,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe tokens.forEach((word) => { if (terms.includes(word) && this._editorView) { this._editorView.dispatch(this._editorView.state.tr.addMark(start, start + word.length, mark).removeStoredMark(mark)); - // else { - // this._editorView.state.tr.addMark(start, start + word.length, mark).removeStoredMark(mark); - // } } start += word.length + 1; }); @@ -484,6 +471,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); this._textReactionDisposer && this._textReactionDisposer(); + this._searchReactionDisposer && this._searchReactionDisposer(); } onPointerDown = (e: React.PointerEvent): void => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index ca0f637eb..1ebeb2d66 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -65,7 +65,7 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; - @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : this.props.Document; } + @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? Doc.GetDataDoc(this.props.DataDoc) : Doc.GetProto(this.props.Document); } protected createDropTarget = (ele: HTMLDivElement) => { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index ba01cfd9c..1df36d719 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -297,6 +297,10 @@ export namespace Doc { export function GetProto(doc: Doc) { return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc); } + export function GetDataDoc(doc: Doc): Doc { + let proto = Doc.GetProto(doc); + return proto === doc ? proto : Doc.GetDataDoc(proto); + } export function allKeys(doc: Doc): string[] { const results: Set = new Set; @@ -371,7 +375,7 @@ export namespace Doc { docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. docExtensionForField.type = DocumentType.EXTENSION; let proto: Doc | undefined = doc; - while (proto && !Doc.IsPrototype(proto)) { + while (proto && !Doc.IsPrototype(proto) && proto.proto) { proto = proto.proto; } (proto ? proto : doc)[fieldKey + "_ext"] = new PrefetchProxy(docExtensionForField); @@ -490,7 +494,8 @@ export namespace Doc { let _applyCount: number = 0; export function ApplyTemplate(templateDoc: Doc) { if (!templateDoc) return undefined; - let otherdoc = new Doc(); + let datadoc = new Doc(); + let otherdoc = Doc.MakeDelegate(datadoc); otherdoc.width = templateDoc[WidthSym](); otherdoc.height = templateDoc[HeightSym](); otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")"; -- cgit v1.2.3-70-g09d2 From ae62e415d18b83fb1ef6b4a8f438b1d7e8d3157a Mon Sep 17 00:00:00 2001 From: HJF Bulterman Date: Fri, 9 Aug 2019 11:31:57 -0400 Subject: initial commit - individual presentation document view showing but with errors --- src/client/documents/Documents.ts | 13 +- src/client/views/MainView.tsx | 7 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FieldView.tsx | 5 + src/client/views/nodes/PresBox.tsx | 880 ++++++++++++++++++++++++ src/client/views/pdf/PDFMenu.tsx | 2 +- src/new_fields/PresField.ts | 6 + 8 files changed, 912 insertions(+), 6 deletions(-) create mode 100644 src/client/views/nodes/PresBox.tsx create mode 100644 src/new_fields/PresField.ts (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ee1b9fd0d..492aebf4a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -40,6 +40,9 @@ import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; +import { PresBox } from "../views/nodes/PresBox"; +//import { PresBox } from "../views/nodes/PresBox"; +//import { PresField } from "../../new_fields/PresField"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -60,7 +63,8 @@ export enum DocumentType { LINKDOC = "linkdoc", BUTTON = "button", TEMPLATE = "template", - EXTENSION = "extension" + EXTENSION = "extension", + PRES = "presentation" } export interface DocumentOptions { @@ -168,6 +172,10 @@ export namespace Docs { }], [DocumentType.BUTTON, { layout: { view: ButtonBox }, + }], + [DocumentType.PRES, { + layout: { view: PresBox }, + options: {} }] ]); @@ -335,6 +343,9 @@ export namespace Docs { .catch((err: any) => console.log(err)); return inst; } + export function PresDocument(initial: List = new List(), options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options); + } export function VideoDocument(url: string, options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f5a6715e5..14cfc6792 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -58,7 +58,8 @@ export class MainView extends React.Component { private set mainContainer(doc: Opt) { if (doc) { if (!("presentationView" in doc)) { - doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })]); + let initialDoc = Docs.Create.TreeDocument([], { title: "Presentation" }); + doc.presentationView = Docs.Create.PresDocument(new List([initialDoc])); } CurrentUserUtils.UserDocument.activeWorkspace = doc; } @@ -384,11 +385,13 @@ export class MainView extends React.Component { let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })); let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); + let addPresentationNode = action(() => Docs.Create.PresDocument(new List([Docs.Create.TreeDocument([], { title: "Presentation" })]))); + let btns: [React.RefObject, IconName, string, () => Doc][] = [ [React.createRef(), "object-group", "Add Collection", addColNode], [React.createRef(), "bolt", "Add Button", addButtonDocument], // [React.createRef(), "clone", "Add Docking Frame", addDockingNode], - [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], + [React.createRef(), "cloud-upload-alt", "Import Directory", addPresentationNode], //remove at some point in favor of addImportCollectionNode ]; if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 91d4fb524..f74336cdd 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -12,6 +12,7 @@ import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { ButtonBox } from "./ButtonBox"; +import { PresBox } from "./PresBox"; import { IconBox } from "./IconBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; @@ -98,7 +99,7 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return (Docu subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); - cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); + cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //change this to cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); let makes: ContextMenuProps[] = []; makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ffaee8042..b1030d19d 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -18,6 +18,7 @@ import { ImageBox } from "./ImageBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; import { Id } from "../../../new_fields/FieldSymbols"; +import { PresBox } from "./PresBox"; // // these properties get assigned through the render() method of the DocumentView when it creates this node. @@ -54,6 +55,7 @@ export interface FieldViewProps { export class FieldView extends React.Component { public static LayoutString(fieldType: { name: string }, fieldStr: string = "data", fieldExt: string = "") { return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} fieldExt={"${fieldExt}"} />`; + //"" } @computed @@ -75,6 +77,9 @@ export class FieldView extends React.Component { else if (field instanceof ImageField) { return ; } + // else if (field instaceof PresBox) { + // return ; + // } else if (field instanceof IconField) { return ; } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx new file mode 100644 index 000000000..2feb32693 --- /dev/null +++ b/src/client/views/nodes/PresBox.tsx @@ -0,0 +1,880 @@ +import React = require("react"); +import { FieldViewProps, FieldView } from './FieldView'; +import { observer } from "mobx-react"; +import { observable, action, runInAction, reaction, autorun, computed } from "mobx"; +import "../presentationview/PresentationView.scss"; +import { DocumentManager } from "../../util/DocumentManager"; +import { Utils } from "../../../Utils"; +import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../../new_fields/Types"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { List } from "../../../new_fields/List"; +import PresentationElement, { buttonIndex } from "../presentationview/PresentationElement"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faArrowRight, faArrowLeft, faPlay, faStop, faPlus, faTimes, faMinus, faEdit } from '@fortawesome/free-solid-svg-icons'; +import { Docs } from "../../documents/Documents"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; +import PresentationViewList from "../presentationview/PresentationList"; + +library.add(faArrowLeft); +library.add(faArrowRight); +library.add(faPlay); +library.add(faStop); +library.add(faPlus); +library.add(faTimes); +library.add(faMinus); +library.add(faEdit); + + +export interface PresViewProps { + Documents: List; +} + +const expandedWidth = 400; + +@observer +export class PresBox extends React.Component { //FieldViewProps? + + @computed + private get presentationDocs() { + let source = Doc.GetProto(this.props.Document); + return DocListCast(source.data); + } + + public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } + + //public static Instance: PresentationView; + public static Instance: PresBox; + + //Mapping from presentation ids to a list of doc that represent a group + @observable groupMappings: Map = new Map(); + //mapping from docs to their rendered component + @observable presElementsMappings: Map = new Map(); + //variable that holds all the docs in the presentation + @observable childrenDocs: Doc[] = []; + //variable to hold if presentation is started + @observable presStatus: boolean = false; + //back-up so that presentation stays the way it's when refreshed + @observable presGroupBackUp: Doc = new Doc(); + @observable presButtonBackUp: Doc = new Doc(); + + //Keeping track of the doc for the current presentation + @observable curPresentation: Doc = new Doc(); + //Mapping of guids to presentations. + @observable presentationsMapping: Map = new Map(); + //Mapping of presentations to guid, so that select option values can be given. + @observable presentationsKeyMapping: Map = new Map(); + //Variable to keep track of guid of the current presentation + @observable currentSelectedPresValue: string | undefined; + //A flag to keep track if title input is open, which is used in rendering. + @observable PresTitleInputOpen: boolean = false; + //Variable that holds reference to title input, so that new presentations get titles assigned. + @observable titleInputElement: HTMLInputElement | undefined; + @observable PresTitleChangeOpen: boolean = false; + + @observable opacity = 1; + @observable persistOpacity = true; + @observable labelOpacity = 0; + + //initilize class variables + constructor(props: FieldViewProps) { //FieldViewProps? + super(props); + //PresentationView.Instance = this; + PresBox.Instance = this; + } + + @action + toggle = (forcedValue: boolean | undefined) => { + if (forcedValue !== undefined) { + this.curPresentation.width = forcedValue ? expandedWidth : 0; + } else { + this.curPresentation.width = this.curPresentation.width === expandedWidth ? 0 : expandedWidth; + } + } + + //The first lifecycle function that gets called to set up the current presentation. + async componentWillMount() { + this.presentationDocs.forEach(async (doc, index: number) => { + + //For each presentation received from mainContainer, a mapping is created. + let curDoc: Doc = await doc; + let newGuid = Utils.GenerateGuid(); + this.presentationsKeyMapping.set(curDoc, newGuid); + this.presentationsMapping.set(newGuid, curDoc); + + //The Presentation at first index gets set as default start presentation + if (index === 0) { + runInAction(() => this.currentSelectedPresValue = newGuid); + runInAction(() => this.curPresentation = curDoc); + } + }); + } + + //Second lifecycle function that gets called when component mounts. It makes sure to + //get the back-up information from previous session for the current presentation. + async componentDidMount() { + let docAtZero = await this.presentationDocs[0]; + runInAction(() => this.curPresentation = docAtZero); + + this.setPresentationBackUps(); + + } + + + /** + * The function that retrieves the backUps for the current Presentation if present, + * otherwise initializes. + */ + setPresentationBackUps = async () => { + //getting both backUp documents + + let castedGroupBackUp = Cast(this.curPresentation.presGroupBackUp, Doc); + let castedButtonBackUp = Cast(this.curPresentation.presButtonBackUp, Doc); + //if instantiated before + if (castedGroupBackUp instanceof Promise) { + castedGroupBackUp.then(doc => { + let toAssign = doc ? doc : new Doc(); + this.curPresentation.presGroupBackUp = toAssign; + runInAction(() => this.presGroupBackUp = toAssign); + if (doc) { + if (toAssign[Id] === doc[Id]) { + this.retrieveGroupMappings(); + } + } + }); + + //if never instantiated a store doc yet + } else if (castedGroupBackUp instanceof Doc) { + let castedDoc: Doc = await castedGroupBackUp; + runInAction(() => this.presGroupBackUp = castedDoc); + this.retrieveGroupMappings(); + } else { + runInAction(() => { + let toAssign = new Doc(); + this.presGroupBackUp = toAssign; + this.curPresentation.presGroupBackUp = toAssign; + + }); + + } + //if instantiated before + if (castedButtonBackUp instanceof Promise) { + castedButtonBackUp.then(doc => { + let toAssign = doc ? doc : new Doc(); + this.curPresentation.presButtonBackUp = toAssign; + runInAction(() => this.presButtonBackUp = toAssign); + }); + + //if never instantiated a store doc yet + } else if (castedButtonBackUp instanceof Doc) { + let castedDoc: Doc = await castedButtonBackUp; + runInAction(() => this.presButtonBackUp = castedDoc); + + } else { + runInAction(() => { + let toAssign = new Doc(); + this.presButtonBackUp = toAssign; + this.curPresentation.presButtonBackUp = toAssign; + }); + + } + + + //storing the presentation status,ie. whether it was stopped or playing + let presStatusBackUp = BoolCast(this.curPresentation.presStatus); + runInAction(() => this.presStatus = presStatusBackUp); + } + + /** + * This is the function that is called to retrieve the groups that have been stored and + * push them to the groupMappings. + */ + retrieveGroupMappings = async () => { + let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); + if (castedGroupDocs !== undefined) { + castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { + let castedGrouping = await DocListCastAsync(groupDoc.grouping); + let castedKey = StrCast(groupDoc.presentIdStore, null); + if (castedGrouping) { + castedGrouping.forEach((doc: Doc) => { + doc.presentId = castedKey; + }); + } + if (castedGrouping !== undefined && castedKey !== undefined) { + this.groupMappings.set(castedKey, castedGrouping); + } + }); + } + } + + //observable means render is re-called every time variable is changed + @observable + collapsed: boolean = false; + closePresentation = action(() => this.curPresentation.width = 0); + next = async () => { + const current = NumCast(this.curPresentation.selectedDoc); + //asking to get document at current index + let docAtCurrentNext = await this.getDocAtIndex(current + 1); + if (docAtCurrentNext === undefined) { + return; + } + //asking for it's presentation id + let curNextPresId = StrCast(docAtCurrentNext.presentId); + let nextSelected = current + 1; + + //if curDoc is in a group, selection slides until last one, if not it's next one + if (this.groupMappings.has(curNextPresId)) { + let currentsArray = this.groupMappings.get(StrCast(docAtCurrentNext.presentId))!; + nextSelected = current + currentsArray.length - currentsArray.indexOf(docAtCurrentNext); + + //end of grup so go beyond + if (nextSelected === current) nextSelected = current + 1; + } + + this.gotoDocument(nextSelected, current); + + } + back = async () => { + const current = NumCast(this.curPresentation.selectedDoc); + //requesting for the doc at current index + let docAtCurrent = await this.getDocAtIndex(current); + if (docAtCurrent === undefined) { + return; + } + + //asking for its presentation id. + let curPresId = StrCast(docAtCurrent.presentId); + let prevSelected = current - 1; + let zoomOut: boolean = false; + + //checking if this presentation id is mapped to a group, if so chosing the first element in group + if (this.groupMappings.has(curPresId)) { + let currentsArray = this.groupMappings.get(StrCast(docAtCurrent.presentId))!; + prevSelected = current - currentsArray.length + (currentsArray.length - currentsArray.indexOf(docAtCurrent)) - 1; + //end of grup so go beyond + if (prevSelected === current) prevSelected = current - 1; + + //checking if any of the group members had used zooming in + currentsArray.forEach((doc: Doc) => { + //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); + if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) { + zoomOut = true; + return; + } + }); + + } + + // if a group set that flag to zero or a single element + //If so making sure to zoom out, which goes back to state before zooming action + if (current > 0) { + if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) { + let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null); + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]); + if (prevScale !== undefined) { + if (prevScale !== curScale) { + DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); + } + } + } + } + this.gotoDocument(prevSelected, current); + + } + + /** + * This is the method that checks for the actions that need to be performed + * after the document has been presented, which involves 3 button options: + * Hide Until Presented, Hide After Presented, Fade After Presented + */ + showAfterPresented = (index: number) => { + this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { + let selectedButtons: boolean[] = presElem.selected; + //the order of cases is aligned based on priority + if (selectedButtons[buttonIndex.HideTillPressed]) { + if (this.childrenDocs.indexOf(key) <= index) { + key.opacity = 1; + } + } + if (selectedButtons[buttonIndex.HideAfter]) { + if (this.childrenDocs.indexOf(key) < index) { + key.opacity = 0; + } + } + if (selectedButtons[buttonIndex.FadeAfter]) { + if (this.childrenDocs.indexOf(key) < index) { + key.opacity = 0.5; + } + } + }); + } + + /** + * This is the method that checks for the actions that need to be performed + * before the document has been presented, which involves 3 button options: + * Hide Until Presented, Hide After Presented, Fade After Presented + */ + hideIfNotPresented = (index: number) => { + this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { + let selectedButtons: boolean[] = presElem.selected; + + //the order of cases is aligned based on priority + + if (selectedButtons[buttonIndex.HideAfter]) { + if (this.childrenDocs.indexOf(key) >= index) { + key.opacity = 1; + } + } + if (selectedButtons[buttonIndex.FadeAfter]) { + if (this.childrenDocs.indexOf(key) >= index) { + key.opacity = 1; + } + } + if (selectedButtons[buttonIndex.HideTillPressed]) { + if (this.childrenDocs.indexOf(key) > index) { + key.opacity = 0; + } + } + }); + } + + /** + * This method makes sure that cursor navigates to the element that + * has the option open and last in the group. If not in the group, and it has + * te option open, navigates to that element. + */ + navigateToElement = async (curDoc: Doc, fromDoc: number) => { + let docToJump: Doc = curDoc; + let curDocPresId = StrCast(curDoc.presentId, null); + let willZoom: boolean = false; + + //checking if in group + if (curDocPresId !== undefined) { + if (this.groupMappings.has(curDocPresId)) { + let currentDocGroup = this.groupMappings.get(curDocPresId)!; + currentDocGroup.forEach((doc: Doc, index: number) => { + let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected; + if (selectedButtons[buttonIndex.Navigate]) { + docToJump = doc; + willZoom = false; + } + if (selectedButtons[buttonIndex.Show]) { + docToJump = doc; + willZoom = true; + } + }); + } + + } + //docToJump stayed same meaning, it was not in the group or was the last element in the group + if (docToJump === curDoc) { + //checking if curDoc has navigation open + let curDocButtons = this.presElementsMappings.get(curDoc)!.selected; + if (curDocButtons[buttonIndex.Navigate]) { + DocumentManager.Instance.jumpToDocument(curDoc, false); + } else if (curDocButtons[buttonIndex.Show]) { + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(curDoc, true); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + + //saving the scale user was on before zooming in + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; + } + + } + return; + } + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(docToJump, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + //saving the scale that user was on + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; + } + + } + + /** + * Async function that supposedly return the doc that is located at given index. + */ + getDocAtIndex = async (index: number) => { + const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + if (!list) { + return undefined; + } + if (index < 0 || index >= list.length) { + return undefined; + } + + this.curPresentation.selectedDoc = index; + //awaiting async call to finish to get Doc instance + const doc = await list[index]; + return doc; + } + + /** + * The function that removes a doc from a presentation. It also makes sure to + * do necessary updates to backUps and mappings stored locally. + */ + @action + public RemoveDoc = async (index: number) => { + const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + if (value) { + let removedDoc = await value.splice(index, 1)[0]; + + //removing the Presentation Element stored for it + this.presElementsMappings.delete(removedDoc); + + let removedDocPresentId = StrCast(removedDoc.presentId); + + //Removing it from local mapping of the groups + if (this.groupMappings.has(removedDocPresentId)) { + let removedDocsGroup = this.groupMappings.get(removedDocPresentId); + if (removedDocsGroup) { + removedDocsGroup.splice(removedDocsGroup.indexOf(removedDoc), 1); + if (removedDocsGroup.length === 0) { + this.groupMappings.delete(removedDocPresentId); + } + } + } + + //removing it from the backUp of selected Buttons + // let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc)); + // if (castedList) { + // castedList.forEach(async (doc, indexOfDoc) => { + // let curDoc = await doc; + // let curDocId = StrCast(curDoc.docId); + // if (curDocId === removedDoc[Id]) { + // if (castedList) { + // castedList.splice(indexOfDoc, 1); + // return; + // } + // } + // }); + + // } + //removing it from the backUp of selected Buttons + + let castedList = Cast(this.presButtonBackUp.selectedButtonDocs, listSpec(Doc)); + if (castedList) { + for (let doc of castedList) { + let curDoc = await doc; + let curDocId = StrCast(curDoc.docId); + if (curDocId === removedDoc[Id]) { + castedList.splice(castedList.indexOf(curDoc), 1); + break; + + } + } + } + + //removing it from the backup of groups + let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); + if (castedGroupDocs) { + castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { + let castedKey = StrCast(groupDoc.presentIdStore, null); + if (castedKey === removedDocPresentId) { + let castedGrouping = await DocListCastAsync(groupDoc.grouping); + if (castedGrouping) { + castedGrouping.splice(castedGrouping.indexOf(removedDoc), 1); + if (castedGrouping.length === 0) { + castedGroupDocs!.splice(castedGroupDocs!.indexOf(groupDoc), 1); + } + } + } + + }); + + } + + + } + } + + public removeDocByRef = (doc: Doc) => { + let indexOfDoc = this.childrenDocs.indexOf(doc); + const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + if (value) { + value.splice(indexOfDoc, 1)[0]; + } + //this.RemoveDoc(indexOfDoc, true); + if (indexOfDoc !== - 1) { + return true; + } + return false; + } + + //The function that is called when a document is clicked or reached through next or back. + //it'll also execute the necessary actions if presentation is playing. + @action + public gotoDocument = async (index: number, fromDoc: number) => { + const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + if (!list) { + return; + } + if (index < 0 || index >= list.length) { + return; + } + this.curPresentation.selectedDoc = index; + + if (!this.presStatus) { + this.presStatus = true; + this.startPresentation(index); + } + + const doc = await list[index]; + if (this.presStatus) { + this.navigateToElement(doc, fromDoc); + this.hideIfNotPresented(index); + this.showAfterPresented(index); + } + + } + + //Function that is called to resetGroupIds, so that documents get new groupIds at + //first load, when presentation is changed. + resetGroupIds = async () => { + let castedGroupDocs = await DocListCastAsync(this.presGroupBackUp.groupDocs); + if (castedGroupDocs !== undefined) { + castedGroupDocs.forEach(async (groupDoc: Doc, index: number) => { + let castedGrouping = await DocListCastAsync(groupDoc.grouping); + if (castedGrouping) { + castedGrouping.forEach((doc: Doc) => { + doc.presentId = Utils.GenerateGuid(); + }); + } + }); + } + runInAction(() => this.groupMappings = new Map()); + } + + /** + * Adds a document to the presentation view + **/ + @undoBatch + @action + public PinDoc(doc: Doc) { + //add this new doc to props.Document + const data = Cast(this.curPresentation.data, listSpec(Doc)); + if (data) { + data.push(doc); + } else { + this.curPresentation.data = new List([doc]); + } + + this.toggle(true); + } + + //Function that sets the store of the children docs. + @action + setChildrenDocs = (docList: Doc[]) => { + this.childrenDocs = docList; + } + + //The function that is called to render the play or pause button depending on + //if presentation is running or not. + renderPlayPauseButton = () => { + if (this.presStatus) { + return ; + } else { + return ; + } + } + + //The function that starts or resets presentaton functionally, depending on status flag. + @action + startOrResetPres = () => { + if (this.presStatus) { + this.resetPresentation(); + } else { + this.presStatus = true; + this.startPresentation(0); + const current = NumCast(this.curPresentation.selectedDoc); + this.gotoDocument(0, current); + } + this.curPresentation.presStatus = this.presStatus; + } + + //The function that resets the presentation by removing every action done by it. It also + //stops the presentaton. + @action + resetPresentation = () => { + this.childrenDocs.forEach((doc: Doc) => { + doc.opacity = 1; + doc.viewScale = 1; + }); + this.curPresentation.selectedDoc = 0; + this.presStatus = false; + this.curPresentation.presStatus = this.presStatus; + if (this.childrenDocs.length === 0) { + return; + } + DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1); + } + + + //The function that starts the presentation, also checking if actions should be applied + //directly at start. + startPresentation = (startIndex: number) => { + let selectedButtons: boolean[]; + this.presElementsMappings.forEach((component: PresentationElement, doc: Doc) => { + selectedButtons = component.selected; + if (selectedButtons[buttonIndex.HideTillPressed]) { + if (this.childrenDocs.indexOf(doc) > startIndex) { + doc.opacity = 0; + } + + } + if (selectedButtons[buttonIndex.HideAfter]) { + if (this.childrenDocs.indexOf(doc) < startIndex) { + doc.opacity = 0; + } + } + if (selectedButtons[buttonIndex.FadeAfter]) { + if (this.childrenDocs.indexOf(doc) < startIndex) { + doc.opacity = 0.5; + } + } + + }); + + } + + /** + * The function that is called to add a new presentation to the presentationView. + * It sets up te mappings and local copies of it. Resets the groupings and presentation. + * Makes the new presentation current selected, and retrieve the back-Ups if present. + */ + @action + addNewPresentation = (presTitle: string) => { + //creating a new presentation doc + let newPresentationDoc = Docs.Create.TreeDocument([], { title: presTitle }); + let presDocs = Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc)); + presDocs && presDocs.push(newPresentationDoc); + + //setting that new doc as current + this.curPresentation = newPresentationDoc; + + //storing the doc in local copies for easier access + let newGuid = Utils.GenerateGuid(); + this.presentationsMapping.set(newGuid, newPresentationDoc); + this.presentationsKeyMapping.set(newPresentationDoc, newGuid); + + //resetting the previous presentation's actions so that new presentation can be loaded. + this.resetGroupIds(); + this.resetPresentation(); + this.presElementsMappings = new Map(); + this.currentSelectedPresValue = newGuid; + this.setPresentationBackUps(); + + } + + /** + * The function that is called to change the current selected presentation. + * Changes the presentation, also resetting groupings and presentation in process. + * Plus retrieving the backUps for the newly selected presentation. + */ + @action + getSelectedPresentation = (e: React.ChangeEvent) => { + //get the guid of the selected presentation + let selectedGuid = e.target.value; + //set that as current presentation + this.curPresentation = this.presentationsMapping.get(selectedGuid)!; + + //reset current Presentations local things so that new one can be loaded + this.resetGroupIds(); + this.resetPresentation(); + this.presElementsMappings = new Map(); + this.currentSelectedPresValue = selectedGuid; + this.setPresentationBackUps(); + + + } + + /** + * The function that is called to render either select for presentations, or title inputting. + */ + renderSelectOrPresSelection = () => { + let presentationList = this.presentationDocs; + if (this.PresTitleInputOpen || this.PresTitleChangeOpen) { + return this.titleInputElement = e!} type="text" className="presentationView-title" placeholder="Enter Name!" onKeyDown={this.submitPresentationTitle} />; + } else { + return ; + } + } + + /** + * The function that is called on enter press of title input. It gives the + * new presentation the title user entered. If nothing is entered, gives a default title. + */ + @action + submitPresentationTitle = (e: React.KeyboardEvent) => { + if (e.keyCode === 13) { + let presTitle = this.titleInputElement!.value; + this.titleInputElement!.value = ""; + if (this.PresTitleInputOpen) { + if (presTitle === "") { + presTitle = "Presentation"; + } + this.PresTitleInputOpen = false; + this.addNewPresentation(presTitle); + } else if (this.PresTitleChangeOpen) { + this.PresTitleChangeOpen = false; + this.changePresentationTitle(presTitle); + } + } + } + + /** + * The function that is called to remove a presentation from all its copies, and the main Container's + * list. Sets up the next presentation as current. + */ + @action + removePresentation = async () => { + if (this.presentationsMapping.size !== 1) { + let presentationList = this.presentationDocs; + let batch = UndoManager.StartBatch("presRemoval"); + + //getting the presentation that will be removed + let removedDoc = this.presentationsMapping.get(this.currentSelectedPresValue!); + //that presentation is removed + presentationList!.splice(presentationList!.indexOf(removedDoc!), 1); + + //its mappings are removed from local copies + this.presentationsKeyMapping.delete(removedDoc!); + this.presentationsMapping.delete(this.currentSelectedPresValue!); + + //the next presentation is set as current + let remainingPresentations = this.presentationsMapping.values(); + let nextDoc = remainingPresentations.next().value; + this.curPresentation = nextDoc; + + + //Storing these for being able to undo changes + let curGuid = this.currentSelectedPresValue!; + let curPresStatus = this.presStatus; + + //resetting the groups and presentation actions so that next presentation gets loaded + this.resetGroupIds(); + this.resetPresentation(); + this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString(); + this.setPresentationBackUps(); + + //Storing for undo + let currentGroups = this.groupMappings; + let curPresElemMapping = this.presElementsMappings; + + //Event to undo actions that are not related to doc directly, aka. local things + UndoManager.AddEvent({ + undo: action(() => { + this.curPresentation = removedDoc!; + this.presentationsMapping.set(curGuid, removedDoc!); + this.presentationsKeyMapping.set(removedDoc!, curGuid); + this.currentSelectedPresValue = curGuid; + + this.presStatus = curPresStatus; + this.groupMappings = currentGroups; + this.presElementsMappings = curPresElemMapping; + this.setPresentationBackUps(); + + }), + redo: action(() => { + this.curPresentation = nextDoc; + this.presStatus = false; + this.presentationsKeyMapping.delete(removedDoc!); + this.presentationsMapping.delete(curGuid); + this.currentSelectedPresValue = this.presentationsKeyMapping.get(nextDoc)!.toString(); + this.setPresentationBackUps(); + + }), + }); + + batch.end(); + } + } + + /** + * The function that is called to change title of presentation to what user entered. + */ + @undoBatch + changePresentationTitle = (newTitle: string) => { + if (newTitle === "") { + return; + } + this.curPresentation.title = newTitle; + } + + addPressElem = (keyDoc: Doc, elem: PresentationElement) => { + this.presElementsMappings.set(keyDoc, elem); + } + + + render() { + + let width = NumCast(this.curPresentation.width); + + return ( +
!this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}> +
+ {this.renderSelectOrPresSelection()} + {/**this.closePresentation CLICK does not work?! Also without the*/} + + + +
+
+ + {this.renderPlayPauseButton()} + +
+ ) => { + this.persistOpacity = e.target.checked; + this.opacity = this.persistOpacity ? 1 : 0.4; + })} + checked={this.persistOpacity} + style={{ position: "absolute", bottom: 5, left: 5 }} + onPointerEnter={action(() => this.labelOpacity = 1)} + onPointerLeave={action(() => this.labelOpacity = 0)} + /> +

opacity {this.persistOpacity ? "persistent" : "on focus"}

+ this.presElementsMappings.clear()} + /> +
+ ); + } + + +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 27c2a8f1a..23d0c0b10 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -250,7 +250,7 @@ export default class PDFMenu extends React.Component { ] : [ , - , + , //change this to pin to 'new' presentation
diff --git a/src/new_fields/PresField.ts b/src/new_fields/PresField.ts new file mode 100644 index 000000000..f236a04fd --- /dev/null +++ b/src/new_fields/PresField.ts @@ -0,0 +1,6 @@ +//insert code here +import { ObjectField } from "./ObjectField"; + +export abstract class PresField extends ObjectField { + +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 915b9332ad72f56b68df148b09eecba527dc9f06 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 9 Aug 2019 12:10:36 -0400 Subject: changed how templates get dataDocs. changed button boxes to be div's --- src/client/views/MainView.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 8 ++--- src/client/views/nodes/ButtonBox.scss | 4 +++ src/client/views/nodes/ButtonBox.tsx | 42 ++-------------------- src/client/views/nodes/DocumentView.tsx | 36 ++++++++++++++++++- src/client/views/nodes/FormattedTextBox.tsx | 4 +-- src/client/views/nodes/ImageBox.tsx | 2 +- src/new_fields/Doc.ts | 2 +- 8 files changed, 51 insertions(+), 49 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a4db753ab..0d8ade247 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -459,7 +459,7 @@ export class MainView extends React.Component { // [React.createRef(), "clone", "Add Docking Frame", addDockingNode], [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], [React.createRef(), "play", "Add Youtube Searcher", addYoutubeSearcher], - [React.createRef(), "bolt", "Add Document Dragger", addDragboxNode] + [React.createRef(), "file", "Add Document Dragger", addDragboxNode] ]; if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index c3e55d825..17111af58 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -50,10 +50,10 @@ export class SelectorContextMenu extends React.Component { render() { return ( <> -

Contexts:

- {this._docs.map(doc =>

{doc.col.title}

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

{doc.col.title}

)} +

Contexts:

+ {this._docs.map(doc =>

{doc.col.title}

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

{doc.col.title}

)} ); } diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss index 92beafa15..5ed435505 100644 --- a/src/client/views/nodes/ButtonBox.scss +++ b/src/client/views/nodes/ButtonBox.scss @@ -3,10 +3,14 @@ height: 100%; pointer-events: all; border-radius: inherit; + display:table; } .buttonBox-mainButton { width: 100%; height: 100%; border-radius: inherit; + display:table-cell; + vertical-align: middle; + text-align: center; } \ No newline at end of file diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index 640795789..8b6f11aac 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -15,6 +15,7 @@ import { Doc } from '../../../new_fields/Doc'; import './ButtonBox.scss'; import { observer } from 'mobx-react'; import { DocumentIconContainer } from './DocumentIcon'; +import { StrCast } from '../../../new_fields/Types'; library.add(faEdit as any); @@ -30,47 +31,10 @@ const ButtonDocument = makeInterface(ButtonSchema); export class ButtonBox extends DocComponent(ButtonDocument) { public static LayoutString() { return FieldView.LayoutString(ButtonBox); } - onClick = (e: React.MouseEvent) => { - const onClick = this.Document.onClick; - if (!onClick) { - return; - } - e.stopPropagation(); - e.preventDefault(); - onClick.script.run({ this: this.props.Document }); - } - - onContextMenu = () => { - ContextMenu.Instance.addItem({ - description: "Edit OnClick script", icon: "edit", event: () => { - let overlayDisposer: () => void = emptyFunction; - const script = this.Document.onClick; - let originalText: string | undefined = undefined; - if (script) originalText = script.script.originalScript; - // tslint:disable-next-line: no-unnecessary-callback-wrapper - let scriptingBox = overlayDisposer()} onSave={(text, onError) => { - const script = CompileScript(text, { - params: { this: Doc.name }, - typecheck: false, - editable: true, - transformer: DocumentIconContainer.getTransformer() - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - return; - } - this.Document.onClick = new ScriptField(script); - overlayDisposer(); - }} showDocumentIcons />; - overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` }); - } - }); - } - render() { return ( -
- +
+
{this.Document.text || this.Document.title}
); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c8eab85c2..cf16db203 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -40,6 +40,10 @@ import React = require("react"); import { DictationManager } from '../../util/DictationManager'; import { MainView } from '../MainView'; import requestPromise = require('request-promise'); +import { ScriptBox } from '../ScriptBox'; +import { CompileScript } from '../../util/Scripting'; +import { DocumentIconContainer } from './DocumentIcon'; +import { ScriptField } from '../../../new_fields/ScriptField'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -109,7 +113,8 @@ const schema = createSchema({ nativeHeight: "number", backgroundColor: "string", opacity: "number", - hidden: "boolean" + hidden: "boolean", + onClick: ScriptField, }); export const positionSchema = createSchema({ @@ -292,6 +297,11 @@ export class DocumentView extends DocComponent(Docu onClick = async (e: React.MouseEvent) => { if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. e.stopPropagation(); + if (this.Document.onClick) { + this.Document.onClick.script.run({ this: this.props.Document }); + e.preventDefault(); + return; + } let altKey = e.altKey; let ctrlKey = e.ctrlKey; if (this._doubleTap && this.props.renderDepth) { @@ -567,6 +577,30 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); + cm.addItem({ + description: "Edit OnClick script", icon: "edit", event: () => { + let overlayDisposer: () => void = emptyFunction; + const script = this.Document.onClick; + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false, + editable: true, + transformer: DocumentIconContainer.getTransformer() + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.Document.onClick = new ScriptField(script); + overlayDisposer(); + }} showDocumentIcons />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` }); + } + }); let makes: ContextMenuProps[] = []; makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 10f50c5a4..cf4f7f668 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -131,7 +131,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); } - @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? Doc.GetDataDoc(this.props.DataDoc) : Doc.GetProto(this.props.Document); } + @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } paste = (e: ClipboardEvent) => { @@ -624,7 +624,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let self = this; let xf = this._ref.current!.getBoundingClientRect(); let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height); - let nh = NumCast(this.dataDoc.nativeHeight, 0); + let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.props.Document.height, 0); let sh = scrBounds.height; const ChromeHeight = MainOverlayTextBox.Instance.ChromeHeight; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 1ebeb2d66..78a6ec66f 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -65,7 +65,7 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; - @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? Doc.GetDataDoc(this.props.DataDoc) : Doc.GetProto(this.props.Document); } + @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } protected createDropTarget = (ele: HTMLDivElement) => { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1df36d719..fc4411d93 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -446,7 +446,7 @@ export namespace Doc { export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) { let layoutDoc = childDocLayout; - let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc ? dataDoc : undefined; + let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined; if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) { Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey); let fieldExtensionDoc = Doc.resolvedFieldDataDoc(resolvedDataDoc, StrCast(childDocLayout.templateField, StrCast(childDocLayout.title)), "dummy"); -- cgit v1.2.3-70-g09d2 From 0af800d2edf120ab2a20842ed67ea7d7616996b9 Mon Sep 17 00:00:00 2001 From: HJF Bulterman Date: Fri, 9 Aug 2019 15:37:49 -0400 Subject: working for about 85% --- src/client/views/MainView.tsx | 27 +++++++++++++++++----- .../views/collections/CollectionDockingView.tsx | 9 ++++++++ src/client/views/nodes/DocumentView.tsx | 3 ++- src/client/views/nodes/PresBox.tsx | 7 +++--- 4 files changed, 36 insertions(+), 10 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 14cfc6792..cb81f9aad 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -247,7 +247,6 @@ export class MainView extends React.Component { @computed get dockingContent() { let flyoutWidth = this.flyoutWidth; let mainCont = this.mainContainer; - let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined; return {({ measureRef }) =>
@@ -271,7 +270,7 @@ export class MainView extends React.Component { zoomToScale={emptyFunction} getScale={returnOne} />} - {castRes ? : null} + {/* {presentationDoc ? : null} */}
}
; @@ -385,13 +384,11 @@ export class MainView extends React.Component { let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })); let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); - let addPresentationNode = action(() => Docs.Create.PresDocument(new List([Docs.Create.TreeDocument([], { title: "Presentation" })]))); - let btns: [React.RefObject, IconName, string, () => Doc][] = [ [React.createRef(), "object-group", "Add Collection", addColNode], [React.createRef(), "bolt", "Add Button", addButtonDocument], // [React.createRef(), "clone", "Add Docking Frame", addDockingNode], - [React.createRef(), "cloud-upload-alt", "Import Directory", addPresentationNode], //remove at some point in favor of addImportCollectionNode + [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode ]; if (!ClientUtils.RELEASE) btns.unshift([React.createRef(), "cat", "Add Cat Image", addImageNode]); @@ -402,7 +399,7 @@ export class MainView extends React.Component {
  • -
  • +
  • {btns.map(btn => @@ -453,6 +450,24 @@ export class MainView extends React.Component { this.isSearchVisible = !this.isSearchVisible; } + togglePresentationView = () => { + let presDoc = this.presentationDoc; + if (!presDoc) { + return; + } + let isOpen = CollectionDockingView.Instance.Has(presDoc); + if (isOpen) { + CollectionDockingView.Instance.CloseRightSplit(presDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(presDoc, undefined); + } + } + + private get presentationDoc() { + let mainCont = this.mainContainer; + return mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + } + render() { return (
    diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1859ebee7..bd83a46a3 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -162,6 +162,14 @@ export class CollectionDockingView extends React.Component { + let docs = Cast(this.props.Document.data, listSpec(Doc)); + if (!docs) { + return false; + } + return docs.includes(document); + } + // // Creates a vertical split on the right side of the docking view, and then adds the Document to that split // @@ -525,6 +533,7 @@ export class DockedFrameRenderer extends React.Component { this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.on("tab", this.onActiveContentItemChanged); this.onActiveContentItemChanged(); + // setTimeout(() => MainView.Instance.openPresentationView(), 2000); } componentWillUnmount() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0ca303dde..6d4c18050 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -41,6 +41,7 @@ import { ClientUtils } from '../../util/ClientUtils'; import { EditableView } from '../EditableView'; import { faHandPointer, faHandPointRight } from '@fortawesome/free-regular-svg-icons'; import { DocumentDecorations } from '../DocumentDecorations'; +import { PresBox } from './PresBox'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -553,7 +554,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); - cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //change this to + cm.addItem({ description: "Pin to Presentation", event: () => PresBox.Instance.PinDoc(this.props.Document), icon: "map-pin" }); //this should work, and it does! A miracle! cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); let makes: ContextMenuProps[] = []; makes.push({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 2feb32693..8316c4469 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -32,7 +32,7 @@ export interface PresViewProps { Documents: List; } -const expandedWidth = 400; +const expandedWidth = 450; @observer export class PresBox extends React.Component { //FieldViewProps? @@ -825,10 +825,11 @@ export class PresBox extends React.Component { //FieldViewProps? render() { - let width = NumCast(this.curPresentation.width); + let width = "100%"; //NumCast(this.curPresentation.width) + console.log("The width is: " + width); return ( -
    !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease" }}> +
    !this.persistOpacity && (this.opacity = 1))} onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} style={{ width: width, overflow: "hidden", opacity: this.opacity, transition: "0.7s opacity ease", pointerEvents: "all" }}>
    {this.renderSelectOrPresSelection()} {/**this.closePresentation CLICK does not work?! Also without the*/} -- cgit v1.2.3-70-g09d2 From 8c4b8ae12fb418d38e5aab6c12514913f1565bb0 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Fri, 9 Aug 2019 17:02:02 -0400 Subject: send halp --- src/client/util/RichTextSchema.tsx | 52 +++++++++++----------- src/client/util/TooltipTextMenu.tsx | 9 ++-- .../views/collections/CollectionViewChromes.tsx | 2 + src/client/views/nodes/FormattedTextBox.tsx | 1 + 4 files changed, 33 insertions(+), 31 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 8e80de1a8..733c50d20 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -6,6 +6,7 @@ import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'; import { EditorState, Transaction, NodeSelection, TextSelection, Selection, } from "prosemirror-state"; import { EditorView, } from "prosemirror-view"; import { View } from '@react-pdf/renderer'; +import { TooltipTextMenu } from './TooltipTextMenu'; const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -111,30 +112,16 @@ export const nodes: { [index: string]: NodeSpec } = { // }] }, - // TODO checkbox: { inline: true, attrs: { - visibility: { default: false }, - // text: { default: undefined }, - // textslice: { default: undefined }, - // textlen: { default: 0 } - + visibility: { default: false } }, group: "inline", toDOM(node) { const attrs = { style: `width: 40px` }; return ["span", { ...node.attrs, ...attrs }]; }, - // parseDOM: [{ - // tag: "star", getAttrs(dom: any) { - // return { - // visibility: dom.getAttribute("visibility"), - // oldtext: dom.getAttribute("oldtext"), - // oldtextlen: dom.getAttribute("oldtextlen"), - // } - // } - // }] }, // :: NodeSpec An inline image (``) node. Supports `src`, @@ -193,12 +180,6 @@ export const nodes: { [index: string]: NodeSpec } = { } }, - checkbox_list2: { - inline: false, - // content: 'list_item+', - group: 'block' - }, - // :: NodeSpec A hard line break, represented in the DOM as `
    `. hard_break: { inline: true, @@ -221,15 +202,20 @@ export const nodes: { [index: string]: NodeSpec } = { // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }], // toDOM() { return ulDOM } }, + checkbox_list: { - ...bulletList, - content: 'list_item+', + content: 'checklist_item+', + marks: '_', group: 'block', - // style: 'list-style-type:none' - itemContent: "+", - // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=square' }], - // toDOM() { return ulDOM; } + // inline: true, + parseDOM: [ + { tag: "ul" } + ], + toDOM() { + return ["ul", { style: 'list-style: none' }, 0]; + }, }, + //bullet_list: { // content: 'list_item+', // group: 'block', @@ -241,6 +227,18 @@ export const nodes: { [index: string]: NodeSpec } = { list_item: { ...listItem, content: 'paragraph block*' + }, + + checklist_item: { + content: 'paragraph block*', + parseDOM: [{ tag: "li" }], + // toDOM() { + // return ["li", { style: 'content: checkbox' }, 0]; + // }, + toDOM() { + return ["li", 0]; + }, + defining: true } }; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index b1243cb1d..8112ed868 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -441,13 +441,10 @@ export class TooltipTextMenu { return true; } - // this needs to change so it makes it into a bulleted list public static insertCheckbox(state: EditorState, dispatch: any) { let newNode = schema.nodes.checkbox.create({ visibility: false }); if (dispatch) { - //console.log(newNode.attrs.text.toString()); dispatch(state.tr.replaceSelectionWith(newNode)); - wrapInList(newNode.type)(state, dispatch); } return true; } @@ -461,7 +458,6 @@ export class TooltipTextMenu { let toAdd: MenuItem[] = []; this.listTypeToIcon.forEach((icon, type) => { toAdd.push(this.dropdownNodeBtn(icon, "color: black; width: 40px;", type, this.view, this.listTypes, this.changeToNodeType)); - console.log(type.name) }); //option to remove the list formatting toAdd.push(this.dropdownNodeBtn("X", "color: black; width: 40px;", undefined, this.view, this.listTypes, this.changeToNodeType)); @@ -529,6 +525,11 @@ export class TooltipTextMenu { liftListItem(schema.nodes.list_item)(view.state, view.dispatch); if (nodeType) { //add new wrapInList(nodeType)(view.state, view.dispatch); + // console.log(nodeType === schema.nodes.checkbox_list) + // if (nodeType === schema.nodes.checkbox_list) { + // TooltipTextMenu.insertCheckbox(view.state, view.dispatch) + // } + } } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 52c47e7e8..5b673c8ec 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -33,6 +33,8 @@ let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); @observer export class CollectionViewBaseChrome extends React.Component { + //.*?doc\.(\w+).*?\("(\w+) + @observable private _viewSpecsOpen: boolean = false; @observable private _dateWithinValue: string = ""; @observable private _dateValue: Date | string = ""; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 303b1ac88..d1771eb0f 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -177,6 +177,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._editorView.updateState(state); if (state.selection.empty && FormattedTextBox._toolTipTextMenu) { const marks = tx.storedMarks; + console.log(marks) if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); } } this._applyingChange = true; -- cgit v1.2.3-70-g09d2 From e291730c407932506a80f96457a84dce1820521d Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 9 Aug 2019 17:42:11 -0400 Subject: made onClick a prop --- src/client/views/MainOverlayTextBox.tsx | 1 + src/client/views/MainView.tsx | 2 ++ src/client/views/collections/CollectionSchemaView.tsx | 16 +++++++++------- src/client/views/collections/CollectionStackingView.tsx | 1 + src/client/views/collections/CollectionView.tsx | 4 ++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 8 ++++++-- src/client/views/nodes/DocumentContentsView.tsx | 13 +++++++++++-- src/client/views/nodes/DocumentView.tsx | 16 +++++++++------- src/client/views/nodes/FieldView.tsx | 3 ++- 9 files changed, 45 insertions(+), 19 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index fccbeb16c..0f20dc3a8 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -142,6 +142,7 @@ export class MainOverlayTextBox extends React.Component diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0d8ade247..bb0048982 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -321,6 +321,7 @@ export class MainView extends React.Component { DataDoc={undefined} addDocument={undefined} addDocTab={emptyFunction} + onClick={emptyFunction} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} ContentScaling={returnOne} @@ -385,6 +386,7 @@ export class MainView extends React.Component { addDocument={undefined} addDocTab={this.addDocTabFunc} removeDocument={undefined} + onClick={emptyFunction} ScreenToLocalTransform={Transform.Identity} ContentScaling={returnOne} PanelWidth={this.flyoutWidthFunc} diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ebfa737be..67b8b4a8d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -30,7 +30,7 @@ import { undoBatch } from "../../util/UndoManager"; import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders"; import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; -import { ComputedField } from "../../../new_fields/ScriptField"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; @@ -899,6 +899,7 @@ interface CollectionSchemaPreviewProps { height: () => number; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; + onClick?: () => void | ScriptField; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; @@ -988,23 +989,24 @@ export class CollectionSchemaPreview extends React.Component diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 112d64e3d..22af98c4d 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -121,6 +121,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { showOverlays={this.overlays} renderDepth={this.props.renderDepth} fitToBox={this.props.fitToBox} + onClick={this.props.onClick} width={width} height={height} getTransform={finalDxf} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7a402798e..8b939259c 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -30,6 +30,10 @@ export class CollectionView extends React.Component { public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } + constructor(props:any) { + super(props); + } + componentDidMount = () => { this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus), () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba8dcff98..30010e826 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -37,8 +37,6 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { DocumentType, Docs } from "../../../documents/Documents"; -import { RouteStore } from "../../../../server/RouteStore"; -import { string, number, elementType } from "prop-types"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard); @@ -195,6 +193,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private inkKey = "ink"; + constructor(props: any) { + super(props); + } + get parentScaling() { return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1; } @@ -631,6 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, + onClick: this.props.onClick, ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform, renderDepth: this.props.renderDepth + 1, selectOnLoad: pair.layout[Id] === this._selectOnLoaded, @@ -655,6 +658,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, + onClick: this.props.onClick, ScreenToLocalTransform: this.getTransform, renderDepth: this.props.renderDepth, selectOnLoad: layoutDoc[Id] === this._selectOnLoaded, diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 6b7b239f0..2466f13f6 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -28,7 +28,7 @@ import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc } from "../../../new_fields/Doc"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { ScriptField } from "../../../new_fields/ScriptField"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; @@ -49,6 +49,7 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; export class DocumentContentsView extends React.Component boolean, select: (ctrl: boolean) => void, + onClick?: ScriptField, layoutKey: string, hideOnLeave?: boolean }> { @@ -81,7 +82,13 @@ export class DocumentContentsView extends React.Component obj.active = this.props.parentActive).omit, Document: this.layoutDoc, DataDoc: this.dataDoc } }; + let list = { + ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, + Document: this.layoutDoc, + DataDoc: this.dataDoc, + onClick: this.props.onClick + }; + return { props: list }; } @computed get templates(): List { @@ -100,10 +107,12 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return { console.log(test); }} />; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index cf16db203..b8e2eb436 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -39,7 +39,6 @@ import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); import { DictationManager } from '../../util/DictationManager'; import { MainView } from '../MainView'; -import requestPromise = require('request-promise'); import { ScriptBox } from '../ScriptBox'; import { CompileScript } from '../../util/Scripting'; import { DocumentIconContainer } from './DocumentIcon'; @@ -84,6 +83,7 @@ export interface DocumentViewProps { Document: Doc; DataDoc?: Doc; fitToBox?: boolean; + onClick?: ScriptField; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; @@ -297,8 +297,8 @@ export class DocumentView extends DocComponent(Docu onClick = async (e: React.MouseEvent) => { if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. e.stopPropagation(); - if (this.Document.onClick) { - this.Document.onClick.script.run({ this: this.props.Document }); + if (this.onClickHandler) { + this.onClickHandler.script.run({ this: this.props.Document }); e.preventDefault(); return; } @@ -687,14 +687,16 @@ export class DocumentView extends DocComponent(Docu onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.props.Document); }; isSelected = () => SelectionManager.IsSelected(this); - @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; - + @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); } @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } + @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } @computed get contents() { return ((Docu } {!showCaption ? (null) :
    - +
    }
    diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index da54ecc3a..3287949e2 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -17,7 +17,7 @@ import { IconBox } from "./IconBox"; import { ImageBox } from "./ImageBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { ScriptField } from "../../../new_fields/ScriptField"; // // these properties get assigned through the render() method of the DocumentView when it creates this node. @@ -32,6 +32,7 @@ export interface FieldViewProps { ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; + onClick?: ScriptField; isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; -- cgit v1.2.3-70-g09d2 From 87e79b5d230cee0d3f5df399b4fee0c62ccb5385 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 13 Aug 2019 00:11:52 -0400 Subject: fixed stacking layouts. added image cross fading. --- src/client/util/DragManager.ts | 2 +- .../views/collections/CollectionSchemaView.scss | 48 +++++++++++----------- .../views/collections/CollectionSchemaView.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/nodes/ImageBox.scss | 21 +++++++++- src/client/views/nodes/ImageBox.tsx | 26 ++++++++---- 6 files changed, 65 insertions(+), 36 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a7aaaed7c..0b6d9b5e5 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -490,7 +490,7 @@ export namespace DragManager { x: e.x, y: e.y, data: dragData, - mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : "" + mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : e.metaKey ? "MetaKey" : "" } }) ); diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 01744fb34..e0cedc210 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -22,30 +22,6 @@ overflow: scroll; } - .collectionSchemaView-previewRegion { - position: relative; - background: $light-color; - height: 100%; - - .collectionSchemaView-previewDoc { - height: 100%; - width: 100%; - position: absolute; - } - - .collectionSchemaView-input { - position: absolute; - max-width: 150px; - width: 100%; - bottom: 0px; - } - - .documentView-node:first-child { - position: relative; - background: $light-color; - } - } - .collectionSchemaView-dividerDragger { position: relative; height: 100%; @@ -62,6 +38,30 @@ } } +.collectionSchemaView-previewRegion { + position: relative; + background: $light-color; + height: auto !important; + + .collectionSchemaView-previewDoc { + height: 100%; + width: 100%; + position: absolute; + } + + .collectionSchemaView-input { + position: absolute; + max-width: 150px; + width: 100%; + bottom: 0px; + } + + .documentView-node:first-child { + position: relative; + background: $light-color; + } +} + .ReactTable { width: 100%; background: white; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9bb0d787d..4537dcc85 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -899,7 +899,7 @@ interface CollectionSchemaPreviewProps { height: () => number; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; - onClick?: () => void | ScriptField; + onClick?: ScriptField; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index df03da376..0a872cfb8 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -328,7 +328,7 @@ export class CollectionStackingViewFieldColumn extends React.Component : (null); for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth}px `; return ( -
    {headingView}
    (ImageD } else if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) { Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); - } else if (de.mods === "CtrlKey") { + } else if (de.mods === "MetaKey") { if (this.extensionDoc !== this.dataDoc) { let layout = StrCast(drop.backgroundLayout); if (layout.indexOf(ImageBox.name) !== -1) { @@ -404,6 +404,7 @@ export class ImageBox extends DocComponent(ImageD let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; let srcpath = paths[Math.min(paths.length - 1, this.Document.curPage || 0)]; + let fadepath = paths[Math.min(paths.length - 1, 1)]; if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document); @@ -411,13 +412,22 @@ export class ImageBox extends DocComponent(ImageD
    - +
    + + {fadepath === srcpath ? (null) : } +
    {paths.length > 1 ? this.dots(paths) : (null)}
    Date: Tue, 13 Aug 2019 15:42:05 -0400 Subject: changes to fix stacking layouts used with templates. rearrangement of menu options. --- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/InkingCanvas.tsx | 20 ++-- src/client/views/ScriptBox.tsx | 30 +++++- .../views/collections/CollectionDockingView.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 57 +++++++---- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 19 ++-- .../views/collections/CollectionViewChromes.tsx | 19 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 64 ++----------- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 105 ++++++++++++--------- src/client/views/nodes/FormattedTextBox.tsx | 15 +-- src/new_fields/Doc.ts | 7 ++ src/new_fields/Types.ts | 4 + 15 files changed, 193 insertions(+), 162 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 8dc5cdb2a..48f02d38a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -963,7 +963,7 @@ export class TooltipTextMenu { }); } } - if (!ref_node.isLeaf) { + if (!ref_node.isLeaf && ref_node.childCount > 0) { ref_node = ref_node.child(0); } return ref_node; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6616d5d58..d537e2dac 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -537,12 +537,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... - let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect) && nwidth && nheight); + let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { proto.nativeWidth = nwidth = doc.width || 0; proto.nativeHeight = nheight = doc.height || 0; } - if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) { + if (nwidth > 0 && nheight > 0 && !BoolCast(doc.ignoreAspect)) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 1c221e3df..b08133d80 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -165,14 +165,18 @@ export class InkingCanvas extends React.Component { } return paths; }, [] as JSX.Element[]); - return [ - {paths.filter(path => path.props.tool !== InkTool.Highlighter)} - , - - {paths.filter(path => path.props.tool === InkTool.Highlighter)} - ]; + let markerPaths = paths.filter(path => path.props.tool === InkTool.Highlighter); + let penPaths = paths.filter(path => path.props.tool !== InkTool.Highlighter); + return [!penPaths.length ? (null) : + + {} + , + !markerPaths.length ? (null) : + + {} + ]; } render() { diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index d073945e5..2b862a81e 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -5,7 +5,11 @@ import { observable, action } from "mobx"; import "./ScriptBox.scss"; import { OverlayView } from "./OverlayView"; import { DocumentIconContainer } from "./nodes/DocumentIcon"; -import { Opt } from "../../new_fields/Doc"; +import { Opt, Doc } from "../../new_fields/Doc"; +import { emptyFunction } from "../../Utils"; +import { ScriptCast } from "../../new_fields/Types"; +import { CompileScript } from "../util/Scripting"; +import { ScriptField } from "../../new_fields/ScriptField"; export interface ScriptBoxProps { onSave: (text: string, onError: (error: string) => void) => void; @@ -62,4 +66,26 @@ export class ScriptBox extends React.Component {
    ); } -} \ No newline at end of file + public static EditClickScript(doc: Doc, fieldKey: string) { + let overlayDisposer: () => void = emptyFunction; + const script = ScriptCast(doc[fieldKey]); + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false, + editable: true, + transformer: DocumentIconContainer.getTransformer() + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + doc[fieldKey] = new ScriptField(script); + overlayDisposer(); + }} showDocumentIcons />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` }); + } +} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c0c82a44e..13bb34037 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -555,8 +555,8 @@ export class DockedFrameRenderer extends React.Component { panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight))); - nativeWidth = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeWidth, this._panelWidth) : 0; - nativeHeight = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeHeight, this._panelHeight) : 0; + nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { const nativeH = this.nativeHeight(); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 9d2671356..ba3d8e6a7 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types"; import { emptyFunction } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; @@ -20,6 +20,9 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; +import { ContextMenu } from "../ContextMenu"; +import { ContextMenuProps } from "../ContextMenuItem"; +import { ScriptBox } from "../ScriptBox"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -76,17 +79,24 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { componentDidMount() { // is there any reason this needs to exist? -syip - this._heightDisposer = reaction(() => [this.props.Document.autoHeight, this.yMargin, this.props.Document[WidthSym](), this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])], - () => { - if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) { - let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => { - let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); - return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); - }, this.yMargin); - (this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc) - .height = hgt * (this.props as any).ContentScaling(); + this._heightDisposer = reaction(() => { + if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) { + let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => { + let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); + return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); + }, this.yMargin); + return hgt * this.props.ContentScaling(); + } + return -1; + }, + (hgt: number) => { + if (hgt !== -1) { + let doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; + doc.height = hgt; } - }, { fireImmediately: true }); + }, + { fireImmediately: true } + ); // reset section headers when a new filter is inputted this._sectionFilterDisposer = reaction( @@ -109,9 +119,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } overlays = (doc: Doc) => { - return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {}; + return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), caption: StrCast(this.props.Document.showCaptions) } : {}; } + @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : ScriptCast(this.Document.onChildClick); } + getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); @@ -121,7 +134,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { showOverlays={this.overlays} renderDepth={this.props.renderDepth} fitToBox={this.props.fitToBox} - onClick={this.props.onClick} + onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} width={width} height={height} getTransform={finalDxf} @@ -141,7 +154,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let nh = NumCast(d.nativeHeight); if (!d.ignoreAspect && nw && nh) { let aspect = nw && nh ? nh / nw : 1; - let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(d[WidthSym](), this.columnWidth / columnScale); + let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), this.columnWidth / columnScale); return wid * aspect; } return d[HeightSym](); @@ -267,7 +280,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode"; + this.props.CollectionView.props.Document.chromeStatus = checked ? "collapsed" : "view-mode"; + } + + onContextMenu = (e: React.MouseEvent): void => { + // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout + if (!e.isPropagationStopped()) { + let subItems: ContextMenuProps[] = []; + subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" }); + subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" }); + subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" }); + subItems.push({ description: "Edit onChildClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onChildClick") }); + ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" }); + } } render() { @@ -282,7 +307,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); return (
    e.stopPropagation()} > + ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) : this.section(undefined, this.filteredChildren)} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 817f9f4cb..9824a501b 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -84,7 +84,7 @@ export class CollectionStackingViewFieldColumn extends React.Component headings.indexOf(i) === idx); let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d); - let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(NumCast(d.nativeWidth), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1); + let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1); let height = () => parent.getDocHeight(pair.layout, uniqueHeadings.length + 1);// / (d.nativeWidth && !BoolCast(d.ignoreAspect) ? uniqueHeadings.length + 1 : 1); let dref = React.createRef(); // if (uniqueHeadings.length > 0) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8b939259c..aaaa623fb 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -30,7 +30,7 @@ export class CollectionView extends React.Component { public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } - constructor(props:any) { + constructor(props: any) { super(props); } @@ -69,7 +69,7 @@ export class CollectionView extends React.Component { @action private collapse = (value: boolean) => { this._collapsed = value; - this.props.Document.chromeStatus = value ? "collapsed" : "visible"; + this.props.Document.chromeStatus = value ? "collapsed" : "enabled"; } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { @@ -90,12 +90,7 @@ export class CollectionView extends React.Component { onContextMenu = (e: React.MouseEvent): void => { if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 let subItems: ContextMenuProps[] = []; - subItems.push({ - description: "Freeform", event: () => { - this.props.Document.viewType = CollectionViewType.Freeform; - delete this.props.Document.usePivotLayout; - }, icon: "signature" - }); + subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; delete this.props.Document.usePivotLayout; }, icon: "signature" }); if (CollectionBaseView.InSafeMode()) { ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } @@ -111,10 +106,10 @@ export class CollectionView extends React.Component { } } ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); - ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); - ContextMenu.Instance.addItem({ - description: this.props.Document.chromeStatus !== "disabled" ? "Hide Chrome" : "Show Chrome", event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" - }); + let existing = ContextMenu.Instance.findByDescription("Layout..."); + let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; + layoutItems.push({ description: "Create Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); + !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); } } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 52c47e7e8..bccc0a5b2 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -37,7 +37,6 @@ export class CollectionViewBaseChrome extends React.Component { - this._collapsed = !this._collapsed; + this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled"; if (this.props.collapse) { - this.props.collapse(this._collapsed); + this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled"); } } @@ -218,16 +216,17 @@ export class CollectionViewBaseChrome extends React.Component +
    -
    diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index eb09b0693..c37f08eca 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -1,16 +1,18 @@ - -.webBox-cont, .webBox-cont-interactive{ +.webBox-cont, +.webBox-cont-interactive { padding: 0vw; position: absolute; top: 0; - left:0; + left: 0; width: 100%; height: 100%; overflow: auto; - pointer-events: none ; + pointer-events: none; } + .webBox-cont-interactive { pointer-events: all; + span { user-select: text !important; } @@ -18,8 +20,8 @@ #webBox-htmlSpan { position: absolute; - top:0; - left:0; + top: 0; + left: 0; } .webBox-overlay { @@ -29,8 +31,123 @@ } .webBox-button { - padding : 0vw; + padding: 0vw; border: none; - width : 100%; + width: 100%; + height: 100%; +} + +.webView-urlEditor { + position: relative; + opacity: 0.9; + z-index: 9001; + transition: top .5s; + background: lightgrey; + padding: 10px; + + + .urlEditor { + display: grid; + grid-template-columns: 1fr auto; + padding-bottom: 10px; + overflow: hidden; + + .collectionViewBaseChrome { + display: flex; + + .collectionViewBaseChrome-viewPicker { + font-size: 75%; + text-transform: uppercase; + letter-spacing: 2px; + background: rgb(238, 238, 238); + color: grey; + outline-color: black; + border: none; + padding: 12px 10px 11px 10px; + margin-left: 50px; + } + + .collectionViewBaseChrome-viewPicker:active { + outline-color: black; + } + + .collectionViewBaseChrome-collapse { + transition: all .5s, opacity 0.3s; + position: absolute; + width: 40px; + transform-origin: top left; + // margin-top: 10px; + } + + .collectionViewBaseChrome-viewSpecs { + margin-left: 10px; + display: grid; + + + + .collectionViewBaseChrome-viewSpecsMenu { + overflow: hidden; + transition: height .5s, display .5s; + position: absolute; + top: 60px; + z-index: 100; + display: flex; + flex-direction: column; + background: rgb(238, 238, 238); + box-shadow: grey 2px 2px 4px; + + .qs-datepicker { + left: unset; + right: 0; + } + + .collectionViewBaseChrome-viewSpecsMenu-row { + display: grid; + grid-template-columns: 150px 200px 150px; + margin-top: 10px; + margin-right: 10px; + + .collectionViewBaseChrome-viewSpecsMenu-rowLeft, + .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, + .collectionViewBaseChrome-viewSpecsMenu-rowRight { + font-size: 75%; + letter-spacing: 2px; + color: grey; + margin-left: 10px; + padding: 5px; + border: none; + outline-color: black; + } + } + + .collectionViewBaseChrome-viewSpecsMenu-lastRow { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-gap: 10px; + margin: 10px; + } + } + } + } + + button:hover { + transform: scale(1); + } + + .collectionStackingViewChrome-sectionFilter:hover { + cursor: text; + } + } +} + +.webpage-urlInput { + padding: 12px 10px 11px 10px; + border: 0px; + color: grey; + letter-spacing: 2px; + outline-color: black; + background: rgb(238, 238, 238); + width: 100%; + margin-right: 10px; height: 100%; } \ No newline at end of file diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index c8749b7cd..ff5297783 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -7,13 +7,23 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./WebBox.scss"; import React = require("react"); import { InkTool } from "../../../new_fields/InkField"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { observable, action, computed } from "mobx"; +import { listSpec } from "../../../new_fields/Schema"; +import { Field, FieldResult } from "../../../new_fields/Doc"; +import { RefField } from "../../../new_fields/RefField"; +import { ObjectField } from "../../../new_fields/ObjectField"; +import { updateSourceFile } from "typescript"; +import { KeyValueBox } from "./KeyValueBox"; @observer export class WebBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(WebBox); } + @observable private collapsed: boolean = true; + @observable private url: string = ""; componentWillMount() { @@ -30,6 +40,76 @@ export class WebBox extends React.Component { } } + @action + onURLChange = (e: React.ChangeEvent) => { + this.url = e.target.value; + } + + @action + submitURL = () => { + const script = KeyValueBox.CompileKVPScript(`new WebField("${this.url}")`); + if (!script) return; + KeyValueBox.ApplyKVPScript(this.props.Document, "data", script); + let mod = document.getElementById("webpage-input"); + if (mod) mod.style.display = "none"; + } + + @computed + get getURL() { + let urlField: FieldResult = Cast(this.props.Document.data, WebField) + if (urlField) return urlField.url.toString(); + return ""; + } + + onValueKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + e.stopPropagation(); + this.submitURL(); + } + } + + urlEditor() { + return ( +
    +
    +
    + +
    + + +
    +
    +
    +
    + ); + } + + @action + toggleCollapse = () => { + // this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "this.collapsed" : "enabled"; + // if (this.props.collapse) { + // this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled"); + // } + this.collapsed = !this.collapsed; + } + _ignore = 0; onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; @@ -53,12 +133,13 @@ export class WebBox extends React.Component { if (field instanceof HtmlField) { view = ; } else if (field instanceof WebField) { - view =