diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ButtonBox.tsx | 21 | ||||
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.scss | 5 | ||||
| -rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 158 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.scss | 42 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 615 | ||||
| -rw-r--r-- | src/client/views/nodes/DragBox.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.scss | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 137 | ||||
| -rw-r--r-- | src/client/views/nodes/IconBox.tsx | 53 | ||||
| -rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 17 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValueBox.tsx | 29 | ||||
| -rw-r--r-- | src/client/views/nodes/KeyValuePair.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 11 |
14 files changed, 649 insertions, 447 deletions
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index 54848344b..68d3b8ae1 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -13,6 +13,8 @@ import { undoBatch } from '../../util/UndoManager'; import { DocComponent } from '../DocComponent'; import './ButtonBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { ContextMenu } from '../ContextMenu'; library.add(faEdit as any); @@ -41,11 +43,24 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } } + + specificContextMenu = (e: React.MouseEvent): void => { + let funcs: ContextMenuProps[] = []; + funcs.push({ + description: "Clear Script Params", event: () => { + let params = Cast(this.props.Document.buttonParams, listSpec("string")); + params && params.map(p => this.props.Document[p] = undefined) + }, icon: "trash" + }); + + ContextMenu.Instance.addItem({ description: "OnClick...", subitems: funcs, icon: "asterisk" }); + } + @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (de.data instanceof DragManager.DocumentDragData) { - Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments); + if (de.data instanceof DragManager.DocumentDragData && e.target) { + this.props.Document[(e.target as any).textContent] = new List<Doc>(de.data.droppedDocuments); e.stopPropagation(); } } @@ -55,7 +70,7 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt let missingParams = params && params.filter(p => this.props.Document[p] === undefined); params && params.map(async p => await DocListCastAsync(this.props.Document[p])); // bcz: really hacky form of prefetching ... return ( - <div className="buttonBox-outerDiv" ref={this.createDropTarget} > + <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}> <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} > <div className="buttonBox-mainButtonCenter"> {(this.Document.text || this.Document.title)} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss new file mode 100644 index 000000000..de0b00a81 --- /dev/null +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss @@ -0,0 +1,5 @@ +.collectionFreeFormDocumentView-container {
+ transform-origin: left top;
+ position: absolute;
+ background-color: transparent;
+}
\ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9692dd8a9..19d4a6784 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,13 +1,14 @@ -import { computed } from "mobx"; +import { computed, action, observable, reaction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types"; +import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; +import { FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; -import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; -import "./DocumentView.scss"; +import { percent2frac } from "../../../Utils" +import { DocumentView, DocumentViewProps, documentSchema } from "./DocumentView"; +import "./CollectionFreeFormDocumentView.scss"; import React = require("react"); -import { Doc } from "../../../new_fields/Doc"; +import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc"; import { random } from "animejs"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @@ -17,56 +18,80 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { height?: number; jitterRotation: number; } - -const schema = createSchema({ - zoomBasis: "number", +const positionSchema = createSchema({ zIndex: "number", + x: "number", + y: "number", + z: "number", }); -//TODO Types: The import order is wrong, so positionSchema is undefined -type FreeformDocument = makeInterface<[typeof schema, typeof positionSchema]>; -const FreeformDocument = makeInterface(schema, positionSchema); +export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>; +export const PositionDocument = makeInterface(documentSchema, positionSchema); @observer -export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) { - @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg) scale(${this.zoom}) `; } - @computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; } - @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; } - @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; } - @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; } - @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); } - @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); } - @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); } - @computed get scaleToOverridingWidth() { return this.width / NumCast(this.props.Document.width, this.width); } +export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) { + _disposer: IReactionDisposer | undefined = undefined; + @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; } + @computed get X() { return this._animx !== undefined ? this._animx : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; } + @computed get Y() { return this._animy !== undefined ? this._animy : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; } + @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); } + @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); } + @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); } + @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); } + @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); } + + @computed get renderScriptDim() { + if (this.Document.renderScript) { + let someView = Cast(this.props.Document.someView, Doc); + let minimap = Cast(this.props.Document.minimap, Doc); + if (someView instanceof Doc && minimap instanceof Doc) { + let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2; + let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2; + let w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width); + let h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height); + return { x: x, y: y, width: w, height: h }; + } + } + return undefined; + } + + componentWillUnmount() { + this._disposer && this._disposer(); + } + componentDidMount() { + this._disposer = reaction(() => this.props.Document.iconTarget, + () => { + const icon = this.props.Document.iconTarget ? Array.from(Cast(this.props.Document.iconTarget, listSpec("number"))!) : undefined; + if (icon) { + let target = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]); + if (icon[2] === 1) { + this._animx = target[0]; + this._animy = target[1]; + } + setTimeout(action(() => { + this._animx = icon[2] === 1 ? this.Document.x : target[0]; + this._animy = icon[2] === 1 ? this.Document.y : target[1]; + }), 25); + } else { + this._animx = this._animy = undefined; + } + }, { fireImmediately: true }); + } - contentScaling = () => this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? this.width / this.nativeWidth : 1; + contentScaling = () => this.nativeWidth > 0 && !this.props.Document.ignoreAspect ? this.width / this.nativeWidth : 1; panelWidth = () => this.props.PanelWidth(); panelHeight = () => this.props.PanelHeight(); getTransform = (): Transform => this.props.ScreenToLocalTransform() .translate(-this.X, -this.Y) - .scale(1 / this.contentScaling()).scale(1 / this.zoom / this.scaleToOverridingWidth) - - animateBetweenIcon = (icon: number[], stime: number, maximizing: boolean) => { - this.props.bringToFront(this.props.Document); - let targetPos = [this.Document.x || 0, this.Document.y || 0]; - let iconPos = this.props.ScreenToLocalTransform().transformPoint(icon[0], icon[1]); - DocumentView.animateBetweenIconFunc(this.props.Document, - this.Document.width || 0, this.Document.height || 0, stime, maximizing, (progress: number) => { - let pval = maximizing ? - [iconPos[0] + (targetPos[0] - iconPos[0]) * progress, iconPos[1] + (targetPos[1] - iconPos[1]) * progress] : - [targetPos[0] + (iconPos[0] - targetPos[0]) * progress, targetPos[1] + (iconPos[1] - targetPos[1]) * progress]; - this.Document.x = progress === 1 ? targetPos[0] : pval[0]; - this.Document.y = progress === 1 ? targetPos[1] : pval[1]; - }); - } + .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth); borderRounding = () => { - let br = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.borderRounding : this.props.Document.borderRounding); + let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; + let br = StrCast(((this.layoutDoc.layout as Doc) || this.Document).borderRounding); + br = !br && ruleRounding ? ruleRounding : br; if (br.endsWith("%")) { - let percent = Number(br.substr(0, br.length - 1)) / 100; - let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight)); - let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight())); - return minDim; + let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight)); + return percent2frac(br) * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight())); } return undefined; } @@ -76,41 +101,31 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF clusterColorFunc = (doc: Doc) => this.clusterColor; + get layoutDoc() { + // if this document's layout field contains a document (ie, a rendering template), then we will use that + // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string. + return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; + } + + @observable _animx: number | undefined = undefined; + @observable _animy: number | undefined = undefined; + render() { - let txf = this.transform; - let w = this.width; - let h = this.height; - let renderScript = this.Document.renderScript; - if (renderScript) { - let someView = Cast(this.Document.someView, Doc); - let minimap = Cast(this.Document.minimap, Doc); - if (someView instanceof Doc && minimap instanceof Doc) { - let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2; - let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2; - w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width); - h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height); - txf = `translate(${x}px,${y}px)`; - } - } const hasPosition = this.props.x !== undefined || this.props.y !== undefined; return ( <div className="collectionFreeFormDocumentView-container" style={{ - transformOrigin: "left top", - position: "absolute", - backgroundColor: "transparent", boxShadow: - this.props.Document.opacity === 0 ? undefined : // if it's not visible, then no shadow - this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow - this.clusterColor ? ( - this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big - `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent - undefined, + this.layoutDoc.opacity === 0 ? undefined : // if it's not visible, then no shadow + this.layoutDoc.z ? `#9c9396 ${StrCast(this.layoutDoc.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow + this.clusterColor ? (`${this.clusterColor} ${StrCast(this.layoutDoc.boxShadow, `0vw 0vw ${(this.layoutDoc.isBackground ? 100 : 50) / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent + this.layoutDoc.isBackground ? `1px 1px 1px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big + StrCast(this.layoutDoc.boxShadow, ""), borderRadius: this.borderRounding(), - transform: txf, - transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition), - width: w, - height: h, + transform: this.transform, + transition: this.props.Document.isIconAnimating ? "transform .5s" : hasPosition ? "transform 1s" : StrCast(this.layoutDoc.transition), + width: this.width, + height: this.height, zIndex: this.Document.zIndex || 0, }} > <DocumentView {...this.props} @@ -119,7 +134,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF backgroundColor={this.clusterColorFunc} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} - animateBetweenIcon={this.animateBetweenIcon} /> </div> ); diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 7c72fb6e6..63011f271 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -4,6 +4,8 @@ position: inherit; top: 0; left:0; + border-radius: inherit; + // background: $light-color; //overflow: hidden; transform-origin: left top; @@ -30,4 +32,44 @@ } .documentView-node-topmost { background: white; +} + +.documentView-styleWrapper { + position: absolute; + display: inline-block; + width:100%; + height:100%; + pointer-events: none; + + .documentView-styleContentWrapper { + width:100%; + display: inline-block; + position: absolute; + } + .documentView-titleWrapper { + overflow:hidden; + color: white; + transform-origin: top left; + top: 0; + height: 25; + background: rgba(0, 0, 0, .4); + padding: 4px; + text-align: center; + text-overflow: ellipsis; + white-space: pre; + } + + .documentView-searchHighlight { + position: absolute; + background: yellow; + bottom: -20px; + border-radius: 5px; + transform-origin: bottom left; + } + + .documentView-captionWrapper { + position: absolute; + bottom: 0; + transform-origin: bottom left; + } }
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 633455f63..37c38cc04 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; -import { action, computed, IReactionDisposer, reaction, runInAction, trace, observable } from "mobx"; +import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; import { Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; @@ -9,12 +9,13 @@ import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; +import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from '../../../server/RouteStore'; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; +import { DocumentType } from '../../documents/DocumentTypes'; import { ClientUtils } from '../../util/ClientUtils'; import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from "../../util/DocumentManager"; @@ -35,12 +36,11 @@ import { MainView } from '../MainView'; import { OverlayView } from '../OverlayView'; import { ScriptBox } from '../ScriptBox'; import { ScriptingRepl } from '../ScriptingRepl'; -import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); -import { DocumentType } from '../../documents/DocumentTypes'; +import { CompileScript, Scripting } from '../../util/Scripting'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -89,6 +89,7 @@ export interface DocumentViewProps { renderDepth: number; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; ContentScaling: () => number; + ruleProvider: Doc | undefined; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc, willZoom: boolean, scale?: number) => void; @@ -101,35 +102,41 @@ export interface DocumentViewProps { zoomToScale: (scale: number) => void; backgroundColor: (doc: Doc) => string | undefined; getScale: () => number; - animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; + animateBetweenIcon?: (maximize: boolean, target: number[]) => void; ChromeHeight?: () => number; } -const schema = createSchema({ - layout: "string", +export const documentSchema = createSchema({ + layout: "string", // should also allow Doc but that can't be expressed in the schema + title: "string", nativeWidth: "number", nativeHeight: "number", backgroundColor: "string", opacity: "number", hidden: "boolean", onClick: ScriptField, -}); - -export const positionSchema = createSchema({ - nativeWidth: "number", - nativeHeight: "number", + ignoreAspect: "boolean", + autoHeight: "boolean", + isTemplate: "boolean", + isButton: "boolean", + isBackground: "boolean", + ignoreClick: "boolean", + type: "string", + maximizeLocation: "string", + lockedPosition: "boolean", + excludeFromLibrary: "boolean", width: "number", height: "number", - x: "number", - y: "number", - z: "number", + borderRounding: "string", + fitToBox: "boolean", + searchFields: "string", + heading: "number", + showCaption: "string", + showTitle: "string" }); -export type PositionDocument = makeInterface<[typeof positionSchema]>; -export const PositionDocument = makeInterface(positionSchema); - -type Document = makeInterface<[typeof schema]>; -const Document = makeInterface(schema); +type Document = makeInterface<[typeof documentSchema]>; +const Document = makeInterface(documentSchema); @observer export class DocumentView extends DocComponent<DocumentViewProps, Document>(Document) { @@ -137,25 +144,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu private _downY: number = 0; private _lastTap: number = 0; private _doubleTap = false; - private _hitExpander = false; private _hitTemplateDrag = false; private _mainCont = React.createRef<HTMLDivElement>(); private _dropDisposer?: DragManager.DragDropDisposer; - _animateToIconDisposer?: IReactionDisposer; - _reactionDisposer?: IReactionDisposer; public get ContentDiv() { return this._mainCont.current; } @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); } @computed get topMost(): boolean { return this.props.renderDepth === 0; } - @computed get templates(): List<string> { - let field = this.props.Document.templates; - if (field && field instanceof List) { - return field; - } - return new List<string>(); - } - set templates(templates: List<string>) { this.props.Document.templates = templates; } - screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect(); @action componentDidMount() { @@ -164,48 +159,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu handlers: { drop: this.drop.bind(this) } }); } - // bcz: kind of ugly .. setup a reaction to update the title of a summary document's target (maximizedDocs) whenver the summary doc's title changes - this._reactionDisposer = reaction(() => [DocListCast(this.props.Document.maximizedDocs).map(md => md.title), - this.props.Document.summaryDoc, this.props.Document.summaryDoc instanceof Doc ? this.props.Document.summaryDoc.title : ""], - () => { - let maxDoc = DocListCast(this.props.Document.maximizedDocs); - if (maxDoc.length === 1 && StrCast(this.props.Document.title).startsWith("-") && StrCast(this.props.Document.layout).indexOf("IconBox") !== -1) { - this.props.Document.proto!.title = "-" + maxDoc[0].title + ".icon"; - } - let sumDoc = Cast(this.props.Document.summaryDoc, Doc); - if (sumDoc instanceof Doc && StrCast(this.props.Document.title).startsWith("-")) { - this.props.Document.proto!.title = "-" + sumDoc.title + ".expanded"; - } - }, { fireImmediately: true }); - this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => - (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false) - , { fireImmediately: true }); DocumentManager.Instance.DocumentViews.push(this); } - animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { - this.props.animateBetweenIcon ? this.props.animateBetweenIcon(iconPos, startTime, maximizing) : - DocumentView.animateBetweenIconFunc(this.props.Document, this.Document[WidthSym](), this.Document[HeightSym](), startTime, maximizing); - } - - public static animateBetweenIconFunc = (doc: Doc, width: number, height: number, stime: number, maximizing: boolean, cb?: (progress: number) => void) => { - setTimeout(() => { - let now = Date.now(); - let progress = now < stime + 200 ? Math.min(1, (now - stime) / 200) : 1; - doc.width = progress === 1 ? width : maximizing ? 25 + (width - 25) * progress : width + (25 - width) * progress; - doc.height = progress === 1 ? height : maximizing ? 25 + (height - 25) * progress : height + (25 - height) * progress; - cb && cb(progress); - if (now < stime + 200) { - DocumentView.animateBetweenIconFunc(doc, width, height, stime, maximizing, cb); - } - else { - doc.isMinimized = !maximizing; - doc.isIconAnimating = undefined; - } - doc.willMaximize = false; - }, - 2); - } @action componentDidUpdate() { this._dropDisposer && this._dropDisposer(); @@ -217,31 +173,24 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } @action componentWillUnmount() { - this._reactionDisposer && this._reactionDisposer(); - this._animateToIconDisposer && this._animateToIconDisposer(); this._dropDisposer && this._dropDisposer(); DocumentManager.Instance.DocumentViews.splice(DocumentManager.Instance.DocumentViews.indexOf(this), 1); } - stopPropagation = (e: React.SyntheticEvent) => { - e.stopPropagation(); - } - get dataDoc() { - if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) { - // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document - // has a template layout document, then we will render the template layout but use - // this document as the data document for the layout. - return this.props.Document; - } + // bcz: don't think we need this, but left it in in case strange behavior pops up. DocumentContentsView has this functionality + // if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) { + // // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document + // // has a template layout document, then we will render the template layout but use + // // this document as the data document for the layout. + // return this.props.Document; + // } return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; } - startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) { + startDragging(x: number, y: number, dropAction: dropActionType, applyAsTemplate?: boolean) { if (this._mainCont.current) { - let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; - let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); - let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected); + let dragData = new DragManager.DocumentDragData([this.props.Document]); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.xOffset = xoff; @@ -256,47 +205,54 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu }); } } - toggleMinimized = async () => { - let minimizedDoc = await Cast(this.props.Document.minimizedDoc, Doc); - if (minimizedDoc) { - let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint( - NumCast(minimizedDoc.x) - NumCast(this.Document.x), NumCast(minimizedDoc.y) - NumCast(this.Document.y)); - this.collapseTargetsToPoint(scrpt, await DocListCastAsync(minimizedDoc.maximizedDocs)); - } - } - static _undoBatch?: UndoManager.Batch = undefined; @action public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { SelectionManager.DeselectAll(); if (expandedDocs) { - if (!DocumentView._undoBatch) { - DocumentView._undoBatch = UndoManager.StartBatch("iconAnimating"); - } let isMinimized: boolean | undefined; expandedDocs.map(maximizedDoc => { - let iconAnimating = Cast(maximizedDoc.isIconAnimating, List); - if (!iconAnimating || (Date.now() - iconAnimating[2] > 1000)) { - if (isMinimized === undefined) { - isMinimized = BoolCast(maximizedDoc.isMinimized); + if (isMinimized === undefined) { + isMinimized = BoolCast(maximizedDoc.isMinimized); + } + let w = NumCast(maximizedDoc.width); + let h = NumCast(maximizedDoc.height); + let iconAnimating = maximizedDoc.isIconAnimating ? Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!) : undefined; + if (isMinimized || (iconAnimating && iconAnimating.length && iconAnimating[0] === 0)) { + // MAXIMIZE DOC + if (maximizedDoc.isMinimized) { + maximizedDoc.isIconAnimating = new List<number>([0, 0]); + maximizedDoc.isMinimized = false; } - maximizedDoc.willMaximize = isMinimized; - maximizedDoc.isMinimized = false; - maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], Date.now(), isMinimized ? 1 : 0]); + maximizedDoc.iconTarget = new List<number>([...scrpt, 1]); + setTimeout(() => { + maximizedDoc.isIconAnimating = new List<number>([w, h]); + setTimeout(() => { + if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] !== 0) { + maximizedDoc.isIconAnimating = undefined; + } + }, 750); + }, 0); + } else { + maximizedDoc.iconTarget = new List<number>([...scrpt, 0]); + // MINIMIZE DOC + maximizedDoc.isIconAnimating = new List<number>([0, 0]); + setTimeout(() => { + if (maximizedDoc.isIconAnimating && Array.from(Cast(maximizedDoc.isIconAnimating, listSpec("number"))!)[0] === 0) { + maximizedDoc.isMinimized = true; + maximizedDoc.isIconAnimating = undefined; + } + }, 750); } }); - setTimeout(() => { - DocumentView._undoBatch && DocumentView._undoBatch.end(); - DocumentView._undoBatch = undefined; - }, 500); } } onClick = async (e: React.MouseEvent) => { - if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. + if (e.nativeEvent.cancelBubble) return; // || SelectionManager.IsSelected(this)) -- bcz: needed because EditableView may stopPropagation which won't apparently stop this event from firing. if (this.onClickHandler && this.onClickHandler.script) { e.stopPropagation(); - this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }); + this.onClickHandler.script.run({ this: this.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }); e.preventDefault(); return; } @@ -305,41 +261,34 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (this._doubleTap && this.props.renderDepth) { e.stopPropagation(); let fullScreenAlias = Doc.MakeAlias(this.props.Document); - fullScreenAlias.templates = new List<string>(); Doc.UseDetailLayout(fullScreenAlias); - fullScreenAlias.showCaption = true; + fullScreenAlias.showCaption = "caption"; this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } - else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] && + else if (!this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { - if (BoolCast(this.props.Document.ignoreClick)) { - return; - } e.stopPropagation(); SelectionManager.SelectDoc(this, e.ctrlKey); - let isExpander = (e.target as any).id === "isExpander"; - if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) { - let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); + if (this.Document.isButton || this.Document.type === DocumentType.BUTTON) { let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; - expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; - // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),]; - if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents + // let expandedDocs = [ ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),]; + if (expandedDocs.length) { SelectionManager.DeselectAll(); - let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); + let maxLocation = StrCast(this.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); if (altKey || ctrlKey) { - maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace")); + maxLocation = this.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace")); if (!maxLocation || maxLocation === "inPlace") { let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView); - let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized), false); + let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !d.isMinimized, false); expandedDocs.forEach(maxDoc => Doc.GetProto(maxDoc).isMinimized = false); let hasView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedDocs[0], this.props.ContainingCollectionView); if (!hasView) { @@ -362,7 +311,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } else if (linkedDocs.length) { SelectionManager.DeselectAll(); - let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document)); + let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored); + let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + if (firstUnshown.length) first = [firstUnshown[0]]; let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]]; // @TODO: shouldn't always follow target context @@ -399,7 +350,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (e.nativeEvent.cancelBubble) return; this._downX = e.clientX; this._downY = e.clientY; - this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; this._hitTemplateDrag = false; for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { @@ -417,11 +367,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu document.removeEventListener("pointermove", this.onPointerMove); } else if (!e.cancelBubble && this.active) { - if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) { - if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) { + if (!this.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) { + if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.Document.lockedPosition)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -439,32 +389,70 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } @undoBatch - fieldsClicked = (): void => { - let kvp = Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }); - this.props.addDocTab(kvp, this.dataDoc, "onRight"); + makeNativeViewClicked = (): void => { + makeNativeView(this.props.Document); + } + @undoBatch + makeCustomViewClicked = (): void => { + this.props.Document.nativeLayout = this.Document.layout; + this.props.Document.nativeType = this.Document.type; + this.props.Document.nonCustomAutoHeight = this.Document.autoHeight; + this.props.Document.nonCustomWidth = this.Document.width; + this.props.Document.nonCustomHeight = this.Document.height; + this.props.Document.nonCustomNativeWidth = this.Document.nativeWidth; + this.props.Document.nonCustomNativeHeight = this.Document.nativeHeight; + this.props.Document.nonCustomIgnoreAspect = this.Document.ignoreAspect; + PromiseValue(Cast(this.props.Document.customLayout, Doc)).then(custom => { + if (custom) { + this.Document.type = DocumentType.TEMPLATE; + this.props.Document.layout = custom; + !custom.nativeWidth && (this.Document.nativeWidth = 0); + !custom.nativeHeight && (this.Document.nativeHeight = 0); + !custom.nativeWidth && (this.Document.ignoreAspect = true); + this.Document.autoHeight = BoolCast(this.Document.customAutoHeight); + this.Document.width = NumCast(this.props.Document.customWidth); + this.Document.height = NumCast(this.props.Document.customHeight); + this.Document.nativeWidth = NumCast(this.props.Document.customNativeWidth); + this.Document.nativeHeight = NumCast(this.props.Document.customNativeHeight); + this.Document.ignoreAspect = BoolCast(this.Document.customIgnoreAspect); + this.props.Document.customAutoHeight = undefined; + this.props.Document.customWidth = undefined; + this.props.Document.customHeight = undefined; + this.props.Document.customNativeWidth = undefined; + this.props.Document.customNativeHeight = undefined; + this.props.Document.customIgnoreAspect = undefined; + } else { + let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, }; + let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : + this.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) : + Docs.Create.ImageDocument("http://www.cs.brown.edu", options); + + fieldTemplate.backgroundColor = this.Document.backgroundColor; + fieldTemplate.heading = 1; + fieldTemplate.autoHeight = true; + + let docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: this.Document.title + "layout", width: (this.Document.width || 0) + 20, height: Math.max(100, (this.Document.height || 0) + 45) }); + let proto = Doc.GetProto(docTemplate); + Doc.MakeMetadataFieldTemplate(fieldTemplate, proto, true); + + Doc.ApplyTemplateTo(docTemplate, this.props.Document, undefined, false); + Doc.GetProto(this.dataDoc || this.props.Document).customLayout = this.Document.layout; + } + }); } @undoBatch makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); - doc.isButton = !BoolCast(doc.isButton); - if (doc.isButton) { - if (!doc.nativeWidth) { - doc.nativeWidth = this.props.Document[WidthSym](); - doc.nativeHeight = this.props.Document[HeightSym](); - } + if (doc.isButton || doc.onClick) { + doc.isButton = false; + doc.onClick = undefined; } else { - doc.nativeWidth = doc.nativeHeight = undefined; + doc.isButton = true; } } @undoBatch - public fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this); - SelectionManager.DeselectAll(); - } - - @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.AnnotationDragData) { @@ -519,50 +507,57 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } + @undoBatch @action - addTemplate = (template: Template) => { - this.templates.push(template.Layout); - this.templates = this.templates; - } - - @action - removeTemplate = (template: Template) => { - for (let i = 0; i < this.templates.length; i++) { - if (this.templates[i] === template.Layout) { - this.templates.splice(i, 1); - break; - } + freezeNativeDimensions = (): void => { + let proto = this.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document); + proto.autoHeight = this.Document.autoHeight = false; + proto.ignoreAspect = !proto.ignoreAspect; + if (!proto.ignoreAspect && !proto.nativeWidth) { + proto.nativeWidth = this.props.PanelWidth(); + proto.nativeHeight = this.props.PanelHeight(); } - this.templates = this.templates; } + @undoBatch @action - clearTemplates = () => { - this.templates.length = 0; - this.templates = this.templates; + makeIntoPortal = (): void => { + if (!DocListCast(this.props.Document.links).find(doc => { + if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.Document.title + ".portal") return true; + return false; + })) { + let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); + DocServer.GetRefField(portalID).then(existingPortal => { + let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID }); + DocUtils.MakeLink(this.props.Document, portal, undefined, portalID); + Doc.GetProto(this.props.Document).isButton = true; + }) + } } - @undoBatch @action - freezeNativeDimensions = (): void => { - let proto = this.props.Document.isTemplate ? this.props.Document : Doc.GetProto(this.props.Document); - this.props.Document.autoHeight = proto.autoHeight = false; - proto.ignoreAspect = !BoolCast(proto.ignoreAspect); - if (!BoolCast(proto.ignoreAspect) && !proto.nativeWidth) { - proto.nativeWidth = this.props.PanelWidth(); - proto.nativeHeight = this.props.PanelHeight(); + toggleCustomView = (): void => { + if (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.DataDoc) { + Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.ContainingCollectionView.props.DataDoc) + } else { + if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) { + this.makeCustomViewClicked(); + } else if (this.Document.nativeLayout) { + this.makeNativeViewClicked(); + } } } + @undoBatch @action makeBackground = (): void => { - this.props.Document.isBackground = !this.props.Document.isBackground; - this.props.Document.isBackground && this.props.bringToFront(this.props.Document, true); + this.layoutDoc.isBackground = !this.layoutDoc.isBackground; + this.layoutDoc.isBackground && this.props.bringToFront(this.layoutDoc, true); } @undoBatch @action toggleLockPosition = (): void => { - this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true; + this.layoutDoc.lockedPosition = BoolCast(this.layoutDoc.lockedPosition) ? undefined : true; } listen = async () => { @@ -590,45 +585,60 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const cm = ContextMenu.Instance; let subitems: ContextMenuProps[] = []; - subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" }); - subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); + subitems.push({ description: "Open Full Screen", event: () => CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(this), icon: "desktop" }); + subitems.push({ description: "Open Tab ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); - let existingMake = ContextMenu.Instance.findByDescription("Make..."); - let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : []; - makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" }); - makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" }); - makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") }); - makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") }); - makes.push({ - description: "Into Portal", event: () => { - if (!DocListCast(this.props.Document.links).find(doc => { - if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true; - return false; - })) { - let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" }); - DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal"); - Doc.GetProto(this.props.Document).isButton = true; + + let existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); + let onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; + onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); + onClicks.push({ + description: "Toggle Detail", event: () => { + let compiled = CompileScript("toggleDetail(this)", { + params: { this: "Doc" }, + typecheck: false, + editable: true, + }); + if (compiled.compiled) { + this.Document.onClick = new ScriptField(compiled); } }, icon: "window-restore" }); - makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" }); - !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); + onClicks.push({ description: this.layoutDoc.ignoreClick ? "Select" : "Do Nothing", event: () => this.layoutDoc.ignoreClick = !this.layoutDoc.ignoreClick, icon: this.layoutDoc.ignoreClick ? "unlock" : "lock" }); + onClicks.push({ description: this.Document.isButton || this.Document.onClick ? "Remove Click Behavior" : "Follow Link", event: this.makeBtnClicked, icon: "concierge-bell" }); + onClicks.push({ description: "Edit onClick Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", obj.x, obj.y) }); + onClicks.push({ + description: "Edit onClick Foreach Doc Script", icon: "edit", event: (obj: any) => { + this.props.Document.collectionContext = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document; + ScriptBox.EditButtonScript("Foreach Collection Doc (d) => ", this.props.Document, "onClick", obj.x, obj.y, "docList(this.collectionContext.data).map(d => {", "});\n"); + } + }); + !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); + let existing = ContextMenu.Instance.findByDescription("Layout..."); let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; - - layoutItems.push({ description: `${this.props.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); - layoutItems.push({ description: `${this.props.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.props.Document.autoHeight = !this.props.Document.autoHeight, icon: "plus" }); - layoutItems.push({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); - layoutItems.push({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); + layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" }); + if (this.props.DataDoc) { + layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" }) + } + layoutItems.push({ description: `${this.layoutDoc.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.layoutDoc.chromeStatus = (this.layoutDoc.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); + layoutItems.push({ description: `${this.layoutDoc.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" }); + layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); + layoutItems.push({ description: this.layoutDoc.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.layoutDoc.lockedPosition) ? "unlock" : "lock" }); layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); - if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) { + if (this.props.Document.detailedLayout && !this.Document.isTemplate) { layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" }); } + if (this.Document.type !== DocumentType.COL && this.Document.type !== DocumentType.TEMPLATE) { + layoutItems.push({ description: "Use Custom Layout", event: this.makeCustomViewClicked, icon: "concierge-bell" }); + } else if (this.props.Document.nativeLayout) { + layoutItems.push({ description: "Use Native Layout", event: this.makeNativeViewClicked, icon: "concierge-bell" }); + } !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); if (!ClientUtils.RELEASE) { let copies: ContextMenuProps[] = []; @@ -642,17 +652,21 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle! cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); - cm.addItem({ description: "Move To Overlay", icon: "laptop-code", event: () => ((o: Doc) => o && Doc.AddDocToList(o, "data", this.props.Document))(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc) }); cm.addItem({ - description: "Download document", icon: "download", event: () => { - const a = document.createElement("a"); - const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); - a.href = url; - a.download = `DocExport-${this.props.Document[Id]}.zip`; - a.click(); + description: "Download document", icon: "download", event: async () => { + let y = JSON.parse(await rp.get(Utils.CorsProxy("http://localhost:8983/solr/dash/select"), { + qs: { q: 'world', fq: 'NOT baseProto_b:true AND NOT deleted:true', start: '0', rows: '100', hl: true, 'hl.fl': '*' } + })); + console.log(y); + // const a = document.createElement("a"); + // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + // a.href = url; + // a.download = `DocExport-${this.props.Document[Id]}.zip`; + // a.click(); } }); + cm.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; @@ -732,7 +746,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu select={this.select} onClick={this.onClickHandler} layoutKey={"layout"} - fitToBox={BoolCast(this.props.Document.fitToBox) ? true : this.props.fitToBox} + fitToBox={this.Document.fitToBox ? true : this.props.fitToBox} DataDoc={this.dataDoc} />); } @@ -748,45 +762,68 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return (showTitle ? 25 : 0) + 1;// bcz: why 8?? } - get layoutDoc() { + get layoutDoc(): Document { // if this document's layout field contains a document (ie, a rendering template), then we will use that // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string. - return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; + return Document(this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document); } - render() { - let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ? + const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined; + const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; + const colorSet = this.layoutDoc.backgroundColor !== this.layoutDoc.defaultBackgroundColor; + const clusterCol = this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground; + const backgroundColor = this.layoutDoc.isBackground || (clusterCol && !colorSet) ? this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) : - StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc); - let foregroundColor = StrCast(this.layoutDoc.color); - var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; - let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined; - let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle); - let showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : StrCast(this.layoutDoc.showCaption); - let templates = Cast(this.layoutDoc.templates, listSpec("string")); - if (!showOverlays && templates instanceof List) { - templates.map(str => { - if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title"; - if (!showCaption && str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption"; - }); - } - let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined; - let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document); - let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding); - let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree; + ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc); + + const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; + const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined; + const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.layoutDoc.showTitle; + const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.layoutDoc.showCaption; + const showTextTitle = showTitle && StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1 ? showTitle : undefined; + const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document); + const borderRounding = this.Document.borderRounding || ruleRounding; + const localScale = this.props.ScreenToLocalTransform().Scale * fullDegree; + const iconAnimating = this.Document.isIconAnimating ? Array.from(Cast(this.Document.isIconAnimating, listSpec("number"))!) : undefined; + const searchHighlight = (!this.Document.searchFields ? (null) : + <div className="documentView-searchHighlight" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}> + {this.Document.searchFields} + </div>); + const captionView = (!showCaption ? (null) : + <div className="documentView-captionWrapper" style={{ width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}> + <FormattedTextBox {...this.props} + onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} + isSelected={this.isSelected} focus={emptyFunction} select={this.select} + fieldExt={""} hideOnLeave={true} fieldKey={showCaption} + /> + </div>); + const titleView = (!showTitle ? (null) : + <div className="documentView-titleWrapper" style={{ + position: showTextTitle ? "relative" : "absolute", + pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all", + width: `${100 * this.props.ContentScaling()}%`, + transform: `scale(${1 / this.props.ContentScaling()})` + }}> + <EditableView + contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]} + display={"block"} height={72} fontSize={12} + GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle])} + SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle] = value) ? true : true} + /> + </div>); return ( <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} style={{ + transition: iconAnimating ? "transform .5s" : StrCast(this.layoutDoc.transition), pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all", - color: foregroundColor, + color: StrCast(this.layoutDoc.color), outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree], outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree], outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px", border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined, - borderRadius: "inherit", background: backgroundColor, width: nativeWidth, height: nativeHeight, @@ -796,37 +833,89 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > - {!showTitle && !showCaption ? this.contents : - <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}> - - <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}> + {!showTitle && !showCaption ? + this.Document.searchFields ? + (<div className="documentView-searchWrapper"> + {this.contents} + {searchHighlight} + </div>) + : + this.contents + : + <div className="documentView-styleWrapper" > + <div className="documentView-styleContentWrapper" style={{ height: showTextTitle ? "calc(100% - 29px)" : "100%", top: showTextTitle ? "29px" : undefined }}> {this.contents} </div> - {!showTitle ? (null) : - <div style={{ - position: showTextTitle ? "relative" : "absolute", top: 0, padding: "4px", textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre", - pointerEvents: SelectionManager.GetIsDragging() ? "none" : "all", - overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white", - transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})` - }}> - <EditableView - contents={(this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle]} - display={"block"} - height={72} - fontSize={12} - GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])} - SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true} - /> - </div> - } - {!showCaption ? (null) : - <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}> - <FormattedTextBox {...this.props} onClick={this.onClickHandler} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} /> - </div> - } + {titleView} + {captionView} + {searchHighlight} </div> } </div> ); } -}
\ No newline at end of file +} + + +let makeNativeView = (doc: any): void => { + doc.layout = doc.nativeLayout; + doc.nativeLayout = undefined; + doc.type = doc.nativeType; + + doc.customAutoHeight = doc.autoHeight; + doc.customWidth = doc.width; + doc.customHeight = doc.height; + doc.customNativeWidth = doc.nativeWidth; + doc.customNativeHeight = doc.nativeHeight; + doc.customIgnoreAspect = doc.ignoreAspect; + + doc.autoHeight = doc.nonCustomAutoHeight; + doc.width = doc.nonCustomWidth; + doc.height = doc.nonCustomHeight; + doc.nativeWidth = doc.nonCustomNativeWidth; + doc.nativeHeight = doc.nonCustomNativeHeight; + doc.ignoreAspect = doc.nonCustomIgnoreAspect; + doc.nonCustomAutoHeight = undefined; + doc.nonCustomWidth = undefined; + doc.nonCustomHeight = undefined; + doc.nonCustomNativeWidth = undefined; + doc.nonCustomNativeHeight = undefined; + doc.nonCustomIgnoreAspect = undefined; +} +let makeCustomView = (doc: any): void => { + doc.nativeLayout = doc.layout; + doc.nativeType = doc.type; + doc.nonCustomAutoHeight = doc.autoHeight; + doc.nonCustomWidth = doc.nativeWidth; + doc.nonCustomHeight = doc.nativeHeight; + doc.nonCustomNativeWidth = doc.nativeWidth; + doc.nonCustomNativeHeight = doc.nativeHeight; + doc.nonCustomIgnoreAspect = doc.ignoreAspect; + let custom = doc.customLayout as Doc; + if (custom instanceof Doc) { + doc.type = DocumentType.TEMPLATE; + doc.layout = custom; + !custom.nativeWidth && (doc.nativeWidth = 0); + !custom.nativeHeight && (doc.nativeHeight = 0); + !custom.nativeWidth && (doc.ignoreAspect = true); + doc.autoHeight = doc.autoHeight; + doc.width = doc.customWidth; + doc.height = doc.customHeight; + doc.nativeWidth = doc.customNativeWidth; + doc.nativeHeight = doc.customNativeHeight; + doc.ignoreAspect = doc.ignoreAspect; + doc.customAutoHeight = undefined; + doc.customWidth = undefined; + doc.customHeight = undefined; + doc.customNativeWidth = undefined; + doc.customNativeHeight = undefined; + doc.customIgnoreAspect = undefined; + } +} +Scripting.addGlobal(function toggleDetail(doc: any) { + if (doc.type !== DocumentType.COL && doc.type !== DocumentType.TEMPLATE) { + makeCustomView(doc); + } else if (doc.nativeLayout) { + makeNativeView(doc); + } +});
\ No newline at end of file diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx index 1f2c88086..067d47de4 100644 --- a/src/client/views/nodes/DragBox.tsx +++ b/src/client/views/nodes/DragBox.tsx @@ -55,7 +55,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu 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); + doc && DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY); } e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index d9774303b..943d181d6 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -30,6 +30,7 @@ export interface FieldViewProps { leaveNativeSize?: boolean; fitToBox?: boolean; ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>; + ruleProvider: Doc | undefined; Document: Doc; DataDoc?: Doc; onClick?: ScriptField; diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index d7ac7a9c5..0d7277cbe 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -4,7 +4,6 @@ width: 100%; height: 100%; min-height: 100%; - font-family: $serif; } .ProseMirror:focus { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9abc7b329..a3cfe10fc 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -39,7 +39,6 @@ import { ReplaceStep } from 'prosemirror-transform'; import { DocumentType } from '../../documents/DocumentTypes'; import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment'; import { inputRules } from 'prosemirror-inputrules'; -import { select } from 'async'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -79,15 +78,19 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _linkClicked = ""; private _nodeClicked: any; private _undoTyping?: UndoManager.Batch; - private _reactionDisposer: Opt<IReactionDisposer>; private _searchReactionDisposer?: Lambda; + private _reactionDisposer: Opt<IReactionDisposer>; private _textReactionDisposer: Opt<IReactionDisposer>; private _heightReactionDisposer: Opt<IReactionDisposer>; + private _rulesReactionDisposer: Opt<IReactionDisposer>; private _proxyReactionDisposer: Opt<IReactionDisposer>; private _pullReactionDisposer: Opt<IReactionDisposer>; private _pushReactionDisposer: Opt<IReactionDisposer>; private dropDisposer?: DragManager.DragDropDisposer; + @observable private _fontSize = 13; + @observable private _fontFamily = "Arial"; + @observable private _fontAlign = ""; @observable private _entered = false; @observable public static InputBoxOverlay?: FormattedTextBox = undefined; public static SelectOnLoad = ""; @@ -119,14 +122,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @undoBatch public setFontColor(color: string) { - this._editorView!.state.storedMarks; - if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false; - if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) { + let view = this._editorView!; + if (view.state.selection.from === view.state.selection.to) return false; + if (view.state.selection.to - view.state.selection.from > view.state.doc.nodeSize - 3) { this.props.Document.color = color; } - let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color }); - this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from, - this._editorView!.state.selection.to, colorMark)); + let colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color }); + view.dispatch(view.state.tr.addMark(view.state.selection.from, view.state.selection.to, colorMark)); return true; } @@ -230,6 +232,25 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + linkOnDeselect: Map<string, string> = new Map(); + + doLinkOnDeselect() { + Array.from(this.linkOnDeselect.entries()).map(entry => { + let key = entry[0]; + let value = entry[1]; + let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); + DocServer.GetRefField(value).then(doc => { + DocServer.GetRefField(id).then(linkDoc => { + this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); + DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); + if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } + else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true); + }) + }); + }) + this.linkOnDeselect.clear(); + } + dispatchTransaction = (tx: Transaction) => { if (this._editorView) { let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata); @@ -244,15 +265,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (split.length > 1 && split[1]) { let key = split[0]; let value = split[split.length - 1]; + this.linkOnDeselect.set(key, value); let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); - DocServer.GetRefField(value).then(doc => { - DocServer.GetRefField(id).then(linkDoc => { - this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); - if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } - else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id); - }) - }); const link = this._editorView!.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value }); const mval = this._editorView!.state.schema.marks.metadataVal.create(); let offset = (tx.selection.to === range!.end - 1 ? -1 : 0); @@ -316,12 +331,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } }); - // const fieldkey = 'search_string'; - // if (Object.keys(this.props.Document).indexOf(fieldkey) !== -1) { - // this.props.Document[fieldkey] = undefined; - // } - // else this.props.Document.proto![fieldkey] = undefined; - // } } } setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => { @@ -351,16 +360,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe e.stopPropagation(); } else if (de.data instanceof DragManager.DocumentDragData) { const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0]; - if (draggedDoc && draggedDoc.type === DocumentType.TEXT && StrCast(draggedDoc.layout) !== "") { - this.props.Document.layout = draggedDoc; - draggedDoc.isTemplate = true; + if (draggedDoc && draggedDoc.type === DocumentType.TEXT) { + if (!Doc.AreProtosEqual(draggedDoc, this.props.Document)) { + draggedDoc.isTemplate = true; + if (typeof (draggedDoc.layout) === "string") { + let layoutDelegateToOverrideFieldKey = Doc.MakeDelegate(draggedDoc); + layoutDelegateToOverrideFieldKey.layout = StrCast(layoutDelegateToOverrideFieldKey.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`); + this.props.Document.layout = layoutDelegateToOverrideFieldKey; + } else { + this.props.Document.layout = draggedDoc.layout instanceof Doc ? draggedDoc.layout : draggedDoc; + } + } e.stopPropagation(); } } } recordKeyHandler = (e: KeyboardEvent) => { - if (this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) { + if (SelectionManager.SelectedDocuments().length && this.props.Document === SelectionManager.SelectedDocuments()[0].props.Document) { if (e.key === "R" && e.altKey) { e.stopPropagation(); e.preventDefault(); @@ -439,6 +456,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } componentDidMount() { + if (!this.props.isOverlay) { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), () => { @@ -521,6 +539,39 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.unhighlightSearchTerms(); } }, { fireImmediately: true }); + + + this._rulesReactionDisposer = reaction(() => { + let ruleProvider = this.props.ruleProvider; + let heading = NumCast(this.props.Document.heading); + if (ruleProvider instanceof Doc) { + return { + align: StrCast(ruleProvider["ruleAlign_" + heading], ""), + font: StrCast(ruleProvider["ruleFont_" + heading], "Arial"), + size: NumCast(ruleProvider["ruleSize_" + heading], 13) + }; + } + return undefined; + }, + action((rules: any) => { + this._fontFamily = rules ? rules.font : "Arial"; + this._fontSize = rules ? rules.size : 13; + rules && setTimeout(() => { + const view = this._editorView!; + if (this._proseRef) { + let n = new NodeSelection(view.state.doc.resolve(0)); + if (this._editorView!.state.doc.textContent === "") { + view.dispatch(view.state.tr.setSelection(new TextSelection(view.state.doc.resolve(0), view.state.doc.resolve(2))). + replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: rules.align }), true)); + } else if (n.node && n.node.type === view.state.schema.nodes.paragraph) { + view.dispatch(view.state.tr.setNodeMarkup(0, n.node.type, { ...n.node.attrs, align: rules.align })); + } + this.tryUpdateHeight(); + } + }, 0); + }), { fireImmediately: true } + ); + setTimeout(() => this.tryUpdateHeight(), 0); } @@ -728,27 +779,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe else if (this.props.isOverlay) this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })]; - let heading = this.props.Document.heading; - if (heading && selectOnLoad) { - PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => { - if (ruleProvider) { - let align = StrCast(ruleProvider["ruleAlign_" + heading]); - let font = StrCast(ruleProvider["ruleFont_" + heading]); - let size = NumCast(ruleProvider["ruleSize_" + heading]); - if (align) { - let tr = this._editorView!.state.tr; - tr = tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(2))). - replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: align }), true). - setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(0))); - this._editorView!.dispatch(tr); - } - let sm = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })]; - size && (sm = [...sm, schema.marks.pFontSize.create({ fontSize: size })]); - font && (sm = [...sm, this.getFont(font)]); - this._editorView!.dispatch(this._editorView!.state.tr.setStoredMarks(sm)); - } - }); - } } getFont(font: string) { switch (font) { @@ -764,6 +794,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } componentWillUnmount() { + this._rulesReactionDisposer && this._rulesReactionDisposer(); this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); this._textReactionDisposer && this._textReactionDisposer(); @@ -921,6 +952,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._undoTyping.end(); this._undoTyping = undefined; } + this.doLinkOnDeselect(); } onKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Escape") { @@ -930,7 +962,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (e.key === "Tab" || e.key === "Enter") { e.preventDefault(); } - this._editorView!.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })); + this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })); if (!this._undoTyping) { this._undoTyping = UndoManager.StartBatch("undoTyping"); @@ -949,12 +981,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } - render() { let style = this.props.isOverlay ? "scroll" : "hidden"; let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : ""; - let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground || - (this.props.Document.isButton && !this.props.isSelected()) ? "none" : "all"; + let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground + //|| (this.props.Document.isButton && !this.props.isSelected()) + ? "none" : "all"; Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); return ( <div className={`formattedTextBox-cont-${style}`} ref={this._ref} @@ -965,7 +997,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe opacity: this.props.hideOnLeave ? (this._entered || this.props.isSelected() || Doc.IsBrushed(this.props.Document) ? 1 : 0.1) : 1, color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "inherit", pointerEvents: interactive, - fontSize: "13px" + fontSize: this._fontSize, + fontFamily: this._fontFamily, }} onKeyDown={this.onKeyPress} onFocus={this.onFocused} @@ -978,7 +1011,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onPointerEnter={action(() => this._entered = true)} onPointerLeave={action(() => this._entered = false)} > - <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap" }} /> + <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: (this.props.Document.isButton && !this.props.isSelected()) ? "none" : undefined }} ref={this.createDropTarget} /> </div> ); } diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 7e78ec684..92cb5a9c9 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -12,6 +12,8 @@ import { IconField } from "../../../new_fields/IconField"; import { ContextMenu } from "../ContextMenu"; import Measure from "react-measure"; import { MINIMIZED_ICON_SIZE } from "../../views/globalCssVariables.scss"; +import { Scripting, CompileScript } from "../../util/Scripting"; +import { ComputedField } from "../../../new_fields/ScriptField"; library.add(faCaretUp); @@ -27,6 +29,26 @@ export class IconBox extends React.Component<FieldViewProps> { @computed get layout(): string { const field = Cast(this.props.Document[this.props.fieldKey], IconField); return field ? field.icon : "<p>Error loading icon data</p>"; } @computed get minimizedIcon() { return IconBox.DocumentIcon(this.layout); } + public static summaryTitleScript(inputDoc: Doc) { + const sumDoc = Cast(inputDoc.summaryDoc, Doc) as Doc; + if (sumDoc && StrCast(sumDoc.title).startsWith("-")) { + return sumDoc.title + ".expanded"; + } + return "???"; + } + public static titleScript(inputDoc: Doc) { + const maxDoc = DocListCast(inputDoc.maximizedDocs); + if (maxDoc.length === 1) { + return maxDoc[0].title + ".icon"; + } + return maxDoc.length > 1 ? "-multiple-.icon" : "???"; + } + + public static AutomaticTitle(doc: Doc) { + let computed = CompileScript(`return iconTitle(this);`, { params: { this: "Doc" }, typecheck: false }); + computed.compiled && (Doc.GetProto(doc).title = new ComputedField(computed)); + } + public static DocumentIcon(layout: string) { let button = layout.indexOf("PDFBox") !== -1 ? faFilePdf : layout.indexOf("ImageBox") !== -1 ? faImage : @@ -38,35 +60,20 @@ export class IconBox extends React.Component<FieldViewProps> { } setLabelField = (): void => { - this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel); - } - setUseOwnTitleField = (): void => { - this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle); + this.props.Document.hideLabel = !this.props.Document.hideLabel; } specificContextMenu = (): void => { - ContextMenu.Instance.addItem({ - description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon", - event: this.setLabelField, - icon: "tag" - }); - let maxDocs = DocListCast(this.props.Document.maximizedDocs); - if (maxDocs.length === 1 && !BoolCast(this.props.Document.hideLabel)) { - ContextMenu.Instance.addItem({ - description: BoolCast(this.props.Document.useOwnTitle) ? "Use target title for label" : "Use own title label", - event: this.setUseOwnTitleField, - icon: "text-height" - }); + let cm = ContextMenu.Instance; + cm.addItem({ description: this.props.Document.hideLabel ? "Show label with icon" : "Remove label from icon", event: this.setLabelField, icon: "tag" }); + if (!this.props.Document.hideLabel) { + cm.addItem({ description: "Use Target Title", event: () => IconBox.AutomaticTitle(this.props.Document), icon: "text-height" }); } } @observable _panelWidth: number = 0; @observable _panelHeight: number = 0; render() { - let labelField = StrCast(this.props.Document.labelField); - let hideLabel = BoolCast(this.props.Document.hideLabel); - let maxDocs = DocListCast(this.props.Document.maximizedDocs); - let firstDoc = maxDocs.length ? maxDocs[0] : undefined; - let label = hideLabel ? "" : (firstDoc && labelField && !BoolCast(this.props.Document.useOwnTitle) ? firstDoc[labelField] : this.props.Document.title); + let label = this.props.Document.hideLabel ? "" : this.props.Document.title; return ( <div className="iconBox-container" onContextMenu={this.specificContextMenu}> {this.minimizedIcon} @@ -82,4 +89,6 @@ export class IconBox extends React.Component<FieldViewProps> { </Measure> </div>); } -}
\ No newline at end of file +} +Scripting.addGlobal(function iconTitle(doc: any) { return IconBox.titleScript(doc); }); +Scripting.addGlobal(function summaryTitle(doc: any) { return IconBox.summaryTitleScript(doc); });
\ No newline at end of file diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6fc94a140..beccce9dd 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -216,12 +216,13 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD funcs.push({ description: "Record 1sec audio", event: this.recordAudioAnnotation, icon: "expand-arrows-alt" }); funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" }); - let modes: ContextMenuProps[] = []; + let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); + let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" }); modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" }); + !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" }) ContextMenu.Instance.addItem({ description: "Image Funcs...", subitems: funcs, icon: "asterisk" }); - ContextMenu.Instance.addItem({ description: "Analyze...", subitems: modes, icon: "eye" }); } } @@ -261,13 +262,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD onDotDown(index: number) { this.Document.curPage = index; } - - @computed get fieldExtensionDoc() { - return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true"); - } - @computed private get url() { - let data = Cast(Doc.GetProto(this.props.Document).data, ImageField); + let data = Cast(Doc.GetProto(this.props.Document)[this.props.fieldKey], ImageField); return data ? data.url.href : undefined; } @@ -380,7 +376,6 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); // let w = bptX - sptX; - let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx let nativeWidth = FieldValue(this.Document.nativeWidth, pw); let nativeHeight = FieldValue(this.Document.nativeHeight, 0); let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; @@ -406,11 +401,11 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document); return ( - <div id={id} className={`imageBox-cont${interactive}`} style={{ background: "transparent" }} + <div className={`imageBox-cont${interactive}`} style={{ background: "transparent" }} onPointerDown={this.onPointerDown} onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}> <div id="cf"> - <img id={id} + <img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys src={srcpath} style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 653c5c27f..ee70942de 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -2,26 +2,21 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { CompileScript, ScriptOptions, CompiledScript } from "../../util/Scripting"; -import { FieldView, FieldViewProps } from './FieldView'; -import "./KeyValueBox.scss"; -import { KeyValuePair } from "./KeyValuePair"; -import React = require("react"); -import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types"; -import { Doc, Field, FieldResult, DocListCastAsync } from "../../../new_fields/Doc"; -import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; -import { SetupDrag } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; -import { RawDataOperationParameters } from "../../northstar/model/idea/idea"; -import { Templates } from "../Templates"; +import { Doc, Field, FieldResult } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; -import { TextField } from "../../util/ProsemirrorCopy/prompt"; import { RichTextField } from "../../../new_fields/RichTextField"; -import { ImageField } from "../../../new_fields/URLField"; -import { SelectionManager } from "../../util/SelectionManager"; import { listSpec } from "../../../new_fields/Schema"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; +import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { ImageField } from "../../../new_fields/URLField"; +import { Docs } from "../../documents/Documents"; +import { SetupDrag } from "../../util/DragManager"; +import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting"; import { undoBatch } from "../../util/UndoManager"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./KeyValueBox.scss"; +import { KeyValuePair } from "./KeyValuePair"; +import React = require("react"); export type KVPScript = { script: CompiledScript; @@ -203,7 +198,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { return; } let previousViewType = fieldTemplate.viewType; - Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc)); + Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc)); previousViewType && (fieldTemplate.viewType = previousViewType); Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 5afd4d834..7e0f3735d 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -55,6 +55,7 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { Document: this.props.doc, DataDoc: this.props.doc, ContainingCollectionView: undefined, + ruleProvider: undefined, fieldKey: this.props.keyName, fieldExt: "", isSelected: returnFalse, @@ -112,7 +113,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> { <div className="keyValuePair-td-value-container"> <EditableView contents={contents} - height={36} + maxHeight={36} + height={"auto"} GetValue={() => { return Field.toKeyValueString(props.Document, props.fieldKey); }} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 3f4ee8960..96f011eff 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import * as rp from 'request-promise'; import { InkTool } from "../../../new_fields/InkField"; import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { Cast, FieldValue, NumCast, BoolCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; import { RouteStore } from "../../../server/RouteStore"; import { Utils } from "../../../Utils"; @@ -204,7 +204,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD } } specificContextMenu = (e: React.MouseEvent): void => { - let field = Cast(this.Document[this.props.fieldKey], VideoField); + let field = Cast(this.dataDoc[this.props.fieldKey], VideoField); if (field) { let url = field.url.href; let subitems: ContextMenuProps[] = []; @@ -216,7 +216,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD } @computed get content() { - let field = Cast(this.Document[this.props.fieldKey], VideoField); + let field = Cast(this.dataDoc[this.props.fieldKey], VideoField); let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"; let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; return !field ? <div>Loading</div> : @@ -228,7 +228,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD } @computed get youtubeVideoId() { - let field = Cast(this.Document[this.props.fieldKey], VideoField); + let field = Cast(this.dataDoc[this.props.fieldKey], VideoField); return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : ""; } @@ -269,6 +269,8 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD } + @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 youtubeContent() { this._youtubeIframeId = VideoBox._youtubeIframeCounter++; this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true; @@ -281,6 +283,7 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD } render() { + Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); return <div style={{ pointerEvents: "all", width: "100%", height: "100%" }} onContextMenu={this.specificContextMenu}> {this.youtubeVideoId ? this.youtubeContent : this.content} </div>; |
