aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx')
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx139
1 files changed, 70 insertions, 69 deletions
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index bf36b2668..5874364e0 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable no-use-before-define */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, Size, Type } from 'browndash-components';
import { action, computed, makeObservable, observable } from 'mobx';
@@ -7,36 +10,36 @@ import * as React from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import Select from 'react-select';
-import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils';
+import { ClientUtils, StopEvent, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field } from '../../../../fields/Doc';
import { RichTextField } from '../../../../fields/RichTextField';
-import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
+import { ColumnType } from '../../../../fields/SchemaHeaderField';
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { FInfo, FInfoFieldType } from '../../../documents/Documents';
-import { DocFocusOrOpen } from '../../../util/DocumentManager';
-import { dropActionType } from '../../../util/DragManager';
-import { SettingsManager } from '../../../util/SettingsManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, undoable } from '../../../util/UndoManager';
import { EditableView } from '../../EditableView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
-import { OpenWhere, returnEmptyDocViewList } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
export interface SchemaTableCellProps {
Document: Doc;
col: number;
deselectCell: () => void;
- selectCell: (doc: Doc, col: number) => void;
- selectedCell: () => [Doc, number] | undefined;
+ selectCell: (doc: Doc, col: number, shift: boolean, ctrl: boolean) => void;
+ selectedCells: () => Doc[] | undefined;
+ selectedCol: () => number;
fieldKey: string;
maxWidth?: () => number;
columnWidth: () => number;
@@ -45,6 +48,7 @@ export interface SchemaTableCellProps {
isRowActive: () => boolean | undefined;
getFinfo: (fieldKey: string) => FInfo | undefined;
setColumnValues: (field: string, value: string) => boolean;
+ setSelectedColumnValues: (field: string, value: string) => boolean;
oneLine?: boolean; // whether all input should fit on one line vs allowing textare multiline inputs
allowCRs?: boolean; // allow carriage returns in text input (othewrise CR ends the edit)
finishEdit?: () => void; // notify container that edit is over (eg. to hide view in DashFieldView)
@@ -55,6 +59,14 @@ export interface SchemaTableCellProps {
rootSelected?: () => boolean;
}
+function selectedCell(props: SchemaTableCellProps) {
+ return (
+ props.isRowActive() &&
+ props.selectedCol() === props.col && //
+ props.selectedCells()?.filter(d => d === props.Document)?.length
+ );
+}
+
@observer
export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> {
constructor(props: SchemaTableCellProps) {
@@ -62,8 +74,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
makeObservable(this);
}
- static addFieldDoc = (doc: Doc, where: OpenWhere) => {
- DocFocusOrOpen(doc);
+ static addFieldDoc = (docs: Doc | Doc[] /* , where: OpenWhere */) => {
+ DocumentView.FocusOrOpen(toList(docs)[0]);
return true;
};
public static renderProps(props: SchemaTableCellProps) {
@@ -78,7 +90,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
doc = DocCast(doc.proto);
}
const parenCount = Math.max(0, protoCount - 1);
- const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue';
+ const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells
const textDecoration = color !== 'black' && parenCount ? 'underline' : '';
const fieldProps: FieldViewProps = {
childFilters: returnEmptyFilter,
@@ -110,11 +122,6 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return { color, textDecoration, fieldProps, cursor, pointerEvents };
}
- @computed get selected() {
- const selected: [Doc, number] | undefined = this._props.selectedCell();
- return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
- }
-
@computed get defaultCellContent() {
const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props);
@@ -128,12 +135,12 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
pointerEvents,
}}>
<EditableView
- ref={r => this.selected && this._props.autoFocus && r?.setIsFocused(true)}
+ ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
oneLine={this._props.oneLine}
allowCRs={this._props.allowCRs}
contents={undefined}
fieldContents={fieldProps}
- editing={this.selected ? undefined : false}
+ editing={selectedCell(this._props) ? undefined : false}
GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)}
SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
@@ -141,7 +148,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
this._props.finishEdit?.();
return true;
}
- const ret = KeyValueBox.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined);
+ const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined);
this._props.finishEdit?.();
return ret;
}, 'edit schema cell')}
@@ -153,30 +160,27 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
get getCellType() {
const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
const cellValue = this._props.Document[this._props.fieldKey];
+
if (cellValue instanceof ImageField) return ColumnType.Image;
if (cellValue instanceof DateField) return ColumnType.Date;
if (cellValue instanceof RichTextField) return ColumnType.RTF;
if (typeof cellValue === 'number') return ColumnType.Any;
if (typeof cellValue === 'string' && columnTypeStr !== FInfoFieldType.enumeration) return ColumnType.Any;
if (typeof cellValue === 'boolean') return ColumnType.Boolean;
-
- if (columnTypeStr && columnTypeStr in FInfotoColType) {
- return FInfotoColType[columnTypeStr];
- }
+ if (columnTypeStr && columnTypeStr in FInfotoColType) return FInfotoColType[columnTypeStr];
return ColumnType.Any;
}
get content() {
- const cellType: ColumnType = this.getCellType;
// prettier-ignore
- switch (cellType) {
- case ColumnType.Image: return <SchemaImageCell {...this._props} />;
- case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />;
- case ColumnType.RTF: return <SchemaRTFCell {...this._props} />;
- case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => val.toString())} />;
- case ColumnType.Date: return <SchemaDateCell {...this._props} />;
- default: return this.defaultCellContent;
+ switch (this.getCellType) {
+ case ColumnType.Image: return <SchemaImageCell {...this._props} />;
+ case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />;
+ case ColumnType.RTF: return <SchemaRTFCell {...this._props} />;
+ case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => Field.toString(val))} />;
+ case ColumnType.Date: return <SchemaDateCell {...this._props} />;
+ default: return this.defaultCellContent;
}
}
@@ -184,8 +188,18 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return (
<div
className="schema-table-cell"
- onPointerDown={action(e => !this.selected && this._props.selectCell(this._props.Document, this._props.col))}
- style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+ onContextMenu={e => StopEvent(e)}
+ onPointerDown={action(e => {
+ const shift: boolean = e.shiftKey;
+ const ctrl: boolean = e.ctrlKey;
+ if (this._props.isRowActive?.() !== false) {
+ if (selectedCell(this._props) && ctrl) {
+ this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ e.stopPropagation();
+ } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ }
+ })}
+ style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
{this.content}
</div>
);
@@ -204,8 +218,8 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
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
+ if (url.href.indexOf(window.location.origin) === -1) return ClientUtils.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);
@@ -220,7 +234,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
.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 url = paths.length ? paths : [ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
return url[0];
}
@@ -244,7 +258,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
};
@action
- removeHoverPreview = (e: React.PointerEvent) => {
+ removeHoverPreview = () => {
if (!this._previewRef) return;
document.body.removeChild(this._previewRef);
};
@@ -256,7 +270,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined;
const width = height ? height * aspect : undefined; // increase the width of the image if necessary to maintain proportionality
- return <img src={this.url} width={width ? width : undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
+ return <img src={this.url} width={width || undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
}
}
@@ -280,26 +294,26 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
// } else {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
date && (this._props.Document[this._props.fieldKey] = new DateField(date));
- //}
+ // }
}, 'date change');
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
+ const { pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<>
<div style={{ pointerEvents: 'none' }}>
- <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={e => {}} />
+ <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} />
</div>
{pointerEvents === 'none' ? null : (
<Popup
icon={<FontAwesomeIcon size="sm" icon="caret-down" />}
size={Size.XSMALL}
type={Type.TERT}
- color={SettingsManager.userColor}
- background={SettingsManager.userBackgroundColor}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
popup={
<div style={{ width: 'fit-content', height: '200px' }}>
- <DatePicker open={true} dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
+ <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
</div>
}
/>
@@ -315,19 +329,14 @@ export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps
makeObservable(this);
}
- @computed get selected() {
- const selected: [Doc, number] | undefined = this._props.selectedCell();
- return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
- }
-
// if the text box blurs and none of its contents are focused(), then the edit finishes
- selectedFunc = () => this.selected;
+ selectedFunc = () => !!selectedCell(this._props);
render() {
const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
fieldProps.isContentActive = this.selectedFunc;
return (
- <div className="schemaRTFCell" style={{ fontStyle: this.selected ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox {...fieldProps} autoFocus={true} onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ <div className="schemaRTFCell" style={{ fontStyle: selectedCell(this._props) ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}>
+ {selectedCell(this._props) ? <FormattedTextBox {...fieldProps} autoFocus onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
@@ -339,10 +348,6 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
makeObservable(this);
}
- @computed get selected() {
- const selected: [Doc, number] | undefined = this._props.selectedCell();
- return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
- }
render() {
const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
@@ -353,14 +358,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
checked={BoolCast(this._props.Document[this._props.fieldKey])}
onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
if ((value?.nativeEvent as any).shiftKey) {
- this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
- } else KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
+ this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
+ } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
})}
/>
<EditableView
contents={undefined}
fieldContents={fieldProps}
- editing={this.selected ? undefined : false}
+ editing={selectedCell(this._props) ? undefined : false}
GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
@@ -368,7 +373,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
this._props.finishEdit?.();
return true;
}
- const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+ const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
this._props.finishEdit?.();
return set;
})}
@@ -384,12 +389,8 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
makeObservable(this);
}
- @computed get selected() {
- const selected: [Doc, number] | undefined = this._props.selectedCell();
- return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
- }
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
+ const { color, textDecoration, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
const options = this._props.options?.map(facet => ({ value: facet, label: facet }));
return (
<div className="schemaSelectionCell" style={{ color, textDecoration, cursor, pointerEvents }}>
@@ -434,7 +435,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')}
options={options}
isMulti={false}
- onChange={val => KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+ onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
/>
</div>
</div>