// 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; // // currently unused // ContainingCollection: Opt; // 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 { // // 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(); // 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 => { // // 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( // // {contents?.slice(0, positions[0])} // // ); // positions.forEach((num, cur) => { // results.push( // // {contents?.slice(num, num + length)} // // ); // let end = 0; // cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]); // results.push( // // {contents?.slice(num + length, end)} // // ); // }); // return results; // } // return {contents ? contents?.valueOf() : 'undefined'}; // } // @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 = 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 ( //
(this._isEditing = true))} // onPointerEnter={onPointerEnter} // onPointerLeave={onPointerLeave}> //
//
// {!this.props.Document._searchDoc ? ( // { // 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) // )} //
//
//
// ); // } // 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 { // // 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 ? ( // {this._date ? Field.toString(this._date as Field) : '--'} // ) : ( // 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(, { 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') // ) : ( //
//
// StrCast(this._doc?.title)} // SetValue={action((value: string) => { // this.onSetValue(value); // return true; // })} // /> //
//
this._doc && this.props.addDocTab(this._doc, OpenWhere.addRight)} className="collectionSchemaView-cellContents-docButton"> // //
//
// ); // } // } // @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(); // return ( //
//
// //
//
// ); // } // } // @observer // export class CollectionSchemaListCell extends CollectionSchemaCell { // _overlayDisposer?: () => void; // @computed get _field() { // return this._rowDoc[this.renderFieldKey]; // } // @computed get _optionsList() { // return this._field as List; // } // @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).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(, { x: 0, y: 0 }); // }; // render() { // const link = false; // const reference = React.createRef(); // // if the list is not opened, don't display it; otherwise, do. // if (this._optionsList?.length) { // const options = !this._opened ? null : ( //
// {this._optionsList.map((element, index) => { // const val = Field.toString(element); // return ( //
this.onSelected(StrCast(element), index)}> // {val} //
// ); // })} //
// ); // const plainText =
{this._text}
; // const textarea = ( //
// this._text} // SetValue={action((value: string) => { // // add special for params // this.onSetValue(value); // return true; // })} // /> //
// ); // //☰ // return ( //
//
//
// //
{link ? plainText : textarea}
//
// {options} //
//
// ); // } // return this.renderCellWithType('list'); // } // } // @observer // export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { // @computed get _isChecked() { // return BoolCast(this._rowDoc[this.renderFieldKey]); // } // render() { // const reference = React.createRef(); // return ( //
// (this._rowDoc[this.renderFieldKey] = e.target.checked)} /> //
// ); // } // } // @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) ? ( // <> // ) : ( //
// // //
// ); // } // }