diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 410 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 174 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 19 | ||||
-rw-r--r-- | src/fields/Document.ts | 6 |
4 files changed, 381 insertions, 228 deletions
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 0d615dc01..863bb256a 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -1,162 +1,218 @@ @import "../global_variables"; - .collectionSchemaView-container { - border: 1px solid $intermediate-color; - border-radius: $border-radius; - box-sizing: border-box; - position: absolute; - width: 100%; - height: 100%; - overflow: hidden; - -.collectionSchemaView-content { - position: absolute; - height:100%; - width:100%; - overflow:auto; -} - .collectionSchemaView-previewRegion { - position: relative; - background: $light-color; - float: left; - height: 100%; - } - .collectionSchemaView-previewHandle { - position: absolute; - height: 37px; - width: 20px; - z-index: 20; - right: 0; - top: 0; - background: $main-accent; - } - .collectionSchemaView-dividerDragger { - position: relative; + border: 1px solid $intermediate-color; + border-radius: $border-radius; box-sizing: border-box; - border-left: 1px solid $intermediate-color; - border-right: 1px solid $intermediate-color; - float: left; - height: 100%; - } - .collectionSchemaView-tableContainer { - position: relative; - float: left; - height: 100%; - } - - .ReactTable { position: absolute; - // display: inline-block; - // overflow: auto; width: 100%; height: 100%; - background: $light-color; - box-sizing: border-box; - border: none !important; - .rt-table { - overflow-y: auto; - overflow-x: auto; - height: 100%; - display: -webkit-inline-box; - direction: ltr; - // direction:rtl; - // display:block; - } - .rt-tbody { - //direction: ltr; - direction: rtl; - } - .rt-tr-group { - direction: ltr; - max-height: 44px; - } - .rt-td { - border-width: 1px; - border-right-color: $intermediate-color; - .imageBox-cont { + overflow: hidden; + .collectionSchemaView-content { + position: absolute; + height: 100%; + width: 100%; + overflow: auto; + } + .collectionSchemaView-previewRegion { + position: relative; + background: $light-color; + float: left; + height: 100%; + } + .collectionSchemaView-previewHandle { + position: absolute; + height: 37px; + width: 20px; + z-index: 20; + right: 0; + top: 0; + background: $main-accent; + } + .collectionSchemaView-columnsHandle { + position: absolute; + height: 37px; + width: 20px; + z-index: 20; + left: 0; + bottom: 0; + background: $main-accent; + } + .collectionSchemaView-colDividerDragger { + position: relative; + box-sizing: border-box; + border-top: 1px solid $intermediate-color; + border-bottom: 1px solid $intermediate-color; + float: top; + width: 100%; + } + .collectionSchemaView-dividerDragger { + position: relative; + box-sizing: border-box; + border-left: 1px solid $intermediate-color; + border-right: 1px solid $intermediate-color; + float: left; + height: 100%; + } + .collectionSchemaView-tableContainer { position: relative; - max-height: 100%; - } - .imageBox-cont img { - object-fit: contain; - max-width: 100%; + float: left; height: 100%; - } - .videobox-cont { - object-fit: contain; - width:auto; + } + .ReactTable { + // position: absolute; // display: inline-block; + // overflow: auto; + width: 100%; height: 100%; - } - } - } - .ReactTable .rt-thead.-header { - background: $intermediate-color; - color: $light-color; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 12px; - height: 30px; - padding-top: 4px; - } - .ReactTable .rt-th, - .ReactTable .rt-td { - max-height: 44; - padding: 3px 7px; - font-size: 13px; - text-align: center; - } - .ReactTable .rt-tbody .rt-tr-group:last-child { - border-bottom: $intermediate-color; - border-bottom-style: solid; - border-bottom-width: 1; - } - .documentView-node:first-child { - background: $light-color; - .imageBox-cont img { - object-fit: contain; - } - } + background: $light-color; + box-sizing: border-box; + border: none !important; + .rt-table { + overflow-y: auto; + overflow-x: auto; + height: 100%; + display: -webkit-inline-box; + direction: ltr; // direction:rtl; + // display:block; + } + .rt-tbody { + //direction: ltr; + direction: rtl; + } + .rt-tr-group { + direction: ltr; + max-height: 44px; + } + .rt-td { + border-width: 1px; + border-right-color: $intermediate-color; + .imageBox-cont { + position: relative; + max-height: 100%; + } + .imageBox-cont img { + object-fit: contain; + max-width: 100%; + height: 100%; + } + .videobox-cont { + object-fit: contain; + width: auto; + height: 100%; + } + } + } + .ReactTable .rt-thead.-header { + background: $intermediate-color; + color: $light-color; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 12px; + height: 30px; + padding-top: 4px; + } + .ReactTable .rt-th, + .ReactTable .rt-td { + max-height: 44; + padding: 3px 7px; + font-size: 13px; + text-align: center; + } + .ReactTable .rt-tbody .rt-tr-group:last-child { + border-bottom: $intermediate-color; + border-bottom-style: solid; + border-bottom-width: 1; + } + .documentView-node:first-child { + background: $light-color; + .imageBox-cont img { + object-fit: contain; + } + } } .Resizer { - box-sizing: border-box; - background: #000; - opacity: 0.5; - z-index: 1; - background-clip: padding-box; - &.horizontal { - height: 11px; - margin: -5px 0; - border-top: 5px solid rgba(255, 255, 255, 0); - border-bottom: 5px solid rgba(255, 255, 255, 0); - cursor: row-resize; - width: 100%; - &:hover { - border-top: 5px solid rgba(0, 0, 0, 0.5); - border-bottom: 5px solid rgba(0, 0, 0, 0.5); - } - } - &.vertical { - width: 11px; - margin: 0 -5px; - border-left: 5px solid rgba(255, 255, 255, 0); - border-right: 5px solid rgba(255, 255, 255, 0); - cursor: col-resize; + box-sizing: border-box; + background: #000; + opacity: 0.5; + z-index: 1; + background-clip: padding-box; + &.horizontal { + height: 11px; + margin: -5px 0; + border-top: 5px solid rgba(255, 255, 255, 0); + border-bottom: 5px solid rgba(255, 255, 255, 0); + cursor: row-resize; + width: 100%; + &:hover { + border-top: 5px solid rgba(0, 0, 0, 0.5); + border-bottom: 5px solid rgba(0, 0, 0, 0.5); + } + } + &.vertical { + width: 11px; + margin: 0 -5px; + border-left: 5px solid rgba(255, 255, 255, 0); + border-right: 5px solid rgba(255, 255, 255, 0); + cursor: col-resize; + &:hover { + border-left: 5px solid rgba(0, 0, 0, 0.5); + border-right: 5px solid rgba(0, 0, 0, 0.5); + } + } &:hover { - border-left: 5px solid rgba(0, 0, 0, 0.5); - border-right: 5px solid rgba(0, 0, 0, 0.5); - } - } - &:hover { - -webkit-transition: all 2s ease; - transition: all 2s ease; - } + -webkit-transition: all 2s ease; + transition: all 2s ease; + } } .vertical { - section { - width: 100vh; - height: 100vh; + section { + width: 100vh; + height: 100vh; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } + header { + padding: 1rem; + background: #eee; + } + footer { + padding: 1rem; + background: #eee; + } +} + +.horizontal { + section { + width: 100vh; + height: 100vh; + display: flex; + flex-direction: column; + } + header { + padding: 1rem; + background: #eee; + } + footer { + padding: 1rem; + background: #eee; + } +} + +.parent { + width: 100%; + height: 100%; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -166,71 +222,27 @@ -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - } - header { - padding: 1rem; - background: #eee; - } - footer { - padding: 1rem; - background: #eee; - } -} - -.horizontal { - section { - width: 100vh; - height: 100vh; - display: flex; - flex-direction: column; - } - header { - padding: 1rem; - background: #eee; - } - footer { - padding: 1rem; - background: #eee; - } -} - -.parent { - width: 100%; - height: 100%; - -webkit-box-flex: 1; - -webkit-flex: 1; - -ms-flex: 1; - flex: 1; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; } .header { - background: #aaa; - height: 3rem; - line-height: 3rem; + background: #aaa; + height: 3rem; + line-height: 3rem; } .wrapper { - background: #ffa; - margin: 5rem; - -webkit-box-flex: 1; - -webkit-flex: 1; - -ms-flex: 1; - flex: 1; + background: #ffa; + margin: 5rem; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; } .-even { - background: $light-color !important; + background: $light-color !important; } .-odd { - background: $light-color-secondary !important; -} + background: $light-color-secondary !important; +}
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 02e840b39..1c5ff1b12 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -1,11 +1,11 @@ import React = require("react") -import { action, observable, computed } from "mobx"; +import { action, observable, ObservableMap, computed } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; import "react-table/react-table.css"; import { Document } from "../../../fields/Document"; -import { Field } from "../../../fields/Field"; +import { Field, Opt } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; import { CompileScript, ToField } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; @@ -17,20 +17,50 @@ import "./CollectionSchemaView.scss"; import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView"; import { CollectionViewBase } from "./CollectionViewBase"; import { setupDrag } from "../../util/DragManager"; +import { Key } from "./../../../fields/Key"; +import { Server } from "../../Server"; +import { ListField } from "../../../fields/ListField"; + // 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; + + componentWillReceiveProps() { + Server.GetField(this.props.keyId, action((field: Opt<Field>) => { + if (field instanceof Key) { + this.key = field; + } + })) + } + + render() { + if (this.key) { + return (<div key={this.key.Id}> + <input type="checkbox" checked={this.props.checked} onChange={() => this.key && this.props.toggle(this.key)} />{this.key.Name} + </div>) + } else { + return <div></div> + } + } +} + +@observer export class CollectionSchemaView extends CollectionViewBase { private _mainCont = React.createRef<HTMLDivElement>(); 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; @computed get splitPercentage() { return this.props.Document.GetNumber(KeyStore.SchemaSplitPercentage, 0); } renderCell = (rowProps: CellInfo) => { @@ -105,6 +135,43 @@ export class CollectionSchemaView extends CollectionViewBase { }; } + get columns() { + return this.props.Document.GetList<Key>(KeyStore.ColumnsKey, []); + } + + @action + toggleKey = (key: Key) => { + this.props.Document.GetOrCreateAsync<ListField<Key>>(KeyStore.ColumnsKey, ListField, + (field) => { + const index = field.Data.indexOf(key); + if (index === -1) { + this.columns.push(key); + } else { + this.columns.splice(index, 1); + } + + }) + } + + @observable keys: Key[] = []; + + findAllDocumentKeys = (): { [id: string]: boolean } => { + const docs = this.props.Document.GetList<Document>(this.props.fieldKey, []); + let keys: { [id: string]: boolean } = {} + docs.forEach(doc => { + let protos = doc.GetAllPrototypes(); + for (const proto of protos) { + proto._proxies.forEach((val: any, key: string) => { + keys[key] = false + }) + } + }) + this.columns.forEach(key => { + keys[key.Id] = true; + }) + return keys; + } + _startSplitPercent = 0; @action onDividerMove = (e: PointerEvent): void => { @@ -126,6 +193,25 @@ export class CollectionSchemaView extends CollectionViewBase { document.addEventListener("pointermove", this.onDividerMove); document.addEventListener('pointerup', this.onDividerUp); } + + + @action + onColDividerMove = (e: PointerEvent): void => { + let nativeWidth = this._mainCont.current!.getBoundingClientRect(); + this._columnsPercentage = 100 - (e.clientY - nativeWidth.top) / nativeWidth.height * 100; + } + @action + onColDividerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onColDividerMove); + document.removeEventListener('pointerup', this.onColDividerUp); + } + onColDividerDown = (e: React.PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + document.addEventListener("pointermove", this.onColDividerMove); + document.addEventListener('pointerup', this.onColDividerUp); + } + @action onExpanderMove = (e: PointerEvent): void => { e.stopPropagation(); @@ -137,12 +223,8 @@ export class CollectionSchemaView extends CollectionViewBase { e.preventDefault(); document.removeEventListener("pointermove", this.onExpanderMove); document.removeEventListener('pointerup', this.onExpanderUp); - if (this._startSplitPercent == this.splitPercentage) { - this.props.Document.SetNumber(KeyStore.SchemaSplitPercentage, this.splitPercentage == 0 ? 33 : 0); - } } onExpanderDown = (e: React.PointerEvent) => { - this._startSplitPercent = this.splitPercentage; e.stopPropagation(); e.preventDefault(); document.addEventListener("pointermove", this.onExpanderMove); @@ -164,6 +246,27 @@ export class CollectionSchemaView extends CollectionViewBase { } @action + onColumnsMove = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + } + @action + onColumnsUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + document.removeEventListener("pointermove", this.onColumnsMove); + document.removeEventListener('pointerup', this.onColumnsUp); + this._columnsPercentage = this._columnsPercentage ? 0 : 50; + } + onColumnsDown = (e: React.PointerEvent) => { + this._startSplitPercent = this.splitPercentage; + e.stopPropagation(); + e.preventDefault(); + document.addEventListener("pointermove", this.onColumnsMove); + document.addEventListener('pointerup', this.onColumnsUp); + } + + @action setScaling = (r: any) => { const children = this.props.Document.GetList<Document>(this.props.fieldKey, []); const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined; @@ -182,9 +285,10 @@ export class CollectionSchemaView extends CollectionViewBase { focusDocument = (doc: Document) => { } render() { - const columns = this.props.Document.GetList(KeyStore.ColumnsKey, [KeyStore.Title, KeyStore.Data, KeyStore.Author]) + const columns = this.columns; const children = this.props.Document.GetList<Document>(this.props.fieldKey, []); const selected = children.length > this._selectedIndex ? children[this._selectedIndex] : undefined; + const allKeys = this.findAllDocumentKeys(); let content = this._selectedIndex == -1 || !selected ? (null) : ( <Measure onResize={this.setScaling}> {({ measureRef }) => @@ -206,8 +310,13 @@ export class CollectionSchemaView extends CollectionViewBase { ) let previewHandle = !this.props.active() ? (null) : ( <div className="collectionSchemaView-previewHandle" onPointerDown={this.onExpanderDown} />); + let columnsHandle = !this.props.active() ? (null) : ( + <div className="collectionSchemaView-columnsHandle" onPointerDown={this.onColumnsDown} />); let dividerDragger = this.splitPercentage == 0 ? (null) : <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} style={{ width: `${this.DIVIDER_WIDTH}px` }} /> + let colDividerDragger = this._columnsPercentage == 0 ? (null) : + <div className="collectionSchemaView-colDividerDragger" onPointerDown={this.onColDividerDown} style={{ height: `${this.DIVIDER_WIDTH}px` }} /> + return ( <div className="collectionSchemaView-container" onPointerDown={this.onPointerDown} ref={this._mainCont} style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }} > <div className="collectionSchemaView-dropTarget" onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createDropTarget}> @@ -218,23 +327,38 @@ export class CollectionSchemaView extends CollectionViewBase { {({ measureRef }) => <div ref={measureRef} className="collectionSchemaView-tableContainer" 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, - - }} - getTrProps={this.getTrProps} - /> + <div className="collectionSchemaView-reactContainer" style={{ height: `calc(100% - ${this._columnsPercentage}%)` }}> + <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, + + }} + getTrProps={this.getTrProps} + /> + </div> + {colDividerDragger} + <div className="collectionSchemaView-addColumn" style={{ height: `${this._columnsPercentage}%` }} > + {/* <input type="checkbox" id="addColumn-toggle" /> + <label htmlFor="addColumn-toggle" title="Add Column"><p>+</p></label> */} + + <div className="addColumn-options"> + <ul style={{ overflow: "scroll" }}> + {Array.from(Object.keys(allKeys)).map(item => { + return (<KeyToggle checked={allKeys[item]} key={item} keyId={item} toggle={this.toggleKey} />) + })} + </ul> + </div> + </div> </div> } </Measure> @@ -243,6 +367,8 @@ export class CollectionSchemaView extends CollectionViewBase { {content} </div> {previewHandle} + {columnsHandle} + </div> </div > ) diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 49f4cefce..e84c5f933 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -16,6 +16,7 @@ import { WebBox } from "./WebBox"; import { VideoBox } from "./VideoBox"; import { AudioBox } from "./AudioBox"; import { AudioField } from "../../../fields/AudioField"; +import { ListField } from "../../../fields/ListField"; // @@ -60,12 +61,20 @@ export class FieldView extends React.Component<FieldViewProps> { } else if (field instanceof WebField) { return <WebBox {...this.props} /> - } - else if (field instanceof VideoField){ - return <VideoBox {...this.props}/> } - else if (field instanceof AudioField){ - return <AudioBox {...this.props}/> + else if (field instanceof VideoField) { + return <VideoBox {...this.props} /> + } + else if (field instanceof AudioField) { + return <AudioBox {...this.props} /> + } else if (field instanceof Document) { + return <div>{field.Title}</div> + } else if (field instanceof ListField) { + return (<div> + {(field as ListField<Field>).Data.map(f => { + return f instanceof Document ? f.Title : f.GetValue().toString(); + }).join(", ")} + </div>) } // bcz: this belongs here, but it doesn't render well so taking it out for now // else if (field instanceof HtmlField) { diff --git a/src/fields/Document.ts b/src/fields/Document.ts index 763b79de2..be0137128 100644 --- a/src/fields/Document.ts +++ b/src/fields/Document.ts @@ -39,6 +39,11 @@ export class Document extends Field { return this.GetText(KeyStore.Title, "<untitled>"); } + @computed + public get Fields() { + return this.fields; + } + /** * Get the field in the document associated with the given key. If the * associated field has not yet been filled in from the server, a request @@ -305,6 +310,7 @@ export class Document extends Field { throw new Error("Method not implemented."); } GetValue() { + return this.Title; var title = (this._proxies.has(KeyStore.Title.Id) ? "???" : this.Title) + "(" + this.Id + ")"; return title; //throw new Error("Method not implemented."); |