diff options
| author | Fawn <fangrui_tong@brown.edu> | 2019-04-22 00:09:25 -0400 |
|---|---|---|
| committer | Fawn <fangrui_tong@brown.edu> | 2019-04-22 00:09:25 -0400 |
| commit | 97dcec0e5ac07deb9af9acb16948cc6678778cba (patch) | |
| tree | 74dd3d2bd938a5ab5b3bb082a8fc2846eefa5999 /src/client/views/collections | |
| parent | e794b4b38e8ab2f4e7a79f223f9488cc845c724f (diff) | |
| parent | b63bcb791013766d5d16e4f38964499268f904c4 (diff) | |
merge
Diffstat (limited to 'src/client/views/collections')
14 files changed, 522 insertions, 554 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index bff56e8c3..eec01bb3f 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -8,6 +8,7 @@ import { ListField } from '../../../fields/ListField'; import { NumberField } from '../../../fields/NumberField'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; +import { TextField } from '../../../fields/TextField'; export enum CollectionViewType { Invalid, @@ -22,7 +23,7 @@ export interface CollectionRenderProps { removeDocument: (document: Document) => boolean; moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; active: () => boolean; - onActiveChanged: (isActive: boolean) => void; + whenActiveChanged: (isActive: boolean) => void; } export interface CollectionViewProps extends FieldViewProps { @@ -55,19 +56,22 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { //TODO should this be observable? private _isChildActive = false; - onActiveChanged = (isActive: boolean) => { + whenActiveChanged = (isActive: boolean) => { this._isChildActive = isActive; - this.props.onActiveChanged(isActive); + this.props.whenActiveChanged(isActive); } createsCycle(documentToAdd: Document, containerDocument: Document): boolean { - let data = documentToAdd.GetList<Document>(KeyStore.Data, []); - for (const doc of data) { + if (!(documentToAdd instanceof Document)) { + return false; + } + let data = documentToAdd.GetList(KeyStore.Data, [] as Document[]); + for (const doc of data.filter(d => d instanceof Document)) { if (this.createsCycle(doc, containerDocument)) { return true; } } - let annots = documentToAdd.GetList<Document>(KeyStore.Annotations, []); + let annots = documentToAdd.GetList(KeyStore.Annotations, [] as Document[]); for (const annot of annots) { if (this.createsCycle(annot, containerDocument)) { return true; @@ -84,56 +88,50 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { @action.bound addDocument(doc: Document, allowDuplicates: boolean = false): boolean { - let props = this.props; - var curPage = props.Document.GetNumber(KeyStore.CurPage, -1); + var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); doc.SetOnPrototype(KeyStore.Page, new NumberField(curPage)); - if (this.isAnnotationOverlay) { - doc.SetOnPrototype(KeyStore.Zoom, new NumberField(this.props.Document.GetNumber(KeyStore.Scale, 1))); - } if (curPage >= 0) { - doc.SetOnPrototype(KeyStore.AnnotationOn, props.Document); + doc.SetOnPrototype(KeyStore.AnnotationOn, this.props.Document); } - if (props.Document.Get(props.fieldKey) instanceof Field) { - //TODO This won't create the field if it doesn't already exist - const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>()); - if (!this.createsCycle(doc, props.Document)) { + if (this.props.Document.Get(this.props.fieldKey) instanceof Field) { + const value = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); + if (!this.createsCycle(doc, this.props.Document)) { if (!value.some(v => v.Id === doc.Id) || allowDuplicates) { value.push(doc); + doc.SetNumber(KeyStore.ZoomBasis, this.props.Document.GetNumber(KeyStore.Scale, 1)); } - } - else { - return false; - } - } else { - let proto = props.Document.GetPrototype(); - if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) { - const field = new ListField([doc]); - // const script = CompileScript(` - // if(added) { - // console.log("added " + field.Title + " " + doc.Title); - // } else { - // console.log("removed " + field.Title + " " + doc.Title); - // } - // `, { - // addReturn: false, - // params: { - // field: Document.name, - // added: "boolean" - // }, - // capturedVariables: { - // doc: this.props.Document - // } - // }); - // if (script.compiled) { - // field.addScript(new ScriptField(script)); - // } - props.Document.SetOnPrototype(props.fieldKey, field); - } - else { - return false; + return true; } } - return true; + // bcz: What is this code trying to do? + // else { + // let proto = props.Document.GetPrototype(); + // if (!proto || proto === FieldWaiting || !this.createsCycle(proto, doc)) { + // const field = new ListField([doc]); + // // const script = CompileScript(` + // // if(added) { + // // console.log("added " + field.Title + " " + doc.Title); + // // } else { + // // console.log("removed " + field.Title + " " + doc.Title); + // // } + // // `, { + // // addReturn: false, + // // params: { + // // field: Document.name, + // // added: "boolean" + // // }, + // // capturedVariables: { + // // doc: this.props.Document + // // } + // // }); + // // if (script.compiled) { + // // field.addScript(new ScriptField(script)); + // // } + // props.Document.SetOnPrototype(props.fieldKey, field); + // return true; + // } + // } + return false; } @action.bound @@ -181,7 +179,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { removeDocument: this.removeDocument, moveDocument: this.moveDocument, active: this.active, - onActiveChanged: this.onActiveChanged, + whenActiveChanged: this.whenActiveChanged, }; const viewtype = this.collectionViewType; return ( diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2b96e7678..e4c647635 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -19,6 +19,7 @@ import { ServerUtils } from "../../../server/ServerUtil"; import { DragManager, DragLinksAsDocuments } from "../../util/DragManager"; import { TextField } from "../../../fields/TextField"; import { ListField } from "../../../fields/ListField"; +import { Transform } from '../../util/Transform' @observer export class CollectionDockingView extends React.Component<SubCollectionViewProps> { @@ -263,7 +264,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp let counter: any = this.htmlToElement(`<div class="messageCounter">${count}</div>`); tab.element.append(counter); counter.DashDocId = tab.contentItem.config.props.documentId; - (tab as any).reactionDisposer = reaction(() => [f.GetT(KeyStore.LinkedFromDocs, ListField), f.GetT(KeyStore.LinkedToDocs, ListField)], + tab.reactionDisposer = reaction(() => [f.GetT(KeyStore.LinkedFromDocs, ListField), f.GetT(KeyStore.LinkedToDocs, ListField)], (lists) => { let count = (lists.length > 0 && lists[0] && lists[0]!.Data ? lists[0]!.Data.length : 0) + (lists.length > 1 && lists[1] && lists[1]!.Data ? lists[1]!.Data.length : 0); @@ -315,7 +316,7 @@ interface DockedFrameProps { @observer export class DockedFrameRenderer extends React.Component<DockedFrameProps> { - private _mainCont = React.createRef<HTMLDivElement>(); + _mainCont = React.createRef<HTMLDivElement>(); @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt<Document>; @@ -325,38 +326,49 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { Server.GetField(this.props.documentId, action((f: Opt<Field>) => this._document = f as Document)); } - private _nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); - private _nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); - private _contentScaling = () => this._panelWidth / (this._nativeWidth() ? this._nativeWidth() : this._panelWidth); + nativeWidth = () => this._document!.GetNumber(KeyStore.NativeWidth, this._panelWidth); + nativeHeight = () => this._document!.GetNumber(KeyStore.NativeHeight, this._panelHeight); + contentScaling = () => { + let wscale = this._panelWidth / (this.nativeWidth() ? this.nativeWidth() : this._panelWidth); + if (wscale * this.nativeHeight() > this._panelHeight) + return this._panelHeight / (this.nativeHeight() ? this.nativeHeight() : this._panelHeight); + return wscale; + } ScreenToLocalTransform = () => { - let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!); - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this._contentScaling()); + if (this._mainCont.current && this._mainCont.current.children) { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current!.children[0].firstChild as HTMLElement); + scale = Utils.GetScreenTransform(this._mainCont.current!).scale; + return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(scale / this.contentScaling()); + } + return Transform.Identity(); } + get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } - render() { - if (!this._document) { - return (null); - } - var content = - <div className="collectionDockingView-content" ref={this._mainCont}> - <DocumentView key={this._document.Id} Document={this._document} + get content() { + return ( + <div className="collectionDockingView-content" ref={this._mainCont} + style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}> + <DocumentView key={this._document!.Id} Document={this._document!} addDocument={undefined} removeDocument={undefined} - ContentScaling={this._contentScaling} - PanelWidth={this._nativeWidth} - PanelHeight={this._nativeHeight} + ContentScaling={this.contentScaling} + PanelWidth={this.nativeWidth} + PanelHeight={this.nativeHeight} ScreenToLocalTransform={this.ScreenToLocalTransform} isTopMost={true} selectOnLoad={false} parentActive={returnTrue} - onActiveChanged={emptyFunction} + whenActiveChanged={emptyFunction} focus={emptyDocFunction} ContainingCollectionView={undefined} /> - </div>; + </div>); + } - return <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}> - {({ measureRef }) => <div ref={measureRef}> {content} </div>} - </Measure>; + render() { + return !this._document ? (null) : + <Measure onResize={action((r: any) => { this._panelWidth = r.entry.width; this._panelHeight = r.entry.height; })}> + {({ measureRef }) => <div ref={measureRef}> {this.content} </div>} + </Measure>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 40e49bb5f..cfdb3ab22 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -1,56 +1,6 @@ @import "../globalCssVariables"; -//options menu styling -#schemaOptionsMenuBtn { - position: absolute; - height: 20px; - width: 20px; - border-radius: 50%; - z-index: 21; - right: 4px; - top: 4px; - pointer-events: auto; - background-color:black; - display:inline-block; - padding: 0px; - font-size: 100%; -} -#schema-options-header { - text-align: center; - padding: 0px; - margin: 0px; -} -.schema-options-subHeader { - color: $intermediate-color; - margin-bottom: 5px; -} -#schemaOptionsMenuBtn:hover { - transform: scale(1.15); -} - -#preview-schema-checkbox-div { - margin-left: 20px; - font-size: 12px; -} - #options-flyout-div { - text-align: left; - padding:0px; - z-index: 100; - font-family: $sans-serif; - padding-left: 5px; - } - - #schema-col-checklist { - overflow: scroll; - text-align: left; - //background-color: $light-color-secondary; - line-height: 25px; - max-height: 175px; - font-family: $sans-serif; - font-size: 12px; - } - .collectionSchemaView-container { border-width: $COLLECTION_BORDER_WIDTH; @@ -61,18 +11,31 @@ position: absolute; width: 100%; height: 100%; - - .collectionSchemaView-content { - position: absolute; - height: 100%; - width: 100%; - overflow: auto; + + .collectionSchemaView-cellContents { + height: $MAX_ROW_HEIGHT; } + .collectionSchemaView-previewRegion { position: relative; background: $light-color; float: left; height: 100%; + .collectionSchemaView-previewDoc { + height: 100%; + width: 100%; + position: absolute; + } + .collectionSchemaView-input { + position: absolute; + max-width: 150px; + width: 100%; + bottom: 0px; + } + .documentView-node:first-child { + position: relative; + background: $light-color; + } } .collectionSchemaView-previewHandle { position: absolute; @@ -146,7 +109,7 @@ } .rt-tr-group { direction: ltr; - max-height: 44px; + max-height: $MAX_ROW_HEIGHT; } .rt-td { border-width: 1px; @@ -178,7 +141,7 @@ } .ReactTable .rt-th, .ReactTable .rt-td { - max-height: 44; + max-height: $MAX_ROW_HEIGHT; padding: 3px 7px; font-size: 13px; text-align: center; @@ -188,13 +151,71 @@ border-bottom-style: solid; border-bottom-width: 1; } + .documentView-node-topmost { + text-align:left; + transform-origin: center top; + display: inline-block; + } .documentView-node:first-child { background: $light-color; - .imageBox-cont img { - object-fit: contain; - } } } +//options menu styling +#schemaOptionsMenuBtn { + position: absolute; + height: 20px; + width: 20px; + border-radius: 50%; + z-index: 21; + right: 4px; + top: 4px; + pointer-events: auto; + background-color:black; + display:inline-block; + padding: 0px; + font-size: 100%; +} + +ul { + list-style-type: disc; +} + +#schema-options-header { + text-align: center; + padding: 0px; + margin: 0px; +} +.schema-options-subHeader { + color: $intermediate-color; + margin-bottom: 5px; +} +#schemaOptionsMenuBtn:hover { + transform: scale(1.15); +} + +#preview-schema-checkbox-div { + margin-left: 20px; + font-size: 12px; +} + + #options-flyout-div { + text-align: left; + padding:0px; + z-index: 100; + font-family: $sans-serif; + padding-left: 5px; + } + + #schema-col-checklist { + overflow: scroll; + text-align: left; + //background-color: $light-color-secondary; + line-height: 25px; + max-height: 175px; + font-family: $sans-serif; + font-size: 12px; + } + .Resizer { box-sizing: border-box; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 4f4068e7a..90077b053 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -4,15 +4,15 @@ import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, untracked } from "mobx"; import { observer } from "mobx-react"; -import Measure from "react-measure"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; +import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss' import "react-table/react-table.css"; import { Document } from "../../../fields/Document"; import { Field, Opt } from "../../../fields/Field"; import { Key } from "../../../fields/Key"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { emptyDocFunction, emptyFunction, returnFalse, returnOne } from "../../../Utils"; +import { emptyDocFunction, emptyFunction, returnFalse } from "../../../Utils"; import { Server } from "../../Server"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript, ToField } from "../../util/Scripting"; @@ -34,42 +34,35 @@ import { CollectionSubView } from "./CollectionSubView"; class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> { @observable key: Key | undefined; - componentWillReceiveProps() { - Server.GetField(this.props.keyId, action((field: Opt<Field>) => { - if (field instanceof Key) { - this.key = field; - } - })); + constructor(props: any) { + super(props); + Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field))); } render() { - if (this.key) { - return (<div key={this.key.Id}> + return !this.key ? (null) : + (<div key={this.key.Id}> <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} /> {this.key.Name} </div>); - } - return (null); } } @observer export class CollectionSchemaView extends CollectionSubView { - private _mainCont = React.createRef<HTMLDivElement>(); + private _mainCont?: HTMLDivElement; private _startSplitPercent = 0; private DIVIDER_WIDTH = 4; @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author]; - @observable _contentScaling = 1; // used to transfer the dimensions of the content pane in the DOM to the ContentScaling prop of the DocumentView - @observable _dividerX = 0; - @observable _panelWidth = 0; - @observable _panelHeight = 0; @observable _selectedIndex = 0; @observable _columnsPercentage = 0; @observable _keys: Key[] = []; + @observable _newKeyName: string = ""; @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); } - + @computed get columns() { return this.props.Document.GetList(KeyStore.ColumnsKey, [] as Key[]); } + @computed get borderWidth() { return COLLECTION_BORDER_WIDTH; } renderCell = (rowProps: CellInfo) => { let props: FieldViewProps = { @@ -83,7 +76,7 @@ export class CollectionSchemaView extends CollectionSubView { ScreenToLocalTransform: Transform.Identity, focus: emptyDocFunction, active: returnFalse, - onActiveChanged: emptyFunction, + whenActiveChanged: emptyFunction, }; let contents = ( <FieldView {...props} /> @@ -107,11 +100,11 @@ export class CollectionSchemaView extends CollectionSubView { return false; }; return ( - <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} style={{ height: "56px" }} key={props.Document.Id} ref={reference}> + <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}> <EditableView display={"inline"} contents={contents} - height={56} + height={Number(MAX_ROW_HEIGHT)} GetValue={() => { let field = props.Document.Get(props.fieldKey); if (field && field instanceof Field) { @@ -165,9 +158,9 @@ export class CollectionSchemaView extends CollectionSubView { }; } - @computed - get columns() { - return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []); + private createTarget = (ele: HTMLDivElement) => { + this._mainCont = ele; + super.CreateDropTarget(ele); } @action @@ -187,31 +180,12 @@ export class CollectionSchemaView extends CollectionSubView { //toggles preview side-panel of schema @action toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => { - this._startSplitPercent = this.splitPercentage; - if (this._startSplitPercent === this.splitPercentage) { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); - } - } - - @computed - get findAllDocumentKeys(): { [id: string]: boolean } { - const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []); - let keys: { [id: string]: boolean } = {}; - if (this._optionsActivated > -1) { - // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // is displayed (unlikely) it won't show up until something else changes. - untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)))); - } - this.columns.forEach(key => keys[key.Id] = true); - return keys; + this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); } @action onDividerMove = (e: PointerEvent): void => { - let nativeWidth = this._mainCont.current!.getBoundingClientRect(); + let nativeWidth = this._mainCont!.getBoundingClientRect(); this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100))); } @action @@ -230,155 +204,149 @@ export class CollectionSchemaView extends CollectionSubView { document.addEventListener('pointerup', this.onDividerUp); } - @observable _tableWidth = 0; - @action - setTableDimensions = (r: any) => { - this._tableWidth = r.entry.width; - } - @action - setScaling = (r: any) => { - const children = this.props.Document.GetList<Document>(this.props.fieldKey, []); - const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined; - this._panelWidth = r.entry.width; - this._panelHeight = r.entry.height ? r.entry.height : this._panelHeight; - this._contentScaling = r.entry.width / selected!.GetNumber(KeyStore.NativeWidth, r.entry.width); + onPointerDown = (e: React.PointerEvent): void => { + if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (this.props.isSelected()) + e.stopPropagation(); + else e.preventDefault(); + } } - @computed - get borderWidth() { return COLLECTION_BORDER_WIDTH; } - getContentScaling = (): number => this._contentScaling; - getPanelWidth = (): number => this._panelWidth; - getPanelHeight = (): number => this._panelHeight; - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX, - this.borderWidth).scale(1 / this._contentScaling); - getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate(- this.borderWidth - this.DIVIDER_WIDTH - this._dividerX - this._tableWidth, - this.borderWidth).scale(1 / this._contentScaling); - - onPointerDown = (e: React.PointerEvent): void => { - if (e.button === 1 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { + onWheel = (e: React.WheelEvent): void => { + if (this.props.active()) { e.stopPropagation(); } } @action addColumn = () => { - this.columns.push(new Key(this.newKeyName)); - this.newKeyName = ""; + this.columns.push(new Key(this._newKeyName)); + this._newKeyName = ""; } - @observable - newKeyName: string = ""; - @action newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => { - this.newKeyName = e.currentTarget.value; - } - onWheel = (e: React.WheelEvent): void => { - if (this.props.active()) { - e.stopPropagation(); - } - } - - @observable _optionsActivated: number = 0; - @action - OptionsMenuDown = (e: React.PointerEvent) => { - this._optionsActivated++; + this._newKeyName = e.currentTarget.value; } - @observable previewScript: string = "this"; + @observable previewScript: string = ""; @action onPreviewScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.previewScript = e.currentTarget.value; } - render() { - library.add(faCog); - library.add(faPlus); - const columns = this.columns; - const children = this.props.Document.GetList<Document>(this.props.fieldKey, []); + get previewDocument(): Document | undefined { + const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined; - //all the keys/columns that will be displayed in the schema - const allKeys = this.findAllDocumentKeys; - let doc: any = selected ? selected.Get(new Key(this.previewScript)) : undefined; + return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined; + } + get tableWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * (1 - this.splitPercentage / 100); } + get previewRegionHeight() { return this.props.PanelHeight() - 2 * this.borderWidth; } + get previewRegionWidth() { return (this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH) * this.splitPercentage / 100; } + + private previewDocNativeWidth = () => this.previewDocument!.GetNumber(KeyStore.NativeWidth, this.previewRegionWidth); + private previewDocNativeHeight = () => this.previewDocument!.GetNumber(KeyStore.NativeHeight, this.previewRegionHeight); + private previewContentScaling = () => { + let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth); + if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) + return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight); + return wscale; + } + private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling(); + private previewPanelHeight = () => this.previewDocNativeHeight() * this.previewContentScaling(); + get previewPanelCenteringOffset() { return (this.previewRegionWidth - this.previewDocNativeWidth() * this.previewContentScaling()) / 2; } + getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate( + - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth - this.previewPanelCenteringOffset, + - this.borderWidth).scale(1 / this.previewContentScaling()); + @computed + get previewPanel() { // let doc = CompileScript(this.previewScript, { this: selected }, true)(); - let content = this._selectedIndex === -1 || !selected ? (null) : ( - <Measure onResize={this.setScaling}> - {({ measureRef }) => - <div className="collectionSchemaView-content" ref={measureRef}> - {doc instanceof Document ? - <DocumentView Document={doc} - addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} - isTopMost={false} - selectOnLoad={false} - ScreenToLocalTransform={this.getPreviewTransform} - ContentScaling={this.getContentScaling} - PanelWidth={this.getPanelWidth} - PanelHeight={this.getPanelHeight} - ContainingCollectionView={this.props.CollectionView} - focus={emptyDocFunction} - parentActive={this.props.active} - onActiveChanged={this.props.onActiveChanged} /> : null} - <input value={this.previewScript} onChange={this.onPreviewScriptChange} - style={{ position: 'absolute', bottom: '0px' }} /> - </div> - } - </Measure> - ); - let dividerDragger = this.splitPercentage === 0 ? (null) : - <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />; - - //options button and menu - let optionsMenu = !this.props.active() ? (null) : (<Flyout - anchorPoint={anchorPoints.LEFT_TOP} - content={<div> - <div id="schema-options-header"><h5><b>Options</b></h5></div> - <div id="options-flyout-div"> - <h6 className="schema-options-subHeader">Preview Window</h6> - <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div> - <h6 className="schema-options-subHeader" >Displayed Columns</h6> - <ul id="schema-col-checklist" > - {Array.from(Object.keys(allKeys)).map(item => - (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />))} - </ul> - <input value={this.newKeyName} onChange={this.newKeyChange} /> - <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button> + return !this.previewDocument ? (null) : ( + <div className="collectionSchemaView-previewRegion" style={{ width: `${this.previewRegionWidth}px` }}> + <div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}> + <DocumentView Document={this.previewDocument} isTopMost={false} selectOnLoad={false} + addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} + ScreenToLocalTransform={this.getPreviewTransform} + ContentScaling={this.previewContentScaling} + PanelWidth={this.previewPanelWidth} PanelHeight={this.previewPanelHeight} + ContainingCollectionView={this.props.CollectionView} + focus={emptyDocFunction} + parentActive={this.props.active} + whenActiveChanged={this.props.whenActiveChanged} + /> </div> + <input className="collectionSchemaView-input" value={this.previewScript} onChange={this.onPreviewScriptChange} + style={{ left: `calc(50% - ${Math.min(75, this.previewPanelWidth() / 2)}px)` }} /> </div> - }> - <button id="schemaOptionsMenuBtn" onPointerDown={this.OptionsMenuDown}><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button> - </Flyout>); + ); + } - return ( - <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} ref={this._mainCont}> - <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}> - <Measure onResize={this.setTableDimensions}> - {({ measureRef }) => - <div className="collectionSchemaView-tableContainer" ref={measureRef} style={{ width: `calc(100% - ${this.splitPercentage}%)` }}> - <ReactTable - data={children} - pageSize={children.length} - page={0} - showPagination={false} - columns={columns.map(col => ({ - Header: col.Name, - accessor: (doc: Document) => [doc, col], - id: col.Id - }))} - column={{ - ...ReactTableDefaults.column, - Cell: this.renderCell, + get documentKeysCheckList() { + const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); + let keys: { [id: string]: boolean } = {}; + // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. + // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be + // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. + // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu + // is displayed (unlikely) it won't show up until something else changes. + untracked(() => docs.map(doc => doc.GetAllPrototypes().map(proto => proto._proxies.forEach((val: any, key: string) => keys[key] = false)))); + + this.columns.forEach(key => keys[key.Id] = true); + return Array.from(Object.keys(keys)).map(item => + (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />)); + } - }} - getTrProps={this.getTrProps} - /> - </div>} - </Measure> - {dividerDragger} - <div className="collectionSchemaView-previewRegion" style={{ width: `calc(${this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0)}% - ${this.DIVIDER_WIDTH}px)` }}> - {content} + get tableOptionsPanel() { + return !this.props.active() ? (null) : + (<Flyout + anchorPoint={anchorPoints.LEFT_TOP} + content={<div> + <div id="schema-options-header"><h5><b>Options</b></h5></div> + <div id="options-flyout-div"> + <h6 className="schema-options-subHeader">Preview Window</h6> + <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.splitPercentage !== 0} onChange={this.toggleExpander} /> Show Preview </div> + <h6 className="schema-options-subHeader" >Displayed Columns</h6> + <ul id="schema-col-checklist" > + {this.documentKeysCheckList} + </ul> + <input value={this._newKeyName} onChange={this.newKeyChange} /> + <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button> </div> - {optionsMenu} </div> - </div > + }> + <button id="schemaOptionsMenuBtn" ><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button> + </Flyout>); + } + + @computed + get dividerDragger() { + return this.splitPercentage === 0 ? (null) : + <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} />; + } + + render() { + library.add(faCog); + library.add(faPlus); + const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); + return ( + <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} onWheel={this.onWheel} + onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}> + <div className="collectionSchemaView-tableContainer" style={{ width: `${this.tableWidth}px` }}> + <ReactTable data={children} page={0} pageSize={children.length} showPagination={false} + columns={this.columns.map(col => ({ + Header: col.Name, + accessor: (doc: Document) => [doc, col], + id: col.Id + }))} + column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }} + getTrProps={this.getTrProps} + /> + </div> + {this.dividerDragger} + {this.previewPanel} + {this.tableOptionsPanel} + </div> ); } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d3d69b1af..ead559bd9 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -15,7 +15,6 @@ import { ServerUtils } from "../../../server/ServerUtil"; import { Server } from "../../Server"; import { FieldViewProps } from "../nodes/FieldView"; import * as rp from 'request-promise'; -import { emptyFunction } from "../../../Utils"; import { CollectionView } from "./CollectionView"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; @@ -24,6 +23,8 @@ export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Document, allowDuplicates?: boolean) => boolean; removeDocument: (document: Document) => boolean; moveDocument: (document: Document, targetCollection: Document, addDocument: (document: Document) => boolean) => boolean; + PanelWidth: () => number; + PanelHeight: () => number; } export interface SubCollectionViewProps extends CollectionViewProps { @@ -42,6 +43,9 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> { this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } } + protected CreateDropTarget(ele: HTMLDivElement) { + this.createDropTarget(ele); + } @action protected setCursorPosition(position: [number, number]) { @@ -155,10 +159,7 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> { e.preventDefault(); if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) { - console.log("not good"); - let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 }); - htmlDoc.SetText(KeyStore.DocumentText, text); - this.props.addDocument(htmlDoc, false); + this.props.addDocument(Documents.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text }), false); return; } @@ -184,36 +185,17 @@ export class CollectionSubView extends React.Component<SubCollectionViewProps> { let type = item.type; if (item.kind === "file") { let file = item.getAsFile(); - let formData = new FormData(); - - if (file) { - formData.append('file', file); - } let dropFileName = file ? file.name : "-empty-"; + let formData = new FormData(); + if (file) formData.append('file', file); - let prom = fetch(upload, { + promises.push(fetch(upload, { method: 'POST', body: formData - }).then(async (res: Response) => { - (await res.json()).map(action((file: any) => { - let path = window.location.origin + file; - let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 600, width: 300, title: dropFileName }); - - docPromise.then(action((doc?: Document) => { - let docs = this.props.Document.GetT(KeyStore.Data, ListField); - if (docs !== FieldWaiting) { - if (!docs) { - docs = new ListField<Document>(); - this.props.Document.Set(KeyStore.Data, docs); - } - if (doc) { - docs.Data.push(doc); - } - } - })); - })); - }); - promises.push(prom); + }).then(async (res: Response) => + (await res.json()).map(action((file: any) => + this.getDocumentFromType(type, window.location.origin + file, { ...options, nativeWidth: 600, width: 300, title: dropFileName }). + then(doc => doc && this.props.addDocument(doc, false)))))); } } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 973eead97..8ecc5b67b 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -50,15 +50,15 @@ .docContainer:hover { .delete-button { display: inline; - width: auto; + // width: auto; } } .delete-button { color: $intermediate-color; - float: right; + // float: right; margin-left: 15px; - margin-top: 3px; + // margin-top: 3px; display: inline; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 51a02fc25..e0387f4b4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,20 +1,17 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { FieldWaiting } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { ListField } from "../../../fields/ListField"; -import { SetupDrag, DragManager } from "../../util/DragManager"; +import { DragManager, SetupDrag } from "../../util/DragManager"; import { EditableView } from "../EditableView"; -import "./CollectionTreeView.scss"; -import { CollectionView } from "./CollectionView"; -import * as globalCssVariables from "../../views/globalCssVariables.scss"; import { CollectionSubView } from "./CollectionSubView"; +import "./CollectionTreeView.scss"; import React = require("react"); -import { props } from 'bluebird'; export interface TreeViewProps { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 1f69a69e8..675e720e2 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -10,6 +10,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_ import { KeyStore } from '../../../fields/KeyStore'; import { observer } from 'mobx-react'; import { undoBatch } from '../../util/UndoManager'; +import { trace } from 'mobx'; @observer export class CollectionView extends React.Component<FieldViewProps> { @@ -28,8 +29,10 @@ export class CollectionView extends React.Component<FieldViewProps> { return (null); } + get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? + onContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document.Id !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Freeform", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Freeform)) }); ContextMenu.Instance.addItem({ description: "Schema", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Schema)) }); ContextMenu.Instance.addItem({ description: "Treeview", event: undoBatch(() => this.props.Document.SetNumber(KeyStore.ViewType, CollectionViewType.Tree)) }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 081b3eb6c..20c5a84bf 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -23,10 +23,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo let l = this.props.LinkDocs; let a = this.props.A; let b = this.props.B; - let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Width, 0) / 2); - let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.Minimized, false) ? 5 : a.GetNumber(KeyStore.Height, 0) / 2); - let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Width, 0) / 2); - let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.Minimized, false) ? 5 : b.GetNumber(KeyStore.Height, 0) / 2); + let x1 = a.GetNumber(KeyStore.X, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Width() / 2); + let y1 = a.GetNumber(KeyStore.Y, 0) + (a.GetBoolean(KeyStore.IsMinimized, false) ? 5 : a.Height() / 2); + let x2 = b.GetNumber(KeyStore.X, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Width() / 2); + let y2 = b.GetNumber(KeyStore.Y, 0) + (b.GetBoolean(KeyStore.IsMinimized, false) ? 5 : b.Height() / 2); return ( <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown} style={{ strokeWidth: `${l.length * 5}` }} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 2f684a54e..ebdb0c75c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -16,9 +16,9 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP _brushReactionDisposer?: IReactionDisposer; componentDidMount() { - this._brushReactionDisposer = reaction(() => this.props.Document.GetList<Document>(this.props.fieldKey, []).map(doc => doc.GetNumber(KeyStore.X, 0)), + this._brushReactionDisposer = reaction(() => this.props.Document.GetList(this.props.fieldKey, [] as Document[]).map(doc => doc.GetNumber(KeyStore.X, 0)), () => { - let views = this.props.Document.GetList<Document>(this.props.fieldKey, []); + let views = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc.GetText(KeyStore.BackgroundLayout, "").indexOf("istogram") !== -1); for (let i = 0; i < views.length; i++) { for (let j = 0; j < views.length; j++) { let srcDoc = views[j]; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 751ea8190..cf0a6de00 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -12,7 +12,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV protected getCursors(): CursorEntry[] { let doc = this.props.Document; let id = CurrentUserUtils.id; - let cursors = doc.GetList<CursorEntry>(KeyStore.Cursors, []); + let cursors = doc.GetList(KeyStore.Cursors, [] as CursorEntry[]); let notMe = cursors.filter(entry => entry.Data[0][0] !== id); return id ? notMe : []; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 26c794e91..57706b51e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -1,11 +1,4 @@ @import "../../globalCssVariables"; -.collectionfreeformview-measure { - position: inherit; - top: 0; - left: 0; - width: 100%; - height: 100%; - } .collectionfreeformview { position: inherit; top: 0; @@ -48,10 +41,11 @@ } .formattedTextBox-cont { background: $light-color-secondary; + overflow: visible; } opacity: 0.99; - border-width: 0; + border-width: 0; border-color: transparent; border-style: solid; border-radius: $border-radius; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4db3bf81b..83b7f9be4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,10 +1,7 @@ -import { action, computed, observable, runInAction, untracked } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; -import Measure from "react-measure"; import { Document } from "../../../../fields/Document"; -import { FieldWaiting } from "../../../../fields/Field"; import { KeyStore } from "../../../../fields/KeyStore"; -import { TextField } from "../../../../fields/TextField"; import { emptyFunction, returnFalse, returnOne } from "../../../../Utils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from "../../../util/DragManager"; @@ -13,43 +10,52 @@ import { Transform } from "../../../util/Transform"; import { undoBatch } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; import { InkingCanvas } from "../../InkingCanvas"; -import { Main } from "../../Main"; -import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewProps } from "../../nodes/CollectionFreeFormDocumentView"; +import { MainOverlayTextBox } from "../../MainOverlayTextBox"; +import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps } from "../../nodes/DocumentView"; -import { CollectionSubView, SubCollectionViewProps } from "../CollectionSubView"; +import { CollectionSubView } from "../CollectionSubView"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); -import { MainOverlayTextBox } from "../../MainOverlayTextBox"; +import { BooleanField } from "../../../../fields/BooleanField"; @observer export class CollectionFreeFormView extends CollectionSubView { - public _canvasRef = React.createRef<HTMLDivElement>(); private _selectOnLoaded: string = ""; // id of document that should be selected once it's loaded (used for click-to-type) + private _lastX: number = 0; + private _lastY: number = 0; + private get _pwidth() { return this.props.PanelWidth(); } + private get _pheight() { return this.props.PanelHeight(); } - public addLiveTextBox = (newBox: Document) => { - // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself and receive text input - this._selectOnLoaded = newBox.Id; + @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } + @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } + private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } + private get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? + private childViews = () => this.views; + private panX = () => this.props.Document.GetNumber(KeyStore.PanX, 0); + private panY = () => this.props.Document.GetNumber(KeyStore.PanY, 0); + private zoomScaling = () => this.props.Document.GetNumber(KeyStore.Scale, 1); + private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections + private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections + private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); + private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); + private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); + private addLiveTextBox = (newBox: Document) => { + this._selectOnLoaded = newBox.Id;// track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newBox, false); } - - public addDocument = (newBox: Document, allowDuplicates: boolean) => { - if (this.isAnnotationOverlay) { - newBox.SetNumber(KeyStore.Zoom, this.props.Document.GetNumber(KeyStore.Scale, 1)); - } + private addDocument = (newBox: Document, allowDuplicates: boolean) => { return this.props.addDocument(this.bringToFront(newBox), false); } - - public selectDocuments = (docs: Document[]) => { + private selectDocuments = (docs: Document[]) => { SelectionManager.DeselectAll; docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).filter(dv => dv).map(dv => SelectionManager.SelectDoc(dv!, true)); } - public getActiveDocuments = () => { var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); return this.props.Document.GetList(this.props.fieldKey, [] as Document[]).reduce((active, doc) => { @@ -61,42 +67,35 @@ export class CollectionFreeFormView extends CollectionSubView { }, [] as Document[]); } - @observable public DownX: number = 0; - @observable public DownY: number = 0; - @observable private _lastX: number = 0; - @observable private _lastY: number = 0; - @observable private _pwidth: number = 0; - @observable private _pheight: number = 0; - - @computed get panX(): number { return this.props.Document.GetNumber(KeyStore.PanX, 0); } - @computed get panY(): number { return this.props.Document.GetNumber(KeyStore.PanY, 0); } - @computed get scale(): number { return this.props.Document.GetNumber(KeyStore.Scale, 1); } - @computed get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey.Id === KeyStore.Annotations.Id; } // bcz: ? Why do we need to compare Id's? - @computed get nativeWidth() { return this.props.Document.GetNumber(KeyStore.NativeWidth, 0); } - @computed get nativeHeight() { return this.props.Document.GetNumber(KeyStore.NativeHeight, 0); } - @computed get zoomScaling() { return this.props.Document.GetNumber(KeyStore.Scale, 1); } - @computed get centeringShiftX() { return !this.props.Document.GetNumber(KeyStore.NativeWidth, 0) ? this._pwidth / 2 : 0; } // shift so pan position is at center of window for non-overlay collections - @computed get centeringShiftY() { return !this.props.Document.GetNumber(KeyStore.NativeHeight, 0) ? this._pheight / 2 : 0; }// shift so pan position is at center of window for non-overlay collections - @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { - const [x, y] = this.getTransform().transformPoint(de.x - de.data.xOffset, de.y - de.data.yOffset); if (de.data.droppedDocuments.length) { - let dropX = de.data.droppedDocuments[0].GetNumber(KeyStore.X, 0); - let dropY = de.data.droppedDocuments[0].GetNumber(KeyStore.Y, 0); + let dragDoc = de.data.droppedDocuments[0]; + let zoom = dragDoc.GetNumber(KeyStore.ZoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + + let dropX = dragDoc.GetNumber(KeyStore.X, 0); + let dropY = dragDoc.GetNumber(KeyStore.Y, 0); de.data.droppedDocuments.map(d => { - d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0) - dropX)); - d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0) - dropY)); - if (!d.GetNumber(KeyStore.Width, 0)) { - d.SetNumber(KeyStore.Width, 300); - } - if (!d.GetNumber(KeyStore.Height, 0)) { - d.SetNumber(KeyStore.Height, 300); + d.SetNumber(KeyStore.X, x + (d.GetNumber(KeyStore.X, 0)) - dropX); + d.SetNumber(KeyStore.Y, y + (d.GetNumber(KeyStore.Y, 0)) - dropY); + if (!d.GetBoolean(KeyStore.IsMinimized, false)) { + if (!d.GetNumber(KeyStore.Width, 0)) { + d.SetNumber(KeyStore.Width, 300); + } + if (!d.GetNumber(KeyStore.Height, 0)) { + let nw = d.GetNumber(KeyStore.NativeWidth, 0); + let nh = d.GetNumber(KeyStore.NativeHeight, 0); + d.SetNumber(KeyStore.Height, nw && nh ? nh / nw * d.Width() : 300); + } } this.bringToFront(d); }); + SelectionManager.ReselectAll(); } return true; } @@ -111,13 +110,17 @@ export class CollectionFreeFormView extends CollectionSubView { @action onPointerDown = (e: React.PointerEvent): void => { - if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling !== 1)) || e.button === 0) && this.props.active()) { + let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => { + var dv = DocumentManager.Instance.getDocumentView(doc); + return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false); + }, false); + if (((e.button === 2 && (!this.isAnnotationOverlay || this.zoomScaling() !== 1)) || (e.button === 0 && e.altKey)) && (childSelected || this.props.active())) { document.removeEventListener("pointermove", this.onPointerMove); document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointerup", this.onPointerUp); - this._lastX = this.DownX = e.pageX; - this._lastY = this.DownY = e.pageY; + this._lastX = e.pageX; + this._lastY = e.pageY; } } @@ -130,38 +133,36 @@ export class CollectionFreeFormView extends CollectionSubView { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && this.props.active()) { - if ((!this.isAnnotationOverlay || this.zoomScaling !== 1) && !e.shiftKey) { - let x = this.props.Document.GetNumber(KeyStore.PanX, 0); - let y = this.props.Document.GetNumber(KeyStore.PanY, 0); - let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); - let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - if (!this.isAnnotationOverlay) { - let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0; - let maxx = docs.length ? docs[0].GetNumber(KeyStore.Width, 0) + minx : minx; - let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0; - let maxy = docs.length ? docs[0].GetNumber(KeyStore.Height, 0) + miny : miny; - let ranges = docs.filter(doc => doc).reduce((range, doc) => { - let x = doc.GetNumber(KeyStore.X, 0); - let xe = x + doc.GetNumber(KeyStore.Width, 0); - let y = doc.GetNumber(KeyStore.Y, 0); - let ye = y + doc.GetNumber(KeyStore.Height, 0); - return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]], - [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]]; - }, [[minx, maxx], [miny, maxy]]); - let panelwidth = this._pwidth / this.scale / 2; - let panelheight = this._pheight / this.scale / 2; - if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx; - if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx; - if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy; - if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy; - } - this.SetPan(x - dx, y - dy); - this._lastX = e.pageX; - this._lastY = e.pageY; - e.stopPropagation(); - e.preventDefault(); + if (!e.cancelBubble) { + let x = this.props.Document.GetNumber(KeyStore.PanX, 0); + let y = this.props.Document.GetNumber(KeyStore.PanY, 0); + let docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); + let [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); + if (!this.isAnnotationOverlay) { + let minx = docs.length ? docs[0].GetNumber(KeyStore.X, 0) : 0; + let maxx = docs.length ? docs[0].Width() + minx : minx; + let miny = docs.length ? docs[0].GetNumber(KeyStore.Y, 0) : 0; + let maxy = docs.length ? docs[0].Height() + miny : miny; + let ranges = docs.filter(doc => doc).reduce((range, doc) => { + let x = doc.GetNumber(KeyStore.X, 0); + let xe = x + doc.GetNumber(KeyStore.Width, 0); + let y = doc.GetNumber(KeyStore.Y, 0); + let ye = y + doc.GetNumber(KeyStore.Height, 0); + return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]], + [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]]; + }, [[minx, maxx], [miny, maxy]]); + let panelwidth = this._pwidth / this.zoomScaling() / 2; + let panelheight = this._pheight / this.zoomScaling() / 2; + if (x - dx < ranges[0][0] - panelwidth) x = ranges[0][1] + panelwidth + dx; + if (x - dx > ranges[0][1] + panelwidth) x = ranges[0][0] - panelwidth + dx; + if (y - dy < ranges[1][0] - panelheight) y = ranges[1][1] + panelheight + dy; + if (y - dy > ranges[1][1] + panelheight) y = ranges[1][0] - panelheight + dy; } + this.setPan(x - dx, y - dy); + this._lastX = e.pageX; + this._lastY = e.pageY; + e.stopPropagation(); + e.preventDefault(); } } @@ -170,45 +171,45 @@ export class CollectionFreeFormView extends CollectionSubView { // if (!this.props.active()) { // return; // } + let childSelected = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((childSelected, doc) => { + var dv = DocumentManager.Instance.getDocumentView(doc); + return childSelected || (dv && SelectionManager.IsSelected(dv) ? true : false); + }, false); + if (!this.props.isSelected() && !childSelected && !this.props.isTopMost) { + return; + } e.stopPropagation(); - let coefficient = 1000; + const coefficient = 1000; if (e.ctrlKey) { - var nativeWidth = this.props.Document.GetNumber(KeyStore.NativeWidth, 0); - var nativeHeight = this.props.Document.GetNumber(KeyStore.NativeHeight, 0); - const coefficient = 1000; let deltaScale = (1 - (e.deltaY / coefficient)); - this.props.Document.SetNumber(KeyStore.NativeWidth, nativeWidth * deltaScale); - this.props.Document.SetNumber(KeyStore.NativeHeight, nativeHeight * deltaScale); + this.props.Document.SetNumber(KeyStore.NativeWidth, this.nativeWidth * deltaScale); + this.props.Document.SetNumber(KeyStore.NativeHeight, this.nativeHeight * deltaScale); e.stopPropagation(); e.preventDefault(); } else { // if (modes[e.deltaMode] === 'pixels') coefficient = 50; // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height?? - let transform = this.getTransform(); - let deltaScale = (1 - (e.deltaY / coefficient)); - if (deltaScale * this.zoomScaling < 1 && this.isAnnotationOverlay) { - deltaScale = 1 / this.zoomScaling; + if (deltaScale < 0) deltaScale = -deltaScale; + if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) { + deltaScale = 1 / this.zoomScaling(); } - let [x, y] = transform.transformPoint(e.clientX, e.clientY); - - let localTransform = this.getLocalTransform(); - localTransform = localTransform.inverse().scaleAbout(deltaScale, x, y); - // console.log(localTransform) + let [x, y] = this.getTransform().transformPoint(e.clientX, e.clientY); + let localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y); - this.props.Document.SetNumber(KeyStore.Scale, localTransform.Scale); - this.SetPan(-localTransform.TranslateX / localTransform.Scale, -localTransform.TranslateY / localTransform.Scale); + let safeScale = Math.abs(localTransform.Scale); + this.props.Document.SetNumber(KeyStore.Scale, Math.abs(safeScale)); + this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale); e.stopPropagation(); } } @action - private SetPan(panX: number, panY: number) { - MainOverlayTextBox.Instance.SetTextDoc(); - var x1 = this.getLocalTransform().inverse().Scale; - const newPanX = Math.min((1 - 1 / x1) * this.nativeWidth, Math.max(0, panX)); - const newPanY = Math.min((1 - 1 / x1) * this.nativeHeight, Math.max(0, panY)); + setPan(panX: number, panY: number) { + var scale = this.getLocalTransform().inverse().Scale; + const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); + const newPanY = Math.min((1 - 1 / scale) * this.nativeHeight, Math.max(0, panY)); this.props.Document.SetNumber(KeyStore.PanX, this.isAnnotationOverlay ? newPanX : panX); this.props.Document.SetNumber(KeyStore.PanY, this.isAnnotationOverlay ? newPanY : panY); } @@ -224,39 +225,18 @@ export class CollectionFreeFormView extends CollectionSubView { @action bringToFront(doc: Document) { - const { fieldKey: fieldKey, Document: Document } = this.props; - - const value: Document[] = Document.GetList<Document>(fieldKey, []).slice(); - value.sort((doc1, doc2) => { - if (doc1 === doc) { - return 1; - } - if (doc2 === doc) { - return -1; - } + this.props.Document.GetList(this.props.fieldKey, [] as Document[]).slice().sort((doc1, doc2) => { + if (doc1 === doc) return 1; + if (doc2 === doc) return -1; return doc1.GetNumber(KeyStore.ZIndex, 0) - doc2.GetNumber(KeyStore.ZIndex, 0); - }).map((doc, index) => - doc.SetNumber(KeyStore.ZIndex, index + 1)); + }).map((doc, index) => doc.SetNumber(KeyStore.ZIndex, index + 1)); return doc; } - @computed get backgroundLayout(): string | undefined { - let field = this.props.Document.GetT(KeyStore.BackgroundLayout, TextField); - if (field && field !== FieldWaiting) { - return field.Data; - } - } - @computed get overlayLayout(): string | undefined { - let field = this.props.Document.GetT(KeyStore.OverlayLayout, TextField); - if (field && field !== FieldWaiting) { - return field.Data; - } - } - focusDocument = (doc: Document) => { - let x = doc.GetNumber(KeyStore.X, 0) + doc.GetNumber(KeyStore.Width, 0) / 2; - let y = doc.GetNumber(KeyStore.Y, 0) + doc.GetNumber(KeyStore.Height, 0) / 2; - this.SetPan(x, y); + this.setPan( + doc.GetNumber(KeyStore.X, 0) + doc.Width() / 2, + doc.GetNumber(KeyStore.Y, 0) + doc.Height() / 2); this.props.focus(this.props.Document); } @@ -271,11 +251,11 @@ export class CollectionFreeFormView extends CollectionSubView { selectOnLoad: document.Id === this._selectOnLoaded, PanelWidth: document.Width, PanelHeight: document.Height, - ContentScaling: this.noScaling, + ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, parentActive: this.props.active, - onActiveChanged: this.props.active, + whenActiveChanged: this.props.active, }; } @@ -284,79 +264,93 @@ export class CollectionFreeFormView extends CollectionSubView { var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); let docviews = this.props.Document.GetList(this.props.fieldKey, [] as Document[]).filter(doc => doc).reduce((prev, doc) => { var page = doc.GetNumber(KeyStore.Page, -1); - var zoom = doc.GetNumber(KeyStore.Zoom, 1); - var dv = DocumentManager.Instance.getDocumentView(doc); - let zoomFade = !this.isAnnotationOverlay || (dv && SelectionManager.IsSelected(dv)) ? 1 : - Math.max(0, 2 - (zoom < this.scale ? this.scale / zoom : zoom / this.scale)); if (page === curPage || page === -1) { - prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} zoomFade={zoomFade} />); + let minim = doc.GetT(KeyStore.IsMinimized, BooleanField); + if (minim === undefined || (minim && !minim.Data)) + prev.push(<CollectionFreeFormDocumentView key={doc.Id} {...this.getDocumentViewProps(doc)} />); } return prev; }, [] as JSX.Element[]); - setTimeout(() => { // bcz: surely there must be a better way .... - this._selectOnLoaded = ""; - }, 600); + setTimeout(() => this._selectOnLoaded = "", 600);// bcz: surely there must be a better way .... return docviews; } - @computed - get backgroundView() { - return !this.backgroundLayout ? (null) : - (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)} - layoutKey={KeyStore.BackgroundLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); + @action + onCursorMove = (e: React.PointerEvent) => { + super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } - @computed - get overlayView() { - return !this.overlayLayout ? (null) : - (<DocumentContentsView {...this.getDocumentViewProps(this.props.Document)} - layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); + + render() { + const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; + return ( + <div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel} + onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} > + <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} + addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} + getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}> + <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY} + zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> + <CollectionFreeFormBackgroundView {...this.getDocumentViewProps(this.props.Document)} /> + <CollectionFreeFormLinksView {...this.props} key="freeformLinks"> + <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} > + {this.childViews} + </InkingCanvas> + </CollectionFreeFormLinksView> + <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" /> + </CollectionFreeFormViewPannableContents> + <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} /> + </MarqueeView> + </div> + ); } +} - @computed - get borderWidth() { - return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; +@observer +class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> { + @computed get overlayView() { + let overlayLayout = this.props.Document.GetText(KeyStore.OverlayLayout, ""); + return !overlayLayout ? (null) : + (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout} + isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); + } + render() { + return this.overlayView; } - getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth).translate(-this.centeringShiftX, -this.centeringShiftY).transform(this.getLocalTransform()); - getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); - getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.scale).translate(this.panX, this.panY); - noScaling = () => 1; - childViews = () => this.views; +} +@observer +class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> { + @computed get backgroundView() { + let backgroundLayout = this.props.Document.GetText(KeyStore.BackgroundLayout, ""); + return !backgroundLayout ? (null) : + (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout} + isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); + } render() { - const [dx, dy] = [this.centeringShiftX, this.centeringShiftY]; - const panx: number = -this.props.Document.GetNumber(KeyStore.PanX, 0); - const pany: number = -this.props.Document.GetNumber(KeyStore.PanY, 0); - const zoom: number = this.zoomScaling;// needs to be a variable outside of the <Measure> otherwise, reactions won't fire - const backgroundView = this.backgroundView; // needs to be a variable outside of the <Measure> otherwise, reactions won't fire - const overlayView = this.overlayView;// needs to be a variable outside of the <Measure> otherwise, reactions won't fire + return this.backgroundView; + } +} - return ( - <Measure onResize={(r: any) => runInAction(() => { this._pwidth = r.entry.width; this._pheight = r.entry.height; })}> - {({ measureRef }) => ( - <div className={`collectionfreeformview-measure`} ref={measureRef}> - <div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`} - onPointerDown={this.onPointerDown} onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))} - onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onWheel={this.onPointerWheel} ref={this.createDropTarget}> - <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} - addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} - getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}> - <div className="collectionfreeformview" ref={this._canvasRef} - style={{ transform: `translate(${dx}px, ${dy}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}> - {backgroundView} - <CollectionFreeFormLinksView {...this.props}> - <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} > - {this.childViews} - </InkingCanvas> - </CollectionFreeFormLinksView> - <CollectionFreeFormRemoteCursors {...this.props} /> - </div> - {overlayView} - </MarqueeView> - </div> - </div>)} - </Measure> - ); +interface CollectionFreeFormViewPannableContentsProps { + centeringShiftX: () => number; + centeringShiftY: () => number; + panX: () => number; + panY: () => number; + zoomScaling: () => number; +} + +@observer +class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{ + render() { + const cenx = this.props.centeringShiftX(); + const ceny = this.props.centeringShiftY(); + const panx = -this.props.panX(); + const pany = -this.props.panY(); + const zoom = this.props.zoomScaling();// needs to be a variable outside of the <Measure> otherwise, reactions won't fire + return <div className="collectionfreeformview" style={{ transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}, ${zoom}) translate(${panx}px, ${pany}px)` }}> + {this.props.children} + </div>; } }
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index dbab790d4..318adbe85 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -11,6 +11,7 @@ import { undoBatch } from "../../../util/UndoManager"; import { InkingCanvas } from "../../InkingCanvas"; import { PreviewCursor } from "../../PreviewCursor"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; +import { MINIMIZED_ICON_SIZE } from '../../../views/globalCssVariables.scss' import "./MarqueeView.scss"; import React = require("react"); @@ -147,26 +148,24 @@ export class MarqueeView extends React.Component<MarqueeViewProps> d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2); d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2); d.SetNumber(KeyStore.Page, -1); - d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0)); return d; }); let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField); let inkData = ink && ink !== FieldWaiting ? ink.Data : undefined; - //setTimeout(() => { + let zoomBasis = this.props.container.props.Document.GetNumber(KeyStore.Scale, 1); let newCollection = Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, - width: bounds.width, - height: bounds.height, - backgroundColor: "Transparent", + scale: zoomBasis, + width: bounds.width * zoomBasis, + height: bounds.height * zoomBasis, ink: inkData ? this.marqueeInkSelect(inkData) : undefined, title: "a nested collection" }); this.props.addDocument(newCollection, false); this.marqueeInkDelete(inkData); - // }, 100); this.cleanupInteractions(); SelectionManager.DeselectAll(); } @@ -209,11 +208,11 @@ export class MarqueeView extends React.Component<MarqueeViewProps> let selRect = this.Bounds; let selection: Document[] = []; this.props.activeDocuments().map(doc => { - var z = doc.GetNumber(KeyStore.Zoom, 1); + var z = doc.GetNumber(KeyStore.ZoomBasis, 1); var x = doc.GetNumber(KeyStore.X, 0); var y = doc.GetNumber(KeyStore.Y, 0); - var w = doc.GetNumber(KeyStore.Width, 0) / z; - var h = doc.GetNumber(KeyStore.Height, 0) / z; + var w = doc.Width() / z; + var h = doc.Height() / z; if (this.intersectRect({ left: x, top: y, width: w, height: h }, selRect)) { selection.push(doc); } |
