diff options
11 files changed, 2451 insertions, 2385 deletions
diff --git a/package-lock.json b/package-lock.json index 26e7a41d7..7f93ef3c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5303,16 +5303,6 @@ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "d3-array": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", @@ -6480,28 +6470,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", - "dev": true, - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -6513,7 +6481,6 @@ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "requires": { - "d": "^1.0.1", "ext": "^1.1.2" } }, @@ -21699,12 +21666,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1fd07d61d..58e318879 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1150,6 +1150,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }); } + export function SchemaRowDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.SCHEMAROW), undefined, { ...(options || {}) }); + } + export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, viewType: CollectionViewType.Docking, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 4fa5d80e2..e0d0101a2 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -126,10 +126,16 @@ } } +.schema-row-wrapper { + max-height: 70px; + overflow: hidden; +} + .schema-header-row, .schema-row { display: flex; flex-direction: row; + height: 100%; max-height: 70px; overflow: auto; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index f7c68c803..c9f934aec 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,14 +1,14 @@ import React = require('react'); import { action, computed, observable, ObservableMap, ObservableSet, trace, untracked } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; +import { DataSym, Doc, DocListCast, Opt, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; import { listSpec } from '../../../../fields/Schema'; -import { Cast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; -import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -21,6 +21,9 @@ import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; +import { DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView'; +import { DefaultStyleProvider } from '../../StyleProvider'; +import { Transform } from '../../../util/Transform'; export enum ColumnType { Number, @@ -40,7 +43,7 @@ export class CollectionSchemaView extends CollectionSubView() { private _closestDropIndex: number = 0; private _minColWidth: number = 150; - @observable _rowMenuWidth: number = 100; + public static _rowMenuWidth: number = 100; @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>(); @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>(); @observable _isDragging: boolean = false; @@ -69,16 +72,15 @@ export class CollectionSchemaView extends CollectionSubView() { let widths = Cast( this.layoutDoc.columnWidths, listSpec('number'), - this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length) + this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length) ); const totalWidth = widths.reduce((sum, width) => sum + width, 0); - if (totalWidth !== this.props.PanelWidth() - this._rowMenuWidth) { + if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) { widths = widths.map(w => { const proportion = w / totalWidth; - return proportion * (this.props.PanelWidth() - this._rowMenuWidth); + return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth); }); - // this.layoutDoc.columnWidths = new List<number>(widths); } return widths; @@ -110,8 +112,8 @@ export class CollectionSchemaView extends CollectionSubView() { let currWidths = [...this.storedColumnWidths]; const newColWidth = this.props.PanelWidth() / (currWidths.length + 1); currWidths = currWidths.map(w => { - const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth); - return proportion * (this.props.PanelWidth() - this._rowMenuWidth - newColWidth); + const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth); + return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - newColWidth); }); currWidths.splice(index + 1, 0, newColWidth); this.layoutDoc.columnWidths = new List<number>(currWidths); @@ -133,8 +135,8 @@ export class CollectionSchemaView extends CollectionSubView() { let currWidths = [...this.storedColumnWidths]; const removedColWidth = currWidths[index]; currWidths = currWidths.map(w => { - const proportion = w / (this.props.PanelWidth() - this._rowMenuWidth - removedColWidth); - return proportion * (this.props.PanelWidth() - this._rowMenuWidth); + const proportion = w / (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth - removedColWidth); + return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth); }); currWidths.splice(index, 1); this.layoutDoc.columnWidths = new List<number>(currWidths); @@ -142,8 +144,6 @@ export class CollectionSchemaView extends CollectionSubView() { let currKeys = [...this.columnKeys]; currKeys.splice(index, 1); this.layoutDoc.columnKeys = new List<string>(currKeys); - console.log([...this.storedColumnWidths]); - console.log([...this.columnKeys]); }; @action @@ -399,6 +399,34 @@ export class CollectionSchemaView extends CollectionSubView() { ContextMenu.Instance.displayMenu(x, y, undefined, true); }; + focusDocument = (doc: Doc, options: DocFocusOptions) => { + Doc.BrushDoc(doc); + + let focusSpeed = 0; + const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); + if (found) { + const top = found.getBoundingClientRect().top; + const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); + if (Math.floor(localTop[1]) !== 0) { + smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + } + } + const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing; + this.props.focus(this.rootDoc, { + ...options, + afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), + }); + }; + + isChildContentActive = () => + this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; + + getDocTransform(doc: Doc, dref?: DocumentView) { + const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined); + // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off + return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale); + } + render() { return ( <div @@ -413,7 +441,7 @@ export class CollectionSchemaView extends CollectionSubView() { onDrop={this.onExternalDrop.bind(this)}> <div className="schema-table"> <div className="schema-header-row"> - <div className="row-menu" style={{ width: this._rowMenuWidth }}></div> + <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div> {this.columnKeys.map((key, index) => { return ( <SchemaColumnHeader @@ -432,25 +460,58 @@ export class CollectionSchemaView extends CollectionSubView() { })} </div> <div className="schema-table-content"> - {this.childDocs.map((doc: Doc, index: number) => ( - <SchemaRowBox - {...this.props} - key={index} - Document={doc} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} - rowIndex={index} - columnKeys={this.columnKeys} - columnWidths={this.displayColumnWidths} - rowMenuWidth={this._rowMenuWidth} - selectedDocs={this._selectedDocs} - selectRow={this.selectRow} - startDrag={this.startDrag} - dragging={this._isDragging} - dropIndex={this.setDropIndex} - addRowRef={this.addRowRef} - /> - ))} + {this.childDocs.map((doc: Doc, index: number) => { + const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; + let dref: Opt<DocumentView>; + + return ( + <div className="schema-row-wrapper"> + <DocumentView + {...this.props} + ref={r => (dref = r || undefined)} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)} + renderDepth={this.props.renderDepth + 1} + Document={doc} + DataDoc={dataDoc} + ContainingCollectionView={this.props.CollectionView} + ContainingCollectionDoc={this.Document} + PanelWidth={this.props.PanelWidth} + PanelHeight={() => 70} + styleProvider={DefaultStyleProvider} + focus={this.focusDocument} + docFilters={this.childDocFilters} + docRangeFilters={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + rootSelected={this.rootSelected} + ScreenToLocalTransform={() => this.getDocTransform(doc, dref)} + bringToFront={emptyFunction} + isContentActive={this.isChildContentActive} + hideDecorations={true} + hideTitle={true} + hideDocumentButtonBar={true} + /> + </div> + ); + + // <SchemaRowBox + // {...this.props} + // key={index} + // Document={doc} + // ContainingCollectionDoc={this.props.CollectionView?.props.Document} + // ContainingCollectionView={this.props.CollectionView} + // rowIndex={index} + // columnKeys={this.columnKeys} + // columnWidths={this.displayColumnWidths} + // rowMenuWidth={CollectionSchemaView._rowMenuWidth} + // selectedDocs={this._selectedDocs} + // selectRow={this.selectRow} + // startDrag={this.startDrag} + // dragging={this._isDragging} + // dropIndex={this.setDropIndex} + // addRowRef={this.addRowRef} + // /> + })} </div> </div> <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} /> diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 5c1f32565..f790e9dbf 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -1,8 +1,8 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, ObservableMap, ObservableSet } from 'mobx'; +import { action, computed, ObservableMap, ObservableSet } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc } from '../../../../fields/Doc'; +import { Doc, StrListCast } from '../../../../fields/Doc'; import { undoBatch } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; @@ -11,6 +11,10 @@ import './CollectionSchemaView.scss'; import { SchemaTableCell } from './SchemaTableCell'; import { emptyFunction, setupMoveUpEvents } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; +import { OpenWhere } from '../../nodes/DocumentView'; +import { Cast } from '../../../../fields/Types'; +import { listSpec } from '../../../../fields/Schema'; +import { CollectionSchemaView } from './CollectionSchemaView'; export interface SchemaRowBoxProps extends FieldViewProps { rowIndex: number; @@ -26,40 +30,61 @@ export interface SchemaRowBoxProps extends FieldViewProps { } @observer -export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { +export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SchemaRowBox, fieldKey); } private _ref: HTMLDivElement | null = null; - isSelected = () => this.props.selectedDocs.has(this.props.Document); bounds = () => this._ref?.getBoundingClientRect(); + @computed get columnKeys() { + return StrListCast(this.props.ContainingCollectionDoc?.columnKeys); + } + + @computed get storedColumnWidths() { + let widths = Cast( + this.props.ContainingCollectionDoc?.columnWidths, + listSpec('number'), + this.columnKeys.map(() => (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length) + ); + + const totalWidth = widths.reduce((sum, width) => sum + width, 0); + if (totalWidth !== this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth) { + widths = widths.map(w => { + const proportion = w / totalWidth; + return proportion * (this.props.PanelWidth() - CollectionSchemaView._rowMenuWidth); + }); + } + + return widths; + } + @action onRowPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); - setupMoveUpEvents( - this, - e, - e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex), - emptyFunction, - e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex) - ); + // setupMoveUpEvents( + // this, + // e, + // e => this.props.startDrag(e, this.props.Document, this._ref!, this.props.rowIndex), + // emptyFunction, + // e => this.props.selectRow(e, this.props.Document, this._ref!, this.props.rowIndex) + // ); }; onPointerEnter = (e: any) => { - if (!this.props.dragging) return; + // if (!this.props.dragging) return; document.removeEventListener('pointermove', this.onPointerMove); document.addEventListener('pointermove', this.onPointerMove); }; onPointerMove = (e: any) => { - if (!this.props.dragging) return; + // if (!this.props.dragging) return; let dragIsRow: boolean = true; DragManager.docsBeingDragged.forEach(doc => { - dragIsRow = this.props.selectedDocs.has(doc); + // dragIsRow = this.props.selectedDocs.has(doc); }); if (this._ref && dragIsRow) { const rect = this._ref.getBoundingClientRect(); @@ -69,11 +94,11 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { if (y <= halfLine) { this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; this._ref.style.borderBottom = '0px'; - this.props.dropIndex(this.props.rowIndex); + // this.props.dropIndex(this.props.rowIndex); } else if (y > halfLine) { this._ref.style.borderTop = '0px'; this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; - this.props.dropIndex(this.props.rowIndex + 1); + // this.props.dropIndex(this.props.rowIndex + 1); } } }; @@ -90,15 +115,22 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { return ( <div className="schema-row" - style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}} - onPointerDown={this.onRowPointerDown} - onPointerEnter={this.onPointerEnter} + style={this.props.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE } : {}} + // style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1 } : {}} + // onPointerDown={this.onRowPointerDown} + // onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} ref={(row: HTMLDivElement | null) => { - row && this.props.addRowRef(this.props.Document, row); + // row && this.props.addRowRef(this.props.Document, row); this._ref = row; }}> - <div className="row-menu" style={{ width: this.props.rowMenuWidth }}> + <div + className="row-menu" + style={ + { + /* width: this.props.rowMenuWidth */ + } + }> <div className="schema-row-button" onPointerDown={undoBatch(e => { @@ -111,14 +143,14 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() { className="schema-row-button" onPointerDown={e => { e.stopPropagation(); - this.props.addDocTab(this.props.Document, 'add:right'); + this.props.addDocTab(this.props.Document, OpenWhere.addRight); }}> <FontAwesomeIcon icon="external-link-alt" /> </div> </div> <div className="row-cells"> - {this.props.columnKeys.map((key, index) => ( - <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.props.columnWidths[index]} /> + {this.columnKeys.map((key, index) => ( + <SchemaTableCell key={key} Document={this.props.Document} fieldKey={key} columnWidth={this.storedColumnWidths[index]} /> ))} </div> </div> diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx index 97a6c5c18..5953f85ad 100644 --- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx +++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx @@ -1,683 +1,683 @@ -import React = require('react'); -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import { extname } from 'path'; -import DatePicker from 'react-datepicker'; -import { CellInfo } from 'react-table'; -import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; -import { Id } from '../../../../fields/FieldSymbols'; -import { List } from '../../../../fields/List'; -import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; -import { ComputedField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; -import { emptyFunction, Utils } from '../../../../Utils'; -import { Docs } from '../../../documents/Documents'; -import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager } from '../../../util/DragManager'; -import { KeyCodes } from '../../../util/KeyCodes'; -import { CompileScript } from '../../../util/Scripting'; -import { SearchUtil } from '../../../util/SearchUtil'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { undoBatch } from '../../../util/UndoManager'; -import '../../../views/DocumentDecorations.scss'; -import { EditableView } from '../../EditableView'; -import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss'; -import { DocumentIconContainer } from '../../nodes/DocumentIcon'; -import { OverlayView } from '../../OverlayView'; -import { CollectionView } from '../CollectionView'; -import './CollectionSchemaView.scss'; -import { OpenWhere } from '../../nodes/DocumentView'; -import { PinProps } from '../../nodes/trails'; - -// intialize cell properties -export interface CellProps { - row: number; - col: number; - rowProps: CellInfo; - // currently unused - CollectionView: Opt<CollectionView>; - // currently unused - ContainingCollection: Opt<CollectionView>; - Document: Doc; - // column name - fieldKey: string; - // currently unused - renderDepth: number; - // called when a button is pressed on the node itself - addDocTab: (document: Doc, where: OpenWhere) => boolean; - pinToPres: (document: Doc, pinProps: PinProps) => void; - moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; - isFocused: boolean; - changeFocusedCellByIndex: (row: number, col: number) => void; - // set whether the cell is in the isEditing mode - setIsEditing: (isEditing: boolean) => void; - isEditable: boolean; - setPreviewDoc: (doc: Doc) => void; - setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean; - getField: (row: number, col?: number) => void; - // currnetly unused - showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void; -} - -@observer -export class CollectionSchemaCell extends React.Component<CellProps> { - // return a field key that is corrected for whether it COMMENT - public static resolvedFieldKey(column: string, rowDoc: Doc) { - const fieldKey = column; - if (fieldKey.startsWith('*')) { - const rootKey = fieldKey.substring(1); - const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))]; - const matchedKeys = allKeys.filter(key => key.includes(rootKey)); - if (matchedKeys.length) return matchedKeys[0]; - } - return fieldKey; - } - @observable protected _isEditing: boolean = false; - protected _focusRef = React.createRef<HTMLDivElement>(); - protected _rowDoc = this.props.rowProps.original; - // Gets the serialized data in proto form of the base proto that this document's proto inherits from - protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original); - // methods for dragging and dropping - protected _dropDisposer?: DragManager.DragDropDisposer; - @observable contents: string = ''; - - componentDidMount() { - document.addEventListener('keydown', this.onKeyDown); - } - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeyDown); - } - - @action - onKeyDown = (e: KeyboardEvent): void => { - // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it - if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) { - document.removeEventListener('keydown', this.onKeyDown); - this._isEditing = true; - this.props.setIsEditing(true); - } - }; - - @action - isEditingCallback = (isEditing: boolean): void => { - // a general method that takes a boolean that determines whether the cell should be in - // is-editing mode - // remove the event listener if it's there - document.removeEventListener('keydown', this.onKeyDown); - // it's not already in is-editing mode, re-add the event listener - isEditing && document.addEventListener('keydown', this.onKeyDown); - this._isEditing = isEditing; - this.props.setIsEditing(isEditing); - this.props.changeFocusedCellByIndex(this.props.row, this.props.col); - }; - - @action - onPointerDown = async (e: React.PointerEvent): Promise<void> => { - // pan to the cell - this.onItemDown(e); - // focus on it - this.props.changeFocusedCellByIndex(this.props.row, this.props.col); - this.props.setPreviewDoc(this.props.rowProps.original); - - let url: string; - if ((url = StrCast(this.props.rowProps.row.href))) { - // opens up the the doc in a new window, blurring the old one - try { - new URL(url); - const temp = window.open(url)!; - temp.blur(); - window.focus(); - } catch {} - } - - const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null); - doc && this.props.setPreviewDoc(doc); - }; - - @undoBatch - applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => { - // apply a specified change to the cell - const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) }); - if (!res.success) return false; - // change what is rendered to this new changed cell content - doc[this.renderFieldKey] = res.result; - return true; - // return whether the change was successful - }; - - private drop = (e: Event, de: DragManager.DropEvent) => { - // if the drag has data at its completion - if (de.complete.docDragData) { - // if only one doc was dragged - if (de.complete.docDragData.draggedDocuments.length === 1) { - // update the renderFieldKey - this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0]; - } else { - // create schema document reflecting the new column arrangement - const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {}); - this._rowDataDoc[this.renderFieldKey] = coll; - } - e.stopPropagation(); - } - }; - - protected dropRef = (ele: HTMLElement | null) => { - // if the drop disposer is not undefined, run its function - this._dropDisposer?.(); - // if ele is not null, give ele a non-undefined drop disposer - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); - }; - - returnHighlights(contents: string, positions?: number[]) { - if (positions) { - const results = []; - StrCast(this.props.Document._searchString); - const length = StrCast(this.props.Document._searchString).length; - const color = contents ? 'black' : 'grey'; - - results.push( - <span key="-1" style={{ color }}> - {contents?.slice(0, positions[0])} - </span> - ); - positions.forEach((num, cur) => { - results.push( - <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}> - {contents?.slice(num, num + length)} - </span> - ); - let end = 0; - cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]); - results.push( - <span key={'end' + cur} style={{ color }}> - {contents?.slice(num + length, end)} - </span> - ); - }); - return results; - } - return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>; - } - - @computed get renderFieldKey() { - // gets the resolved field key of this cell - return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); - } - - onItemDown = async (e: React.PointerEvent) => { - // if the document is a document used to change UI for search results in schema view - if (this.props.Document._searchDoc) { - const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); - const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); - // Jump to the this document - DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc)); - } - }; - - renderCellWithType(type: string | undefined) { - const dragRef: React.RefObject<HTMLDivElement> = React.createRef(); - - // the column - const fieldKey = this.renderFieldKey; - // the exact cell - const field = this._rowDoc[fieldKey]; - - const onPointerEnter = (e: React.PointerEvent): void => { - // e.buttons === 1 means the left moue pointer is down - if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) { - dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over'; - } - }; - const onPointerLeave = (e: React.PointerEvent): void => { - // change the class name to indicate that the cell is no longer being dragged - dragRef.current!.className = 'collectionSchemaView-cellContainer'; - }; - - let contents = Field.toString(field as Field); - // display 2 hyphens instead of a blank box for empty cells - contents = contents === '' ? '--' : contents; - - // classname reflects the tatus of the cell - let className = 'collectionSchemaView-cellWrapper'; - if (this._isEditing) className += ' editing'; - if (this.props.isFocused && this.props.isEditable) className += ' focused'; - if (this.props.isFocused && !this.props.isEditable) className += ' inactive'; - - const positions = []; - if (StrCast(this.props.Document._searchString).toLowerCase() !== '') { - // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents - let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase(); - const search = StrCast(this.props.Document._searchString).toLowerCase(); - let start = term.indexOf(search); - let tally = 0; - // if search is found in term - if (start !== -1) { - positions.push(start); - } - // if search is found in term, continue finding all instances of search in term - while (start < contents?.length && start !== -1) { - term = term.slice(start + search.length + 1); - tally += start + search.length + 1; - start = term.indexOf(search); - positions.push(tally + start); - } - // remove the last position - if (positions.length > 1) { - positions.pop(); - } - } - const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined'; - return ( - <div - className="collectionSchemaView-cellContainer" - style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }} - ref={dragRef} - onPointerDown={this.onPointerDown} - onClick={action(e => (this._isEditing = true))} - onPointerEnter={onPointerEnter} - onPointerLeave={onPointerLeave}> - <div className={className} ref={this._focusRef} tabIndex={-1}> - <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}> - {!this.props.Document._searchDoc ? ( - <EditableView - editing={this._isEditing} - isEditingCallback={this.isEditingCallback} - display={'inline'} - contents={contents} - height={'auto'} - maxHeight={Number(MAX_ROW_HEIGHT)} - placeholder={placeholder} - GetValue={() => { - const cfield = ComputedField.WithoutComputed(() => FieldValue(field)); - const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; - const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1]; - return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : ''; - }} - SetValue={action((value: string) => { - // sets what is displayed after the user makes an input - let retVal = false; - if (value.startsWith(':=') || value.startsWith('=:=')) { - // decides how to compute a value when given either of the above strings - const script = value.substring(value.startsWith('=:=') ? 3 : 2); - retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col); - } else { - // check if the input is a number - let inputIsNum = true; - for (const s of value) { - if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) { - inputIsNum = false; - } - } - // check if the input is a boolean - const inputIsBool: boolean = value === 'false' || value === 'true'; - // what to do in the case - if (!inputIsNum && !inputIsBool && !value.startsWith('=')) { - // if it's not a number, it's a string, and should be processed as such - // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically - // after each edit - let valueSansQuotes = value; - if (this._isEditing) { - const vsqLength = valueSansQuotes.length; - // get rid of outer quotes - valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength); - } - let inputAsString = '"'; - // escape any quotes in the string - for (const i of valueSansQuotes) { - if (i === '"') { - inputAsString += '\\"'; - } else { - inputAsString += i; - } - } - // add a closing quote - inputAsString += '"'; - //two options here: we can strip off outer quotes or we can figure out what's going on with the script - const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); - const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length; - // change it if a change is made, otherwise, just compile using the old cell conetnts - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - // handle numbers and expressions - } else if (inputIsNum || value.startsWith('=')) { - //TODO: make accept numbers - const inputscript = value.substring(value.startsWith('=') ? 1 : 0); - // if commas are not stripped, the parser only considers the numbers after the last comma - let inputSansCommas = ''; - for (const s of inputscript) { - if (!(s === ',')) { - inputSansCommas += s; - } - } - const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); - const changeMade = value.length - 2 !== value.length; - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - // handle booleans - } else if (inputIsBool) { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); - const changeMade = value.length - 2 !== value.length; - script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); - } - } - if (retVal) { - this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' - this.props.setIsEditing(false); - } - return retVal; - })} - OnFillDown={async (value: string) => { - // computes all of the value preceded by := - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); - script.compiled && - DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) => - value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run) - ); - }} - /> - ) : ( - this.returnHighlights(contents, positions) - )} - </div> - </div> - </div> - ); - } - - render() { - return this.renderCellWithType(undefined); - } -} - -@observer -export class CollectionSchemaNumberCell extends CollectionSchemaCell { - render() { - return this.renderCellWithType('number'); - } -} - -@observer -export class CollectionSchemaBooleanCell extends CollectionSchemaCell { - render() { - return this.renderCellWithType('boolean'); - } -} - -@observer -export class CollectionSchemaStringCell extends CollectionSchemaCell { - render() { - return this.renderCellWithType('string'); - } -} - -@observer -export class CollectionSchemaDateCell extends CollectionSchemaCell { - @computed get _date(): Opt<DateField> { - // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. - return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined; - } - - @action - handleChange = (date: any) => { - // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } }); - // if (script.compiled) { - // this.applyToDoc(this._document, this.props.row, this.props.col, script.run); - // } else { - // ^ DateCast is always undefined for some reason, but that is what the field should be set to - this._rowDoc[this.renderFieldKey] = new DateField(date as Date); - //} - }; - - render() { - return !this.props.isFocused ? ( - <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span> - ) : ( - <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} /> - ); - } -} - -@observer -export class CollectionSchemaDocCell extends CollectionSchemaCell { - _overlayDisposer?: () => void; - - @computed get _doc() { - return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc)); - } - - @action - onSetValue = (value: string) => { - this._doc && (Doc.GetProto(this._doc).title = value); - - const script = CompileScript(value, { - addReturn: true, - typecheck: true, - transformer: DocumentIconContainer.getTransformer(), - }); - // compile the script - const results = script.compiled && script.run(); - // if the script was compiled and run - if (results && results.success) { - this._rowDoc[this.renderFieldKey] = results.result; - return true; - } - return false; - }; - - componentWillUnmount() { - this.onBlur(); - } - - onBlur = () => { - this._overlayDisposer?.(); - }; - onFocus = () => { - this.onBlur(); - this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); - }; - - @action - isEditingCallback = (isEditing: boolean): void => { - // the isEditingCallback from a general CollectionSchemaCell - document.removeEventListener('keydown', this.onKeyDown); - isEditing && document.addEventListener('keydown', this.onKeyDown); - this._isEditing = isEditing; - this.props.setIsEditing(isEditing); - this.props.changeFocusedCellByIndex(this.props.row, this.props.col); - }; - - render() { - // if there's a doc, render it - return !this._doc ? ( - this.renderCellWithType('document') - ) : ( - <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> - <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}> - <EditableView - editing={this._isEditing} - isEditingCallback={this.isEditingCallback} - display={'inline'} - contents={this._doc.title || '--'} - height={'auto'} - maxHeight={Number(MAX_ROW_HEIGHT)} - GetValue={() => StrCast(this._doc?.title)} - SetValue={action((value: string) => { - this.onSetValue(value); - return true; - })} - /> - </div> - <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton"> - <FontAwesomeIcon icon="external-link-alt" size="lg" /> - </div> - </div> - ); - } -} - -@observer -export class CollectionSchemaImageCell extends CollectionSchemaCell { - choosePath(url: URL) { - if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href - if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver - if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question - - const ext = extname(url.href); - return url.href.replace(ext, '_o' + ext); - } - - render() { - const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc - const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images - const altpaths = alts - .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url) - .filter(url => url) - .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents - const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; - // If there is a path, follow it; otherwise, follow a link to a default image icon - const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; - - const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio - let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px - const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px - width = height * aspect; // increase the width of the image if necessary to maintain proportionality - - const reference = React.createRef<HTMLDivElement>(); - return ( - <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> - <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}> - <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} /> - </div> - </div> - ); - } -} - -@observer -export class CollectionSchemaListCell extends CollectionSchemaCell { - _overlayDisposer?: () => void; - - @computed get _field() { - return this._rowDoc[this.renderFieldKey]; - } - @computed get _optionsList() { - return this._field as List<any>; - } - @observable private _opened = false; // whether the list is opened - @observable private _text = 'select an item'; - @observable private _selectedNum = 0; // the index of the list item selected - - @action - onSetValue = (value: string) => { - // change if it's a document - this._optionsList[this._selectedNum] = this._text = value; - - (this._field as List<any>).splice(this._selectedNum, 1, value); - }; - - @action - onSelected = (element: string, index: number) => { - // if an item is selected, the private variables should update to reflect this - this._text = element; - this._selectedNum = index; - }; - - onFocus = () => { - this._overlayDisposer?.(); - this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); - }; - - render() { - const link = false; - const reference = React.createRef<HTMLDivElement>(); - - // if the list is not opened, don't display it; otherwise, do. - if (this._optionsList?.length) { - const options = !this._opened ? null : ( - <div> - {this._optionsList.map((element, index) => { - const val = Field.toString(element); - return ( - <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}> - {val} - </div> - ); - })} - </div> - ); - - const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>; - const textarea = ( - <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}> - <EditableView - editing={this._isEditing} - isEditingCallback={this.isEditingCallback} - display={'inline'} - contents={this._text} - height={'auto'} - maxHeight={Number(MAX_ROW_HEIGHT)} - GetValue={() => this._text} - SetValue={action((value: string) => { - // add special for params - this.onSetValue(value); - return true; - })} - /> - </div> - ); - - //☰ - return ( - <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> - <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}> - <div className="collectionSchemaView-dropDownWrapper"> - <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}> - <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" /> - </button> - <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div> - </div> - {options} - </div> - </div> - ); - } - return this.renderCellWithType('list'); - } -} - -@observer -export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { - @computed get _isChecked() { - return BoolCast(this._rowDoc[this.renderFieldKey]); - } - - render() { - const reference = React.createRef<HTMLDivElement>(); - return ( - <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> - <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} /> - </div> - ); - } -} - -@observer -export class CollectionSchemaButtons extends CollectionSchemaCell { - // the navigation buttons for schema view when it is used for search. - render() { - return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? ( - <></> - ) : ( - <div style={{ paddingTop: 8, paddingLeft: 3 }}> - <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}> - <FontAwesomeIcon icon="arrow-up" size="sm" /> - </button> - <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}> - <FontAwesomeIcon icon="arrow-down" size="sm" /> - </button> - </div> - ); - } -} +// import React = require('react'); +// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// import { action, computed, observable } from 'mobx'; +// import { observer } from 'mobx-react'; +// import { extname } from 'path'; +// import DatePicker from 'react-datepicker'; +// import { CellInfo } from 'react-table'; +// import { DateField } from '../../../../fields/DateField'; +// import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +// import { Id } from '../../../../fields/FieldSymbols'; +// import { List } from '../../../../fields/List'; +// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; +// import { ComputedField } from '../../../../fields/ScriptField'; +// import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types'; +// import { ImageField } from '../../../../fields/URLField'; +// import { emptyFunction, Utils } from '../../../../Utils'; +// import { Docs } from '../../../documents/Documents'; +// import { DocumentType } from '../../../documents/DocumentTypes'; +// import { DocumentManager } from '../../../util/DocumentManager'; +// import { DragManager } from '../../../util/DragManager'; +// import { KeyCodes } from '../../../util/KeyCodes'; +// import { CompileScript } from '../../../util/Scripting'; +// import { SearchUtil } from '../../../util/SearchUtil'; +// import { SnappingManager } from '../../../util/SnappingManager'; +// import { undoBatch } from '../../../util/UndoManager'; +// import '../../../views/DocumentDecorations.scss'; +// import { EditableView } from '../../EditableView'; +// import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss'; +// import { DocumentIconContainer } from '../../nodes/DocumentIcon'; +// import { OverlayView } from '../../OverlayView'; +// import { CollectionView } from '../CollectionView'; +// import './CollectionSchemaView.scss'; +// import { OpenWhere } from '../../nodes/DocumentView'; +// import { PinProps } from '../../nodes/trails'; + +// // intialize cell properties +// export interface CellProps { +// row: number; +// col: number; +// rowProps: CellInfo; +// // currently unused +// CollectionView: Opt<CollectionView>; +// // currently unused +// ContainingCollection: Opt<CollectionView>; +// Document: Doc; +// // column name +// fieldKey: string; +// // currently unused +// renderDepth: number; +// // called when a button is pressed on the node itself +// addDocTab: (document: Doc, where: OpenWhere) => boolean; +// pinToPres: (document: Doc, pinProps: PinProps) => void; +// moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; +// isFocused: boolean; +// changeFocusedCellByIndex: (row: number, col: number) => void; +// // set whether the cell is in the isEditing mode +// setIsEditing: (isEditing: boolean) => void; +// isEditable: boolean; +// setPreviewDoc: (doc: Doc) => void; +// setComputed: (script: string, doc: Doc, field: string, row: number, col: number) => boolean; +// getField: (row: number, col?: number) => void; +// // currnetly unused +// showDoc: (doc: Doc | undefined, dataDoc?: any, screenX?: number, screenY?: number) => void; +// } + +// @observer +// export class CollectionSchemaCell extends React.Component<CellProps> { +// // return a field key that is corrected for whether it COMMENT +// public static resolvedFieldKey(column: string, rowDoc: Doc) { +// const fieldKey = column; +// if (fieldKey.startsWith('*')) { +// const rootKey = fieldKey.substring(1); +// const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))]; +// const matchedKeys = allKeys.filter(key => key.includes(rootKey)); +// if (matchedKeys.length) return matchedKeys[0]; +// } +// return fieldKey; +// } +// @observable protected _isEditing: boolean = false; +// protected _focusRef = React.createRef<HTMLDivElement>(); +// protected _rowDoc = this.props.rowProps.original; +// // Gets the serialized data in proto form of the base proto that this document's proto inherits from +// protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original); +// // methods for dragging and dropping +// protected _dropDisposer?: DragManager.DragDropDisposer; +// @observable contents: string = ''; + +// componentDidMount() { +// document.addEventListener('keydown', this.onKeyDown); +// } +// componentWillUnmount() { +// document.removeEventListener('keydown', this.onKeyDown); +// } + +// @action +// onKeyDown = (e: KeyboardEvent): void => { +// // If a cell is editable and clicked, hitting enter shoudl allow the user to edit it +// if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) { +// document.removeEventListener('keydown', this.onKeyDown); +// this._isEditing = true; +// this.props.setIsEditing(true); +// } +// }; + +// @action +// isEditingCallback = (isEditing: boolean): void => { +// // a general method that takes a boolean that determines whether the cell should be in +// // is-editing mode +// // remove the event listener if it's there +// document.removeEventListener('keydown', this.onKeyDown); +// // it's not already in is-editing mode, re-add the event listener +// isEditing && document.addEventListener('keydown', this.onKeyDown); +// this._isEditing = isEditing; +// this.props.setIsEditing(isEditing); +// this.props.changeFocusedCellByIndex(this.props.row, this.props.col); +// }; + +// @action +// onPointerDown = async (e: React.PointerEvent): Promise<void> => { +// // pan to the cell +// this.onItemDown(e); +// // focus on it +// this.props.changeFocusedCellByIndex(this.props.row, this.props.col); +// this.props.setPreviewDoc(this.props.rowProps.original); + +// let url: string; +// if ((url = StrCast(this.props.rowProps.row.href))) { +// // opens up the the doc in a new window, blurring the old one +// try { +// new URL(url); +// const temp = window.open(url)!; +// temp.blur(); +// window.focus(); +// } catch {} +// } + +// const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null); +// doc && this.props.setPreviewDoc(doc); +// }; + +// @undoBatch +// applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => { +// // apply a specified change to the cell +// const res = run({ this: doc, $r: row, $c: col, $: (r: number = 0, c: number = 0) => this.props.getField(r + row, c + col) }); +// if (!res.success) return false; +// // change what is rendered to this new changed cell content +// doc[this.renderFieldKey] = res.result; +// return true; +// // return whether the change was successful +// }; + +// private drop = (e: Event, de: DragManager.DropEvent) => { +// // if the drag has data at its completion +// if (de.complete.docDragData) { +// // if only one doc was dragged +// if (de.complete.docDragData.draggedDocuments.length === 1) { +// // update the renderFieldKey +// this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0]; +// } else { +// // create schema document reflecting the new column arrangement +// const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {}); +// this._rowDataDoc[this.renderFieldKey] = coll; +// } +// e.stopPropagation(); +// } +// }; + +// protected dropRef = (ele: HTMLElement | null) => { +// // if the drop disposer is not undefined, run its function +// this._dropDisposer?.(); +// // if ele is not null, give ele a non-undefined drop disposer +// ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); +// }; + +// returnHighlights(contents: string, positions?: number[]) { +// if (positions) { +// const results = []; +// StrCast(this.props.Document._searchString); +// const length = StrCast(this.props.Document._searchString).length; +// const color = contents ? 'black' : 'grey'; + +// results.push( +// <span key="-1" style={{ color }}> +// {contents?.slice(0, positions[0])} +// </span> +// ); +// positions.forEach((num, cur) => { +// results.push( +// <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}> +// {contents?.slice(num, num + length)} +// </span> +// ); +// let end = 0; +// cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]); +// results.push( +// <span key={'end' + cur} style={{ color }}> +// {contents?.slice(num + length, end)} +// </span> +// ); +// }); +// return results; +// } +// return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>; +// } + +// @computed get renderFieldKey() { +// // gets the resolved field key of this cell +// return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); +// } + +// onItemDown = async (e: React.PointerEvent) => { +// // if the document is a document used to change UI for search results in schema view +// if (this.props.Document._searchDoc) { +// const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); +// const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); +// // Jump to the this document +// DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc)); +// } +// }; + +// renderCellWithType(type: string | undefined) { +// const dragRef: React.RefObject<HTMLDivElement> = React.createRef(); + +// // the column +// const fieldKey = this.renderFieldKey; +// // the exact cell +// const field = this._rowDoc[fieldKey]; + +// const onPointerEnter = (e: React.PointerEvent): void => { +// // e.buttons === 1 means the left moue pointer is down +// if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) { +// dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over'; +// } +// }; +// const onPointerLeave = (e: React.PointerEvent): void => { +// // change the class name to indicate that the cell is no longer being dragged +// dragRef.current!.className = 'collectionSchemaView-cellContainer'; +// }; + +// let contents = Field.toString(field as Field); +// // display 2 hyphens instead of a blank box for empty cells +// contents = contents === '' ? '--' : contents; + +// // classname reflects the tatus of the cell +// let className = 'collectionSchemaView-cellWrapper'; +// if (this._isEditing) className += ' editing'; +// if (this.props.isFocused && this.props.isEditable) className += ' focused'; +// if (this.props.isFocused && !this.props.isEditable) className += ' inactive'; + +// const positions = []; +// if (StrCast(this.props.Document._searchString).toLowerCase() !== '') { +// // term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents +// let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase(); +// const search = StrCast(this.props.Document._searchString).toLowerCase(); +// let start = term.indexOf(search); +// let tally = 0; +// // if search is found in term +// if (start !== -1) { +// positions.push(start); +// } +// // if search is found in term, continue finding all instances of search in term +// while (start < contents?.length && start !== -1) { +// term = term.slice(start + search.length + 1); +// tally += start + search.length + 1; +// start = term.indexOf(search); +// positions.push(tally + start); +// } +// // remove the last position +// if (positions.length > 1) { +// positions.pop(); +// } +// } +// const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined'; +// return ( +// <div +// className="collectionSchemaView-cellContainer" +// style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }} +// ref={dragRef} +// onPointerDown={this.onPointerDown} +// onClick={action(e => (this._isEditing = true))} +// onPointerEnter={onPointerEnter} +// onPointerLeave={onPointerLeave}> +// <div className={className} ref={this._focusRef} tabIndex={-1}> +// <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}> +// {!this.props.Document._searchDoc ? ( +// <EditableView +// editing={this._isEditing} +// isEditingCallback={this.isEditingCallback} +// display={'inline'} +// contents={contents} +// height={'auto'} +// maxHeight={Number(MAX_ROW_HEIGHT)} +// placeholder={placeholder} +// GetValue={() => { +// const cfield = ComputedField.WithoutComputed(() => FieldValue(field)); +// const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; +// const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1]; +// return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : ''; +// }} +// SetValue={action((value: string) => { +// // sets what is displayed after the user makes an input +// let retVal = false; +// if (value.startsWith(':=') || value.startsWith('=:=')) { +// // decides how to compute a value when given either of the above strings +// const script = value.substring(value.startsWith('=:=') ? 3 : 2); +// retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col); +// } else { +// // check if the input is a number +// let inputIsNum = true; +// for (const s of value) { +// if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) { +// inputIsNum = false; +// } +// } +// // check if the input is a boolean +// const inputIsBool: boolean = value === 'false' || value === 'true'; +// // what to do in the case +// if (!inputIsNum && !inputIsBool && !value.startsWith('=')) { +// // if it's not a number, it's a string, and should be processed as such +// // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically +// // after each edit +// let valueSansQuotes = value; +// if (this._isEditing) { +// const vsqLength = valueSansQuotes.length; +// // get rid of outer quotes +// valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength); +// } +// let inputAsString = '"'; +// // escape any quotes in the string +// for (const i of valueSansQuotes) { +// if (i === '"') { +// inputAsString += '\\"'; +// } else { +// inputAsString += i; +// } +// } +// // add a closing quote +// inputAsString += '"'; +// //two options here: we can strip off outer quotes or we can figure out what's going on with the script +// const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); +// const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length; +// // change it if a change is made, otherwise, just compile using the old cell conetnts +// script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); +// // handle numbers and expressions +// } else if (inputIsNum || value.startsWith('=')) { +// //TODO: make accept numbers +// const inputscript = value.substring(value.startsWith('=') ? 1 : 0); +// // if commas are not stripped, the parser only considers the numbers after the last comma +// let inputSansCommas = ''; +// for (const s of inputscript) { +// if (!(s === ',')) { +// inputSansCommas += s; +// } +// } +// const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); +// const changeMade = value.length - 2 !== value.length; +// script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); +// // handle booleans +// } else if (inputIsBool) { +// const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); +// const changeMade = value.length - 2 !== value.length; +// script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run)); +// } +// } +// if (retVal) { +// this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' +// this.props.setIsEditing(false); +// } +// return retVal; +// })} +// OnFillDown={async (value: string) => { +// // computes all of the value preceded by := +// const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } }); +// script.compiled && +// DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) => +// value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run) +// ); +// }} +// /> +// ) : ( +// this.returnHighlights(contents, positions) +// )} +// </div> +// </div> +// </div> +// ); +// } + +// render() { +// return this.renderCellWithType(undefined); +// } +// } + +// @observer +// export class CollectionSchemaNumberCell extends CollectionSchemaCell { +// render() { +// return this.renderCellWithType('number'); +// } +// } + +// @observer +// export class CollectionSchemaBooleanCell extends CollectionSchemaCell { +// render() { +// return this.renderCellWithType('boolean'); +// } +// } + +// @observer +// export class CollectionSchemaStringCell extends CollectionSchemaCell { +// render() { +// return this.renderCellWithType('string'); +// } +// } + +// @observer +// export class CollectionSchemaDateCell extends CollectionSchemaCell { +// @computed get _date(): Opt<DateField> { +// // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. +// return this._rowDoc[this.renderFieldKey] instanceof DateField ? DateCast(this._rowDoc[this.renderFieldKey]) : undefined; +// } + +// @action +// handleChange = (date: any) => { +// // const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } }); +// // if (script.compiled) { +// // this.applyToDoc(this._document, this.props.row, this.props.col, script.run); +// // } else { +// // ^ DateCast is always undefined for some reason, but that is what the field should be set to +// this._rowDoc[this.renderFieldKey] = new DateField(date as Date); +// //} +// }; + +// render() { +// return !this.props.isFocused ? ( +// <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span> +// ) : ( +// <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} /> +// ); +// } +// } + +// @observer +// export class CollectionSchemaDocCell extends CollectionSchemaCell { +// _overlayDisposer?: () => void; + +// @computed get _doc() { +// return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc)); +// } + +// @action +// onSetValue = (value: string) => { +// this._doc && (Doc.GetProto(this._doc).title = value); + +// const script = CompileScript(value, { +// addReturn: true, +// typecheck: true, +// transformer: DocumentIconContainer.getTransformer(), +// }); +// // compile the script +// const results = script.compiled && script.run(); +// // if the script was compiled and run +// if (results && results.success) { +// this._rowDoc[this.renderFieldKey] = results.result; +// return true; +// } +// return false; +// }; + +// componentWillUnmount() { +// this.onBlur(); +// } + +// onBlur = () => { +// this._overlayDisposer?.(); +// }; +// onFocus = () => { +// this.onBlur(); +// this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); +// }; + +// @action +// isEditingCallback = (isEditing: boolean): void => { +// // the isEditingCallback from a general CollectionSchemaCell +// document.removeEventListener('keydown', this.onKeyDown); +// isEditing && document.addEventListener('keydown', this.onKeyDown); +// this._isEditing = isEditing; +// this.props.setIsEditing(isEditing); +// this.props.changeFocusedCellByIndex(this.props.row, this.props.col); +// }; + +// render() { +// // if there's a doc, render it +// return !this._doc ? ( +// this.renderCellWithType('document') +// ) : ( +// <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> +// <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}> +// <EditableView +// editing={this._isEditing} +// isEditingCallback={this.isEditingCallback} +// display={'inline'} +// contents={this._doc.title || '--'} +// height={'auto'} +// maxHeight={Number(MAX_ROW_HEIGHT)} +// GetValue={() => StrCast(this._doc?.title)} +// SetValue={action((value: string) => { +// this.onSetValue(value); +// return true; +// })} +// /> +// </div> +// <div onClick={() => this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton"> +// <FontAwesomeIcon icon="external-link-alt" size="lg" /> +// </div> +// </div> +// ); +// } +// } + +// @observer +// export class CollectionSchemaImageCell extends CollectionSchemaCell { +// choosePath(url: URL) { +// if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href +// if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver +// if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question + +// const ext = extname(url.href); +// return url.href.replace(ext, '_o' + ext); +// } + +// render() { +// const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc +// const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images +// const altpaths = alts +// .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url) +// .filter(url => url) +// .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents +// const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths; +// // If there is a path, follow it; otherwise, follow a link to a default image icon +// const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')]; + +// const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio +// let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px +// const height = Math.min(75, width / aspect); // get a height either proportional to that or 75 px +// width = height * aspect; // increase the width of the image if necessary to maintain proportionality + +// const reference = React.createRef<HTMLDivElement>(); +// return ( +// <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> +// <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}> +// <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} /> +// </div> +// </div> +// ); +// } +// } + +// @observer +// export class CollectionSchemaListCell extends CollectionSchemaCell { +// _overlayDisposer?: () => void; + +// @computed get _field() { +// return this._rowDoc[this.renderFieldKey]; +// } +// @computed get _optionsList() { +// return this._field as List<any>; +// } +// @observable private _opened = false; // whether the list is opened +// @observable private _text = 'select an item'; +// @observable private _selectedNum = 0; // the index of the list item selected + +// @action +// onSetValue = (value: string) => { +// // change if it's a document +// this._optionsList[this._selectedNum] = this._text = value; + +// (this._field as List<any>).splice(this._selectedNum, 1, value); +// }; + +// @action +// onSelected = (element: string, index: number) => { +// // if an item is selected, the private variables should update to reflect this +// this._text = element; +// this._selectedNum = index; +// }; + +// onFocus = () => { +// this._overlayDisposer?.(); +// this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 }); +// }; + +// render() { +// const link = false; +// const reference = React.createRef<HTMLDivElement>(); + +// // if the list is not opened, don't display it; otherwise, do. +// if (this._optionsList?.length) { +// const options = !this._opened ? null : ( +// <div> +// {this._optionsList.map((element, index) => { +// const val = Field.toString(element); +// return ( +// <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}> +// {val} +// </div> +// ); +// })} +// </div> +// ); + +// const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>; +// const textarea = ( +// <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}> +// <EditableView +// editing={this._isEditing} +// isEditingCallback={this.isEditingCallback} +// display={'inline'} +// contents={this._text} +// height={'auto'} +// maxHeight={Number(MAX_ROW_HEIGHT)} +// GetValue={() => this._text} +// SetValue={action((value: string) => { +// // add special for params +// this.onSetValue(value); +// return true; +// })} +// /> +// </div> +// ); + +// //☰ +// return ( +// <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> +// <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}> +// <div className="collectionSchemaView-dropDownWrapper"> +// <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}> +// <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" /> +// </button> +// <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div> +// </div> +// {options} +// </div> +// </div> +// ); +// } +// return this.renderCellWithType('list'); +// } +// } + +// @observer +// export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { +// @computed get _isChecked() { +// return BoolCast(this._rowDoc[this.renderFieldKey]); +// } + +// render() { +// const reference = React.createRef<HTMLDivElement>(); +// return ( +// <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}> +// <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} /> +// </div> +// ); +// } +// } + +// @observer +// export class CollectionSchemaButtons extends CollectionSchemaCell { +// // the navigation buttons for schema view when it is used for search. +// render() { +// return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? ( +// <></> +// ) : ( +// <div style={{ paddingTop: 8, paddingLeft: 3 }}> +// <button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}> +// <FontAwesomeIcon icon="arrow-up" size="sm" /> +// </button> +// <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}> +// <FontAwesomeIcon icon="arrow-down" size="sm" /> +// </button> +// </div> +// ); +// } +// } diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx index 28d2e6ab1..c2182ae0c 100644 --- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx +++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx @@ -1,138 +1,138 @@ -import React = require('react'); -import { action } from 'mobx'; -import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; -import { DragManager } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { Transform } from '../../../util/Transform'; -import './CollectionSchemaView.scss'; +// import React = require('react'); +// import { action } from 'mobx'; +// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; +// import { DragManager } from '../../../util/DragManager'; +// import { SnappingManager } from '../../../util/SnappingManager'; +// import { Transform } from '../../../util/Transform'; +// import './CollectionSchemaView.scss'; -export interface MovableColumnProps { - columnRenderer: React.ReactNode; - columnValue: SchemaHeaderField; - allColumns: SchemaHeaderField[]; - reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; - ScreenToLocalTransform: () => Transform; -} -export class MovableColumn extends React.Component<MovableColumnProps> { - // The header of the column - private _header?: React.RefObject<HTMLDivElement> = React.createRef(); - // The container of the function that is responsible for moving the column over to a new plac - private _colDropDisposer?: DragManager.DragDropDisposer; - // initial column position - private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 }; - // sensitivity to being dragged, in pixels - private _sensitivity: number = 16; - // Column reference ID - private _dragRef: React.RefObject<HTMLDivElement> = React.createRef(); +// export interface MovableColumnProps { +// columnRenderer: React.ReactNode; +// columnValue: SchemaHeaderField; +// allColumns: SchemaHeaderField[]; +// reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; +// ScreenToLocalTransform: () => Transform; +// } +// export class MovableColumn extends React.Component<MovableColumnProps> { +// // The header of the column +// private _header?: React.RefObject<HTMLDivElement> = React.createRef(); +// // The container of the function that is responsible for moving the column over to a new plac +// private _colDropDisposer?: DragManager.DragDropDisposer; +// // initial column position +// private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 }; +// // sensitivity to being dragged, in pixels +// private _sensitivity: number = 16; +// // Column reference ID +// private _dragRef: React.RefObject<HTMLDivElement> = React.createRef(); - onPointerEnter = (e: React.PointerEvent): void => { - // if the column is left-clicked and it is being dragged - if (e.buttons === 1 && SnappingManager.GetIsDragging()) { - this._header!.current!.className = 'collectionSchema-col-wrapper'; - document.addEventListener('pointermove', this.onDragMove, true); - } - }; +// onPointerEnter = (e: React.PointerEvent): void => { +// // if the column is left-clicked and it is being dragged +// if (e.buttons === 1 && SnappingManager.GetIsDragging()) { +// this._header!.current!.className = 'collectionSchema-col-wrapper'; +// document.addEventListener('pointermove', this.onDragMove, true); +// } +// }; - onPointerLeave = (e: React.PointerEvent): void => { - this._header!.current!.className = 'collectionSchema-col-wrapper'; - document.removeEventListener('pointermove', this.onDragMove, true); - !e.buttons && document.removeEventListener('pointermove', this.onPointerMove); - }; +// onPointerLeave = (e: React.PointerEvent): void => { +// this._header!.current!.className = 'collectionSchema-col-wrapper'; +// document.removeEventListener('pointermove', this.onDragMove, true); +// !e.buttons && document.removeEventListener('pointermove', this.onPointerMove); +// }; - onDragMove = (e: PointerEvent): void => { - // only take into account the horizonal direction when a column is dragged - const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); - const rect = this._header!.current!.getBoundingClientRect(); - // Now store the point at the top center of the column when it was in its original position - const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); - // to be compared with its new horizontal position - const before = x[0] < bounds[0]; - this._header!.current!.className = 'collectionSchema-col-wrapper'; - if (before) this._header!.current!.className += ' col-before'; - if (!before) this._header!.current!.className += ' col-after'; - e.stopPropagation(); - }; +// onDragMove = (e: PointerEvent): void => { +// // only take into account the horizonal direction when a column is dragged +// const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); +// const rect = this._header!.current!.getBoundingClientRect(); +// // Now store the point at the top center of the column when it was in its original position +// const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); +// // to be compared with its new horizontal position +// const before = x[0] < bounds[0]; +// this._header!.current!.className = 'collectionSchema-col-wrapper'; +// if (before) this._header!.current!.className += ' col-before'; +// if (!before) this._header!.current!.className += ' col-after'; +// e.stopPropagation(); +// }; - createColDropTarget = (ele: HTMLDivElement) => { - this._colDropDisposer?.(); - if (ele) { - this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this)); - } - }; +// createColDropTarget = (ele: HTMLDivElement) => { +// this._colDropDisposer?.(); +// if (ele) { +// this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this)); +// } +// }; - colDrop = (e: Event, de: DragManager.DropEvent) => { - document.removeEventListener('pointermove', this.onDragMove, true); - // we only care about whether the column is shifted to the side - const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); - // get the dimensions of the smallest rectangle that bounds the header - const rect = this._header!.current!.getBoundingClientRect(); - const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); - // get whether the column was dragged before or after where it is now - const before = x[0] < bounds[0]; - const colDragData = de.complete.columnDragData; - // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables - if (colDragData) { - e.stopPropagation(); - this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns); - return true; - } - return false; - }; +// colDrop = (e: Event, de: DragManager.DropEvent) => { +// document.removeEventListener('pointermove', this.onDragMove, true); +// // we only care about whether the column is shifted to the side +// const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); +// // get the dimensions of the smallest rectangle that bounds the header +// const rect = this._header!.current!.getBoundingClientRect(); +// const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top); +// // get whether the column was dragged before or after where it is now +// const before = x[0] < bounds[0]; +// const colDragData = de.complete.columnDragData; +// // if there is colDragData, which happen when the drag is complete, reorder the columns according to the established variables +// if (colDragData) { +// e.stopPropagation(); +// this.props.reorderColumns(colDragData.colKey, this.props.columnValue, before, this.props.allColumns); +// return true; +// } +// return false; +// }; - onPointerMove = (e: PointerEvent) => { - const onRowMove = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); +// onPointerMove = (e: PointerEvent) => { +// const onRowMove = (e: PointerEvent) => { +// e.stopPropagation(); +// e.preventDefault(); - document.removeEventListener('pointermove', onRowMove); - document.removeEventListener('pointerup', onRowUp); - const dragData = new DragManager.ColumnDragData(this.props.columnValue); - DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y); - }; - const onRowUp = (): void => { - document.removeEventListener('pointermove', onRowMove); - document.removeEventListener('pointerup', onRowUp); - }; - // if the left mouse button is the one being held - if (e.buttons === 1) { - const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); - // If the movemnt of the drag exceeds the sensitivity value - if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { - document.removeEventListener('pointermove', this.onPointerMove); - e.stopPropagation(); +// document.removeEventListener('pointermove', onRowMove); +// document.removeEventListener('pointerup', onRowUp); +// const dragData = new DragManager.ColumnDragData(this.props.columnValue); +// DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y); +// }; +// const onRowUp = (): void => { +// document.removeEventListener('pointermove', onRowMove); +// document.removeEventListener('pointerup', onRowUp); +// }; +// // if the left mouse button is the one being held +// if (e.buttons === 1) { +// const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y); +// // If the movemnt of the drag exceeds the sensitivity value +// if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { +// document.removeEventListener('pointermove', this.onPointerMove); +// e.stopPropagation(); - document.addEventListener('pointermove', onRowMove); - document.addEventListener('pointerup', onRowUp); - } - } - }; +// document.addEventListener('pointermove', onRowMove); +// document.addEventListener('pointerup', onRowUp); +// } +// } +// }; - onPointerUp = (e: React.PointerEvent) => { - document.removeEventListener('pointermove', this.onPointerMove); - }; +// onPointerUp = (e: React.PointerEvent) => { +// document.removeEventListener('pointermove', this.onPointerMove); +// }; - @action - onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => { - this._dragRef = ref; - const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); - // If the cell thing dragged is not being edited - if (!(e.target as any)?.tagName.includes('INPUT')) { - this._startDragPosition = { x: dx, y: dy }; - document.addEventListener('pointermove', this.onPointerMove); - } - }; +// @action +// onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => { +// this._dragRef = ref; +// const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY); +// // If the cell thing dragged is not being edited +// if (!(e.target as any)?.tagName.includes('INPUT')) { +// this._startDragPosition = { x: dx, y: dy }; +// document.addEventListener('pointermove', this.onPointerMove); +// } +// }; - render() { - const reference = React.createRef<HTMLDivElement>(); +// render() { +// const reference = React.createRef<HTMLDivElement>(); - 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={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}> - {this.props.columnRenderer} - </div> - </div> - </div> - ); - } -} +// 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={e => this.onPointerDown(e, reference)} onPointerUp={this.onPointerUp}> +// {this.props.columnRenderer} +// </div> +// </div> +// </div> +// ); +// } +// } diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx index 3cb2df7d3..2b39df201 100644 --- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx +++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx @@ -1,152 +1,152 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action } from 'mobx'; -import * as React from 'react'; -import { ReactTableDefaults, RowInfo } from 'react-table'; -import { Doc } from '../../../../fields/Doc'; -import { Cast, FieldValue, StrCast } from '../../../../fields/Types'; -import { DocumentManager } from '../../../util/DocumentManager'; -import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { Transform } from '../../../util/Transform'; -import { undoBatch } from '../../../util/UndoManager'; -import { ContextMenu } from '../../ContextMenu'; -import { OpenWhere } from '../../nodes/DocumentView'; -import './CollectionSchemaView.scss'; +// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// import { action } from 'mobx'; +// import * as React from 'react'; +// import { ReactTableDefaults, RowInfo } from 'react-table'; +// import { Doc } from '../../../../fields/Doc'; +// import { Cast, FieldValue, StrCast } from '../../../../fields/Types'; +// import { DocumentManager } from '../../../util/DocumentManager'; +// import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager'; +// import { SnappingManager } from '../../../util/SnappingManager'; +// import { Transform } from '../../../util/Transform'; +// import { undoBatch } from '../../../util/UndoManager'; +// import { ContextMenu } from '../../ContextMenu'; +// import { OpenWhere } from '../../nodes/DocumentView'; +// import './CollectionSchemaView.scss'; -export interface MovableRowProps { - rowInfo: RowInfo; - ScreenToLocalTransform: () => Transform; - addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; - removeDoc: (doc: Doc | Doc[]) => boolean; - rowFocused: boolean; - textWrapRow: (doc: Doc) => void; - rowWrapped: boolean; - dropAction: string; - addDocTab: any; -} +// export interface MovableRowProps { +// rowInfo: RowInfo; +// ScreenToLocalTransform: () => Transform; +// addDoc: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; +// removeDoc: (doc: Doc | Doc[]) => boolean; +// rowFocused: boolean; +// textWrapRow: (doc: Doc) => void; +// rowWrapped: boolean; +// dropAction: string; +// addDocTab: any; +// } -export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> { - private _header?: React.RefObject<HTMLDivElement> = React.createRef(); - private _rowDropDisposer?: DragManager.DragDropDisposer; +// export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> { +// private _header?: React.RefObject<HTMLDivElement> = React.createRef(); +// private _rowDropDisposer?: DragManager.DragDropDisposer; - // Event listeners are only necessary when the user is hovering over the table - // Create one when the mouse starts hovering... - onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && SnappingManager.GetIsDragging()) { - this._header!.current!.className = 'collectionSchema-row-wrapper'; - document.addEventListener('pointermove', this.onDragMove, true); - } - }; - // ... and delete it when the mouse leaves - onPointerLeave = (e: React.PointerEvent): void => { - this._header!.current!.className = 'collectionSchema-row-wrapper'; - document.removeEventListener('pointermove', this.onDragMove, true); - }; - // The method for the event listener, reorders columns when dragged to their new locations. - onDragMove = (e: PointerEvent): void => { - const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); - const rect = this._header!.current!.getBoundingClientRect(); - const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); - const before = x[1] < bounds[1]; - this._header!.current!.className = 'collectionSchema-row-wrapper'; - if (before) this._header!.current!.className += ' row-above'; - if (!before) this._header!.current!.className += ' row-below'; - e.stopPropagation(); - }; - componentWillUnmount() { - this._rowDropDisposer?.(); - } - // - createRowDropTarget = (ele: HTMLDivElement) => { - this._rowDropDisposer?.(); - if (ele) { - this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this)); - } - }; - // Controls what hppens when a row is dragged and dropped - rowDrop = (e: Event, de: DragManager.DropEvent) => { - this.onPointerLeave(e as any); - const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc)); - if (!rowDoc) return false; +// // Event listeners are only necessary when the user is hovering over the table +// // Create one when the mouse starts hovering... +// onPointerEnter = (e: React.PointerEvent): void => { +// if (e.buttons === 1 && SnappingManager.GetIsDragging()) { +// this._header!.current!.className = 'collectionSchema-row-wrapper'; +// document.addEventListener('pointermove', this.onDragMove, true); +// } +// }; +// // ... and delete it when the mouse leaves +// onPointerLeave = (e: React.PointerEvent): void => { +// this._header!.current!.className = 'collectionSchema-row-wrapper'; +// document.removeEventListener('pointermove', this.onDragMove, true); +// }; +// // The method for the event listener, reorders columns when dragged to their new locations. +// onDragMove = (e: PointerEvent): void => { +// const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY); +// const rect = this._header!.current!.getBoundingClientRect(); +// const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); +// const before = x[1] < bounds[1]; +// this._header!.current!.className = 'collectionSchema-row-wrapper'; +// if (before) this._header!.current!.className += ' row-above'; +// if (!before) this._header!.current!.className += ' row-below'; +// e.stopPropagation(); +// }; +// componentWillUnmount() { +// this._rowDropDisposer?.(); +// } +// // +// createRowDropTarget = (ele: HTMLDivElement) => { +// this._rowDropDisposer?.(); +// if (ele) { +// this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this)); +// } +// }; +// // Controls what hppens when a row is dragged and dropped +// rowDrop = (e: Event, de: DragManager.DropEvent) => { +// this.onPointerLeave(e as any); +// const rowDoc = FieldValue(Cast(this.props.rowInfo.original, Doc)); +// if (!rowDoc) return false; - const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); - const rect = this._header!.current!.getBoundingClientRect(); - const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); - const before = x[1] < bounds[1]; +// const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); +// const rect = this._header!.current!.getBoundingClientRect(); +// const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); +// const before = x[1] < bounds[1]; - const docDragData = de.complete.docDragData; - if (docDragData) { - e.stopPropagation(); - if (docDragData.draggedDocuments[0] === rowDoc) return true; - const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before); - const movedDocs = docDragData.draggedDocuments; - return docDragData.dropAction || docDragData.userDropAction - ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false) - : docDragData.moveDocument - ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false) - : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false); - } - return false; - }; +// const docDragData = de.complete.docDragData; +// if (docDragData) { +// e.stopPropagation(); +// if (docDragData.draggedDocuments[0] === rowDoc) return true; +// const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before); +// const movedDocs = docDragData.draggedDocuments; +// return docDragData.dropAction || docDragData.userDropAction +// ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false) +// : docDragData.moveDocument +// ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false) +// : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false); +// } +// return false; +// }; - onRowContextMenu = (e: React.MouseEvent): void => { - const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row'; - ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' }); - }; +// onRowContextMenu = (e: React.MouseEvent): void => { +// const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row'; +// ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' }); +// }; - @undoBatch - @action - move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => { - const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection); - return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc); - }; +// @undoBatch +// @action +// move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => { +// const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection); +// return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc); +// }; - @action - onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { - console.log('yes'); - if (e.key === 'Backspace' || e.key === 'Delete') { - undoBatch(() => this.props.removeDoc(this.props.rowInfo.original)); - } - }; +// @action +// onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { +// console.log('yes'); +// if (e.key === 'Backspace' || e.key === 'Delete') { +// undoBatch(() => this.props.removeDoc(this.props.rowInfo.original)); +// } +// }; - render() { - const { children = null, rowInfo } = this.props; +// render() { +// const { children = null, rowInfo } = this.props; - if (!rowInfo) { - return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>; - } +// if (!rowInfo) { +// return <ReactTableDefaults.TrComponent>{children}</ReactTableDefaults.TrComponent>; +// } - const { original } = rowInfo; - const doc = FieldValue(Cast(original, Doc)); +// const { original } = rowInfo; +// const doc = FieldValue(Cast(original, Doc)); - if (!doc) return null; +// if (!doc) return null; - const reference = React.createRef<HTMLDivElement>(); - const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType); +// const reference = React.createRef<HTMLDivElement>(); +// const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType); - let className = 'collectionSchema-row'; - if (this.props.rowFocused) className += ' row-focused'; - if (this.props.rowWrapped) className += ' row-wrapped'; +// let className = 'collectionSchema-row'; +// if (this.props.rowFocused) className += ' row-focused'; +// if (this.props.rowWrapped) className += ' row-wrapped'; - return ( - <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}> - <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}> - <div className="row-dragger"> - <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}> - <FontAwesomeIcon icon="trash" size="sm" /> - </div> - <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}> - <FontAwesomeIcon icon="grip-vertical" size="sm" /> - </div> - <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}> - <FontAwesomeIcon icon="external-link-alt" size="sm" /> - </div> - </div> - {children} - </ReactTableDefaults.TrComponent> - </div> - </div> - ); - } -} +// return ( +// <div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}> +// <div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> +// <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}> +// <div className="row-dragger"> +// <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}> +// <FontAwesomeIcon icon="trash" size="sm" /> +// </div> +// <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}> +// <FontAwesomeIcon icon="grip-vertical" size="sm" /> +// </div> +// <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, OpenWhere.addRight)}> +// <FontAwesomeIcon icon="external-link-alt" size="sm" /> +// </div> +// </div> +// {children} +// </ReactTableDefaults.TrComponent> +// </div> +// </div> +// ); +// } +// } diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx index 260db4b88..f3f09cbf0 100644 --- a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx +++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx @@ -1,649 +1,649 @@ -import React = require('react'); -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked } from 'mobx'; -import { observer } from 'mobx-react'; -import Measure from 'react-measure'; -// import { Resize } from 'react-table'; -import { Doc, Opt } from '../../../../fields/Doc'; -import { List } from '../../../../fields/List'; -import { listSpec } from '../../../../fields/Schema'; -import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; -import { Cast, NumCast } from '../../../../fields/Types'; -import { TraceMobx } from '../../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils'; -import { DocUtils } from '../../../documents/Documents'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { SnappingManager } from '../../../util/SnappingManager'; -import { Transform } from '../../../util/Transform'; -import { undoBatch } from '../../../util/UndoManager'; -import { ContextMenu } from '../../ContextMenu'; -import { ContextMenuProps } from '../../ContextMenuItem'; -import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss'; -import { DocumentView } from '../../nodes/DocumentView'; -import { DefaultStyleProvider } from '../../StyleProvider'; -import { CollectionSubView } from '../CollectionSubView'; -import './CollectionSchemaView.scss'; -// import { SchemaTable } from './SchemaTable'; -// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 - -export enum ColumnType { - Any, - Number, - String, - Boolean, - Doc, - Image, - List, - Date, -} -// this map should be used for keys that should have a const type of value -const columnTypes: Map<string, ColumnType> = new Map([ - ['title', ColumnType.String], - ['x', ColumnType.Number], - ['y', ColumnType.Number], - ['_width', ColumnType.Number], - ['_height', ColumnType.Number], - ['_nativeWidth', ColumnType.Number], - ['_nativeHeight', ColumnType.Number], - ['isPrototype', ColumnType.Boolean], - ['_curPage', ColumnType.Number], - ['_currentTimecode', ColumnType.Number], - ['zIndex', ColumnType.Number], -]); - -@observer -export class CollectionSchemaView extends CollectionSubView() { - private _previewCont?: HTMLDivElement; - - @observable _previewDoc: Doc | undefined = undefined; - @observable _focusedTable: Doc = this.props.Document; - @observable _col: any = ''; - @observable _menuWidth = 0; - @observable _headerOpen = false; - @observable _headerIsEditing = false; - @observable _menuHeight = 0; - @observable _pointerX = 0; - @observable _pointerY = 0; - @observable _openTypes: boolean = false; - - @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 - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); - } - @computed get borderWidth() { - return Number(COLLECTION_BORDER_WIDTH); - } - @computed get scale() { - return this.props.ScreenToLocalTransform().Scale; - } - @computed get columns() { - return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); - } - set columns(columns: SchemaHeaderField[]) { - this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); - } - - @computed get menuCoordinates() { - let searchx = 0; - let searchy = 0; - if (this.props.Document._searchDoc) { - const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0]; - if (el !== undefined) { - const rect = el.getBoundingClientRect(); - searchx = rect.x; - searchy = rect.y; - } - } - const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx; - const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy; - return this.props.ScreenToLocalTransform().transformPoint(x, y); - } - - get documentKeys() { - const docs = this.childDocs; - const 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. - //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.heading] = true)); - return Array.from(Object.keys(keys)); - } - - @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing); - - @undoBatch - setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => { - this._openTypes = false; - if (columnTypes.get(columnField.heading)) return; - - const columns = this.columns; - const index = columns.indexOf(columnField); - if (index > -1) { - columnField.setType(NumCast(type)); - columns[index] = columnField; - this.columns = columns; - } - }); - - @undoBatch - setColumnColor = (columnField: SchemaHeaderField, color: string): void => { - const columns = this.columns; - const index = columns.indexOf(columnField); - if (index > -1) { - columnField.setColor(color); - columns[index] = columnField; - this.columns = columns; // need to set the columns to trigger rerender - } - }; - - @undoBatch - @action - setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => { - const columns = this.columns; - columns.forEach(col => col.setDesc(undefined)); - - const index = columns.findIndex(c => c.heading === columnField.heading); - const column = columns[index]; - column.setDesc(descending); - columns[index] = column; - this.columns = columns; - }; - - renderTypes = (col: any) => { - if (columnTypes.get(col.heading)) return null; - - const type = col.type; - - const anyType = ( - <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}> - <FontAwesomeIcon icon={'align-justify'} size="sm" /> - Any - </div> - ); - - const numType = ( - <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}> - <FontAwesomeIcon icon={'hashtag'} size="sm" /> - Number - </div> - ); - - const textType = ( - <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}> - <FontAwesomeIcon icon={'font'} size="sm" /> - Text - </div> - ); - - const boolType = ( - <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}> - <FontAwesomeIcon icon={'check-square'} size="sm" /> - Checkbox - </div> - ); - - const listType = ( - <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}> - <FontAwesomeIcon icon={'list-ul'} size="sm" /> - List - </div> - ); - - const docType = ( - <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}> - <FontAwesomeIcon icon={'file'} size="sm" /> - Document - </div> - ); - - const imageType = ( - <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}> - <FontAwesomeIcon icon={'image'} size="sm" /> - Image - </div> - ); - - const dateType = ( - <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}> - <FontAwesomeIcon icon={'calendar'} size="sm" /> - Date - </div> - ); - - const allColumnTypes = ( - <div className="columnMenu-types"> - {anyType} - {numType} - {textType} - {boolType} - {listType} - {docType} - {imageType} - {dateType} - </div> - ); - - const justColType = - type === ColumnType.Any - ? anyType - : type === ColumnType.Number - ? numType - : type === ColumnType.String - ? textType - : type === ColumnType.Boolean - ? boolType - : type === ColumnType.List - ? listType - : type === ColumnType.Doc - ? docType - : type === ColumnType.Date - ? dateType - : imageType; - - return ( - <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}> - <div> - <label style={{ cursor: 'pointer' }}>Column type:</label> - <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} /> - </div> - {this._openTypes ? allColumnTypes : justColType} - </div> - ); - }; - - renderSorting = (col: any) => { - const sort = col.desc; - return ( - <div className="collectionSchema-headerMenu-group"> - <label>Sort by:</label> - <div className="columnMenu-sort"> - <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}> - <FontAwesomeIcon icon="sort-amount-down" size="sm" /> - Sort descending - </div> - <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}> - <FontAwesomeIcon icon="sort-amount-up" size="sm" /> - Sort ascending - </div> - <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}> - <FontAwesomeIcon icon="times" size="sm" /> - Clear sorting - </div> - </div> - </div> - ); - }; - - renderColors = (col: any) => { - const selected = col.color; - - const pink = PastelSchemaPalette.get('pink2'); - const purple = PastelSchemaPalette.get('purple2'); - const blue = PastelSchemaPalette.get('bluegreen1'); - const yellow = PastelSchemaPalette.get('yellow4'); - const red = PastelSchemaPalette.get('red2'); - const gray = '#f1efeb'; - - return ( - <div className="collectionSchema-headerMenu-group"> - <label>Color:</label> - <div className="columnMenu-colors"> - <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div> - <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div> - <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div> - <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div> - <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div> - <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div> - </div> - </div> - ); - }; - - @undoBatch - @action - changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => { - const columns = this.columns; - if (columns === undefined) { - this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]); - } else { - if (addNew) { - columns.push(new SchemaHeaderField(newKey, 'f1efeb')); - this.columns = columns; - } else { - const index = columns.map(c => c.heading).indexOf(oldKey); - if (index > -1) { - const column = columns[index]; - column.setHeading(newKey); - columns[index] = column; - this.columns = columns; - if (filter) { - Doc.setDocFilter(this.props.Document, newKey, filter, 'match'); - } else { - this.props.Document._docFilters = undefined; - } - } - } - } - }; - - @action - openHeader = (col: any, screenx: number, screeny: number) => { - this._col = col; - this._headerOpen = true; - this._pointerX = screenx; - this._pointerY = screeny; - }; - - @action - closeHeader = () => { - this._headerOpen = false; - }; - - @undoBatch - @action - deleteColumn = (key: string) => { - const columns = this.columns; - if (columns === undefined) { - this.columns = new List<SchemaHeaderField>([]); - } else { - const index = columns.map(c => c.heading).indexOf(key); - if (index > -1) { - columns.splice(index, 1); - this.columns = columns; - } - } - this.closeHeader(); - }; - - getPreviewTransform = (): Transform => { - return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth); - }; - - @action - onHeaderClick = (e: React.PointerEvent) => { - e.stopPropagation(); - }; - - @action - onWheel(e: React.WheelEvent) { - const scale = this.props.ScreenToLocalTransform().Scale; - this.props.isContentActive(true) && e.stopPropagation(); - } - - @computed get renderMenuContent() { - TraceMobx(); - return ( - <div className="collectionSchema-header-menuOptions"> - {this.renderTypes(this._col)} - {this.renderColors(this._col)} - <div className="collectionSchema-headerMenu-group"> - <button - onClick={() => { - this.deleteColumn(this._col.heading); - }}> - Hide Column - </button> - </div> - </div> - ); - } - - private createTarget = (ele: HTMLDivElement) => { - this._previewCont = ele; - super.CreateDropTarget(ele); - }; - - isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable; - - @action setFocused = (doc: Doc) => (this._focusedTable = doc); - - @action setPreviewDoc = (doc: Opt<Doc>) => { - SelectionManager.SelectSchemaViewDoc(doc); - this._previewDoc = doc; - }; - - //toggles preview side-panel of schema - @action - toggleExpander = () => { - this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0; - }; - - onDividerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander); - }; - @action - onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { - const nativeWidth = this._previewCont!.getBoundingClientRect(); - const minWidth = 40; - const maxWidth = 1000; - const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; - const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; - this.props.Document.schemaPreviewWidth = width; - return false; - }; - - onPointerDown = (e: React.PointerEvent): void => { - if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { - if (this.props.isSelected(true)) e.stopPropagation(); - else this.props.select(false); - } - }; - - @computed - get previewDocument(): Doc | undefined { - return this._previewDoc; - } - - @computed - get dividerDragger() { - return this.previewWidth() === 0 ? null : ( - <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}> - <div className="collectionSchemaView-dividerDragger" /> - </div> - ); - } - - @computed - get previewPanel() { - return ( - <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}> - {!this.previewDocument ? null : ( - <DocumentView - Document={this.previewDocument} - DataDoc={undefined} - fitContentsToBox={returnTrue} - dontCenter={'y'} - focus={DocUtils.DefaultFocus} - renderDepth={this.props.renderDepth} - rootSelected={this.rootSelected} - PanelWidth={this.previewWidth} - PanelHeight={this.previewHeight} - isContentActive={returnTrue} - isDocumentActive={returnFalse} - ScreenToLocalTransform={this.getPreviewTransform} - docFilters={this.childDocFilters} - docRangeFilters={this.childDocRangeFilters} - searchFilterDocs={this.searchFilterDocs} - styleProvider={DefaultStyleProvider} - docViewPath={returnEmptyDoclist} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} - moveDocument={this.props.moveDocument} - addDocument={this.props.addDocument} - removeDocument={this.props.removeDocument} - whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - bringToFront={returnFalse} - /> - )} - </div> - ); - } - - @computed - get schemaTable() { - return ( - <SchemaTable - Document={this.props.Document} - PanelHeight={this.props.PanelHeight} - PanelWidth={this.props.PanelWidth} - childDocs={this.childDocs} - CollectionView={this.props.CollectionView} - ContainingCollectionView={this.props.ContainingCollectionView} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} - fieldKey={this.props.fieldKey} - renderDepth={this.props.renderDepth} - moveDocument={this.props.moveDocument} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} - active={this.props.isContentActive} - onDrop={this.onExternalDrop} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - isSelected={this.props.isSelected} - isFocused={this.isFocused} - setFocused={this.setFocused} - setPreviewDoc={this.setPreviewDoc} - deleteDocument={this.props.removeDocument} - addDocument={this.props.addDocument} - dataDoc={this.props.DataDoc} - columns={this.columns} - documentKeys={this.documentKeys} - headerIsEditing={this._headerIsEditing} - openHeader={this.openHeader} - onClick={this.onTableClick} - onPointerDown={emptyFunction} - onResizedChange={this.onResizedChange} - setColumns={this.setColumns} - reorderColumns={this.reorderColumns} - changeColumns={this.changeColumns} - setHeaderIsEditing={this.setHeaderIsEditing} - changeColumnSort={this.setColumnSort} - /> - ); - } - - @computed - public get schemaToolbar() { - return ( - <div className="collectionSchemaView-toolbar"> - <div className="collectionSchemaView-toolbar-item"> - <div id="preview-schema-checkbox-div"> - <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> - Show Preview - </div> - </div> - </div> - ); - } - - onSpecificMenu = (e: React.MouseEvent) => { - if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) { - const cm = ContextMenu.Instance; - const options = cm.findByDescription('Options...'); - const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; - optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' }); - !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); - cm.displayMenu(e.clientX, e.clientY); - (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this. - e.stopPropagation(); - } - }; - - @action - onTableClick = (e: React.MouseEvent): void => { - if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) { - this.setPreviewDoc(undefined); - } else { - e.stopPropagation(); - } - this.setFocused(this.props.Document); - this.closeHeader(); - }; - - onResizedChange = (newResized: Resize[], event: any) => { - const columns = this.columns; - newResized.forEach(resized => { - const index = columns.findIndex(c => c.heading === resized.id); - const column = columns[index]; - column.setWidth(resized.value); - columns[index] = column; - }); - this.columns = columns; - }; - - @action - setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns); - - @undoBatch - reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { - const columns = [...columnsValues]; - const oldIndex = columns.indexOf(toMove); - const relIndex = columns.indexOf(relativeTo); - const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex; - - if (oldIndex === newIndex) return; - - columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); - this.columns = columns; - }; - - onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); - - render() { - TraceMobx(); - if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); - const menuContent = this.renderMenuContent; - const menu = ( - <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}> - <Measure - offset - onResize={action((r: any) => { - const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); - this._menuWidth = dim[0]; - this._menuHeight = dim[1]; - })}> - {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} - </Measure> - </div> - ); - return ( - <div - className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')} - style={{ - overflow: this.props.scrollOverflow === true ? 'scroll' : undefined, - backgroundColor: 'white', - pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined, - width: this.props.PanelWidth() || '100%', - height: this.props.PanelHeight() || '100%', - position: 'relative', - }}> - <div - className="collectionSchemaView-tableContainer" - style={{ width: `calc(100% - ${this.previewWidth()}px)` }} - onContextMenu={this.onSpecificMenu} - onPointerDown={this.onPointerDown} - onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} - onDrop={e => this.onExternalDrop(e, {})} - ref={this.createTarget}> - {this.schemaTable} - </div> - {this.dividerDragger} - {!this.previewWidth() ? null : this.previewPanel} - {this._headerOpen && this.props.isContentActive() ? menu : null} - </div> - ); - TraceMobx(); - return <div>HELLO</div>; - } -} +// import React = require('react'); +// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// import { action, computed, observable, untracked } from 'mobx'; +// import { observer } from 'mobx-react'; +// import Measure from 'react-measure'; +// // import { Resize } from 'react-table'; +// import { Doc, Opt } from '../../../../fields/Doc'; +// import { List } from '../../../../fields/List'; +// import { listSpec } from '../../../../fields/Schema'; +// import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; +// import { Cast, NumCast } from '../../../../fields/Types'; +// import { TraceMobx } from '../../../../fields/util'; +// import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils'; +// import { DocUtils } from '../../../documents/Documents'; +// import { SelectionManager } from '../../../util/SelectionManager'; +// import { SnappingManager } from '../../../util/SnappingManager'; +// import { Transform } from '../../../util/Transform'; +// import { undoBatch } from '../../../util/UndoManager'; +// import { ContextMenu } from '../../ContextMenu'; +// import { ContextMenuProps } from '../../ContextMenuItem'; +// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss'; +// import { DocumentView } from '../../nodes/DocumentView'; +// import { DefaultStyleProvider } from '../../StyleProvider'; +// import { CollectionSubView } from '../CollectionSubView'; +// import './CollectionSchemaView.scss'; +// // import { SchemaTable } from './SchemaTable'; +// // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 + +// export enum ColumnType { +// Any, +// Number, +// String, +// Boolean, +// Doc, +// Image, +// List, +// Date, +// } +// // this map should be used for keys that should have a const type of value +// const columnTypes: Map<string, ColumnType> = new Map([ +// ['title', ColumnType.String], +// ['x', ColumnType.Number], +// ['y', ColumnType.Number], +// ['_width', ColumnType.Number], +// ['_height', ColumnType.Number], +// ['_nativeWidth', ColumnType.Number], +// ['_nativeHeight', ColumnType.Number], +// ['isPrototype', ColumnType.Boolean], +// ['_curPage', ColumnType.Number], +// ['_currentTimecode', ColumnType.Number], +// ['zIndex', ColumnType.Number], +// ]); + +// @observer +// export class CollectionSchemaView extends CollectionSubView() { +// private _previewCont?: HTMLDivElement; + +// @observable _previewDoc: Doc | undefined = undefined; +// @observable _focusedTable: Doc = this.props.Document; +// @observable _col: any = ''; +// @observable _menuWidth = 0; +// @observable _headerOpen = false; +// @observable _headerIsEditing = false; +// @observable _menuHeight = 0; +// @observable _pointerX = 0; +// @observable _pointerY = 0; +// @observable _openTypes: boolean = false; + +// @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 - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); +// } +// @computed get borderWidth() { +// return Number(COLLECTION_BORDER_WIDTH); +// } +// @computed get scale() { +// return this.props.ScreenToLocalTransform().Scale; +// } +// @computed get columns() { +// return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); +// } +// set columns(columns: SchemaHeaderField[]) { +// this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); +// } + +// @computed get menuCoordinates() { +// let searchx = 0; +// let searchy = 0; +// if (this.props.Document._searchDoc) { +// const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0]; +// if (el !== undefined) { +// const rect = el.getBoundingClientRect(); +// searchx = rect.x; +// searchy = rect.y; +// } +// } +// const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx; +// const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy; +// return this.props.ScreenToLocalTransform().transformPoint(x, y); +// } + +// get documentKeys() { +// const docs = this.childDocs; +// const 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. +// //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.heading] = true)); +// return Array.from(Object.keys(keys)); +// } + +// @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing); + +// @undoBatch +// setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => { +// this._openTypes = false; +// if (columnTypes.get(columnField.heading)) return; + +// const columns = this.columns; +// const index = columns.indexOf(columnField); +// if (index > -1) { +// columnField.setType(NumCast(type)); +// columns[index] = columnField; +// this.columns = columns; +// } +// }); + +// @undoBatch +// setColumnColor = (columnField: SchemaHeaderField, color: string): void => { +// const columns = this.columns; +// const index = columns.indexOf(columnField); +// if (index > -1) { +// columnField.setColor(color); +// columns[index] = columnField; +// this.columns = columns; // need to set the columns to trigger rerender +// } +// }; + +// @undoBatch +// @action +// setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => { +// const columns = this.columns; +// columns.forEach(col => col.setDesc(undefined)); + +// const index = columns.findIndex(c => c.heading === columnField.heading); +// const column = columns[index]; +// column.setDesc(descending); +// columns[index] = column; +// this.columns = columns; +// }; + +// renderTypes = (col: any) => { +// if (columnTypes.get(col.heading)) return null; + +// const type = col.type; + +// const anyType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}> +// <FontAwesomeIcon icon={'align-justify'} size="sm" /> +// Any +// </div> +// ); + +// const numType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}> +// <FontAwesomeIcon icon={'hashtag'} size="sm" /> +// Number +// </div> +// ); + +// const textType = ( +// <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}> +// <FontAwesomeIcon icon={'font'} size="sm" /> +// Text +// </div> +// ); + +// const boolType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}> +// <FontAwesomeIcon icon={'check-square'} size="sm" /> +// Checkbox +// </div> +// ); + +// const listType = ( +// <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}> +// <FontAwesomeIcon icon={'list-ul'} size="sm" /> +// List +// </div> +// ); + +// const docType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}> +// <FontAwesomeIcon icon={'file'} size="sm" /> +// Document +// </div> +// ); + +// const imageType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}> +// <FontAwesomeIcon icon={'image'} size="sm" /> +// Image +// </div> +// ); + +// const dateType = ( +// <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}> +// <FontAwesomeIcon icon={'calendar'} size="sm" /> +// Date +// </div> +// ); + +// const allColumnTypes = ( +// <div className="columnMenu-types"> +// {anyType} +// {numType} +// {textType} +// {boolType} +// {listType} +// {docType} +// {imageType} +// {dateType} +// </div> +// ); + +// const justColType = +// type === ColumnType.Any +// ? anyType +// : type === ColumnType.Number +// ? numType +// : type === ColumnType.String +// ? textType +// : type === ColumnType.Boolean +// ? boolType +// : type === ColumnType.List +// ? listType +// : type === ColumnType.Doc +// ? docType +// : type === ColumnType.Date +// ? dateType +// : imageType; + +// return ( +// <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}> +// <div> +// <label style={{ cursor: 'pointer' }}>Column type:</label> +// <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} /> +// </div> +// {this._openTypes ? allColumnTypes : justColType} +// </div> +// ); +// }; + +// renderSorting = (col: any) => { +// const sort = col.desc; +// return ( +// <div className="collectionSchema-headerMenu-group"> +// <label>Sort by:</label> +// <div className="columnMenu-sort"> +// <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}> +// <FontAwesomeIcon icon="sort-amount-down" size="sm" /> +// Sort descending +// </div> +// <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}> +// <FontAwesomeIcon icon="sort-amount-up" size="sm" /> +// Sort ascending +// </div> +// <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}> +// <FontAwesomeIcon icon="times" size="sm" /> +// Clear sorting +// </div> +// </div> +// </div> +// ); +// }; + +// renderColors = (col: any) => { +// const selected = col.color; + +// const pink = PastelSchemaPalette.get('pink2'); +// const purple = PastelSchemaPalette.get('purple2'); +// const blue = PastelSchemaPalette.get('bluegreen1'); +// const yellow = PastelSchemaPalette.get('yellow4'); +// const red = PastelSchemaPalette.get('red2'); +// const gray = '#f1efeb'; + +// return ( +// <div className="collectionSchema-headerMenu-group"> +// <label>Color:</label> +// <div className="columnMenu-colors"> +// <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div> +// <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div> +// <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div> +// <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div> +// <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div> +// <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div> +// </div> +// </div> +// ); +// }; + +// @undoBatch +// @action +// changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => { +// const columns = this.columns; +// if (columns === undefined) { +// this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]); +// } else { +// if (addNew) { +// columns.push(new SchemaHeaderField(newKey, 'f1efeb')); +// this.columns = columns; +// } else { +// const index = columns.map(c => c.heading).indexOf(oldKey); +// if (index > -1) { +// const column = columns[index]; +// column.setHeading(newKey); +// columns[index] = column; +// this.columns = columns; +// if (filter) { +// Doc.setDocFilter(this.props.Document, newKey, filter, 'match'); +// } else { +// this.props.Document._docFilters = undefined; +// } +// } +// } +// } +// }; + +// @action +// openHeader = (col: any, screenx: number, screeny: number) => { +// this._col = col; +// this._headerOpen = true; +// this._pointerX = screenx; +// this._pointerY = screeny; +// }; + +// @action +// closeHeader = () => { +// this._headerOpen = false; +// }; + +// @undoBatch +// @action +// deleteColumn = (key: string) => { +// const columns = this.columns; +// if (columns === undefined) { +// this.columns = new List<SchemaHeaderField>([]); +// } else { +// const index = columns.map(c => c.heading).indexOf(key); +// if (index > -1) { +// columns.splice(index, 1); +// this.columns = columns; +// } +// } +// this.closeHeader(); +// }; + +// getPreviewTransform = (): Transform => { +// return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth); +// }; + +// @action +// onHeaderClick = (e: React.PointerEvent) => { +// e.stopPropagation(); +// }; + +// @action +// onWheel(e: React.WheelEvent) { +// const scale = this.props.ScreenToLocalTransform().Scale; +// this.props.isContentActive(true) && e.stopPropagation(); +// } + +// @computed get renderMenuContent() { +// TraceMobx(); +// return ( +// <div className="collectionSchema-header-menuOptions"> +// {this.renderTypes(this._col)} +// {this.renderColors(this._col)} +// <div className="collectionSchema-headerMenu-group"> +// <button +// onClick={() => { +// this.deleteColumn(this._col.heading); +// }}> +// Hide Column +// </button> +// </div> +// </div> +// ); +// } + +// private createTarget = (ele: HTMLDivElement) => { +// this._previewCont = ele; +// super.CreateDropTarget(ele); +// }; + +// isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable; + +// @action setFocused = (doc: Doc) => (this._focusedTable = doc); + +// @action setPreviewDoc = (doc: Opt<Doc>) => { +// SelectionManager.SelectSchemaViewDoc(doc); +// this._previewDoc = doc; +// }; + +// //toggles preview side-panel of schema +// @action +// toggleExpander = () => { +// this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0; +// }; + +// onDividerDown = (e: React.PointerEvent) => { +// setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander); +// }; +// @action +// onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { +// const nativeWidth = this._previewCont!.getBoundingClientRect(); +// const minWidth = 40; +// const maxWidth = 1000; +// const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; +// const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; +// this.props.Document.schemaPreviewWidth = width; +// return false; +// }; + +// onPointerDown = (e: React.PointerEvent): void => { +// if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { +// if (this.props.isSelected(true)) e.stopPropagation(); +// else this.props.select(false); +// } +// }; + +// @computed +// get previewDocument(): Doc | undefined { +// return this._previewDoc; +// } + +// @computed +// get dividerDragger() { +// return this.previewWidth() === 0 ? null : ( +// <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}> +// <div className="collectionSchemaView-dividerDragger" /> +// </div> +// ); +// } + +// @computed +// get previewPanel() { +// return ( +// <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}> +// {!this.previewDocument ? null : ( +// <DocumentView +// Document={this.previewDocument} +// DataDoc={undefined} +// fitContentsToBox={returnTrue} +// dontCenter={'y'} +// focus={DocUtils.DefaultFocus} +// renderDepth={this.props.renderDepth} +// rootSelected={this.rootSelected} +// PanelWidth={this.previewWidth} +// PanelHeight={this.previewHeight} +// isContentActive={returnTrue} +// isDocumentActive={returnFalse} +// ScreenToLocalTransform={this.getPreviewTransform} +// docFilters={this.childDocFilters} +// docRangeFilters={this.childDocRangeFilters} +// searchFilterDocs={this.searchFilterDocs} +// styleProvider={DefaultStyleProvider} +// docViewPath={returnEmptyDoclist} +// ContainingCollectionDoc={this.props.CollectionView?.props.Document} +// ContainingCollectionView={this.props.CollectionView} +// moveDocument={this.props.moveDocument} +// addDocument={this.props.addDocument} +// removeDocument={this.props.removeDocument} +// whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} +// addDocTab={this.props.addDocTab} +// pinToPres={this.props.pinToPres} +// bringToFront={returnFalse} +// /> +// )} +// </div> +// ); +// } + +// @computed +// get schemaTable() { +// return ( +// <SchemaTable +// Document={this.props.Document} +// PanelHeight={this.props.PanelHeight} +// PanelWidth={this.props.PanelWidth} +// childDocs={this.childDocs} +// CollectionView={this.props.CollectionView} +// ContainingCollectionView={this.props.ContainingCollectionView} +// ContainingCollectionDoc={this.props.ContainingCollectionDoc} +// fieldKey={this.props.fieldKey} +// renderDepth={this.props.renderDepth} +// moveDocument={this.props.moveDocument} +// ScreenToLocalTransform={this.props.ScreenToLocalTransform} +// active={this.props.isContentActive} +// onDrop={this.onExternalDrop} +// addDocTab={this.props.addDocTab} +// pinToPres={this.props.pinToPres} +// isSelected={this.props.isSelected} +// isFocused={this.isFocused} +// setFocused={this.setFocused} +// setPreviewDoc={this.setPreviewDoc} +// deleteDocument={this.props.removeDocument} +// addDocument={this.props.addDocument} +// dataDoc={this.props.DataDoc} +// columns={this.columns} +// documentKeys={this.documentKeys} +// headerIsEditing={this._headerIsEditing} +// openHeader={this.openHeader} +// onClick={this.onTableClick} +// onPointerDown={emptyFunction} +// onResizedChange={this.onResizedChange} +// setColumns={this.setColumns} +// reorderColumns={this.reorderColumns} +// changeColumns={this.changeColumns} +// setHeaderIsEditing={this.setHeaderIsEditing} +// changeColumnSort={this.setColumnSort} +// /> +// ); +// } + +// @computed +// public get schemaToolbar() { +// return ( +// <div className="collectionSchemaView-toolbar"> +// <div className="collectionSchemaView-toolbar-item"> +// <div id="preview-schema-checkbox-div"> +// <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> +// Show Preview +// </div> +// </div> +// </div> +// ); +// } + +// onSpecificMenu = (e: React.MouseEvent) => { +// if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) { +// const cm = ContextMenu.Instance; +// const options = cm.findByDescription('Options...'); +// const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; +// optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' }); +// !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); +// cm.displayMenu(e.clientX, e.clientY); +// (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this. +// e.stopPropagation(); +// } +// }; + +// @action +// onTableClick = (e: React.MouseEvent): void => { +// if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) { +// this.setPreviewDoc(undefined); +// } else { +// e.stopPropagation(); +// } +// this.setFocused(this.props.Document); +// this.closeHeader(); +// }; + +// onResizedChange = (newResized: Resize[], event: any) => { +// const columns = this.columns; +// newResized.forEach(resized => { +// const index = columns.findIndex(c => c.heading === resized.id); +// const column = columns[index]; +// column.setWidth(resized.value); +// columns[index] = column; +// }); +// this.columns = columns; +// }; + +// @action +// setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns); + +// @undoBatch +// reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { +// const columns = [...columnsValues]; +// const oldIndex = columns.indexOf(toMove); +// const relIndex = columns.indexOf(relativeTo); +// const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex; + +// if (oldIndex === newIndex) return; + +// columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); +// this.columns = columns; +// }; + +// onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); + +// render() { +// TraceMobx(); +// if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); +// const menuContent = this.renderMenuContent; +// const menu = ( +// <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}> +// <Measure +// offset +// onResize={action((r: any) => { +// const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); +// this._menuWidth = dim[0]; +// this._menuHeight = dim[1]; +// })}> +// {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} +// </Measure> +// </div> +// ); +// return ( +// <div +// className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')} +// style={{ +// overflow: this.props.scrollOverflow === true ? 'scroll' : undefined, +// backgroundColor: 'white', +// pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined, +// width: this.props.PanelWidth() || '100%', +// height: this.props.PanelHeight() || '100%', +// position: 'relative', +// }}> +// <div +// className="collectionSchemaView-tableContainer" +// style={{ width: `calc(100% - ${this.previewWidth()}px)` }} +// onContextMenu={this.onSpecificMenu} +// onPointerDown={this.onPointerDown} +// onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} +// onDrop={e => this.onExternalDrop(e, {})} +// ref={this.createTarget}> +// {this.schemaTable} +// </div> +// {this.dividerDragger} +// {!this.previewWidth() ? null : this.previewPanel} +// {this._headerOpen && this.props.isContentActive() ? menu : null} +// </div> +// ); +// TraceMobx(); +// return <div>HELLO</div>; +// } +// } diff --git a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx index 1b4fcf0a4..bc33f3be5 100644 --- a/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx +++ b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx @@ -1,694 +1,694 @@ -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table'; -import { DateField } from '../../../../fields/DateField'; -import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; -import { Id } from '../../../../fields/FieldSymbols'; -import { List } from '../../../../fields/List'; -import { listSpec } from '../../../../fields/Schema'; -import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; -import { ComputedField } from '../../../../fields/ScriptField'; -import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types'; -import { ImageField } from '../../../../fields/URLField'; -import { GetEffectiveAcl } from '../../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils'; -import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents'; -import { DocumentType } from '../../../documents/DocumentTypes'; -import { CompileScript, Transformer, ts } from '../../../util/Scripting'; -import { Transform } from '../../../util/Transform'; -import { undoBatch } from '../../../util/UndoManager'; -import '../../../views/DocumentDecorations.scss'; -import { ContextMenu } from '../../ContextMenu'; -import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss'; -import { DocumentView, OpenWhere } from '../../nodes/DocumentView'; -import { PinProps } from '../../nodes/trails'; -import { DefaultStyleProvider } from '../../StyleProvider'; -import { CollectionView } from '../CollectionView'; -import { - CellProps, - CollectionSchemaButtons, - CollectionSchemaCell, - CollectionSchemaCheckboxCell, - CollectionSchemaDateCell, - CollectionSchemaDocCell, - CollectionSchemaImageCell, - CollectionSchemaListCell, - CollectionSchemaNumberCell, - CollectionSchemaStringCell, -} from './CollectionSchemaCells'; -import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders'; -import { MovableColumn } from './OldCollectionSchemaMovableColumn'; -import { MovableRow } from './CollectionSchemaMovableRow'; -import './CollectionSchemaView.scss'; - -enum ColumnType { - Any, - Number, - String, - Boolean, - Doc, - Image, - List, - Date, -} - -// this map should be used for keys that should have a const type of value -const columnTypes: Map<string, ColumnType> = new Map([ - ['title', ColumnType.String], - ['x', ColumnType.Number], - ['y', ColumnType.Number], - ['_width', ColumnType.Number], - ['_height', ColumnType.Number], - ['_nativeWidth', ColumnType.Number], - ['_nativeHeight', ColumnType.Number], - ['isPrototype', ColumnType.Boolean], - ['_curPage', ColumnType.Number], - ['_currentTimecode', ColumnType.Number], - ['zIndex', ColumnType.Number], -]); - -export interface SchemaTableProps { - Document: Doc; // child doc - dataDoc?: Doc; - PanelHeight: () => number; - PanelWidth: () => number; - childDocs?: Doc[]; - CollectionView: Opt<CollectionView>; - ContainingCollectionView: Opt<CollectionView>; - ContainingCollectionDoc: Opt<Doc>; - fieldKey: string; - renderDepth: number; - deleteDocument?: (document: Doc | Doc[]) => boolean; - addDocument?: (document: Doc | Doc[]) => boolean; - moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; - ScreenToLocalTransform: () => Transform; - active: (outsideReaction: boolean | undefined) => boolean | undefined; - onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void; - addDocTab: (document: Doc, where: OpenWhere) => boolean; - pinToPres: (document: Doc, pinProps: PinProps) => void; - isSelected: (outsideReaction?: boolean) => boolean; - isFocused: (document: Doc, outsideReaction: boolean) => boolean; - setFocused: (document: Doc) => void; - setPreviewDoc: (document: Opt<Doc>) => void; - columns: SchemaHeaderField[]; - documentKeys: any[]; - headerIsEditing: boolean; - openHeader: (column: any, screenx: number, screeny: number) => void; - onClick: (e: React.MouseEvent) => void; - onPointerDown: (e: React.PointerEvent) => void; - onResizedChange: (newResized: Resize[], event: any) => void; - setColumns: (columns: SchemaHeaderField[]) => void; - reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void; - changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void; - setHeaderIsEditing: (isEditing: boolean) => void; - changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void; -} - -@observer -export class SchemaTable extends React.Component<SchemaTableProps> { - @observable _cellIsEditing: boolean = false; - @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 }; - @observable _openCollections: Set<number> = new Set(); - - @observable _showDoc: Doc | undefined; - @observable _showDataDoc: any = ''; - @observable _showDocPos: number[] = []; - - @observable _showTitleDropdown: boolean = false; - - @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 - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); - } - - @computed get childDocs() { - if (this.props.childDocs) return this.props.childDocs; - - const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - return DocListCast(doc[this.props.fieldKey]); - } - set childDocs(docs: Doc[]) { - const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; - doc[this.props.fieldKey] = new List<Doc>(docs); - } - - @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.props.columns.reduce((resized, shf) => { - shf.width > -1 && resized.push({ id: shf.heading, value: shf.width }); - return resized; - }, [] as { id: string; value: number }[]); - } - @computed get sorted(): SortingRule[] { - return this.props.columns.reduce((sorted, shf) => { - shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc }); - return sorted; - }, [] as SortingRule[]); - } - - @action - changeSorting = (col: any) => { - this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true); - }; - - @action - changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown); - - @computed get borderWidth() { - return Number(COLLECTION_BORDER_WIDTH); - } - @computed get tableColumns(): Column<Doc>[] { - const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); - const columns: Column<Doc>[] = []; - const tableIsFocused = this.props.isFocused(this.props.Document, false); - const focusedRow = this._focusedCell.row; - const focusedCol = this._focusedCell.col; - const isEditable = !this.props.headerIsEditing; - - columns.push({ - expander: true, - Header: '', - width: 58, - Expander: rowInfo => { - return rowInfo.original.type !== DocumentType.COL ? null : ( - <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}> - <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" /> - </div> - ); - }, - }); - columns.push( - ...this.props.columns.map(col => { - const icon: IconProp = - this.getColumnType(col) === ColumnType.Number - ? 'hashtag' - : this.getColumnType(col) === ColumnType.String - ? 'font' - : this.getColumnType(col) === ColumnType.Boolean - ? 'check-square' - : this.getColumnType(col) === ColumnType.Doc - ? 'file' - : this.getColumnType(col) === ColumnType.Image - ? 'image' - : this.getColumnType(col) === ColumnType.List - ? 'list-ul' - : this.getColumnType(col) === ColumnType.Date - ? 'calendar' - : 'align-justify'; - - const keysDropdown = ( - <KeysDropdown - keyValue={col.heading} - possibleKeys={possibleKeys} - existingKeys={this.props.columns.map(c => c.heading)} - canAddNew={true} - addNew={false} - onSelect={this.props.changeColumns} - setIsEditing={this.props.setHeaderIsEditing} - docs={this.props.childDocs} - Document={this.props.Document} - dataDoc={this.props.dataDoc} - fieldKey={this.props.fieldKey} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} - ContainingCollectionView={this.props.ContainingCollectionView} - active={this.props.active} - openHeader={this.props.openHeader} - icon={icon} - col={col} - // try commenting this out - width={'100%'} - /> - ); - - const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up'; - const header = ( - <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}> - {keysDropdown} - <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}> - <FontAwesomeIcon icon={sortIcon} size="lg" /> - </div> - {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */} - </div> - ); - - return { - Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />, - accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0), - id: col.heading, - Cell: (rowProps: CellInfo) => { - const rowIndex = rowProps.index; - const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!); - const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; - - const props: CellProps = { - row: rowIndex, - col: columnIndex, - rowProps: rowProps, - isFocused: isFocused, - changeFocusedCellByIndex: this.changeFocusedCellByIndex, - CollectionView: this.props.CollectionView, - ContainingCollection: this.props.ContainingCollectionView, - Document: this.props.Document, - fieldKey: this.props.fieldKey, - renderDepth: this.props.renderDepth, - addDocTab: this.props.addDocTab, - pinToPres: this.props.pinToPres, - moveDocument: this.props.moveDocument, - setIsEditing: this.setCellIsEditing, - isEditable: isEditable, - setPreviewDoc: this.props.setPreviewDoc, - setComputed: this.setComputed, - getField: this.getField, - showDoc: this.showDoc, - }; - - switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) { - case ColumnType.Number: - return <CollectionSchemaNumberCell {...props} />; - case ColumnType.String: - return <CollectionSchemaStringCell {...props} />; - case ColumnType.Boolean: - return <CollectionSchemaCheckboxCell {...props} />; - case ColumnType.Doc: - return <CollectionSchemaDocCell {...props} />; - case ColumnType.Image: - return <CollectionSchemaImageCell {...props} />; - case ColumnType.List: - return <CollectionSchemaListCell {...props} />; - case ColumnType.Date: - return <CollectionSchemaDateCell {...props} />; - default: - return <CollectionSchemaCell {...props} />; - } - }, - minWidth: 200, - }; - }) - ); - columns.push({ - Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />, - accessor: (doc: Doc) => 0, - id: 'add', - Cell: (rowProps: CellInfo) => { - const rowIndex = rowProps.index; - const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!); - const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; - return ( - <CollectionSchemaButtons - {...{ - row: rowProps.index, - col: columnIndex, - rowProps: rowProps, - isFocused: isFocused, - changeFocusedCellByIndex: this.changeFocusedCellByIndex, - CollectionView: this.props.CollectionView, - ContainingCollection: this.props.ContainingCollectionView, - Document: this.props.Document, - fieldKey: this.props.fieldKey, - renderDepth: this.props.renderDepth, - addDocTab: this.props.addDocTab, - pinToPres: this.props.pinToPres, - moveDocument: this.props.moveDocument, - setIsEditing: this.setCellIsEditing, - isEditable: isEditable, - setPreviewDoc: this.props.setPreviewDoc, - setComputed: this.setComputed, - getField: this.getField, - showDoc: this.showDoc, - }} - /> - ); - }, - width: 28, - resizable: false, - }); - return columns; - } - - constructor(props: SchemaTableProps) { - super(props); - if (this.props.Document._schemaHeaders === undefined) { - this.props.Document._schemaHeaders = new List<SchemaHeaderField>([ - new SchemaHeaderField('title', '#f1efeb'), - new SchemaHeaderField('author', '#f1efeb'), - new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date), - new SchemaHeaderField('text', '#f1efeb', ColumnType.String), - new SchemaHeaderField('type', '#f1efeb'), - new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc), - ]); - } - } - - componentDidMount() { - document.addEventListener('keydown', this.onKeyDown); - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeyDown); - } - - tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => { - const tableDoc = this.props.Document[DataSym]; - const effectiveAcl = GetEffectiveAcl(tableDoc); - - if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) { - doc.context = this.props.Document; - tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())); - return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); - } - return false; - }; - - private getTrProps: ComponentPropsGetterR = (state, rowInfo) => { - return !rowInfo - ? {} - : { - ScreenToLocalTransform: this.props.ScreenToLocalTransform, - addDoc: this.tableAddDoc, - removeDoc: this.props.deleteDocument, - rowInfo, - rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true), - textWrapRow: this.toggleTextWrapRow, - rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1, - dropAction: StrCast(this.props.Document.childDropAction), - addDocTab: this.props.addDocTab, - }; - }; - - private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => { - if (!rowInfo || column) return {}; - - const row = rowInfo.index; - //@ts-ignore - const col = this.columns.map(c => c.heading).indexOf(column!.id); - const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true); - // TODO: editing border doesn't work :( - return { - style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' }, - }; - }; - - @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing); - - @action - onKeyDown = (e: KeyboardEvent): void => { - if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) { - // && this.props.isSelected(true)) { - const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : ''; - this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col); - - if (direction) { - const pdoc = FieldValue(this.childDocs[this._focusedCell.row]); - pdoc && this.props.setPreviewDoc(pdoc); - e.stopPropagation(); - } - } else if (e.keyCode === 27) { - this.props.setPreviewDoc(undefined); - e.stopPropagation(); // stopPropagation for left/right arrows - } - }; - - changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => { - switch (direction) { - case 'tab': - return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 }; - case 'right': - return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 }; - case 'left': - return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 }; - case 'up': - return { row: curRow === 0 ? curRow : curRow - 1, col: curCol }; - case 'down': - return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol }; - } - return this._focusedCell; - }; - - @action - changeFocusedCellByIndex = (row: number, col: number): void => { - if (this._focusedCell.row !== row || this._focusedCell.col !== col) { - this._focusedCell = { row: row, col: col }; - } - this.props.setFocused(this.props.Document); - }; - - @undoBatch - createRow = action(() => { - this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 })); - this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col }; - }); - - @undoBatch - @action - createColumn = () => { - const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`; - for (let index = 0; index < 100; index++) { - if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) { - this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb')); - break; - } - } - }; - - @action - getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => { - if (doc && field && column.type === ColumnType.Any) { - const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)]; - if (val instanceof ImageField) return ColumnType.Image; - if (val instanceof Doc) return ColumnType.Doc; - if (val instanceof DateField) return ColumnType.Date; - if (val instanceof List) return ColumnType.List; - } - if (column.type && column.type !== 0) { - return column.type; - } - if (columnTypes.get(column.heading)) { - return (column.type = columnTypes.get(column.heading)!); - } - return (column.type = ColumnType.Any); - }; - - @undoBatch - @action - toggleTextwrap = async () => { - const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []); - if (textwrappedRows.length) { - this.props.Document.textwrappedSchemaRows = new List<string>([]); - } else { - const docs = DocListCast(this.props.Document[this.props.fieldKey]); - const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]); - this.props.Document.textwrappedSchemaRows = new List<string>(allRows); - } - }; - - @action - toggleTextWrapRow = (doc: Doc): void => { - const textWrapped = this.textWrappedRows; - const index = textWrapped.findIndex(id => doc[Id] === id); - - index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]); - - this.textWrappedRows = textWrapped; - }; - - @computed - get reactTable() { - const children = this.childDocs; - const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false); - const expanded: { [name: string]: any } = {}; - Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true)); - const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :(((( - - return ( - <ReactTable - style={{ position: 'relative' }} - data={children} - page={0} - pageSize={children.length} - showPagination={false} - columns={this.tableColumns} - getTrProps={this.getTrProps} - getTdProps={this.getTdProps} - sortable={false} - TrComponent={MovableRow} - sorted={this.sorted} - expanded={expanded} - resized={this.resized} - onResizedChange={this.props.onResizedChange} - // if it has a child, render another table with the children - SubComponent={ - !hasCollectionChild - ? undefined - : row => - row.original.type !== DocumentType.COL ? null : ( - <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub"> - <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /> - </div> - ) - } - /> - ); - } - - onContextMenu = (e: React.MouseEvent): void => { - ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' }); - }; - - getField = (row: number, col?: number) => { - const docs = this.childDocs; - - row = row % docs.length; - while (row < 0) row += docs.length; - const columns = this.props.columns; - const doc = docs[row]; - if (col === undefined) { - return doc; - } - if (col >= 0 && col < columns.length) { - const column = this.props.columns[col].heading; - return doc[column]; - } - return undefined; - }; - - createTransformer = (row: number, col: number): Transformer => { - const self = this; - const captures: { [name: string]: Field } = {}; - - const transformer: ts.TransformerFactory<ts.SourceFile> = context => { - return root => { - function visit(node: ts.Node) { - node = ts.visitEachChild(node, visit, context); - if (ts.isIdentifier(node)) { - const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node; - const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node; - if (isntPropAccess && isntPropAssign) { - if (node.text === '$r') { - return ts.createNumericLiteral(row.toString()); - } else if (node.text === '$c') { - return ts.createNumericLiteral(col.toString()); - } else if (node.text === '$') { - if (ts.isCallExpression(node.parent)) { - // captures.doc = self.props.Document; - // captures.key = self.props.fieldKey; - } - } - } - } - - return node; - } - return ts.visitNode(root, visit); - }; - }; - - // const getVars = () => { - // return { capturedVariables: captures }; - // }; - - return { transformer /*getVars*/ }; - }; - - setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => { - script = `const $ = (row:number, col?:number) => { - const rval = (doc as any)[key][row + ${row}]; - return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading]; - } - return ${script}`; - const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) }); - if (compiled.compiled) { - doc[field] = new ComputedField(compiled); - return true; - } - return false; - }; - - @action - showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => { - this._showDoc = doc; - if (dataDoc && screenX && screenY) { - this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY); - } - }; - - onOpenClick = () => { - this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight); - }; - - getPreviewTransform = (): Transform => { - return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth); - }; - - render() { - const preview = ''; - return ( - <div - className="collectionSchemaView-table" - onPointerDown={this.props.onPointerDown} - onClick={this.props.onClick} - onWheel={e => this.props.active(true) && e.stopPropagation()} - onDrop={e => this.props.onDrop(e, {})} - onContextMenu={this.onContextMenu}> - {this.reactTable} - {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : ( - <div className="collectionSchemaView-addRow" onClick={this.createRow}> - + new - </div> - )} - {!this._showDoc ? null : ( - <div - className="collectionSchemaView-documentPreview" - ref="overlay" - style={{ - position: 'absolute', - width: 150, - height: 150, - background: 'dimgray', - display: 'block', - top: 0, - left: 0, - transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`, - }}> - <DocumentView - Document={this._showDoc} - DataDoc={this._showDataDoc} - styleProvider={DefaultStyleProvider} - docViewPath={returnEmptyDoclist} - focus={DocUtils.DefaultFocus} - renderDepth={this.props.renderDepth} - rootSelected={returnFalse} - isContentActive={returnTrue} - isDocumentActive={returnFalse} - PanelWidth={() => 150} - PanelHeight={() => 150} - ScreenToLocalTransform={this.getPreviewTransform} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} - moveDocument={this.props.moveDocument} - whenChildContentsActiveChanged={emptyFunction} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - bringToFront={returnFalse}></DocumentView> - </div> - )} - </div> - ); - } -} +// import { IconProp } from '@fortawesome/fontawesome-svg-core'; +// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// import { action, computed, observable } from 'mobx'; +// import { observer } from 'mobx-react'; +// import * as React from 'react'; +// import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table'; +// import { DateField } from '../../../../fields/DateField'; +// import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +// import { Id } from '../../../../fields/FieldSymbols'; +// import { List } from '../../../../fields/List'; +// import { listSpec } from '../../../../fields/Schema'; +// import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; +// import { ComputedField } from '../../../../fields/ScriptField'; +// import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types'; +// import { ImageField } from '../../../../fields/URLField'; +// import { GetEffectiveAcl } from '../../../../fields/util'; +// import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils'; +// import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents'; +// import { DocumentType } from '../../../documents/DocumentTypes'; +// import { CompileScript, Transformer, ts } from '../../../util/Scripting'; +// import { Transform } from '../../../util/Transform'; +// import { undoBatch } from '../../../util/UndoManager'; +// import '../../../views/DocumentDecorations.scss'; +// import { ContextMenu } from '../../ContextMenu'; +// import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss'; +// import { DocumentView, OpenWhere } from '../../nodes/DocumentView'; +// import { PinProps } from '../../nodes/trails'; +// import { DefaultStyleProvider } from '../../StyleProvider'; +// import { CollectionView } from '../CollectionView'; +// import { +// CellProps, +// CollectionSchemaButtons, +// CollectionSchemaCell, +// CollectionSchemaCheckboxCell, +// CollectionSchemaDateCell, +// CollectionSchemaDocCell, +// CollectionSchemaImageCell, +// CollectionSchemaListCell, +// CollectionSchemaNumberCell, +// CollectionSchemaStringCell, +// } from './CollectionSchemaCells'; +// import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders'; +// import { MovableColumn } from './OldCollectionSchemaMovableColumn'; +// import { MovableRow } from './CollectionSchemaMovableRow'; +// import './CollectionSchemaView.scss'; + +// enum ColumnType { +// Any, +// Number, +// String, +// Boolean, +// Doc, +// Image, +// List, +// Date, +// } + +// // this map should be used for keys that should have a const type of value +// const columnTypes: Map<string, ColumnType> = new Map([ +// ['title', ColumnType.String], +// ['x', ColumnType.Number], +// ['y', ColumnType.Number], +// ['_width', ColumnType.Number], +// ['_height', ColumnType.Number], +// ['_nativeWidth', ColumnType.Number], +// ['_nativeHeight', ColumnType.Number], +// ['isPrototype', ColumnType.Boolean], +// ['_curPage', ColumnType.Number], +// ['_currentTimecode', ColumnType.Number], +// ['zIndex', ColumnType.Number], +// ]); + +// export interface SchemaTableProps { +// Document: Doc; // child doc +// dataDoc?: Doc; +// PanelHeight: () => number; +// PanelWidth: () => number; +// childDocs?: Doc[]; +// CollectionView: Opt<CollectionView>; +// ContainingCollectionView: Opt<CollectionView>; +// ContainingCollectionDoc: Opt<Doc>; +// fieldKey: string; +// renderDepth: number; +// deleteDocument?: (document: Doc | Doc[]) => boolean; +// addDocument?: (document: Doc | Doc[]) => boolean; +// moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; +// ScreenToLocalTransform: () => Transform; +// active: (outsideReaction: boolean | undefined) => boolean | undefined; +// onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void; +// addDocTab: (document: Doc, where: OpenWhere) => boolean; +// pinToPres: (document: Doc, pinProps: PinProps) => void; +// isSelected: (outsideReaction?: boolean) => boolean; +// isFocused: (document: Doc, outsideReaction: boolean) => boolean; +// setFocused: (document: Doc) => void; +// setPreviewDoc: (document: Opt<Doc>) => void; +// columns: SchemaHeaderField[]; +// documentKeys: any[]; +// headerIsEditing: boolean; +// openHeader: (column: any, screenx: number, screeny: number) => void; +// onClick: (e: React.MouseEvent) => void; +// onPointerDown: (e: React.PointerEvent) => void; +// onResizedChange: (newResized: Resize[], event: any) => void; +// setColumns: (columns: SchemaHeaderField[]) => void; +// reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => void; +// changeColumns: (oldKey: string, newKey: string, addNew: boolean) => void; +// setHeaderIsEditing: (isEditing: boolean) => void; +// changeColumnSort: (columnField: SchemaHeaderField, descending: boolean | undefined) => void; +// } + +// @observer +// export class SchemaTable extends React.Component<SchemaTableProps> { +// @observable _cellIsEditing: boolean = false; +// @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 }; +// @observable _openCollections: Set<number> = new Set(); + +// @observable _showDoc: Doc | undefined; +// @observable _showDataDoc: any = ''; +// @observable _showDocPos: number[] = []; + +// @observable _showTitleDropdown: boolean = false; + +// @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 - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); +// } + +// @computed get childDocs() { +// if (this.props.childDocs) return this.props.childDocs; + +// const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; +// return DocListCast(doc[this.props.fieldKey]); +// } +// set childDocs(docs: Doc[]) { +// const doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; +// doc[this.props.fieldKey] = new List<Doc>(docs); +// } + +// @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.props.columns.reduce((resized, shf) => { +// shf.width > -1 && resized.push({ id: shf.heading, value: shf.width }); +// return resized; +// }, [] as { id: string; value: number }[]); +// } +// @computed get sorted(): SortingRule[] { +// return this.props.columns.reduce((sorted, shf) => { +// shf.desc !== undefined && sorted.push({ id: shf.heading, desc: shf.desc }); +// return sorted; +// }, [] as SortingRule[]); +// } + +// @action +// changeSorting = (col: any) => { +// this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true); +// }; + +// @action +// changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown); + +// @computed get borderWidth() { +// return Number(COLLECTION_BORDER_WIDTH); +// } +// @computed get tableColumns(): Column<Doc>[] { +// const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); +// const columns: Column<Doc>[] = []; +// const tableIsFocused = this.props.isFocused(this.props.Document, false); +// const focusedRow = this._focusedCell.row; +// const focusedCol = this._focusedCell.col; +// const isEditable = !this.props.headerIsEditing; + +// columns.push({ +// expander: true, +// Header: '', +// width: 58, +// Expander: rowInfo => { +// return rowInfo.original.type !== DocumentType.COL ? null : ( +// <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}> +// <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" /> +// </div> +// ); +// }, +// }); +// columns.push( +// ...this.props.columns.map(col => { +// const icon: IconProp = +// this.getColumnType(col) === ColumnType.Number +// ? 'hashtag' +// : this.getColumnType(col) === ColumnType.String +// ? 'font' +// : this.getColumnType(col) === ColumnType.Boolean +// ? 'check-square' +// : this.getColumnType(col) === ColumnType.Doc +// ? 'file' +// : this.getColumnType(col) === ColumnType.Image +// ? 'image' +// : this.getColumnType(col) === ColumnType.List +// ? 'list-ul' +// : this.getColumnType(col) === ColumnType.Date +// ? 'calendar' +// : 'align-justify'; + +// const keysDropdown = ( +// <KeysDropdown +// keyValue={col.heading} +// possibleKeys={possibleKeys} +// existingKeys={this.props.columns.map(c => c.heading)} +// canAddNew={true} +// addNew={false} +// onSelect={this.props.changeColumns} +// setIsEditing={this.props.setHeaderIsEditing} +// docs={this.props.childDocs} +// Document={this.props.Document} +// dataDoc={this.props.dataDoc} +// fieldKey={this.props.fieldKey} +// ContainingCollectionDoc={this.props.ContainingCollectionDoc} +// ContainingCollectionView={this.props.ContainingCollectionView} +// active={this.props.active} +// openHeader={this.props.openHeader} +// icon={icon} +// col={col} +// // try commenting this out +// width={'100%'} +// /> +// ); + +// const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up'; +// const header = ( +// <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}> +// {keysDropdown} +// <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}> +// <FontAwesomeIcon icon={sortIcon} size="lg" /> +// </div> +// {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */} +// </div> +// ); + +// return { +// Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />, +// accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0), +// id: col.heading, +// Cell: (rowProps: CellInfo) => { +// const rowIndex = rowProps.index; +// const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!); +// const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; + +// const props: CellProps = { +// row: rowIndex, +// col: columnIndex, +// rowProps: rowProps, +// isFocused: isFocused, +// changeFocusedCellByIndex: this.changeFocusedCellByIndex, +// CollectionView: this.props.CollectionView, +// ContainingCollection: this.props.ContainingCollectionView, +// Document: this.props.Document, +// fieldKey: this.props.fieldKey, +// renderDepth: this.props.renderDepth, +// addDocTab: this.props.addDocTab, +// pinToPres: this.props.pinToPres, +// moveDocument: this.props.moveDocument, +// setIsEditing: this.setCellIsEditing, +// isEditable: isEditable, +// setPreviewDoc: this.props.setPreviewDoc, +// setComputed: this.setComputed, +// getField: this.getField, +// showDoc: this.showDoc, +// }; + +// switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) { +// case ColumnType.Number: +// return <CollectionSchemaNumberCell {...props} />; +// case ColumnType.String: +// return <CollectionSchemaStringCell {...props} />; +// case ColumnType.Boolean: +// return <CollectionSchemaCheckboxCell {...props} />; +// case ColumnType.Doc: +// return <CollectionSchemaDocCell {...props} />; +// case ColumnType.Image: +// return <CollectionSchemaImageCell {...props} />; +// case ColumnType.List: +// return <CollectionSchemaListCell {...props} />; +// case ColumnType.Date: +// return <CollectionSchemaDateCell {...props} />; +// default: +// return <CollectionSchemaCell {...props} />; +// } +// }, +// minWidth: 200, +// }; +// }) +// ); +// columns.push({ +// Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />, +// accessor: (doc: Doc) => 0, +// id: 'add', +// Cell: (rowProps: CellInfo) => { +// const rowIndex = rowProps.index; +// const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!); +// const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; +// return ( +// <CollectionSchemaButtons +// {...{ +// row: rowProps.index, +// col: columnIndex, +// rowProps: rowProps, +// isFocused: isFocused, +// changeFocusedCellByIndex: this.changeFocusedCellByIndex, +// CollectionView: this.props.CollectionView, +// ContainingCollection: this.props.ContainingCollectionView, +// Document: this.props.Document, +// fieldKey: this.props.fieldKey, +// renderDepth: this.props.renderDepth, +// addDocTab: this.props.addDocTab, +// pinToPres: this.props.pinToPres, +// moveDocument: this.props.moveDocument, +// setIsEditing: this.setCellIsEditing, +// isEditable: isEditable, +// setPreviewDoc: this.props.setPreviewDoc, +// setComputed: this.setComputed, +// getField: this.getField, +// showDoc: this.showDoc, +// }} +// /> +// ); +// }, +// width: 28, +// resizable: false, +// }); +// return columns; +// } + +// constructor(props: SchemaTableProps) { +// super(props); +// if (this.props.Document._schemaHeaders === undefined) { +// this.props.Document._schemaHeaders = new List<SchemaHeaderField>([ +// new SchemaHeaderField('title', '#f1efeb'), +// new SchemaHeaderField('author', '#f1efeb'), +// new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date), +// new SchemaHeaderField('text', '#f1efeb', ColumnType.String), +// new SchemaHeaderField('type', '#f1efeb'), +// new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc), +// ]); +// } +// } + +// componentDidMount() { +// document.addEventListener('keydown', this.onKeyDown); +// } + +// componentWillUnmount() { +// document.removeEventListener('keydown', this.onKeyDown); +// } + +// tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => { +// const tableDoc = this.props.Document[DataSym]; +// const effectiveAcl = GetEffectiveAcl(tableDoc); + +// if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) { +// doc.context = this.props.Document; +// tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())); +// return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); +// } +// return false; +// }; + +// private getTrProps: ComponentPropsGetterR = (state, rowInfo) => { +// return !rowInfo +// ? {} +// : { +// ScreenToLocalTransform: this.props.ScreenToLocalTransform, +// addDoc: this.tableAddDoc, +// removeDoc: this.props.deleteDocument, +// rowInfo, +// rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true), +// textWrapRow: this.toggleTextWrapRow, +// rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1, +// dropAction: StrCast(this.props.Document.childDropAction), +// addDocTab: this.props.addDocTab, +// }; +// }; + +// private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => { +// if (!rowInfo || column) return {}; + +// const row = rowInfo.index; +// //@ts-ignore +// const col = this.columns.map(c => c.heading).indexOf(column!.id); +// const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true); +// // TODO: editing border doesn't work :( +// return { +// style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' }, +// }; +// }; + +// @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing); + +// @action +// onKeyDown = (e: KeyboardEvent): void => { +// if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) { +// // && this.props.isSelected(true)) { +// const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : ''; +// this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col); + +// if (direction) { +// const pdoc = FieldValue(this.childDocs[this._focusedCell.row]); +// pdoc && this.props.setPreviewDoc(pdoc); +// e.stopPropagation(); +// } +// } else if (e.keyCode === 27) { +// this.props.setPreviewDoc(undefined); +// e.stopPropagation(); // stopPropagation for left/right arrows +// } +// }; + +// changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => { +// switch (direction) { +// case 'tab': +// return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 }; +// case 'right': +// return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 }; +// case 'left': +// return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 }; +// case 'up': +// return { row: curRow === 0 ? curRow : curRow - 1, col: curCol }; +// case 'down': +// return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol }; +// } +// return this._focusedCell; +// }; + +// @action +// changeFocusedCellByIndex = (row: number, col: number): void => { +// if (this._focusedCell.row !== row || this._focusedCell.col !== col) { +// this._focusedCell = { row: row, col: col }; +// } +// this.props.setFocused(this.props.Document); +// }; + +// @undoBatch +// createRow = action(() => { +// this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 })); +// this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col }; +// }); + +// @undoBatch +// @action +// createColumn = () => { +// const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`; +// for (let index = 0; index < 100; index++) { +// if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) { +// this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb')); +// break; +// } +// } +// }; + +// @action +// getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => { +// if (doc && field && column.type === ColumnType.Any) { +// const val = doc[CollectionSchemaCell.resolvedFieldKey(field, doc)]; +// if (val instanceof ImageField) return ColumnType.Image; +// if (val instanceof Doc) return ColumnType.Doc; +// if (val instanceof DateField) return ColumnType.Date; +// if (val instanceof List) return ColumnType.List; +// } +// if (column.type && column.type !== 0) { +// return column.type; +// } +// if (columnTypes.get(column.heading)) { +// return (column.type = columnTypes.get(column.heading)!); +// } +// return (column.type = ColumnType.Any); +// }; + +// @undoBatch +// @action +// toggleTextwrap = async () => { +// const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []); +// if (textwrappedRows.length) { +// this.props.Document.textwrappedSchemaRows = new List<string>([]); +// } else { +// const docs = DocListCast(this.props.Document[this.props.fieldKey]); +// const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]); +// this.props.Document.textwrappedSchemaRows = new List<string>(allRows); +// } +// }; + +// @action +// toggleTextWrapRow = (doc: Doc): void => { +// const textWrapped = this.textWrappedRows; +// const index = textWrapped.findIndex(id => doc[Id] === id); + +// index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]); + +// this.textWrappedRows = textWrapped; +// }; + +// @computed +// get reactTable() { +// const children = this.childDocs; +// const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false); +// const expanded: { [name: string]: any } = {}; +// Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true)); +// const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :(((( + +// return ( +// <ReactTable +// style={{ position: 'relative' }} +// data={children} +// page={0} +// pageSize={children.length} +// showPagination={false} +// columns={this.tableColumns} +// getTrProps={this.getTrProps} +// getTdProps={this.getTdProps} +// sortable={false} +// TrComponent={MovableRow} +// sorted={this.sorted} +// expanded={expanded} +// resized={this.resized} +// onResizedChange={this.props.onResizedChange} +// // if it has a child, render another table with the children +// SubComponent={ +// !hasCollectionChild +// ? undefined +// : row => +// row.original.type !== DocumentType.COL ? null : ( +// <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub"> +// <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /> +// </div> +// ) +// } +// /> +// ); +// } + +// onContextMenu = (e: React.MouseEvent): void => { +// ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' }); +// }; + +// getField = (row: number, col?: number) => { +// const docs = this.childDocs; + +// row = row % docs.length; +// while (row < 0) row += docs.length; +// const columns = this.props.columns; +// const doc = docs[row]; +// if (col === undefined) { +// return doc; +// } +// if (col >= 0 && col < columns.length) { +// const column = this.props.columns[col].heading; +// return doc[column]; +// } +// return undefined; +// }; + +// createTransformer = (row: number, col: number): Transformer => { +// const self = this; +// const captures: { [name: string]: Field } = {}; + +// const transformer: ts.TransformerFactory<ts.SourceFile> = context => { +// return root => { +// function visit(node: ts.Node) { +// node = ts.visitEachChild(node, visit, context); +// if (ts.isIdentifier(node)) { +// const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node; +// const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node; +// if (isntPropAccess && isntPropAssign) { +// if (node.text === '$r') { +// return ts.createNumericLiteral(row.toString()); +// } else if (node.text === '$c') { +// return ts.createNumericLiteral(col.toString()); +// } else if (node.text === '$') { +// if (ts.isCallExpression(node.parent)) { +// // captures.doc = self.props.Document; +// // captures.key = self.props.fieldKey; +// } +// } +// } +// } + +// return node; +// } +// return ts.visitNode(root, visit); +// }; +// }; + +// // const getVars = () => { +// // return { capturedVariables: captures }; +// // }; + +// return { transformer /*getVars*/ }; +// }; + +// setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => { +// script = `const $ = (row:number, col?:number) => { +// const rval = (doc as any)[key][row + ${row}]; +// return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading]; +// } +// return ${script}`; +// const compiled = CompileScript(script, { params: { this: Doc.name }, capturedVariables: { doc: this.props.Document, key: this.props.fieldKey }, typecheck: false, transformer: this.createTransformer(row, col) }); +// if (compiled.compiled) { +// doc[field] = new ComputedField(compiled); +// return true; +// } +// return false; +// }; + +// @action +// showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => { +// this._showDoc = doc; +// if (dataDoc && screenX && screenY) { +// this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY); +// } +// }; + +// onOpenClick = () => { +// this._showDoc && this.props.addDocTab(this._showDoc, OpenWhere.addRight); +// }; + +// getPreviewTransform = (): Transform => { +// return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth); +// }; + +// render() { +// const preview = ''; +// return ( +// <div +// className="collectionSchemaView-table" +// onPointerDown={this.props.onPointerDown} +// onClick={this.props.onClick} +// onWheel={e => this.props.active(true) && e.stopPropagation()} +// onDrop={e => this.props.onDrop(e, {})} +// onContextMenu={this.onContextMenu}> +// {this.reactTable} +// {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : ( +// <div className="collectionSchemaView-addRow" onClick={this.createRow}> +// + new +// </div> +// )} +// {!this._showDoc ? null : ( +// <div +// className="collectionSchemaView-documentPreview" +// ref="overlay" +// style={{ +// position: 'absolute', +// width: 150, +// height: 150, +// background: 'dimgray', +// display: 'block', +// top: 0, +// left: 0, +// transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`, +// }}> +// <DocumentView +// Document={this._showDoc} +// DataDoc={this._showDataDoc} +// styleProvider={DefaultStyleProvider} +// docViewPath={returnEmptyDoclist} +// focus={DocUtils.DefaultFocus} +// renderDepth={this.props.renderDepth} +// rootSelected={returnFalse} +// isContentActive={returnTrue} +// isDocumentActive={returnFalse} +// PanelWidth={() => 150} +// PanelHeight={() => 150} +// ScreenToLocalTransform={this.getPreviewTransform} +// docFilters={returnEmptyFilter} +// docRangeFilters={returnEmptyFilter} +// searchFilterDocs={returnEmptyDoclist} +// ContainingCollectionDoc={this.props.CollectionView?.props.Document} +// ContainingCollectionView={this.props.CollectionView} +// moveDocument={this.props.moveDocument} +// whenChildContentsActiveChanged={emptyFunction} +// addDocTab={this.props.addDocTab} +// pinToPres={this.props.pinToPres} +// bringToFront={returnFalse}></DocumentView> +// </div> +// )} +// </div> +// ); +// } +// } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 569579996..459554ebe 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -44,6 +44,7 @@ import { WebBox } from './WebBox'; import React = require('react'); import XRegExp = require('xregexp'); import { LoadingBox } from './LoadingBox'; +import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -268,6 +269,7 @@ export class DocumentContentsView extends React.Component< HTMLtag, ComparisonBox, LoadingBox, + SchemaRowBox, }} bindings={bindings} jsx={layoutFrame} |