diff options
Diffstat (limited to 'src/client/views/collections/CollectionSchemaView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 169 |
1 files changed, 79 insertions, 90 deletions
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b3cc3fbd5..67784fa81 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -5,17 +5,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, untracked, runInAction } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; -import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss' +import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import "react-table/react-table.css"; -import { Document } from "../../../fields/Document"; -import { Field, Opt, FieldWaiting } from "../../../fields/Field"; -import { Key } from "../../../fields/Key"; -import { KeyStore } from "../../../fields/KeyStore"; -import { ListField } from "../../../fields/ListField"; -import { emptyDocFunction, emptyFunction, returnFalse, returnZero } from "../../../Utils"; -import { Server } from "../../Server"; +import { emptyFunction, returnFalse, returnZero } from "../../../Utils"; import { SetupDrag } from "../../util/DragManager"; -import { CompileScript, ToField } from "../../util/Scripting"; +import { CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss"; import { anchorPoints, Flyout } from "../DocumentDecorations"; @@ -25,43 +19,46 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; +import { Opt, Field, Doc } from "../../../new_fields/Doc"; +import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; +import { List } from "../../../new_fields/List"; +import { Id } from "../../../new_fields/RefField"; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 @observer -class KeyToggle extends React.Component<{ keyId: string, checked: boolean, toggle: (key: Key) => void }> { - @observable key: Key | undefined; - +class KeyToggle extends React.Component<{ keyName: string, checked: boolean, toggle: (key: string) => void }> { constructor(props: any) { super(props); - Server.GetField(this.props.keyId, action((field: Opt<Field>) => field instanceof Key && (this.key = field))); } render() { - 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 ( + <div key={this.props.keyName}> + <input type="checkbox" checked={this.props.checked} onChange={() => this.props.toggle(this.props.keyName)} /> + {this.props.keyName} + </div> + ); } } @observer -export class CollectionSchemaView extends CollectionSubView { +export class CollectionSchemaView extends CollectionSubView(doc => doc) { private _mainCont?: HTMLDivElement; private _startSplitPercent = 0; private DIVIDER_WIDTH = 4; - @observable _columns: Array<Key> = [KeyStore.Title, KeyStore.Data, KeyStore.Author]; + @observable _columns: Array<string> = ["title", "data", "author"]; @observable _selectedIndex = 0; @observable _columnsPercentage = 0; - @observable _keys: Key[] = []; + @observable _keys: string[] = []; @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 splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage); } + @computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } renderCell = (rowProps: CellInfo) => { @@ -74,7 +71,7 @@ export class CollectionSchemaView extends CollectionSubView { isTopMost: false, selectOnLoad: false, ScreenToLocalTransform: Transform.Identity, - focus: emptyDocFunction, + focus: emptyFunction, active: returnFalse, whenActiveChanged: emptyFunction, PanelHeight: returnZero, @@ -85,34 +82,27 @@ export class CollectionSchemaView extends CollectionSubView { ); let reference = React.createRef<HTMLDivElement>(); let onItemDown = SetupDrag(reference, () => props.Document, this.props.moveDocument); - let applyToDoc = (doc: Document, run: (args?: { [name: string]: any }) => any) => { + let applyToDoc = (doc: Doc, run: (args?: { [name: string]: any }) => any) => { const res = run({ this: doc }); if (!res.success) return false; const field = res.result; - if (field instanceof Field) { - doc.Set(props.fieldKey, field); - return true; - } else { - let dataField = ToField(field); - if (dataField) { - doc.Set(props.fieldKey, dataField); - return true; - } - } - return false; + doc[props.fieldKey] = field; + return true; }; return ( - <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document.Id} ref={reference}> + <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}> <EditableView display={"inline"} contents={contents} height={Number(MAX_ROW_HEIGHT)} GetValue={() => { - let field = props.Document.Get(props.fieldKey); - if (field && field instanceof Field) { - return field.ToScriptString(); + let field = props.Document[props.fieldKey]; + if (field) { + //TODO Types + // return field.ToScriptString(); + return String(field); } - return field || ""; + return ""; }} SetValue={(value: string) => { let script = CompileScript(value, { addReturn: true, params: { this: Document.name } }); @@ -128,14 +118,13 @@ export class CollectionSchemaView extends CollectionSubView { } const run = script.run; //TODO This should be able to be refactored to compile the script once - this.props.Document.GetTAsync<ListField<Document>>(this.props.fieldKey, ListField).then((val) => { - if (val) { - val.Data.forEach(doc => applyToDoc(doc, run)); - } - }); + const val = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)); + if (val) { + val.forEach(doc => applyToDoc(doc, run)); + } }}> </EditableView> - </div> + </div > ); } @@ -165,41 +154,38 @@ export class CollectionSchemaView extends CollectionSubView { super.CreateDropTarget(ele); } - toggleKey = (key: Key) => { - this.props.Document.GetTAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField).then(field => - runInAction(() => { - if (field !== FieldWaiting) { - if (field) { - const index = field.Data.indexOf(key); - if (index === -1) { - this.columns.push(key); - } else { - this.columns.splice(index, 1); - } - } else { - this.props.Document.SetData(KeyStore.ColumnsKey, [key], ListField); - } - } - })); + @action + toggleKey = (key: string) => { + let list = Cast(this.props.Document.schemaColumns, listSpec("string")); + if (list === undefined) { + this.props.Document.schemaColumns = list = new List<string>([key]); + } else { + const index = list.indexOf(key); + if (index === -1) { + list.push(key); + } else { + list.splice(index, 1); + } + } } //toggles preview side-panel of schema @action toggleExpander = (event: React.ChangeEvent<HTMLInputElement>) => { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); + this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0; } @action onDividerMove = (e: PointerEvent): void => { let nativeWidth = this._mainCont!.getBoundingClientRect(); - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100))); + this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)); } @action onDividerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onDividerMove); document.removeEventListener('pointerup', this.onDividerUp); if (this._startSplitPercent === this.splitPercentage) { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage === 0 ? 33 : 0); + this.props.Document.schemaSplitPercentage = this.splitPercentage === 0 ? 33 : 0; } } onDividerDown = (e: React.PointerEvent) => { @@ -212,8 +198,7 @@ export class CollectionSchemaView extends CollectionSubView { onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { - if (this.props.isSelected()) - e.stopPropagation(); + if (this.props.isSelected()) e.stopPropagation(); else e.preventDefault(); } } @@ -226,7 +211,7 @@ export class CollectionSchemaView extends CollectionSubView { @action addColumn = () => { - this.columns.push(new Key(this._newKeyName)); + this.columns.push(this._newKeyName); this._newKeyName = ""; } @@ -241,21 +226,22 @@ export class CollectionSchemaView extends CollectionSubView { this.previewScript = e.currentTarget.value; } - 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; - return selected ? (this.previewScript ? selected.Get(new Key(this.previewScript)) as Document : selected) : undefined; + get previewDocument(): Doc | undefined { + const children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); + const selected = children.length > this._selectedIndex ? FieldValue(children[this._selectedIndex]) : undefined; + return selected ? (this.previewScript ? FieldValue(Cast(selected[this.previewScript], Doc)) : 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 previewDocNativeWidth = () => Cast(this.previewDocument!.nativeWidth, "number", this.previewRegionWidth); + private previewDocNativeHeight = () => Cast(this.previewDocument!.nativeHeight, "number", this.previewRegionHeight); private previewContentScaling = () => { let wscale = this.previewRegionWidth / (this.previewDocNativeWidth() ? this.previewDocNativeWidth() : this.previewRegionWidth); - if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) + if (wscale * this.previewDocNativeHeight() > this.previewRegionHeight) { return this.previewRegionHeight / (this.previewDocNativeHeight() ? this.previewDocNativeHeight() : this.previewRegionHeight); + } return wscale; } private previewPanelWidth = () => this.previewDocNativeWidth() * this.previewContentScaling(); @@ -263,22 +249,23 @@ export class CollectionSchemaView extends CollectionSubView { 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()); + - this.borderWidth).scale(1 / this.previewContentScaling()) @computed get previewPanel() { // let doc = CompileScript(this.previewScript, { this: selected }, true)(); - return !this.previewDocument ? (null) : ( + const previewDoc = this.previewDocument; + return !previewDoc ? (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} + <DocumentView Document={previewDoc} isTopMost={false} selectOnLoad={false} toggleMinimized={emptyFunction} 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} + focus={emptyFunction} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} /> @@ -290,24 +277,25 @@ export class CollectionSchemaView extends CollectionSubView { } get documentKeysCheckList() { - const docs = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); - let keys: { [id: string]: boolean } = {}; + const docs = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); + let keys: { [key: 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)))); + //TODO Types + untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); - this.columns.forEach(key => keys[key.Id] = true); + this.columns.forEach(key => keys[key] = true); return Array.from(Object.keys(keys)).map(item => - (<KeyToggle checked={keys[item]} key={item} keyId={item} toggle={this.toggleKey} />)); + (<KeyToggle checked={keys[item]} key={item} keyName={item} toggle={this.toggleKey} />)); } get tableOptionsPanel() { return !this.props.active() ? (null) : (<Flyout - anchorPoint={anchorPoints.LEFT_TOP} + anchorPoint={anchorPoints.RIGHT_TOP} content={<div> <div id="schema-options-header"><h5><b>Options</b></h5></div> <div id="options-flyout-div"> @@ -335,16 +323,17 @@ export class CollectionSchemaView extends CollectionSubView { render() { library.add(faCog); library.add(faPlus); - const children = this.props.Document.GetList(this.props.fieldKey, [] as Document[]); + //This can't just pass FieldValue to filter because filter passes other arguments to the passed in function, which end up as default values in FieldValue + const children = (this.children || []).filter(doc => FieldValue(doc)); 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 + Header: col, + accessor: (doc: Doc) => [doc, col], + id: col }))} column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }} getTrProps={this.getTrProps} |