diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 8 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaCells.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaHeaders.tsx | 162 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaMovableTableHOC.tsx | 35 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 180 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 279 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 5 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 41 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewChromes.scss | 51 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewChromes.tsx | 50 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkEditor.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkMenuGroup.tsx | 2 | ||||
-rw-r--r-- | src/new_fields/SchemaHeaderField.ts | 35 |
15 files changed, 554 insertions, 304 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2dcf655e3..2a1f63d59 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -414,7 +414,7 @@ export namespace Docs { } export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Freeform }); } export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array<Doc>, options: DocumentOptions) { @@ -422,15 +422,15 @@ export namespace Docs { } export function TreeDocument(documents: Array<Doc>, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Tree }); } export function StackingDocument(documents: Array<Doc>, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Stacking }); } export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 88a636784..444a70f4f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -383,7 +383,7 @@ export class MainView extends React.Component { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); - let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title")], [], { width: 200, height: 200, title: "a schema collection" })); + let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], [], { width: 200, height: 200, title: "a schema collection" })); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index fdf0896ac..4b3dd3cc1 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -108,7 +108,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { this._document[fieldKey] = de.data.draggedDocuments[0]; } else { - let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title")], de.data.draggedDocuments, {}); + let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.data.draggedDocuments, {}); this._document[fieldKey] = coll; } e.stopPropagation(); @@ -285,7 +285,7 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { this._isChecked = e.target.checked; let script = CompileScript(e.target.checked.toString(), { requiredType: "boolean", addReturn: true, params: { this: Doc.name } }); if (script.compiled) { - this.applyToDoc(this._document, script.run); + this.applyToDoc(this._document, this.props.row, this.props.col, script.run); } } diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 387107c55..4f0681f6c 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import "./CollectionSchemaView.scss"; -import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn } from '@fortawesome/free-solid-svg-icons'; +import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons'; import { library, IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Flyout, anchorPoints } from "../DocumentDecorations"; @@ -10,9 +10,9 @@ import { ColumnType } from "./CollectionSchemaView"; import { emptyFunction } from "../../../Utils"; import { contains } from "typescript-collections/dist/lib/arrays"; import { faFile } from "@fortawesome/free-regular-svg-icons"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; -library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile); +library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile, faSortAmountDown, faSortAmountUp, faTimes); export interface HeaderProps { keyValue: SchemaHeaderField; @@ -23,23 +23,24 @@ export interface HeaderProps { onSelect: (oldKey: string, newKey: string, addnew: boolean) => void; setIsEditing: (isEditing: boolean) => void; deleteColumn: (column: string) => void; - setColumnType: (key: string, type: ColumnType) => void; - setColumnSort: (key: string, desc: boolean) => void; - removeColumnSort: (key: string) => void; + setColumnType: (column: SchemaHeaderField, type: ColumnType) => void; + setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void; + setColumnColor: (column: SchemaHeaderField, color: string) => void; + } export class CollectionSchemaHeader extends React.Component<HeaderProps> { render() { let icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" : this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify"; - return ( <div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}> <CollectionSchemaColumnMenu - keyValue={this.props.keyValue.heading} + columnField={this.props.keyValue} + // keyValue={this.props.keyValue.heading} possibleKeys={this.props.possibleKeys} existingKeys={this.props.existingKeys} - keyType={this.props.keyType} + // keyType={this.props.keyType} typeConst={this.props.typeConst} menuButtonContent={<div><FontAwesomeIcon icon={icon} size="sm" />{this.props.keyValue.heading}</div>} addNew={false} @@ -49,7 +50,7 @@ export class CollectionSchemaHeader extends React.Component<HeaderProps> { onlyShowOptions={false} setColumnType={this.props.setColumnType} setColumnSort={this.props.setColumnSort} - removeColumnSort={this.props.removeColumnSort} + setColumnColor={this.props.setColumnColor} /> </div> ); @@ -70,13 +71,12 @@ export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHe } } - - export interface ColumnMenuProps { - keyValue: string; + columnField: SchemaHeaderField; + // keyValue: string; possibleKeys: string[]; existingKeys: string[]; - keyType: ColumnType; + // keyType: ColumnType; typeConst: boolean; menuButtonContent: JSX.Element; addNew: boolean; @@ -84,10 +84,10 @@ export interface ColumnMenuProps { setIsEditing: (isEditing: boolean) => void; deleteColumn: (column: string) => void; onlyShowOptions: boolean; - setColumnType: (key: string, type: ColumnType) => void; - setColumnSort: (key: string, desc: boolean) => void; - removeColumnSort: (key: string) => void; + setColumnType: (column: SchemaHeaderField, type: ColumnType) => void; + setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void; anchorPoint?: any; + setColumnColor: (column: SchemaHeaderField, color: string) => void; } @observer export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> { @@ -116,10 +116,16 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> this.props.setIsEditing(this._isOpen); } - setColumnType = (oldKey: string, newKey: string, addnew: boolean) => { - let typeStr = newKey as keyof typeof ColumnType; - let type = ColumnType[typeStr]; - this.props.setColumnType(this.props.keyValue, type); + changeColumnType = (type: ColumnType): void => { + this.props.setColumnType(this.props.columnField, type); + } + + changeColumnSort = (desc: boolean | undefined): void => { + this.props.setColumnSort(this.props.columnField, desc); + } + + changeColumnColor = (color: string): void => { + this.props.setColumnColor(this.props.columnField, color); } @action @@ -129,78 +135,82 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> } } - changeColumnColor = (color: string): void => { - - } - renderTypes = () => { if (this.props.typeConst) return <></>; + + let type = this.props.columnField.type; return ( <div className="collectionSchema-headerMenu-group"> <label>Column type:</label> <div className="columnMenu-types"> - <button title="Any" className={this.props.keyType === ColumnType.Any ? "active" : ""} onClick={() => this.props.setColumnType(this.props.keyValue, ColumnType.Any)}> + <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}> <FontAwesomeIcon icon={"align-justify"} size="sm" /> - </button> - <button title="Number" className={this.props.keyType === ColumnType.Number ? "active" : ""} onClick={() => this.props.setColumnType(this.props.keyValue, ColumnType.Number)}> + Any + </div> + <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}> <FontAwesomeIcon icon={"hashtag"} size="sm" /> - </button> - <button title="String" className={this.props.keyType === ColumnType.String ? "active" : ""} onClick={() => this.props.setColumnType(this.props.keyValue, ColumnType.String)}> + Number + </div> + <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}> <FontAwesomeIcon icon={"font"} size="sm" /> - </button> - <button title="Checkbox" className={this.props.keyType === ColumnType.Boolean ? "active" : ""} onClick={() => this.props.setColumnType(this.props.keyValue, ColumnType.Boolean)}> + Text + </div> + <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}> <FontAwesomeIcon icon={"check-square"} size="sm" /> - </button> - <button title="Document" className={this.props.keyType === ColumnType.Doc ? "active" : ""} onClick={() => this.props.setColumnType(this.props.keyValue, ColumnType.Doc)}> + Checkbox + </div> + <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}> <FontAwesomeIcon icon={"file"} size="sm" /> - </button> + Document + </div> </div> - </div> + </div > ); } renderSorting = () => { + let sort = this.props.columnField.desc; return ( <div className="collectionSchema-headerMenu-group"> <label>Sort by:</label> <div className="columnMenu-sort"> - <div className="columnMenu-option" onClick={() => this.props.setColumnSort(this.props.keyValue, false)}>Sort ascending</div> - <div className="columnMenu-option" onClick={() => this.props.setColumnSort(this.props.keyValue, true)}>Sort descending</div> - <div className="columnMenu-option" onClick={() => this.props.removeColumnSort(this.props.keyValue)}>Clear sorting</div> + <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}> + <FontAwesomeIcon icon="sort-amount-down" size="sm" /> + Sort descending + </div> + <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}> + <FontAwesomeIcon icon="sort-amount-up" size="sm" /> + Sort ascending + </div> + <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}> + <FontAwesomeIcon icon="times" size="sm" /> + Clear sorting + </div> </div> </div> ); } renderColors = () => { + let selected = this.props.columnField.color; + + let pink = PastelSchemaPalette.get("pink2"); + let purple = PastelSchemaPalette.get("purple2"); + let blue = PastelSchemaPalette.get("bluegreen1"); + let yellow = PastelSchemaPalette.get("yellow4"); + let red = PastelSchemaPalette.get("red2"); + let gray = "#f1efeb"; + return ( <div className="collectionSchema-headerMenu-group"> <label>Color:</label> <div className="columnMenu-colors"> - <input type="radio" name="column-color" id="pink" value="#FFB4E8" onClick={() => this.changeColumnColor("#FFB4E8")} /> - <label htmlFor="pink"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> - <input type="radio" name="column-color" id="purple" value="#b28dff" onClick={() => this.changeColumnColor("#b28dff")} /> - <label htmlFor="purple"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> - <input type="radio" name="column-color" id="blue" value="#afcbff" onClick={() => this.changeColumnColor("#afcbff")} /> - <label htmlFor="blue"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> - <input type="radio" name="column-color" id="yellow" value="#f3ffe3" onClick={() => this.changeColumnColor("#f3ffe3")} /> - <label htmlFor="yellow"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> - <input type="radio" name="column-color" id="red" value="#ffc9de" onClick={() => this.changeColumnColor("#ffc9de")} /> - <label htmlFor="red"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> - <input type="radio" name="column=color" id="none" value="#f1efeb" onClick={() => this.changeColumnColor("#f1efeb")} /> - <label htmlFor="none"> - <div className="columnMenu-colorPicker" style={{ backgroundColor: "#FFB4E8" }}></div> - </label> + <div className={"columnMenu-colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.changeColumnColor(pink!)}></div> + <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div> + <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div> + <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div> + <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div> + <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div> </div> </div> ); @@ -209,10 +219,10 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> renderContent = () => { return ( <div className="collectionSchema-header-menuOptions"> - <label>Key:</label> <div className="collectionSchema-headerMenu-group"> + <label>Key:</label> <KeysDropdown - keyValue={this.props.keyValue} + keyValue={this.props.columnField.heading} possibleKeys={this.props.possibleKeys} existingKeys={this.props.existingKeys} canAddNew={true} @@ -227,7 +237,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> {this.renderSorting()} {this.renderColors()} <div className="collectionSchema-headerMenu-group"> - <button onClick={() => this.props.deleteColumn(this.props.keyValue)}>Delete Column</button> + <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Delete Column</button> </div> </> } @@ -259,9 +269,10 @@ interface KeysDropdownProps { @observer class KeysDropdown extends React.Component<KeysDropdownProps> { @observable private _key: string = this.props.keyValue; - @observable private _searchTerm: string = ""; + @observable private _searchTerm: string = this.props.keyValue; @observable private _isOpen: boolean = false; @observable private _canClose: boolean = true; + @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef(); @action setSearchTerm = (value: string): void => { this._searchTerm = value; }; @action setKey = (key: string): void => { this._key = key; }; @@ -275,6 +286,21 @@ class KeysDropdown extends React.Component<KeysDropdownProps> { this.props.setIsEditing(false); } + @action + onKeyDown = (e: React.KeyboardEvent): void => { + if (e.key === "Enter") { + let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || + this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; + + if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { + this.onSelect(this._searchTerm); + } else { + this._searchTerm = this._key; + } + } + } + onChange = (val: string): void => { this.setSearchTerm(val); } @@ -327,7 +353,7 @@ class KeysDropdown extends React.Component<KeysDropdownProps> { render() { return ( <div className="keys-dropdown"> - <input className="keys-search" type="text" value={this._searchTerm} placeholder="Search for or create a new key" + <input className="keys-search" ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown} onChange={e => this.onChange(e.target.value)} onFocus={this.onFocus} onBlur={this.onBlur}></input> <div className="keys-options-wrapper" onPointerEnter={this.onPointerEnter} onPointerOut={this.onPointerOut}> {this.renderOptions()} diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 7342ede7a..483463c2b 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -26,6 +26,9 @@ export interface MovableColumnProps { export class MovableColumn extends React.Component<MovableColumnProps> { private _header?: React.RefObject<HTMLDivElement> = React.createRef(); private _colDropDisposer?: DragManager.DragDropDisposer; + private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; + private _sensitivity: number = 16; + private _dragRef: React.RefObject<HTMLDivElement> = React.createRef(); onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -36,6 +39,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> { onPointerLeave = (e: React.PointerEvent): void => { this._header!.current!.className = "collectionSchema-col-wrapper"; document.removeEventListener("pointermove", this.onDragMove, true); + document.removeEventListener("pointermove", this.onPointerMove); } onDragMove = (e: PointerEvent): void => { let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); @@ -68,7 +72,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> { return false; } - setupDrag(ref: React.RefObject<HTMLElement>) { + onPointerMove = (e: PointerEvent) => { let onRowMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); @@ -76,35 +80,44 @@ export class MovableColumn extends React.Component<MovableColumnProps> { document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); let dragData = new DragManager.ColumnDragData(this.props.columnValue); - DragManager.StartColumnDrag(ref.current!, dragData, e.x, e.y); + DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y); }; let onRowUp = (): void => { document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); }; - let onItemDown = (e: React.PointerEvent) => { - if (e.button === 0) { + if (e.buttons === 1) { + let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); + if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { + document.removeEventListener("pointermove", this.onPointerMove); e.stopPropagation(); + document.addEventListener("pointermove", onRowMove); document.addEventListener("pointerup", onRowUp); } - }; - return onItemDown; + } } - // onColDrag = (e: React.DragEvent, ref: React.RefObject<HTMLDivElement>) => { - // this.setupDrag(reference); - // } + onPointerUp = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.onPointerMove); + } + + @action + onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => { + this._dragRef = ref; + let [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); + this._startDragPosition = { x: dx, y: dy }; + document.addEventListener("pointermove", this.onPointerMove); + } render() { let reference = React.createRef<HTMLDivElement>(); - let onItemDown = this.setupDrag(reference); return ( <div className="collectionSchema-col" ref={this.createColDropTarget}> <div className="collectionSchema-col-wrapper" ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - <div className="col-dragger" ref={reference} onPointerDown={onItemDown} > + <div className="col-dragger" ref={reference} onPointerDown={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}> {this.props.columnRenderer} </div> </div> diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index b1e98b162..564e4f4a5 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -9,12 +9,18 @@ position: absolute; top: 0; width: 100%; - transition: height .5s; + // transition: height .5s; + // transition: margin-top .5s; height: 100%; // overflow: hidden; // overflow-x: scroll; // border: none; - overflow: hidden; + // overflow: scroll; + // overflow-y: scroll; + transition: top 0.5s; + display: flex; + justify-content: space-between; + flex-wrap: nowrap; // .collectionSchemaView-cellContents { // height: $MAX_ROW_HEIGHT; @@ -25,10 +31,16 @@ // } // } + .collectionSchemaView-tableContainer { + width: 100%; + height: 100%; + overflow: scroll; + } + .collectionSchemaView-previewRegion { position: relative; background: $light-color; - float: left; + // float: left; height: 100%; .collectionSchemaView-previewDoc { @@ -52,7 +64,7 @@ .collectionSchemaView-dividerDragger { position: relative; - float: left; + // float: left; height: 100%; width: 20px; z-index: 20; @@ -69,21 +81,23 @@ .ReactTable { width: 100%; - height: 100%; + // height: 100%; background: white; box-sizing: border-box; border: none !important; + float: none !important; .rt-table { - overflow-y: auto; - overflow-x: auto; + // overflow-y: auto; + // overflow-x: auto; height: 100%; display: -webkit-inline-box; - direction: ltr; + direction: ltr; + overflow: visible; } .rt-thead { - width: calc(100% - 50px); + width: calc(100% - 52px); margin-left: 50px; &.-header { @@ -97,15 +111,6 @@ // margin-right: -30px; } - .rt-resizable-header { - padding: 0; - height: 30px; - - &:last-child { - overflow: visible; - } - } - .rt-resizable-header-content { height: 100%; overflow: visible; @@ -130,6 +135,7 @@ } .rt-tbody { + width: calc(100% - 2px); direction: rtl; overflow: visible; } @@ -176,8 +182,8 @@ padding: 0; font-size: 13px; text-align: center; - // white-space: normal; + white-space: nowrap; .imageBox-cont { position: relative; @@ -198,8 +204,22 @@ } .rt-resizer { - width: 20px; - right: -10px; + width: 8px; + right: -4px; + } + + .rt-resizable-header { + padding: 0; + height: 30px; + } + + .rt-resizable-header:last-child { + overflow: visible; + border: 3px solid red !important; + + .rt-resizer { + width: 5px !important; + } } } @@ -260,16 +280,29 @@ button.add-column { .collectionSchema-header-menuOptions { color: black; - width: 175px; + width: 200px; text-align: left; .collectionSchema-headerMenu-group { - margin-bottom: 10px; + padding: 7px 0; + border-bottom: 1px solid lightgray; + + &:first-child { + padding-top : 0; + } + + &:last-child { + border: none; + text-align: center; + padding: 12px 0 0 0; + } } label { color: $main-accent; font-weight: normal; + letter-spacing: 2px; + text-transform: uppercase; } input { @@ -277,9 +310,43 @@ button.add-column { width: 100%; } + .columnMenu-option { + cursor: pointer; + padding: 3px; + background-color: white; + transition: background-color 0.2s; + + &:hover { + background-color: $light-color-secondary; + } + + &.active { + font-weight: bold; + border: 2px solid $light-color-secondary; + } + + svg { + color: gray; + margin-right: 5px; + width: 10px; + } + } + .keys-dropdown { position: relative; - max-width: 175px; + // max-width: 175px; + width: 100%; + + input { + border: 2px solid $light-color-secondary; + padding: 3px; + height: 28px; + font-weight: bold; + + &:focus { + font-weight: normal; + } + } .keys-options-wrapper { width: 100%; @@ -304,25 +371,21 @@ button.add-column { } } - .columnMenu-types { + .columnMenu-colors { display: flex; justify-content: space-between; - - button { - border-radius: 20px; - } - } - - .columnMenu-colors { - - - input[type="radio"] { - display: none; - } + flex-wrap: wrap; .columnMenu-colorPicker { + cursor: pointer; width: 20px; height: 20px; + border-radius: 10px; + + &.active { + border: 2px solid white; + box-shadow: 0 0 0 2px lightgray; + } } } } @@ -331,13 +394,16 @@ button.add-column { // height: $MAX_ROW_HEIGHT; height: 100%; background-color: white; + // white-space: nowrap; &.row-focused .rt-tr { background-color: rgb(255, 246, 246); //$light-color-secondary; } &.row-wrapped { - white-space: normal; + .rt-td { + white-space: normal; + } } .row-dragger { @@ -470,7 +536,11 @@ button.add-column { } .collectionSchemaView-table { - width: calc(100% - 7px); + // width: calc(100% - 7px); + width: 100%; + height: 100%; + // overflow-y: scroll; + overflow: visible; } .sub { @@ -480,12 +550,11 @@ button.add-column { width: calc(100% - 50px); margin-left: 50px; - .rt-table { - overflow-x: hidden; // todo; this shouldnt be like this :(( - overflow-y: visible; - } - - // TODO fix + // .rt-table { + // // overflow-x: hidden; // todo; this shouldnt be like this :(( + // // overflow-y: visible; + // // overflow: visible; + // } // TODO fix .row-dragger { background-color: rgb(252, 252, 252); @@ -502,4 +571,25 @@ button.add-column { .collectionSchemaView-expander { height: 100%; + min-height: 30px; + position: relative; + color: gray; + + svg { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } +} + +.collectionSchemaView-addRow { + color: gray; + letter-spacing: 2px; + text-transform: uppercase; + cursor: pointer; + font-size: 10.5px; + padding: 10px; + margin-left: 50px; + margin-top: 10px; }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 08ab22725..508d1f99d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -4,10 +4,10 @@ import { faCog, faPlus, faTable, faSortUp, faSortDown } from '@fortawesome/free- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; -import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults, TableCellRenderer, Column, RowInfo } from "react-table"; +import ReactTable, { CellInfo, ComponentPropsGetterR, Column, RowInfo, ResizedChangeFunction, Resize } from "react-table"; import "react-table/react-table.css"; -import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils"; -import { Doc, DocListCast, DocListCastAsync, Field, FieldResult, Opt } from "../../../new_fields/Doc"; +import { emptyFunction, returnOne } from "../../../Utils"; +import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; @@ -17,28 +17,21 @@ import { Gateway } from "../../northstar/manager/Gateway"; import { SetupDrag, DragManager } from "../../util/DragManager"; import { CompileScript, ts, Transformer } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; -import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; +import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss'; import { ContextMenu } from "../ContextMenu"; -import { anchorPoints, Flyout } from "../DocumentDecorations"; import '../DocumentDecorations.scss'; -import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; import { CollectionPDFView } from "./CollectionPDFView"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import { undoBatch } from "../../util/UndoManager"; -import { timesSeries } from "async"; import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders"; import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; -import { SelectionManager } from "../../util/SelectionManager"; -import { DocumentManager } from "../../util/DocumentManager"; -import { ImageBox } from "../nodes/ImageBox"; import { ComputedField } from "../../../new_fields/ScriptField"; -import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; library.add(faCog, faPlus, faSortUp, faSortDown); @@ -121,8 +114,13 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action onDividerMove = (e: PointerEvent): void => { let nativeWidth = this._mainCont!.getBoundingClientRect(); - this.props.Document.schemaPreviewWidth = Math.min(nativeWidth.right - nativeWidth.left - 40, - this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]); + let minWidth = 40; + let maxWidth = 1000; + let movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; + let width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; + this.props.Document.schemaPreviewWidth = width; + // this.props.Document.schemaPreviewWidth = Math.min(nativeWidth.right - nativeWidth.left - 40, + // this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]); } @action onDividerUp = (e: PointerEvent): void => { @@ -234,12 +232,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { render() { Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); - // if (SelectionManager.SelectedDocuments().length > 0) console.log(StrCast(SelectionManager.SelectedDocuments()[0].Document.title)); - // if (DocumentManager.Instance.getDocumentView(this.props.Document)) console.log(StrCast(this.props.Document.title), SelectionManager.IsSelected(DocumentManager.Instance.getDocumentView(this.props.Document)!)) return ( - <div className="collectionSchemaView-container" - onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}> - {this.schemaTable} + <div className="collectionSchemaView-container" style={{ height: this.props.chromeCollapsed ? "100%" : "calc(100% - 70px", marginTop: this.props.chromeCollapsed ? "0" : "70px", transition: "all .5s" }}> + <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.onDrop(e, {})} ref={this.createTarget}> + {this.schemaTable} + </div> {this.dividerDragger} {!this.previewWidth() ? (null) : this.previewPanel} </div> @@ -278,18 +275,19 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @observable _headerIsEditing: boolean = false; @observable _cellIsEditing: boolean = false; @observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 }; - @observable _sortedColumns: Map<string, { id: string, desc: boolean }> = new Map(); @observable _openCollections: Array<string> = []; - @observable _textWrappedRows: Array<string> = []; - @observable private _node: HTMLDivElement | null = null; @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); } + @computed get columns() { - console.log("columns"); return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []); } + set columns(columns: SchemaHeaderField[]) { + this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns); + } + @computed get childDocs() { if (this.props.childDocs) return this.props.childDocs; @@ -300,7 +298,32 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; doc[this.props.fieldKey] = new List<Doc>(docs); } - set columns(columns: SchemaHeaderField[]) { this.props.Document.schemaColumns = new List<SchemaHeaderField>(columns); } + + @computed get textWrappedRows() { + return Cast(this.props.Document.textwrappedSchemaRows, listSpec("string"), []); + } + set textWrappedRows(textWrappedRows: string[]) { + this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows); + } + + @computed get resized(): { "id": string, "value": number }[] { + return this.columns.reduce((resized, shf) => { + if (shf.width > -1) { + resized.push({ "id": shf.heading, "value": shf.width }); + } + return resized; + }, [] as { "id": string, "value": number }[]); + } + + @computed get sorted(): { "id": string, "desc": boolean }[] { + return this.columns.reduce((sorted, shf) => { + if (shf.desc) { + sorted.push({ "id": shf.heading, "desc": shf.desc }); + } + return sorted; + }, [] as { "id": string, "desc": boolean }[]); + } + @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column<Doc>[] { let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); @@ -310,8 +333,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let focusedCol = this._focusedCell.col; let isEditable = !this._headerIsEditing;// && this.props.isSelected(); - // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = DocListCast(cdoc[this.props.fieldKey]); let children = this.childDocs; if (children.reduce((found, doc) => found || doc.type === "collection", false)) { @@ -344,7 +365,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { deleteColumn={this.deleteColumn} setColumnType={this.setColumnType} setColumnSort={this.setColumnSort} - removeColumnSort={this.removeColumnSort} + setColumnColor={this.setColumnColor} />; return { @@ -399,25 +420,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> { return columns; } - // onHeaderDrag = (columnName: string) => { - // let schemaDoc = Cast(this.props.Document.schemaDoc, Doc); - // if (schemaDoc instanceof Doc) { - // let columnDocs = DocListCast(schemaDoc.data); - // if (columnDocs) { - // let ddoc = columnDocs.find(doc => doc.title === columnName); - // if (ddoc) { - // return ddoc; - // } - // } - // } - // return this.props.Document; - // } constructor(props: SchemaTableProps) { super(props); // convert old schema columns (list of strings) into new schema columns (list of schema header fields) let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []); if (oldSchemaColumns && oldSchemaColumns.length && typeof oldSchemaColumns[0] !== "object") { - console.log("REMAKING COLUMNs"); let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); this.props.Document.schemaColumns = new List<SchemaHeaderField>(newSchemaColumns); } @@ -436,8 +443,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } tableRemoveDoc = (document: Doc): boolean => { - // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + let children = this.childDocs; if (children.indexOf(document) !== -1) { children.splice(children.indexOf(document), 1); @@ -456,11 +462,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> { ScreenToLocalTransform: this.props.ScreenToLocalTransform, addDoc: this.tableAddDoc, removeDoc: this.tableRemoveDoc, - // removeDoc: this.props.deleteDocument, rowInfo, rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document), - textWrapRow: this.textWrapRow, - rowWrapped: this._textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1 + textWrapRow: this.toggleTextWrapRow, + rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1 }; } @@ -471,9 +476,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let row = rowInfo.index; //@ts-ignore let col = this.columns.map(c => c.heading).indexOf(column!.id); - // let col = column ? this.columns.indexOf(column!) : -1; let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document); - // let column = this.columns.indexOf(column.id!); return { style: { border: !this._headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" @@ -481,19 +484,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { }; } - // private createTarget = (ele: HTMLDivElement) => { - // this._mainCont = ele; - // this.props.CreateDropTarget(ele); - // } - - // detectClick = (e: PointerEvent): void => { - // if (this._node && this._node.contains(e.target as Node)) { - // } else { - // this._isOpen = false; - // this.props.setIsEditing(false); - // } - // } - @action onExpandCollection = (collection: Doc): void => { this._openCollections.push(collection[Id]); @@ -533,8 +523,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this.changeFocusedCellByDirection(direction); - // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); let children = this.childDocs; const pdoc = FieldValue(children[this._focusedCell.row]); pdoc && this.props.setPreviewDoc(pdoc); @@ -543,8 +531,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action changeFocusedCellByDirection = (direction: string): void => { - // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); let children = this.childDocs; switch (direction) { case "tab": @@ -569,81 +555,73 @@ export class SchemaTable extends React.Component<SchemaTableProps> { this._focusedCell = { row: this._focusedCell.row + 1 === children.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col }; break; } - // const pdoc = FieldValue(children[this._focusedCell.row]); - // pdoc && this.props.setPreviewDoc(pdoc); } @action changeFocusedCellByIndex = (row: number, col: number): void => { - // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); - this._focusedCell = { row: row, col: col }; this.props.setFocused(this.props.Document); - - // const fdoc = FieldValue(children[this._focusedCell.row]); - // fdoc && this.props.setPreviewDoc(fdoc); } createRow = () => { - // let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); let children = this.childDocs; let newDoc = Docs.Create.TextDocument({ width: 100, height: 30 }); let proto = Doc.GetProto(newDoc); proto.title = ""; children.push(newDoc); + this.childDocs = children; } @action createColumn = () => { let index = 0; - let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1; + let columns = this.columns; + let found = columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1; if (!found) { - console.log("create column found"); - this.columns.push(new SchemaHeaderField("New field", "#f1efeb")); + columns.push(new SchemaHeaderField("New field", "#f1efeb")); + this.columns = columns; return; } while (found) { index++; - found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; + found = columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; } - console.log("create column new"); - this.columns.push(new SchemaHeaderField("New field (" + index + ")", "#f1efeb")); + columns.push(new SchemaHeaderField("New field (" + index + ")", "#f1efeb")); + this.columns = columns; } @action deleteColumn = (key: string) => { - console.log("deleting columnnn"); - let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); - if (list === undefined) { - console.log("delete column"); - this.props.Document.schemaColumns = list = new List<SchemaHeaderField>([]); + let columns = this.columns; + if (columns === undefined) { + this.columns = new List<SchemaHeaderField>([]); } else { - const index = list.map(c => c.heading).indexOf(key); + const index = columns.map(c => c.heading).indexOf(key); if (index > -1) { - list.splice(index, 1); + columns.splice(index, 1); + this.columns = columns; } } } @action changeColumns = (oldKey: string, newKey: string, addNew: boolean) => { - console.log("changingin columnsdfhs"); - let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); - if (list === undefined) { - console.log("change columns new"); - this.props.Document.schemaColumns = list = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]); + let columns = this.columns; + if (columns === undefined) { + this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]); } else { - console.log("change column"); if (addNew) { - this.columns.push(new SchemaHeaderField(newKey, "f1efeb")); + columns.push(new SchemaHeaderField(newKey, "f1efeb")); + this.columns = columns; } else { - const index = list.map(c => c.heading).indexOf(oldKey); + const index = columns.map(c => c.heading).indexOf(oldKey); if (index > -1) { - list[index] = new SchemaHeaderField(newKey, "f1efeb"); + let column = columns[index]; + column.setHeading(newKey); + columns[index] = column; + this.columns = columns; } } } @@ -667,16 +645,35 @@ export class SchemaTable extends React.Component<SchemaTableProps> { return NumCast(typesDoc[column.heading]); } - setColumnType = (key: string, type: ColumnType): void => { - if (columnTypes.get(key)) return; - const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc)); - if (!typesDoc) { - let newTypesDoc = new Doc(); - newTypesDoc[key] = type; - this.props.Document.schemaColumnTypes = newTypesDoc; - return; - } else { - typesDoc[key] = type; + setColumnType = (columnField: SchemaHeaderField, type: ColumnType): void => { + if (columnTypes.get(columnField.heading)) return; + + let columns = this.columns; + let index = columns.indexOf(columnField); + if (index > -1) { + columnField.setType(NumCast(type)); + columns[index] = columnField; + this.columns = columns; + } + + // const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc)); + // if (!typesDoc) { + // let newTypesDoc = new Doc(); + // newTypesDoc[key] = type; + // this.props.Document.schemaColumnTypes = newTypesDoc; + // return; + // } else { + // typesDoc[key] = type; + // } + } + + setColumnColor = (columnField: SchemaHeaderField, color: string): void => { + let columns = this.columns; + let index = columns.indexOf(columnField); + if (index > -1) { + columnField.setColor(color); + columns[index] = columnField; + this.columns = columns; // need to set the columns to trigger rerender } } @@ -694,23 +691,21 @@ export class SchemaTable extends React.Component<SchemaTableProps> { if (oldIndex === newIndex) return; columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); - this.setColumns(columns); - } - - @action - setColumnSort = (column: string, descending: boolean) => { - this._sortedColumns.set(column, { id: column, desc: descending }); + this.columns = columns; } @action - removeColumnSort = (column: string) => { - this._sortedColumns.delete(column); + setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => { + let columns = this.columns; + let index = columns.findIndex(c => c.heading === columnField.heading); + let column = columns[index]; + column.setDesc(descending); + columns[index] = column; + this.columns = columns; } get documentKeys() { - const docs = DocListCast(this.props.Document[this.props.fieldKey]); - - // let docs = this.childDocs; + let docs = this.childDocs; 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 @@ -725,33 +720,33 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } @action - textWrapRow = (doc: Doc): void => { - let index = this._textWrappedRows.findIndex(id => doc[Id] === id); + toggleTextWrapRow = (doc: Doc): void => { + let textWrapped = this.textWrappedRows; + let index = textWrapped.findIndex(id => doc[Id] === id); + if (index > -1) { - this._textWrappedRows.splice(index, 1); + textWrapped.splice(index, 1); } else { - this._textWrappedRows.push(doc[Id]); + textWrapped.push(doc[Id]); } + this.textWrappedRows = textWrapped; } @computed get reactTable() { - - // let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = DocListCast(cdoc[this.props.fieldKey]); let children = this.childDocs; - let previewWidth = this.previewWidth(); // + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; + // let previewWidth = this.previewWidth(); // + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; let hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false); let expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString()); let expanded = {}; //@ts-ignore expandedRowsList.forEach(row => expanded[row] = true); - console.log(...[...this._textWrappedRows]); // TODO: get component to rerender on text wrap change without needign to console.log :(((( + console.log("text wrapped rows", ...[...this.textWrappedRows]); // TODO: get component to rerender on text wrap change without needign to console.log :(((( return <ReactTable - style={{ position: "relative", float: "left", width: `calc(100% - ${previewWidth}px` }} + style={{ position: "relative" }} data={children} page={0} pageSize={children.length} @@ -761,12 +756,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> { getTdProps={this.getTdProps} sortable={false} TrComponent={MovableRow} - sorted={Array.from(this._sortedColumns.values())} + sorted={this.sorted} expanded={expanded} + resized={this.resized} + onResizedChange={this.onResizedChange} SubComponent={hasCollectionChild ? row => { if (row.original.type === "collection") { - // let childDocs = DocListCast(row.original[this.props.fieldKey]); return <div className="sub"><SchemaTable {...this.props} Document={row.original} childDocs={undefined} /></div>; } } @@ -775,6 +771,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> { />; } + onResizedChange = (newResized: Resize[], event: any) => { + let columns = this.columns; + newResized.forEach(resized => { + let index = columns.findIndex(c => c.heading === resized.id); + let column = columns[index]; + column.setWidth(resized.value); + columns[index] = column; + }); + this.columns = columns; + } + onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // 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: "Make DB", event: this.makeDB, icon: "table" }); @@ -786,8 +793,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { let csv: string = this.columns.reduce((val, col) => val + col + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; let self = this; - let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - // let children = DocListCast(cdoc[this.props.fieldKey]); this.childDocs.map(doc => { csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; @@ -805,11 +810,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } getField = (row: number, col?: number) => { - // const docs = DocListCast(this.props.Document[this.props.fieldKey]); - - let cdoc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - const docs = DocListCast(cdoc[this.props.fieldKey]); - // let docs = this.childDocs; + let docs = this.childDocs; row = row % docs.length; while (row < 0) row += docs.length; @@ -881,13 +882,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } render() { - // if (SelectionManager.SelectedDocuments().length > 0) console.log(StrCast(SelectionManager.SelectedDocuments()[0].Document.title)); - // if (DocumentManager.Instance.getDocumentView(this.props.Document)) console.log(StrCast(this.props.Document.title), SelectionManager.IsSelected(DocumentManager.Instance.getDocumentView(this.props.Document)!)) return ( <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={this.onWheel} onDrop={(e: React.DragEvent) => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > {this.reactTable} - <button onClick={() => this.createRow()}>new row</button> + <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div> </div> ); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index bcf3a85d7..06881441f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -248,7 +248,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropTarget} />; + createDropTarget={this.createDropTarget} + screenToLocalTransform={this.props.ScreenToLocalTransform} + />; + } @action diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index d8bed7e88..38cc7fc50 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -19,6 +19,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ScriptField } from "../../../new_fields/ScriptField"; import { CompileScript } from "../../util/Scripting"; import { RichTextField } from "../../../new_fields/RichTextField"; +import { Transform } from "../../util/Transform"; interface CSVFieldColumnProps { @@ -30,6 +31,7 @@ interface CSVFieldColumnProps { parent: CollectionStackingView; type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined; createDropTarget: (ele: HTMLDivElement) => void; + screenToLocalTransform: () => Transform; } @observer @@ -39,6 +41,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC private _dropRef: HTMLDivElement | null = null; private dropDisposer?: DragManager.DragDropDisposer; private _headerRef: React.RefObject<HTMLDivElement> = React.createRef(); + private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; + private _sensitivity: number = 16; @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading; @@ -159,6 +163,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC @action pointerLeave = () => { this._background = "inherit"; + document.removeEventListener("pointermove", this.startDrag); } @action @@ -180,22 +185,25 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC } startDrag = (e: PointerEvent) => { - let alias = Doc.MakeAlias(this.props.parent.props.Document); - let key = StrCast(this.props.parent.props.Document.sectionFilter); - let value = this.getValue(this._heading); - value = typeof value === "string" ? `"${value}"` : value; - let script = `return doc.${key} === ${value}`; - let compiled = CompileScript(script, { params: { doc: Doc.name } }); - if (compiled.compiled) { - let scriptField = new ScriptField(compiled); - alias.viewSpecScript = scriptField; - let dragData = new DragManager.DocumentDragData([alias], [alias.proto]); - DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY); - } + let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); + if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { + let alias = Doc.MakeAlias(this.props.parent.props.Document); + let key = StrCast(this.props.parent.props.Document.sectionFilter); + let value = this.getValue(this._heading); + value = typeof value === "string" ? `"${value}"` : value; + let script = `return doc.${key} === ${value}`; + let compiled = CompileScript(script, { params: { doc: Doc.name } }); + if (compiled.compiled) { + let scriptField = new ScriptField(compiled); + alias.viewSpecScript = scriptField; + let dragData = new DragManager.DocumentDragData([alias], [alias.proto]); + DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY); + } - e.stopPropagation(); - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.pointerUp); + e.stopPropagation(); + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.pointerUp); + } } pointerUp = (e: PointerEvent) => { @@ -210,6 +218,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC e.stopPropagation(); e.preventDefault(); + let [dx, dy] = this.props.screenToLocalTransform().transformDirection(e.clientX, e.clientY); + this._startDragPosition = { x: dx, y: dy }; + document.removeEventListener("pointermove", this.startDrag); document.addEventListener("pointermove", this.startDrag); document.removeEventListener("pointerup", this.pointerUp); diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 989315194..5e71ea929 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -168,4 +168,55 @@ cursor: text; } } +} + +.collectionSchemaViewChrome-cont { + display: flex; + font-size: 10.5px; + + .collectionSchemaViewChrome-toggle { + display: flex; + margin-left: 10px; + } + + .collectionSchemaViewChrome-label { + text-transform: uppercase; + letter-spacing: 2px; + margin-right: 5px; + display: flex; + flex-direction: column; + justify-content: center; + } + + .collectionSchemaViewChrome-toggler { + width: 100px; + height: 41px; + background-color: black; + position: relative; + } + + .collectionSchemaViewChrome-togglerButton { + width: 47px; + height: 35px; + background-color: $light-color-secondary; + // position: absolute; + transition: all 0.5s ease; + // top: 3px; + margin-top: 3px; + color: gray; + letter-spacing: 2px; + text-transform: uppercase; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + + &.on { + margin-left: 3px; + } + + &.off { + margin-left: 50px; + } + } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 38aafd3cc..043b62480 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -17,6 +17,10 @@ import { CompileScript } from "../../util/Scripting"; import { ScriptField } from "../../../new_fields/ScriptField"; import { CollectionSchemaView } from "./CollectionSchemaView"; import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss"; +import { listSpec } from "../../../new_fields/Schema"; +import { List } from "../../../new_fields/List"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { threadId } from "worker_threads"; const datepicker = require('js-datepicker'); interface CollectionViewChromeProps { @@ -366,6 +370,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView @observer export class CollectionSchemaViewChrome extends React.Component<CollectionViewChromeProps> { + // private _textwrapAllRows: boolean = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0; togglePreview = () => { let dividerWidth = 4; @@ -374,16 +379,55 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth); let tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth; this.props.CollectionView.props.Document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0; + } + @action + toggleTextwrap = async () => { + let textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []); + if (textwrappedRows.length) { + this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]); + } else { + let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike) + = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]); + if (typeof docs === "function") { + docs = docs(); + } + docs = await docs; + if (docs instanceof Doc) { + let allRows = [docs[Id]]; + this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows); + } else { + let allRows = docs.map(doc => doc[Id]); + this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows); + } + } } render() { let previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth); + let textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0; + return ( - <div className="collectionStackingViewChrome-cont"> - <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={previewWidth !== 0} onChange={this.togglePreview} />Show Preview</div> - </div> + <div className="collectionSchemaViewChrome-cont"> + <div className="collectionSchemaViewChrome-toggle"> + <div className="collectionSchemaViewChrome-label">Wrap Text: </div> + <div className="collectionSchemaViewChrome-toggler" onClick={this.toggleTextwrap}> + <div className={"collectionSchemaViewChrome-togglerButton" + (textWrapped ? " on" : " off")}> + {textWrapped ? "on" : "off"} + </div> + </div> + </div> + + <div className="collectionSchemaViewChrome-toggle"> + <div className="collectionSchemaViewChrome-label">Show Preview: </div> + <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}> + <div className={"collectionSchemaViewChrome-togglerButton" + (previewWidth !== 0 ? " on" : " off")}> + {previewWidth !== 0 ? "on" : "off"} + </div> + </div> + </div> + </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 67bed284f..07d06d053 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -135,7 +135,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> doc.width = 200; docList.push(doc); } - let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); this.props.addDocument(newCol, false); } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 0ea948c81..ecb3e9db4 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -290,7 +290,7 @@ export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef<HTMLDivElement>(); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 0cb216aa6..e04044266 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -72,7 +72,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef<HTMLDivElement>(); diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index d5da56b10..0c19d211a 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -6,7 +6,7 @@ import { scriptingGlobal, Scripting } from "../client/util/Scripting"; import { ColumnType } from "../client/views/collections/CollectionSchemaView"; export const PastelSchemaPalette = new Map<string, string>([ - ["pink1", "#FFB4E8"], + // ["pink1", "#FFB4E8"], ["pink2", "#ff9cee"], ["pink3", "#ffccf9"], ["pink4", "#fcc2ff"], @@ -15,7 +15,7 @@ export const PastelSchemaPalette = new Map<string, string>([ ["purple2", "#c5a3ff"], ["purple3", "#d5aaff"], ["purple4", "#ecd4ff"], - ["purple5", "#fb34ff"], + // ["purple5", "#fb34ff"], ["purple6", "#dcd3ff"], ["purple7", "#a79aff"], ["purple8", "#b5b9ff"], @@ -32,7 +32,7 @@ export const PastelSchemaPalette = new Map<string, string>([ ["yellow2", "#e7ffac"], ["yellow3", "#ffffd1"], ["yellow4", "#fff5ba"], - ["red1", "#ffc9de"], + // ["red1", "#ffc9de"], ["red2", "#ffabab"], ["red3", "#ffbebc"], ["red4", "#ffcbc1"], @@ -45,20 +45,23 @@ export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math. export class SchemaHeaderField extends ObjectField { @serializable(primitive()) heading: string; + @serializable(primitive()) color: string; + @serializable(primitive()) type: number; + @serializable(primitive()) + width: number; + @serializable(primitive()) + desc: boolean | undefined; // boolean determines sort order, undefined when no sort - constructor(heading: string = "", color?: string, type?: ColumnType) { + constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean) { super(); this.heading = heading; - this.color = color === "" || color === undefined ? RandomPastel() : color; - if (type) { - this.type = type; - } - else { - this.type = 0; - } + this.color = color; + this.type = type ? type : 0; + this.width = width ? width : -1; + this.desc = desc; } setHeading(heading: string) { @@ -76,6 +79,16 @@ export class SchemaHeaderField extends ObjectField { this[OnUpdate](); } + setWidth(width: number) { + this.width = width; + this[OnUpdate](); + } + + setDesc(desc: boolean | undefined) { + this.desc = desc; + this[OnUpdate](); + } + [Copy]() { return new SchemaHeaderField(this.heading, this.color, this.type); } |