import React = require('react'); import { observer } from 'mobx-react'; import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; import { Transform } from '../../../util/Transform'; import { EditableView } from '../../EditableView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { action, computed, observable } from 'mobx'; import { extname } from 'path'; import { Cast, DateCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { DateField } from '../../../../fields/DateField'; import DatePicker from 'react-datepicker'; import { Colors } from '../../global/globalEnums'; import { SelectionManager } from '../../../util/SelectionManager'; export interface SchemaTableCellProps { Document: Doc; col: number; schemaView: CollectionSchemaView | undefined; fieldKey: string; columnWidth: number; isRowActive: () => boolean | undefined; setColumnValues: (field: string, value: string) => boolean; } @observer export class SchemaTableCell extends React.Component { private _editorRef: EditableView | null = null; @computed get readOnly() { return this.props.schemaView?.fieldInfos[this.props.fieldKey]?.readOnly ?? false; } @computed get selected() { const selected: [Doc, number] | undefined = this.props.schemaView?._selectedCell; return this.props.isRowActive() && selected && selected[0] == this.props.Document && selected[1] == this.props.col; } componentDidUpdate() { if (!this.selected) { this._editorRef?.setIsFocused(false); } } get defaultCellContent() { const props: FieldViewProps = { Document: this.props.Document, docFilters: returnEmptyFilter, docRangeFilters: returnEmptyFilter, searchFilterDocs: returnEmptyDoclist, styleProvider: DefaultStyleProvider, docViewPath: returnEmptyDoclist, fieldKey: this.props.fieldKey, rootSelected: returnFalse, isSelected: returnFalse, setHeight: returnFalse, select: emptyFunction, dropAction: 'alias', bringToFront: emptyFunction, renderDepth: 1, isContentActive: returnFalse, whenChildContentsActiveChanged: emptyFunction, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, PanelWidth: () => this.props.columnWidth, PanelHeight: () => CollectionSchemaView._rowHeight, addDocTab: returnFalse, pinToPres: returnZero, }; return (
(this._editorRef = ref)} contents={} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { this.props.setColumnValues(this.props.fieldKey, value); } return KeyValueBox.SetField(this.props.Document, this.props.fieldKey, value); }} editing={this.selected ? undefined : false} />
); } get getCellType() { const columnTypeStr = this.props.schemaView?.fieldInfos[this.props.fieldKey]?.fieldType; if (columnTypeStr) { if (columnTypeStr in FInfotoColType) { return FInfotoColType[columnTypeStr]; } return ColumnType.Any; } const cellValue = this.props.Document[this.props.fieldKey]; if (cellValue instanceof ImageField) return ColumnType.Image; if (cellValue instanceof DateField) return ColumnType.Date; return ColumnType.Any; } get content() { const cellType: ColumnType = this.getCellType; switch (cellType) { case ColumnType.Image: return ; case ColumnType.Date: // return ; default: return this.defaultCellContent; } } render() { return (
{ if (!this.selected) this.props.schemaView?.selectCell(this.props.Document, this.props.col); })} style={{ width: this.props.columnWidth, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}> {this.content}
); } } // mj: most of this is adapted from old schema code so I'm not sure what it does tbh @observer export class SchemaImageCell extends React.Component { @observable _previewRef: HTMLImageElement | undefined; 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, '_s' + ext); } get url() { const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc const alts = DocListCast(this.props.Document[this.props.fieldKey + '-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')]; return url[0]; } @action showHoverPreview = (e: React.PointerEvent) => { this._previewRef = document.createElement('img'); document.body.appendChild(this._previewRef); const ext = extname(this.url); this._previewRef.src = this.url.replace('_s' + ext, '_m' + ext); this._previewRef.style.position = 'absolute'; this._previewRef.style.left = e.clientX + 10 + 'px'; this._previewRef.style.top = e.clientY + 10 + 'px'; this._previewRef.style.zIndex = '1000'; }; @action moveHoverPreview = (e: React.PointerEvent) => { if (!this._previewRef) return; this._previewRef.style.left = e.clientX + 10 + 'px'; this._previewRef.style.top = e.clientY + 10 + 'px'; }; @action removeHoverPreview = (e: React.PointerEvent) => { if (!this._previewRef) return; document.body.removeChild(this._previewRef); }; render() { const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px // const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px const height = CollectionSchemaView._rowHeight - 10; const width = height * aspect; // increase the width of the image if necessary to maintain proportionality return ; } } @observer export class SchemaDateCell extends React.Component { @observable _pickingDate: boolean = false; @computed get date(): DateField { // if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined. return DateCast(this.props.Document[this.props.fieldKey]); } @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.props.Document[this.props.fieldKey] = new DateField(date as Date); //} }; render() { return this.handleChange(date)} />; } }