diff options
| author | Lionel Han <47760119+IGoByJoe@users.noreply.github.com> | 2020-08-14 12:55:05 -0700 |
|---|---|---|
| committer | Lionel Han <47760119+IGoByJoe@users.noreply.github.com> | 2020-08-14 12:55:05 -0700 |
| commit | 4fd8f1533be68bdcd3a6b8a832a38655adde4d71 (patch) | |
| tree | cd2110b252c6a73932dfae36ba3d535d5e40fff0 /src/client/views/collections | |
| parent | a9161a668c96750d8bb50647c043a979058ef451 (diff) | |
| parent | 36f7b54914b4d5fce98de3a6d83f1b186ebb17d1 (diff) | |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into new_audio
Diffstat (limited to 'src/client/views/collections')
19 files changed, 812 insertions, 574 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 27aea4b99..8a27f8102 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -64,7 +64,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) </div> <div className="collectionCarouselView-caption" key="caption" style={{ - background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document)), + background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.backgroundColor?.(this.props.Document, this.props.renderDepth)), color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)), borderRadius: StrCast(this.layoutDoc._captionBorderRounding), }}> diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 4204ef5bb..6ebd5103b 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -20,82 +20,6 @@ } } -.miniPres:hover { - opacity: 1; -} - -.miniPres { - position: absolute; - overflow: hidden; - right: 10; - top: 10; - opacity: 0.1; - transition: all 0.4s; - /* border: solid 1px; */ - color: white; - /* box-shadow: black 0.4vw 0.4vw 0.8vw; */ - - .miniPresOverlay { - display: grid; - grid-template-columns: auto auto auto auto auto auto auto auto; - grid-template-rows: 100%; - height: 100%; - justify-items: center; - align-items: center; - - .miniPres-button-text { - display: flex; - height: 20; - font-weight: 400; - min-width: 100%; - border-radius: 5px; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .miniPres-button-frame { - justify-self: center; - align-self: center; - align-items: center; - display: grid; - grid-template-columns: auto auto auto; - justify-content: space-around; - font-size: 11; - margin-left: 7; - width: 30; - height: 85%; - background-color: rgba(91, 157, 221, 0.4); - border-radius: 5px; - } - - .miniPres-divider { - width: 0.5px; - height: 80%; - border-right: solid 2px #5a5a5a; - } - - .miniPres-button { - display: flex; - height: 20; - min-width: 20; - border-radius: 100%; - align-items: center; - justify-content: center; - transition: all 0.3s; - } - - .miniPres-button:hover { - background-color: #5a5a5a; - } - - .miniPres-button-text:hover { - background-color: #5a5a5a; - } - } -} - - .lm_title { margin-top: 3px; border-radius: 5px; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 3691e844f..43da0d3cf 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -191,31 +191,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp return retVal; } - @undoBatch - @action - public static ReplaceTab(document: Doc, stack: any): Opt<Doc> { - if (!CollectionDockingView.Instance) return undefined; - const instance = CollectionDockingView.Instance; - const replaceTab = (doc: Doc, child: any): Opt<Doc> => { - for (const contentItem of child.contentItems) { - const { config, isStack, isRow, isColumn } = contentItem; - if (isRow || isColumn || isStack) { - const val = replaceTab(doc, contentItem); - if (val) return val; - } else if (config.component === "DocumentFrameRenderer" && - config.props.documentId === doc[Id]) { - const alias = Doc.MakeAlias(doc); - config.props.documentId = alias[Id]; - config.title = alias.title; - instance.stateChanged(); - return alias; - } - } - return undefined; - }; - return replaceTab(document, instance._goldenLayout.root); - } - // // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split // @@ -358,6 +333,32 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp return true; } + @undoBatch + @action + public ReplaceTab = (stack: any, document: Doc, libraryPath?: Doc[]) => { + Doc.GetProto(document).lastOpened = new DateField; + const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined, libraryPath); + if (stack === undefined) { + let stack: any = this._goldenLayout.root; + while (!stack.isStack) { + if (stack.contentItems.length) { + stack = stack.contentItems[0]; + } else { + stack.addChild({ type: 'stack', content: [docContentConfig] }); + stack = undefined; + break; + } + } + if (stack) { + stack.addChild(docContentConfig); + } + } else { + stack.addChild(docContentConfig, undefined); + } + this.layoutChanged(); + return true; + } + setupGoldenLayout() { const config = StrCast(this.props.Document.dockingConfig); if (config) { @@ -727,9 +728,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { pinDoc.presZoomButton = true; pinDoc.context = curPres; Doc.AddDocToList(curPres, "data", pinDoc); + if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; if (!DocumentManager.Instance.getDocumentView(curPres)) { CollectionDockingView.AddRightSplit(curPres); } + DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null)); } } } @@ -758,7 +761,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.on("tab", this.onActiveContentItemChanged); this.onActiveContentItemChanged(); - this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, "white") }), + this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white") }), (data) => { const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document)); this._tab && (this._tab.style.backgroundColor = selected ? data.color : ""); @@ -821,7 +824,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { if (this._mainCont && this._mainCont.children) { const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement); const scale = Utils.GetScreenTransform(this._mainCont).scale; - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); + return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); } return Transform.Identity(); } @@ -837,12 +840,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { } else if (location === "close") { return CollectionDockingView.CloseRightSplit(doc); } else if (location === "replace") { - const alias = CollectionDockingView.ReplaceTab(doc, this._stack); - if (alias) { - runInAction(() => this._document = alias); - return true; - } - return false; + CollectionDockingView.UseRightSplit(doc); + return true; } else {// if (location === "inPlace") { return CollectionDockingView.Instance.AddTab(this._stack, doc, libraryPath); } @@ -873,30 +872,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { const currentFrame = Cast(presTargetDoc.currentFrame, "number", null); return currentFrame; } - renderMiniPres() { - return ( - <div className="miniPres" - style={{ width: 250, height: 30, background: '#323232' }} - > - {<div className="miniPresOverlay"> - <div className="miniPres-button" onClick={PresBox.Instance.back}><FontAwesomeIcon icon={"arrow-left"} /></div> - <div className="miniPres-button" onClick={() => PresBox.Instance.startAutoPres(PresBox.Instance.itemIndex)}><FontAwesomeIcon icon={PresBox.Instance.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> - <div className="miniPres-button" onClick={PresBox.Instance.next}><FontAwesomeIcon icon={"arrow-right"} /></div> - <div className="miniPres-divider"></div> - <div className="miniPres-button-text"> - Slide {PresBox.Instance.itemIndex + 1} / {PresBox.Instance.childDocs.length} - {PresBox.Instance.playButtonFrames} - </div> - <div className="miniPres-divider"></div> - <div className="miniPres-button-text" onClick={PresBox.Instance.updateMinimize}>EXIT</div> - </div>} - </div> - ); - } + renderMiniMap() { return <div className="miniMap" style={{ width: this.returnMiniSize(), height: this.returnMiniSize(), background: StrCast(this._document!._backgroundColor, - StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!))), + StrCast(this._document!.backgroundColor, CollectionDockingView.Instance.props.backgroundColor?.(this._document!, 0))), }}> <CollectionFreeFormView Document={this._document!} @@ -975,7 +955,6 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} - {document._viewType === CollectionViewType.Freeform && this._document?.miniPres ? this.renderMiniPres() : (null)} </>; } diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 3cf46dbed..e1b07077e 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -119,7 +119,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { // transform: this.props.Document.linearViewIsExpanded ? "" : "rotate(45deg)" }} onPointerDown={e => e.stopPropagation()} > - <p>+</p> + <p>{BoolCast(this.props.Document.linearViewIsExpanded) ? "–" : "+"}</p> </label>; return <div className="collectionLinearView-outer"> diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index b41cbe92d..8658212b7 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -315,6 +315,7 @@ display: inline-flex; position: relative; align-items: center; + margin-left: 10px; .antimodeMenu-button { text-align: center; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 53d2a136e..5119ff6c9 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -85,7 +85,8 @@ export default class CollectionMenu extends AntimodeMenu { const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel"; const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom"> - <button className="antimodeMenu-button" key="properties" onPointerDown={this.toggleProperties}> + <button className="antimodeMenu-button" key="properties" style={{ backgroundColor: "#424242" }} + onPointerDown={this.toggleProperties}> <FontAwesomeIcon icon={propIcon} size="lg" /> </button> </Tooltip>; @@ -580,9 +581,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })} style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}> {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */} - <div className="color-previewII" style={{ backgroundColor: color }} /> - {color === "" ? <p style={{ fontSize: 45, color: "red", marginTop: -16, marginLeft: -5, position: "fixed" }}>☒</p> : ""} - + <div className="color-previewII" style={{ backgroundColor: color }}> + {color === "" ? <p style={{ fontSize: 45, color: "red", marginTop: -16, marginLeft: -5, position: "fixed" }}>☒</p> : ""} + </div> </button>)} </div>; } @@ -622,7 +623,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu </div> </Tooltip> : null} {!this.isText && !this.props.isDoc ? <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom"> - <div className="numKeyframe" style={{ backgroundColor: this.document.editing ? "#759c75" : "#c56565" }} + <div className="numKeyframe" style={{ color: this.document.editing ? "white" : "black", backgroundColor: this.document.editing ? "#5B9FDD" : "#AEDDF8" }} onClick={action(() => this.document.editing = !this.document.editing)} > {NumCast(this.document.currentFrame)} </div> @@ -669,7 +670,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu @computed get pivotField() { return StrCast(this.document._pivotField); } getKeySuggestions = async (value: string): Promise<string[]> => { - value = value.toLowerCase(); + const val = value.toLowerCase(); const docs = DocListCast(this.document[this.props.fieldKey]); if (Doc.UserDoc().noviceMode) { @@ -677,7 +678,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_")); - return keys.filter(key => key.toLowerCase().indexOf(value.toLowerCase()) > -1); + return keys.filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set<string>(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); @@ -685,16 +686,16 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "ACL" && key !== "UseCors" && key[0] !== "_")); - return noviceKeys.filter(key => key.toLowerCase().indexOf(value.toLowerCase()) > -1); + return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } } if (docs instanceof Doc) { - return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value)); + return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set<string>(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); - return Array.from(keys).filter(key => key.toLowerCase().startsWith(value)); + return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1); } } @@ -735,8 +736,10 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu @action resetValue = () => { this._currentKey = this.pivotField; }; render() { + const doctype = this.props.docView.Document.type; + const isPres: boolean = (doctype === DocumentType.PRES); return ( - <div className="collectionStackingViewChrome-cont"> + isPres ? (null) : <div className="collectionStackingViewChrome-cont"> <div className="collectionStackingViewChrome-pivotField-cont"> <div className="collectionStackingViewChrome-pivotField-label"> GROUP BY: diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 8a1dd8472..20ce6b76d 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { action, observable, trace, computed } from "mobx"; +import { action, observable, trace, computed, runInAction } from "mobx"; import { observer } from "mobx-react"; import { CellInfo } from "react-table"; import "react-table/react-table.css"; @@ -33,6 +33,8 @@ import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { DateField } from "../../../fields/DateField"; import { RichTextField } from "../../../fields/RichTextField"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SearchUtil } from "../../util/SearchUtil"; const path = require('path'); library.add(faExpand); @@ -67,10 +69,29 @@ export class CollectionSchemaCell extends React.Component<CellProps> { protected _document = this.props.rowProps.original; protected _dropDisposer?: DragManager.DragDropDisposer; - componentDidMount() { + async componentWillMount() { + + } + + async componentDidMount() { document.addEventListener("keydown", this.onKeyDown); + console.log("mounted"); + console.log(this.type); + if (this.type === "context") { + console.log("mounted2"); + const doc = Doc.GetProto(this.props.rowProps.original); + const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); + if (aliasdoc.length > 0) { + const targetContext = Cast(aliasdoc[0].context, Doc) as Doc; + console.log(StrCast(targetContext.title)); + runInAction(() => this.contents = StrCast(targetContext.title)); + } + } + } + @observable contents: string = ""; + componentWillUnmount() { document.removeEventListener("keydown", this.onKeyDown); } @@ -159,6 +180,31 @@ export class CollectionSchemaCell extends React.Component<CellProps> { // e.stopPropagation(); // } + returnHighlights(bing: (() => string), positions?: number[]) { + const results = []; + const contents = bing(); + + if (positions !== undefined) { + StrCast(this.props.Document._searchString); + const length = StrCast(this.props.Document._searchString).length; + const color = contents ? "black" : "grey"; + + results.push(<span key="-1" style={{ color }}>{contents?.slice(0, positions[0])}</span>); + positions.forEach((num, cur) => { + results.push(<span key={"start" + cur} style={{ backgroundColor: "#FFFF00", color }}>{contents?.slice(num, num + length)}</span>); + let end = 0; + cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1]; + results.push(<span key={"end" + cur} style={{ color }}>{contents?.slice(num + length, end)}</span>); + } + ); + return results; + } + else { + return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>; + } + } + type: string = ""; + renderCellWithType(type: string | undefined) { const dragRef: React.RefObject<HTMLDivElement> = React.createRef(); @@ -200,12 +246,23 @@ export class CollectionSchemaCell extends React.Component<CellProps> { const doc = FieldValue(Cast(field, Doc)); const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); - const onItemDown = (e: React.PointerEvent) => { - //fieldIsDoc && - SetupDrag(this._focusRef, - () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, - this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument, - this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); + const onItemDown = async (e: React.PointerEvent) => { + if (this.props.Document._searchDoc !== undefined) { + const doc = Doc.GetProto(this.props.rowProps.original); + const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); + let targetContext = undefined; + if (aliasdoc.length > 0) { + targetContext = Cast(aliasdoc[0].context, Doc) as Doc; + } + DocumentManager.Instance.jumpToDocument(this.props.rowProps.original, false, undefined, targetContext); + } + else { + fieldIsDoc && + SetupDrag(this._focusRef, + () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, + this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument, + this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); + } }; const onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) { @@ -250,20 +307,10 @@ export class CollectionSchemaCell extends React.Component<CellProps> { // </div> // ); const positions = []; + let cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); + this.type = props.fieldKey; if (StrCast(this.props.Document._searchString).toLowerCase() !== "") { - const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); - let term = ""; - if (cfield !== undefined) { - if (cfield.Text !== undefined) { - term = cfield.Text; - } - else if (StrCast(cfield)) { - term = StrCast(cfield); - } - else { - term = String(NumCast(cfield)); - } - } + let term = Field.toString(cfield as Field); term = term.toLowerCase(); const search = StrCast(this.props.Document._searchString).toLowerCase(); let start = term.indexOf(search); @@ -281,89 +328,123 @@ export class CollectionSchemaCell extends React.Component<CellProps> { positions.pop(); } } + let search = false; + if (this.props.Document._searchDoc !== undefined) { + search = true; + } + return ( <div className="collectionSchemaView-cellContainer" style={{ cursor: fieldIsDoc ? "grab" : "auto" }} ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}> <div className={className} ref={this._focusRef} onPointerDown={onItemDown} tabIndex={-1}> - <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}> - <EditableView - positions={positions.length > 0 ? positions : undefined} - search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined} - editing={this._isEditing} - isEditingCallback={this.isEditingCallback} - display={"inline"} - contents={contents ? contents : type === "number" ? "0" : "undefined"} - highlight={positions.length > 0 ? true : undefined} - //contents={StrCast(contents)} - height={"auto"} - maxHeight={Number(MAX_ROW_HEIGHT)} - placeholder={"enter value"} - bing={() => { - const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); - if (cfield !== undefined) { - console.log(typeof (cfield)); - // if (typeof(cfield)===RichTextField) - const a = cfield as RichTextField; - if (a.Text !== undefined) { - return (a.Text); - } - else if (StrCast(cfield)) { - return StrCast(cfield); - } - else { - return String(NumCast(cfield)); - } - } - }} - GetValue={() => { - if (type === "number" && (contents === 0 || contents === "0")) { - return "0"; - } else { + <div className="collectionSchemaView-cellContents" + ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}> + {!search ? + <EditableView + positions={positions.length > 0 ? positions : undefined} + search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined} + editing={this._isEditing} + isEditingCallback={this.isEditingCallback} + display={"inline"} + contents={contents ? contents : type === "number" ? "0" : "undefined"} + highlight={positions.length > 0 ? true : undefined} + //contents={StrCast(contents)} + height={"auto"} + maxHeight={Number(MAX_ROW_HEIGHT)} + placeholder={"undefined"} + bing={() => { const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); - if (type === "number") { - return StrCast(cfield); + if (cfield !== undefined) { + // if (typeof(cfield)===RichTextField) + const a = cfield as RichTextField; + const b = cfield as DateField; + console.log(b); + if (a.Text !== undefined) { + return (a.Text); + } + else if (b.toString() !== undefined) { + return b.toString(); + } + else if (StrCast(cfield)) { + return StrCast(cfield); + } + else { + return String(NumCast(cfield)); + } } - const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; - const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1]; - const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : - Field.IsField(cfield) ? Field.toScriptString(cfield) : ""; - return val; + }} + GetValue={() => { + if (type === "number" && (contents === 0 || contents === "0")) { + return "0"; + } else { + const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); + if (type === "number") { + return StrCast(cfield); + } + const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; + const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1]; + const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : + Field.IsField(cfield) ? Field.toScriptString(cfield) : ""; + return val; - } + } - }} - SetValue={action((value: string) => { - let retVal = false; + }} + SetValue={action((value: string) => { + let retVal = false; + + if (value.startsWith(":=")) { + retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col); + } else { + const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + if (script.compiled) { + retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run); + } - if (value.startsWith(":=")) { - retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col); - } else { + } + if (retVal) { + this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' + this.props.setIsEditing(false); + } + return retVal; + + //return true; + })} + OnFillDown={async (value: string) => { const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); if (script.compiled) { - retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run); + DocListCast(this.props.Document[this.props.fieldKey]). + forEach((doc, i) => value.startsWith(":=") ? + this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) : + this.applyToDoc(doc, i, this.props.col, script.run)); } - + }} + /> + : + this.returnHighlights(() => { + console.log(props.fieldKey); + const dateCheck: Date | undefined = this.props.rowProps.original[this.props.rowProps.column.id as string] instanceof DateField ? DateCast(this.props.rowProps.original[this.props.rowProps.column.id as string]).date : undefined; + if (dateCheck !== undefined) { + cfield = dateCheck.toLocaleString(); } - if (retVal) { - this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' - this.props.setIsEditing(false); + if (props.fieldKey === "context") { + cfield = this.contents; + console.log("this should work"); } - return retVal; + if (props.fieldKey === "*lastModified") { + if (FieldValue(props.Document["data-lastModified"]) !== undefined) { + const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["data-lastModified"])) as DateField; + cfield = d.date.toLocaleString(); + } - //return true; - })} - OnFillDown={async (value: string) => { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - if (script.compiled) { - DocListCast(this.props.Document[this.props.fieldKey]). - forEach((doc, i) => value.startsWith(":=") ? - this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) : - this.applyToDoc(doc, i, this.props.col, script.run)); + else if (FieldValue(props.Document["text-lastModified"]) !== undefined) { + const d = ComputedField.WithoutComputed(() => FieldValue(props.Document["text-lastModified"])) as DateField; + cfield = d.date.toLocaleString(); + } } - }} - /> - - + return Field.toString(cfield as Field); + }, positions) + } </div > {/* {fieldIsDoc ? docExpander : null} */} </div> @@ -903,7 +984,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { }}><button onClick={() => { doc.searchMatch = false; setTimeout(() => doc.searchMatch = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); }} style={{ padding: 2, left: 77 }}> <FontAwesomeIcon icon="arrow-up" size="sm" /> </button> @@ -911,7 +991,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { { doc.searchMatchAlt = false; setTimeout(() => doc.searchMatchAlt = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); } }} style={{ padding: 2 }}> <FontAwesomeIcon icon="arrow-down" size="sm" /> @@ -929,7 +1008,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { onClick={() => { doc.searchMatch = false; setTimeout(() => doc.searchMatch = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); }}> <FontAwesomeIcon icon="arrow-down" size="sm" /> </button></div >; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index e65adcf76..5c0e6581b 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -1,22 +1,24 @@ import React = require("react"); -import { action, observable } from "mobx"; -import { observer } from "mobx-react"; -import "./CollectionSchemaView.scss"; -import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl, faCalendar } from '@fortawesome/free-solid-svg-icons'; -import { library, IconProp } from "@fortawesome/fontawesome-svg-core"; +import { IconProp, library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { ColumnType } from "./CollectionSchemaView"; -import { faFile } from "@fortawesome/free-regular-svg-icons"; -import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField"; +import { action, computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; +import { listSpec } from "../../../fields/Schema"; +import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; +import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, StrCast } from "../../../fields/Types"; import { undoBatch } from "../../util/UndoManager"; -import { Doc } from "../../../fields/Doc"; -import { StrCast } from "../../../fields/Types"; +import { SearchBox } from "../search/SearchBox"; +import { ColumnType } from "./CollectionSchemaView"; +import "./CollectionSchemaView.scss"; +import { CollectionView } from "./CollectionView"; +import * as fa from '@fortawesome/free-solid-svg-icons'; + const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; -library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl, faCalendar); - export interface HeaderProps { keyValue: SchemaHeaderField; possibleKeys: string[]; @@ -35,7 +37,7 @@ export interface HeaderProps { export class CollectionSchemaHeader extends React.Component<HeaderProps> { render() { const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" : - this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : + this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "sort-amount-down" : this.props.keyType === ColumnType.Image ? "image" : this.props.keyType === ColumnType.List ? "list-ul" : this.props.keyType === ColumnType.Date ? "calendar" : "align-justify"; return ( @@ -238,18 +240,6 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> renderContent = () => { return ( <div className="collectionSchema-header-menuOptions"> - <div className="collectionSchema-headerMenu-group"> - <label>Key:</label> - <KeysDropdown - keyValue={this.props.columnField.heading} - possibleKeys={this.props.possibleKeys} - existingKeys={this.props.existingKeys} - canAddNew={true} - addNew={this.props.addNew} - onSelect={this.props.onSelect} - setIsEditing={this.props.setIsEditing} - /> - </div> {this.props.onlyShowOptions ? <></> : <> {this.renderTypes()} @@ -286,6 +276,15 @@ export interface KeysDropdownProps { setIsEditing: (isEditing: boolean) => void; width?: string; docs?: Doc[]; + Document: Doc; + dataDoc: Doc | undefined; + fieldKey: string; + ContainingCollectionDoc: Doc | undefined; + ContainingCollectionView: Opt<CollectionView>; + active?: (outsideReaction?: boolean) => boolean; + openHeader: (column: any, screenx: number, screeny: number) => void; + col: SchemaHeaderField; + icon: IconProp; } @observer export class KeysDropdown extends React.Component<KeysDropdownProps> { @@ -301,34 +300,38 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { @action onSelect = (key: string): void => { - if (key.slice(0, this._key.length) === this._key && this._key !== key) { - const filter = key.slice(this._key.length - key.length); - this.props.onSelect(this._key, this._key, this.props.addNew, filter); - } - else { - this.props.onSelect(this._key, key, this.props.addNew); - this.setKey(key); - this._isOpen = false; - this.props.setIsEditing(false); - } - } - - @action - onSelect2 = (key: string): void => { - this._searchTerm = this._searchTerm.slice(0, this._key.length) + key; + this.props.onSelect(this._key, key, this.props.addNew); + this.setKey(key); this._isOpen = false; - + this.props.setIsEditing(false); } @undoBatch onKeyDown = (e: React.KeyboardEvent): void => { if (e.key === "Enter") { - const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); - if (keyOptions.length) { - this.onSelect(keyOptions[0]); - } else if (this._searchTerm !== "" && this.props.canAddNew) { - this.setSearchTerm(this._searchTerm || this._key); - this.onSelect(this._searchTerm); + if (this._searchTerm.includes(":")) { + const colpos = this._searchTerm.indexOf(":"); + const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); + if (temp === "") { + Doc.setDocFilter(this.props.Document, this._key, temp, undefined); + } + else { + Doc.setDocFilter(this.props.Document, this._key, temp, "match"); + this.props.col.setColor("green"); + } + } + else { + let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + const blockedkeys = ["_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"]; + keyOptions = keyOptions.filter(n => !blockedkeys.includes(n)); + if (keyOptions.length) { + this.onSelect(keyOptions[0]); + console.log("case1"); + } else if (this._searchTerm !== "" && this.props.canAddNew) { + this.setSearchTerm(this._searchTerm || this._key); + console.log("case2"); + this.onSelect(this._searchTerm); + } } } } @@ -360,87 +363,172 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { onPointerOut = (e: React.PointerEvent): void => { this._canClose = true; } - + @action renderOptions = (): JSX.Element[] | JSX.Element => { - if (!this._isOpen) return <></>; - + if (!this._isOpen) { + this.defaultMenuHeight = 0; + return <></>; + } const searchTerm = this._searchTerm.trim() === "New field" ? "" : this._searchTerm; - const keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; + const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"]; + keyOptions = keyOptions.filter(n => !blockedkeys.includes(n)); + const options = keyOptions.map(key => { return <div key={key} className="key-option" style={{ border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", }} onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>; }); // if search term does not already exist as a group type, give option to create new group type + if (this._key !== this._searchTerm.slice(0, this._key.length)) { + console.log("little further"); if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { options.push(<div key={""} className="key-option" style={{ - border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", }} onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}> Create "{this._searchTerm}" key</div>); } } + if (options.length === 0) { + this.defaultMenuHeight = 0; + } + else { + if (this.props.docs) { + const panesize = this.props.docs.length * 30; + options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; + } + else { + options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; + } + } return options; } + docSafe: Doc[] = []; + + @action renderFilterOptions = (): JSX.Element[] | JSX.Element => { - if (!this._isOpen) return <></>; + if (!this._isOpen) { + this.defaultMenuHeight = 0; + return <></>; + } + const keyOptions: string[] = []; - const temp = this._searchTerm.slice(this._key.length); - this.props.docs?.forEach((doc) => { + const colpos = this._searchTerm.indexOf(":"); + const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); + if (this.docSafe.length === 0) { + this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]); + } + const docs = this.docSafe; + docs.forEach((doc) => { const key = StrCast(doc[this._key]); if (keyOptions.includes(key) === false && key.includes(temp)) { keyOptions.push(key); } }); + const filters = Cast(this.props.Document._docFilters, listSpec("string")); + for (let i = 0; i < (filters?.length ?? 0) - 1; i += 3) { + if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i + 1]) === false) { + keyOptions.push(filters![i + 1]); + } + } + + if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) { + this.props.col.setColor("rgb(241, 239, 235)"); + } const options = keyOptions.map(key => { + //Doc.setDocFilter(this.props.Document!, this._key, key, undefined); + let bool = false; + console.log(filters); + if (filters !== undefined) { + bool = filters.includes(key) && filters[filters.indexOf(key) + 1] === "check"; + console.log(filters.includes(key)); + } return <div key={key} className="key-option" style={{ - border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + border: "1px solid lightgray", paddingLeft: 5, textAlign: "left", + width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white", }} - onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect2(key); }}>{key}</div>; + > + <input type="checkbox" onChange={(e) => { + e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined); + e.target.checked === true ? this.props.col.setColor("green") : ""; + e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined); + }} + checked={bool} ></input> + <span style={{ paddingLeft: 4 }}> + {key} + </span> + + </div>; }); + if (options.length === 0) { + this.defaultMenuHeight = 0; + } + else { + if (this.props.docs) { + const panesize = this.props.docs.length * 30; + options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; + } + else { + options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; + } + } return options; } + @observable defaultMenuHeight = 0; + + + get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } + + @computed get scriptField() { + console.log("we kinda made it"); + const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)"; + const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); + return script ? () => script : undefined; + } + filterBackground = () => "rgba(105, 105, 105, 0.432)"; + + @observable filterOpen: boolean | undefined = undefined; render() { return ( - <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}> - {this._key === this._searchTerm.slice(0, this._key.length) ? - <div style={{ position: "absolute", marginLeft: "4px", marginTop: "3", color: "grey", pointerEvents: "none", lineHeight: 1.15 }}> - {this._key} + <div style={{ display: "flex" }}> + <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> + + {/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => { + runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen }) + }} /> */} + + <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}> + <input className="keys-search" style={{ width: "100%" }} + ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown} + onChange={e => this.onChange(e.target.value)} + onClick={(e) => { + //this._inputRef.current!.select(); + e.stopPropagation(); + }} onFocus={this.onFocus} onBlur={this.onBlur}></input> + <div className="keys-options-wrapper" style={{ + width: this.props.width, maxWidth: this.props.width, height: "auto", + }} + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}> + {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()} </div> - : undefined} - <input className="keys-search" style={{ width: "100%" }} - ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown} - onChange={e => this.onChange(e.target.value)} - onClick={(e) => { - //this._inputRef.current!.select(); - e.stopPropagation(); - }} onFocus={this.onFocus} onBlur={this.onBlur}></input> - <div className="keys-options-wrapper" style={{ - backgroundColor: "white", - width: this.props.width, maxWidth: this.props.width, - }} - onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}> - {this._key === this._searchTerm.slice(0, this._key.length) ? - this.renderFilterOptions() : this.renderOptions()} - </div> - </div > + </div > + </div> ); } } diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index ba0a259c5..5c2931a8b 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -43,6 +43,49 @@ // } } +.collectionSchemaView-searchContainer { + border-width: $COLLECTION_BORDER_WIDTH; + border-color: $intermediate-color; + border-style: solid; + border-radius: $border-radius; + box-sizing: border-box; + position: relative; + top: 0; + width: 100%; + height: 100%; + margin-top: 0; + transition: top 0.5s; + display: flex; + justify-content: space-between; + flex-wrap: nowrap; + touch-action: none; + + div { + touch-action: none; + } + + + .collectionSchemaView-tableContainer { + width: 100%; + height: 100%; + } + + .collectionSchemaView-dividerDragger { + position: relative; + height: 100%; + width: 20px; + z-index: 20; + right: 0; + top: 0; + background: gray; + cursor: col-resize; + } + + // .documentView-node:first-child { + // background: $light-color; + // } +} + .ReactTable { width: 100%; background: white; @@ -165,7 +208,7 @@ .collectionSchema-header-menu { - height: 100%; + height: auto; z-index: 100; position: absolute; background:white; @@ -280,11 +323,9 @@ button.add-column { background-color: white; .key-option { - //background-color: $light-color; background-color: white; border: 1px solid lightgray; padding: 2px 3px; - &:not(:first-child) { border-top: 0; } @@ -315,12 +356,53 @@ button.add-column { } } +.altcollectionTimeView-treeView { + display: flex; + flex-direction: column; + width: 175px; + height: auto; + position: fixed; + border-left: solid 1px; + z-index: 1; + + .collectionTimeView-addfacet { + display: inline-block; + width: 200px; + height: 30px; + background: darkGray; + text-align: left; + + .collectionTimeView-button { + align-items: center; + display: flex; + width: 100%; + height: 100%; + + .collectionTimeView-span { + margin: auto; + } + } + + >div, + >div>div { + width: 100%; + height: 100%; + } + } + + .altcollectionTimeView-tree { + display: inline-block; + width: 100%; + height: calc(100% - 30px); + } +} + .collectionSchema-row { height: 100%; background-color: white; &.row-focused .rt-td { - background-color: rgb(255, 246, 246); //$light-color-secondary; + background-color: #bfffc0; //$light-color-secondary; } &.row-wrapped { @@ -383,6 +465,9 @@ button.add-column { .collectionSchemaView-cellWrapper { height: 100%; padding: 4px; + text-align:left; + padding-left:19px; + position: relative; &:focus { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index a003de0d3..f1de3cee7 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { Doc } from "../../../fields/Doc"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; -import { Cast, NumCast } from "../../../fields/Types"; +import { Cast, NumCast, BoolCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents } from "../../../Utils"; import { SnappingManager } from "../../util/SnappingManager"; @@ -71,8 +71,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @observable _pointerY = 0; @observable _openTypes: boolean = false; @computed get menuCoordinates() { - const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)); - const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)); + let searchx = 0; + let searchy = 0; + if (this.props.Document._searchDoc !== undefined) { + const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0]; + if (el !== undefined) { + const rect = el.getBoundingClientRect(); + searchx = rect.x; + searchy = rect.y; + } + } + const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx; + const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy; return this.props.ScreenToLocalTransform().transformPoint(x, y); } @@ -110,7 +120,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { document.removeEventListener("pointerdown", this.detectClick); } - @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing; + @action setHeaderIsEditing = (isEditing: boolean) => { + this._headerIsEditing = isEditing; + } detectClick = (e: PointerEvent): void => { if (this._node && this._node.contains(e.target as Node)) { @@ -356,17 +368,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action closeHeader = () => { this._headerOpen = false; } - renderKeysDropDown = (col: any) => { - return <KeysDropdown - keyValue={col.heading} - possibleKeys={this.possibleKeys} - existingKeys={this.columns.map(c => c.heading)} - canAddNew={true} - addNew={false} - onSelect={this.changeColumns} - setIsEditing={this.setHeaderIsEditing} - />; - } + @undoBatch @action @@ -405,10 +407,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get renderMenuContent() { TraceMobx(); return <div className="collectionSchema-header-menuOptions"> - <div className="collectionSchema-headerMenu-group"> - <label>Key:</label> - {this.renderKeysDropDown(this._col)} - </div> {this.renderTypes(this._col)} {this.renderSorting(this._col)} {this.renderColors(this._col)} @@ -424,7 +422,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { super.CreateDropTarget(ele); } - isFocused = (doc: Doc): boolean => this.props.isSelected() && doc === this._focusedTable; + isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable; @action setFocused = (doc: Doc) => this._focusedTable = doc; @@ -611,14 +609,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => { } render() { + let name = "collectionSchemaView-container"; + if (this.props.Document._searchDoc !== undefined) { + name = "collectionSchemaView-searchContainer"; + } TraceMobx(); const menuContent = this.renderMenuContent; const menu = <div className="collectionSchema-header-menu" ref={this.setNode} onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ - position: "fixed", background: "white", - transform: `translate(${this.menuCoordinates[0] / this.scale}px, ${this.menuCoordinates[1] / this.scale}px)` + position: "fixed", background: "white", border: "black 1px solid", + transform: `translate(${(this.menuCoordinates[0] / this.scale)}px, ${(this.menuCoordinates[1] / this.scale)}px)` }}> <Measure offset onResize={action((r: any) => { const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); @@ -627,11 +629,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} </Measure> </div>; - return <div className="collectionSchemaView-container" + return <div className={name} style={{ - overflow: this.props.overflow === true ? "auto" : undefined, + overflow: this.props.overflow === true ? "scroll" : undefined, pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, - width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", + width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" style={{ backgroundColor: "white", width: `calc(100% - ${this.previewWidth()}px)` }} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b27af66ba..fe3d57bdb 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -4,7 +4,7 @@ import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import Switch from 'rc-switch'; -import { DataSym, Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; @@ -28,7 +28,6 @@ import { CollectionViewType } from "./CollectionView"; import { SnappingManager } from "../../util/SnappingManager"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocUtils } from "../../documents/Documents"; -import { MainViewNotifs } from "../MainViewNotifs"; const _global = (window /* browser */ || global /* node */) as any; type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; @@ -299,10 +298,6 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) const srcInd = docs.indexOf(doc); docs.splice(srcInd, 1); docs.splice((targInd > srcInd ? targInd - 1 : targInd) + plusOne, 0, doc); - DocListCastAsync(docs).then(resolvedDocs => { - const pos = resolvedDocs?.findIndex(shareDoc => shareDoc.icon === "users") || 0; // hopefully find out if the sharing doc has been moved - if (MainViewNotifs.NotifsCol && pos !== -1) MainViewNotifs.NotifsCol.position = pos; - }); } else if (i < (newDocs.length / 2)) { //glr: for some reason dragged documents are duplicated if (targInd === -1) targInd = docs.length; else targInd = docs.indexOf(newDocs[0]) + 1; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d1aeb1b65..3f2ad47a5 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -112,10 +112,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; } @computed get childDocs() { - let rawdocs: (Doc | Promise<Doc>)[] = DocListCast(this.props.Document._searchDocs); - - if (rawdocs.length !== 0) { - } else if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; + let rawdocs: (Doc | Promise<Doc>)[] = []; + if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; rawdocs = [this.dataField]; } else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it. rawdocs = Cast(this.dataField, listSpec(Doc), null); @@ -128,30 +126,25 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: const docs = rawdocs.filter(d => !(d instanceof Promise)).map(d => d as Doc); const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); - let childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; + const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; + + let searchDocs = DocListCast(this.props.Document._searchDocs); - const searchDocs = DocListCast(this.props.Document._searchDocs); - // if (searchDocs !== undefined && searchDocs.length > 0) { - // let newdocs: Doc[] = []; - // childDocs.forEach((el) => { - // searchDocs.includes(el) ? newdocs.push(el) : undefined; - // }); - // childDocs = newdocs; - // } let docsforFilter: Doc[] = childDocs; + if (searchDocs !== undefined && searchDocs.length > 0) { docsforFilter = []; - // let newdocs: Doc[] = []; - // let newarray: Doc[] = []; - //while (childDocs.length > 0) { - //newarray = []; + const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); + console.log(searchDocs); + searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript); + console.log(this.docFilters()); + console.log(searchDocs); childDocs.forEach((d) => { if (d.data !== undefined) { - console.log(d); let newdocs = DocListCast(d.data); if (newdocs.length > 0) { - let vibecheck: boolean | undefined = undefined; + let displaycheck: boolean | undefined = undefined; let newarray: Doc[] = []; while (newdocs.length > 0) { newarray = []; @@ -163,12 +156,12 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: }); } if (searchDocs.includes(t)) { - vibecheck = true; + displaycheck = true; } }); newdocs = newarray; } - if (vibecheck === true) { + if (displaycheck === true) { docsforFilter.push(d); } } @@ -177,14 +170,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: docsforFilter.push(d); } }); - //childDocs = newarray; - //} + return docsforFilter; } - childDocs = docsforFilter; - const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - - return this.props.Document.dontRegisterView ? docs : DocUtils.FilterDocs(docs, this.docFilters(), docRangeFilters, viewSpecScript); + return this.props.Document.dontRegisterView ? childDocs : DocUtils.FilterDocs(childDocs, this.docFilters(), docRangeFilters, viewSpecScript); } @action @@ -251,6 +240,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document); added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse); } else added = res; + !added && alert("You don't have permission to perform this move"); + e.stopPropagation(); } else { ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); added = this.addDocument(docDragData.droppedDocuments); @@ -343,7 +334,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: let srcWeb: Doc | undefined; if (SelectionManager.SelectedDocuments().length) { srcWeb = SelectionManager.SelectedDocuments()[0].props.Document; - srcUrl = (srcWeb.data as WebField).url.href?.match(/http[s]?:\/\/[^/]*/)?.[0]; + srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0]; } const reg = new RegExp(Utils.prepend(""), "g"); const modHtml = srcUrl ? html.replace(reg, srcUrl) : html; @@ -351,7 +342,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text; this.props.addDocument(htmlDoc); if (srcWeb) { - const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")[0].contentDocument?.getSelection()?.focusNode as any); + const focusNode = (SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0].contentDocument?.getSelection()?.focusNode as any); if (focusNode) { const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect(); const x = (rect?.x || 0); @@ -481,7 +472,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: completed?.(); } else { if (text && !text.includes("https://")) { - this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); + UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop"); } } disposer(); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3c7471d7c..d096e7d66 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -53,7 +53,7 @@ export interface TreeViewProps { indentDocument?: () => void; outdentDocument?: () => void; ScreenToLocalTransform: () => Transform; - backgroundColor?: (doc: Doc) => string | undefined; + backgroundColor?: (doc: Doc, renderDepth: number) => string | undefined; outerXf: () => { translateX: number, translateY: number }; treeViewDoc: Doc; parentKey: string; @@ -88,7 +88,7 @@ class TreeView extends React.Component<TreeViewProps> { get doc() { return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive - get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; @@ -108,6 +108,7 @@ class TreeView extends React.Component<TreeViewProps> { } @computed get childDocs() { return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } + @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } @computed get boundsOfCollectionDocument() { return StrCast(this.props.document.type).indexOf(DocumentType.COL) === -1 || !DocListCast(this.props.document[this.fieldKey]).length ? undefined : Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey])); @@ -127,7 +128,7 @@ class TreeView extends React.Component<TreeViewProps> { constructor(props: any) { super(props); - const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); selectDoc(self);} `); + const script = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); this._editTitleScript = script && (() => script); if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); } @@ -313,11 +314,11 @@ class TreeView extends React.Component<TreeViewProps> { @computed get renderContent() { TraceMobx(); const expandKey = this.treeViewExpandedView; - if (["links", this.fieldKey].includes(expandKey)) { + if (["links", "annotations", this.fieldKey].includes(expandKey)) { const remDoc = (doc: Doc | Doc[]) => this.remove(doc, expandKey); const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before, false, true), true); - const docs = expandKey === "links" ? this.childLinks : this.childDocs; + const docs = expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs; const sortKey = `${this.fieldKey}-sortAscending`; return <ul key={expandKey + "more"} onClick={(e) => { this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)); @@ -397,7 +398,7 @@ class TreeView extends React.Component<TreeViewProps> { title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} onClick={this.bulletClick} style={{ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}> - {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs ? "caret-square-right" : "caret-right") : (this.childDocs ? "caret-square-down" : "caret-down"))} />} + {<FontAwesomeIcon icon={checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? (this.childDocs?.length ? "caret-square-right" : "caret-right") : (this.childDocs?.length ? "caret-square-down" : "caret-down"))} />} </div>; } @@ -424,7 +425,8 @@ class TreeView extends React.Component<TreeViewProps> { this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : - this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); + (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : + this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> @@ -453,6 +455,7 @@ class TreeView extends React.Component<TreeViewProps> { NativeHeight={returnZero} NativeWidth={returnZero} contextMenuItems={this.contextMenuItems} + opacity={returnOne} renderDepth={1} focus={returnTrue} parentActive={returnTrue} @@ -475,7 +478,7 @@ class TreeView extends React.Component<TreeViewProps> { </div > {headerElements} <div className="treeViewItem-openRight" onClick={this.openRight}> - <FontAwesomeIcon title="open in pane on right" icon="external-link-alt" size="sm" /> + <FontAwesomeIcon title="open in a new pane" icon="external-link-alt" size="sm" /> </div> </>; } @@ -494,7 +497,7 @@ class TreeView extends React.Component<TreeViewProps> { } } } else this._editMaxWidth = ""; - return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active() && SelectionManager.DeselectAll()}> + return <div className="treeViewItem-container" ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()}> <li className="collection-child"> <div className={`treeViewItem-header` + (this._editMaxWidth ? "-editing" : "")} ref={this._header} style={{ maxWidth: this._editMaxWidth }} onClick={e => { if (this.props.active(true)) { @@ -533,7 +536,7 @@ class TreeView extends React.Component<TreeViewProps> { dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, pinToPres: (document: Doc) => void, - backgroundColor: undefined | ((document: Doc) => string | undefined), + backgroundColor: undefined | ((document: Doc, renderDepth: number) => string | undefined), screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: (outsideReaction?: boolean) => boolean, @@ -813,7 +816,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}> <div className="collectionTreeView-dropTarget" id="body" style={{ - background: this.props.backgroundColor?.(this.doc), + background: this.props.backgroundColor?.(this.doc, this.props.renderDepth), paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`, paddingRight: `${NumCast(this.doc._xPadding, 10)}px`, paddingTop: `${NumCast(this.doc._yPadding, 20)}px`, diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 0feec3fbd..6dd21ef7f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -46,6 +46,7 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import { ContextMenuProps } from '../ContextMenuItem'; +import { table } from 'console'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -141,7 +142,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus const targetDataDoc = this.props.Document[DataSym]; const docList = DocListCast(targetDataDoc[this.props.fieldKey]); const added = docs.filter(d => !docList.includes(d)); - const effectiveAcl = GetEffectiveAcl(this.props.Document); + const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); if (added.length) { if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { @@ -182,6 +183,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]); (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added); targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + const lastModified = "lastModified"; + targetDataDoc[lastModified] = new DateField(new Date(Date.now())); } } } @@ -190,16 +193,19 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus @action.bound removeDocument = (doc: any): boolean => { - const collectionEffectiveAcl = GetEffectiveAcl(this.props.Document); - const docEffectiveAcl = GetEffectiveAcl(doc); - // you can remove the document if you either have Admin/Edit access to the collection or to the specific document - if (collectionEffectiveAcl === AclEdit || collectionEffectiveAcl === AclAdmin || docEffectiveAcl === AclAdmin || docEffectiveAcl === AclEdit) { + const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { const docs = doc instanceof Doc ? [doc] : doc as Doc[]; const targetDataDoc = this.props.Document[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); const toRemove = value.filter(v => docs.includes(v)); if (toRemove.length !== 0) { - toRemove.forEach(doc => Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc)); + const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc; + toRemove.forEach(doc => { + Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); + recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); + doc.deleted = true; + }); return true; } } @@ -216,7 +222,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus return true; } const first = doc instanceof Doc ? doc : doc[0]; - if (!first?.stayInCollection && addDocument !== returnFalse) { + if (!first?._stayInCollection && addDocument !== returnFalse) { if (UndoManager.RunInTempBatch(() => this.removeDocument(doc))) { const added = addDocument(doc); if (!added) UndoManager.UndoTempBatch(); @@ -510,56 +516,56 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus </label>)} </div> ); - return !this._facetWidth || this.props.dontRegisterView ? (null) : - <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}> - <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}> - <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}> - <div className="collectionTimeView-button"> - <FontAwesomeIcon icon={faEdit} size={"lg"} /> - <span className="collectionTimeView-span">Facet Filters</span> - </div> - </Flyout> - </div> - <div className="collectionTimeView-tree" key="tree"> - <CollectionTreeView - PanelPosition={""} - Document={facetCollection} - DataDoc={facetCollection} - fieldKey={`${this.props.fieldKey}-filter`} - CollectionView={this} - docFilters={returnEmptyFilter} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} - ContainingCollectionView={this.props.ContainingCollectionView} - PanelWidth={this.facetWidth} - PanelHeight={this.props.PanelHeight} - NativeHeight={returnZero} - NativeWidth={returnZero} - LibraryPath={emptyPath} - rootSelected={this.props.rootSelected} - renderDepth={1} - dropAction={this.props.dropAction} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} - addDocTab={returnFalse} - pinToPres={returnFalse} - isSelected={returnFalse} - select={returnFalse} - bringToFront={emptyFunction} - active={this.props.active} - whenActiveChanged={returnFalse} - treeViewHideTitle={true} - ContentScaling={returnOne} - focus={returnFalse} - treeViewHideHeaderFields={true} - onCheckedClick={this.scriptField} - ignoreFields={this.ignoreFields} - annotationsKey={""} - dontRegisterView={true} - backgroundColor={this.filterBackground} - moveDocument={returnFalse} - removeDocument={returnFalse} - addDocument={returnFalse} /> - </div> - </div>; + + return !this._facetWidth || this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}> + <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}> + <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}> + <div className="collectionTimeView-button"> + <FontAwesomeIcon icon={faEdit} size={"lg"} /> + <span className="collectionTimeView-span">Facet Filters</span> + </div> + </Flyout> + </div> + <div className="collectionTimeView-tree" key="tree"> + <CollectionTreeView + PanelPosition={""} + Document={facetCollection} + DataDoc={facetCollection} + fieldKey={`${this.props.fieldKey}-filter`} + CollectionView={this} + docFilters={returnEmptyFilter} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + PanelWidth={this.facetWidth} + PanelHeight={this.props.PanelHeight} + NativeHeight={returnZero} + NativeWidth={returnZero} + LibraryPath={emptyPath} + rootSelected={this.props.rootSelected} + renderDepth={1} + dropAction={this.props.dropAction} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + addDocTab={returnFalse} + pinToPres={returnFalse} + isSelected={returnFalse} + select={returnFalse} + bringToFront={emptyFunction} + active={this.props.active} + whenActiveChanged={returnFalse} + treeViewHideTitle={true} + ContentScaling={returnOne} + focus={returnFalse} + treeViewHideHeaderFields={true} + onCheckedClick={this.scriptField} + ignoreFields={this.ignoreFields} + annotationsKey={""} + dontRegisterView={true} + backgroundColor={this.filterBackground} + moveDocument={returnFalse} + removeDocument={returnFalse} + addDocument={returnFalse} /> + </div> + </div>; } childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 4c8cac3ed..149d4927b 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -15,6 +15,7 @@ import { faCog, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons"; import { library } from "@fortawesome/fontawesome-svg-core"; import { DocumentView } from "../nodes/DocumentView"; import { SelectionManager } from "../../util/SelectionManager"; +import { Tooltip } from "@material-ui/core"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -121,16 +122,17 @@ export class DockingViewButtonSelector extends React.Component<{ views: () => Do } render() { - return <span title="Tap for menu, drag tab as document" - onPointerDown={e => { + return <Tooltip title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom"> + <span onPointerDown={e => { if (getComputedStyle(this._ref.current!).width !== "100%") { e.stopPropagation(); e.preventDefault(); } this.props.views()[0]?.select(false); }} className="buttonSelector"> - <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}> - <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} /> - </Flyout> - </span>; + <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}> + <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} /> + </Flyout> + </span> + </Tooltip>; } } diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index a974c5496..a2c529a21 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -63,12 +63,12 @@ export interface SchemaTableProps { addDocument: (document: Doc | Doc[]) => boolean; moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; ScreenToLocalTransform: () => Transform; - active: (outsideReaction: boolean) => boolean; + active: (outsideReaction: boolean | undefined) => boolean; onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void; addDocTab: (document: Doc, where: string) => boolean; pinToPres: (document: Doc) => void; isSelected: (outsideReaction?: boolean) => boolean; - isFocused: (document: Doc) => boolean; + isFocused: (document: Doc, outsideReaction: boolean) => boolean; setFocused: (document: Doc) => void; setPreviewDoc: (document: Doc) => void; columns: SchemaHeaderField[]; @@ -155,7 +155,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); const columns: Column<Doc>[] = []; - const tableIsFocused = this.props.isFocused(this.props.Document); + const tableIsFocused = this.props.isFocused(this.props.Document, false); const focusedRow = this._focusedCell.row; const focusedCol = this._focusedCell.col; const isEditable = !this.props.headerIsEditing; @@ -177,8 +177,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } ); } + this.props.active; const cols = this.props.columns.map(col => { + const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" : + this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" : + this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" : + this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify"; const keysDropdown = <KeysDropdown keyValue={col.heading} @@ -189,24 +194,20 @@ export class SchemaTable extends React.Component<SchemaTableProps> { onSelect={this.props.changeColumns} setIsEditing={this.props.setHeaderIsEditing} docs={this.props.childDocs} + Document={this.props.Document} + dataDoc={this.props.dataDoc} + fieldKey={this.props.fieldKey} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + active={this.props.active} + openHeader={this.props.openHeader} + icon={icon} + col={col} // try commenting this out width={"100%"} />; - const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" : - this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" : - this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" : - this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify"; - const headerText = this._showTitleDropdown ? keysDropdown : <div - onClick={this.changeTitleMode} - style={{ - background: col.color, padding: "2px", - letterSpacing: "2px", - textTransform: "uppercase", - display: "flex" - }}> - {col.heading}</div>; const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up"; @@ -218,19 +219,12 @@ export class SchemaTable extends React.Component<SchemaTableProps> { background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}> - <FontAwesomeIcon icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px" }} /> - {/* <div className="keys-dropdown" - style={{ display: "inline", zIndex: 1000 }}> */} + {/* <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> */} {keysDropdown} - {/* </div> */} <div onClick={e => this.changeSorting(col)} - style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit" }}> + style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}> <FontAwesomeIcon icon={sortIcon} size="lg" /> </div> - {/* <div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} - style={{ float: "right", paddingRight: "6px", zIndex: 1, background: "inherit" }}> - <FontAwesomeIcon icon={"compass"} size="sm" /> - </div> */} </div>; return { @@ -318,27 +312,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } - - @action - nextHighlight = (e: React.MouseEvent, doc: Doc) => { - e.preventDefault(); - e.stopPropagation(); - doc.searchMatch = false; - console.log(doc.searchMatch); - setTimeout(() => doc.searchMatch = true, 0); - console.log(doc.searchMatch); - - doc.searchIndex = NumCast(doc.searchIndex); - } - - @action - nextHighlight2 = (doc: Doc) => { - - doc.searchMatchAlt = false; - setTimeout(() => doc.searchMatchAlt = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); - } - constructor(props: SchemaTableProps) { super(props); // convert old schema columns (list of strings) into new schema columns (list of schema header fields) @@ -347,7 +320,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders); } else if (this.props.Document._schemaHeaders === undefined) { - this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]); + this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb"), + new SchemaHeaderField("text", "#f1efeb"), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb")]); } } @@ -369,7 +343,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { addDoc: this.tableAddDoc, removeDoc: this.props.deleteDocument, rowInfo, - rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document), + rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true), textWrapRow: this.toggleTextWrapRow, rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1, dropAction: StrCast(this.props.Document.childDropAction), @@ -383,7 +357,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const row = rowInfo.index; //@ts-ignore const col = this.columns.map(c => c.heading).indexOf(column!.id); - const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document); + const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true); // TODO: editing border doesn't work :( return { style: { @@ -403,7 +377,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action onKeyDown = (e: KeyboardEvent): void => { - if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) { + if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {// && this.props.isSelected(true)) { const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 163554db9..46e30f616 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -15,7 +15,7 @@ import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { TraceMobx } from "../../../../fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; -import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils } from "../../../../Utils"; +import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils, setupMoveUpEvents } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; @@ -378,7 +378,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } getClusterColor = (doc: Doc) => { - let clusterColor = this.props.backgroundColor?.(doc); + let clusterColor = this.props.backgroundColor?.(doc, this.props.renderDepth + 1); const cluster = NumCast(doc.cluster); if (this.Document.useClusters) { if (this._clusterSets.length <= cluster) { @@ -1200,27 +1200,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.props.addDocTab(childDocs as any as Doc, "inParent"); this.props.ContainingCollectionView?.removeDocument(this.props.Document); })); - layoutDocsInGrid = () => { - UndoManager.RunInBatch(() => { - const docs = this.childLayoutPairs; - const startX = this.Document._panX || 0; - let x = startX; - let y = this.Document._panY || 0; - let i = 0; - const width = Math.max(...docs.map(doc => NumCast(doc.layout._width))); - const height = Math.max(...docs.map(doc => NumCast(doc.layout._height))); - docs.forEach(pair => { - pair.layout.x = x; - pair.layout.y = y; - x += width + 20; - if (++i === 6) { - i = 0; - x = startX; - y += height + 20; - } - }); - }, "arrange contents"); - } + + + @undoBatch + layoutDocsInGrid = action(() => { + const docs = this.childLayoutPairs; + const startX = this.Document._panX || 0; + let x = startX; + let y = this.Document._panY || 0; + let i = 0; + const width = Math.max(...docs.map(doc => NumCast(doc.layout._width))); + const height = Math.max(...docs.map(doc => NumCast(doc.layout._height))); + docs.forEach(pair => { + pair.layout.x = x; + pair.layout.y = y; + x += width + 20; + if (++i === 6) { + i = 0; + x = startX; + y += height + 20; + } + }); + }); + @undoBatch @action toggleNativeDimensions = () => { @@ -1495,8 +1497,123 @@ interface CollectionFreeFormViewPannableContentsProps { @observer class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{ + @observable private _drag: string = ''; + + //Adds event listener so knows pointer is down and moving + onPointerDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + const corner = e.target as any; + console.log(corner.id); + if (corner) this._drag = corner.id; + const rect = document.getElementById(this._drag); + if (rect) { + console.log(this._drag); + setupMoveUpEvents(e.target, e, this.onPointerMove, (e) => { }, (e) => { }); + } + } + + //Removes all event listeners + onPointerUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._drag = ""; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + //Adjusts the value in NodeStore + @action + onPointerMove = (e: PointerEvent) => { + const doc = document.getElementById('resizable'); + const rect = doc!.getBoundingClientRect(); + const toNumber = (original: number, delta: number): number => { + return original + (delta * this.props.zoomScaling()); + }; + if (doc) { + const height = doc.offsetHeight; + const width = doc.offsetWidth; + const top = doc.offsetTop; + const left = doc.offsetLeft; + switch (this._drag) { + case "": break; + case "resizer-br": + doc.style.width = toNumber(width, e.movementX) + 'px'; + doc.style.height = toNumber(height, e.movementY) + 'px'; + break; + case "resizer-bl": + doc.style.width = toNumber(width, -e.movementX) + 'px'; + doc.style.height = toNumber(height, e.movementY) + 'px'; + doc.style.left = toNumber(left, e.movementX) + 'px'; + break; + case "resizer-tr": + doc.style.width = toNumber(width, -e.movementX) + 'px'; + doc.style.height = toNumber(height, -e.movementY) + 'px'; + doc.style.top = toNumber(top, e.movementY) + 'px'; + case "resizer-tl": + doc.style.width = toNumber(width, -e.movementX) + 'px'; + doc.style.height = toNumber(height, -e.movementY) + 'px'; + doc.style.top = toNumber(top, e.movementY) + 'px'; + doc.style.left = toNumber(left, e.movementX) + 'px'; + case "resizable": + doc.style.top = toNumber(top, e.movementY) + 'px'; + doc.style.left = toNumber(left, e.movementX) + 'px'; + } + this.updateAll(height, width, top, left); + return false; + } + return true; + } + + @action + updateAll = (width: number, height: number, top: number, left: number) => { + const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + this.updateList(targetDoc, activeItem["viewfinder-width-indexed"], width); + this.updateList(targetDoc, activeItem["viewfinder-height-indexed"], height); + this.updateList(targetDoc, activeItem["viewfinder-top-indexed"], top); + this.updateList(targetDoc, activeItem["viewfinder-left-indexed"], left); + } + + @action + updateList = (doc: Doc, list: any, val: number) => { + const x: List<number> = list; + if (x && x.length >= NumCast(doc.currentFrame) + 1) { + x[NumCast(doc.currentFrame)] = val; + list = x; + } else if (doc && x) { + x.length = NumCast(doc.currentFrame) + 1; + x[NumCast(doc.currentFrame)] = val; + list = x; + } + } + + // scale: NumCast(targetDoc._viewScale), + @computed get zoomProgressivizeContainer() { + const activeItem = Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + if (activeItem && activeItem.zoomProgressivize) { + const vfLeft: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-left-indexed"]); + const vfWidth: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-width-indexed"]); + const vfTop: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-top-indexed"]); + const vfHeight: number = PresBox.Instance.checkList(targetDoc, activeItem["viewfinder-height-indexed"]); + return ( + <> + {!activeItem.editZoomProgressivize ? (null) : <div id="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width: vfWidth, height: vfHeight, top: vfTop, left: vfLeft, position: 'absolute' }}> + <div className='resizers'> + <div id="resizer-tl" className='resizer top-left' onPointerDown={this.onPointerDown}></div> + <div id="resizer-tr" className='resizer top-right' onPointerDown={this.onPointerDown}></div> + <div id="resizer-bl" className='resizer bottom-left' onPointerDown={this.onPointerDown}></div> + <div id="resizer-br" className='resizer bottom-right' onPointerDown={this.onPointerDown}></div> + </div> + </div>} + </> + ); + } + } + @computed get zoomProgressivize() { - return PresBox.Instance && this.props.zoomProgressivize ? PresBox.Instance.zoomProgressivizeContainer : (null); + return PresBox.Instance && this.props.zoomProgressivize ? this.zoomProgressivizeContainer : (null); } @computed get progressivize() { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1a708d67d..c0b19fcd2 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -21,6 +21,7 @@ import { CollectionView } from "../CollectionView"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import "./MarqueeView.scss"; import React = require("react"); +import { ContextMenuItem } from "../../ContextMenuItem"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -70,23 +71,19 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque onKeyPress = (e: KeyboardEvent) => { //make textbox and add it to this collection // tslint:disable-next-line:prefer-const - let [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY); + const cm = ContextMenu.Instance; + const [x, y] = this.props.getTransform().transformPoint(this._downX, this._downY); if (e.key === "?") { - ContextMenu.Instance.setDefaultItem("?", (str: string) => { - const textDoc = Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { - _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, - title: "bing", UseCors: true - }); - this.props.addDocTab(textDoc, "onRight"); - }); + cm.setDefaultItem("?", (str: string) => this.props.addDocTab( + Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight")); - ContextMenu.Instance.displayMenu(this._downX, this._downY); + cm.displayMenu(this._downX, this._downY); e.stopPropagation(); } else if (e.key === ":") { DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y); - ContextMenu.Instance.displayMenu(this._downX, this._downY); + cm.displayMenu(this._downX, this._downY); e.stopPropagation(); } else if (e.key === "a" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); @@ -108,11 +105,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque if (br) break; } } + let ypos = y; ns.map(line => { const indent = line.search(/\S|$/); - const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: y, title: line }); + const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line }); this.props.addDocument(newBox); - y += 40 * this.props.getTransform().Scale; + ypos += 40 * this.props.getTransform().Scale; }); })(); e.stopPropagation(); @@ -280,7 +278,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } else { this._downX = x; this._downY = y; - const effectiveAcl = GetEffectiveAcl(this.props.Document); + const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); if ([AclAdmin, AclEdit, AclAddonly].includes(effectiveAcl)) PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge); this.clearSelection(); } @@ -342,41 +340,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @undoBatch @action delete = () => { - const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc; const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - - selected.map(doc => { - const effectiveAcl = GetEffectiveAcl(doc); - if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete - recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - this.props.removeDocument(doc); - } - }); + selected.forEach(doc => this.props.removeDocument(doc)); this.cleanupInteractions(false); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); } - getCollection = (selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => { - const bounds = this.Bounds; - // const inkData = this.ink ? this.ink.inkData : undefined; - const newCollection = (creator || Docs.Create.FreeformDocument)(selected, { - x: bounds.left, - y: bounds.top, - _panX: 0, - _panY: 0, - isBackground, - backgroundColor: this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined, - _width: bounds.width, - _height: bounds.height, - title: "a nested collection", - }); + getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, isBackground?: boolean) => { + const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => { + Doc.GetProto(doc).data = new List<Doc>(selected); + Doc.GetProto(doc).title = "nested freeform"; + doc._panX = doc._panY = 0; + return doc; + })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); + newCollection.system = undefined; + newCollection.isBackground = isBackground; + newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : isBackground ? "cyan" : undefined; + newCollection._width = this.Bounds.width; + newCollection._height = this.Bounds.height; + newCollection.x = this.Bounds.left; + newCollection.y = this.Bounds.top; selected.forEach(d => d.context = newCollection); this.hideMarquee(); return newCollection; - } + }); @action pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => { diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index e0f3eca44..57e968aa7 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -316,7 +316,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ getPermissionsSelect(user: string, permission: string) { return <select className="permissions-select" - defaultValue={permission} + value={permission} onChange={e => this.changePermissions(e, user)}> {Object.values(SharingPermissions).map(permission => { return ( @@ -963,22 +963,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { } if (this.isPres) { const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0; - return <div className="propertiesView"> - <div className="propertiesView-title"> + return <div className="propertiesView" style={{ width: this.props.width }}> + <div className="propertiesView-title" style={{ width: this.props.width }}> Presentation </div> <div className="propertiesView-name"> {this.editableTitle} <div className="propertiesView-presSelected"> - {PresBox.Instance._selectedArray.length} selected + {PresBox.Instance?._selectedArray.length} selected <div className="propertiesView-selectedList"> - {PresBox.Instance.listOfSelected} + {PresBox.Instance?.listOfSelected} </div> </div> </div> {!selectedItem ? (null) : <div className="propertiesView-presTrails"> <div className="propertiesView-presTrails-title" - onPointerDown={() => runInAction(() => { this.openPresTransitions = !this.openPresTransitions; })} + onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })} style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}> <FontAwesomeIcon icon={"rocket"} /> Transitions <div className="propertiesView-presTrails-title-icon"> @@ -1028,7 +1028,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {PresBox.Instance.newDocumentDropdown} </div> : null} </div> - <div className="propertiesView-sharing"> + {/* <div className="propertiesView-sharing"> <div className="propertiesView-sharing-title" onPointerDown={() => runInAction(() => { this.openSharing = !this.openSharing; })} style={{ backgroundColor: this.openSharing ? "black" : "" }}> @@ -1040,7 +1040,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {this.openSharing ? <div className="propertiesView-sharing-content"> {this.sharingTable} </div> : null} - </div> + </div> */} </div>; } } |
