aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
diff options
context:
space:
mode:
authorsrichman333 <sarah_n_richman@brown.edu>2023-06-14 17:23:23 -0400
committersrichman333 <sarah_n_richman@brown.edu>2023-06-14 17:23:23 -0400
commitf0474c18d092f4db49255a1e92d7f052b7398897 (patch)
tree1e26ccaf42dec4d99904e2eddb36dff6f3b55948 /src/client/views/collections/collectionSchema/SchemaTableCell.tsx
parent20d217d825891cf29a432a048d1f8e7bc04d062a (diff)
parentbf1198fbe73847087b1ec8e00a43306816b3508a (diff)
Merge branch 'master' into collaboration-sarah
Diffstat (limited to 'src/client/views/collections/collectionSchema/SchemaTableCell.tsx')
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx109
1 files changed, 82 insertions, 27 deletions
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 712bd4491..97264508c 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,26 +1,28 @@
-import React = require('react');
+import * as React from 'react';
+import Select, { MenuPlacement } from 'react-select';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
import DatePicker from 'react-datepicker';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field } from '../../../../fields/Doc';
-import { BoolCast, Cast, DateCast, FieldValue } from '../../../../fields/Types';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
import { FInfo } from '../../../documents/Documents';
-import { dropActionType } from '../../../util/DragManager';
+import { DocFocusOrOpen } from '../../../util/DocumentManager';
import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
+import { OpenWhere } from '../../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { KeyValueBox } from '../../nodes/KeyValueBox';
import { DefaultStyleProvider } from '../../StyleProvider';
import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
-import { RichTextField } from '../../../../fields/RichTextField';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
export interface SchemaTableCellProps {
Document: Doc;
@@ -29,17 +31,27 @@ export interface SchemaTableCellProps {
selectCell: (doc: Doc, col: number) => void;
selectedCell: () => [Doc, number] | undefined;
fieldKey: string;
+ maxWidth?: () => number;
columnWidth: () => number;
+ rowHeight: () => number;
+ padding?: number; // default is 5 -- see scss
isRowActive: () => boolean | undefined;
getFinfo: (fieldKey: string) => FInfo | undefined;
setColumnValues: (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)
+ options?: string[];
+ menuTarget: HTMLDivElement | null;
+ transform: () => Transform;
}
@observer
export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
- public static colRowHeightFunc() {
- return CollectionSchemaView._rowHeight;
- }
+ static addFieldDoc = (doc: Doc, where: OpenWhere) => {
+ DocFocusOrOpen(doc);
+ return true;
+ };
public static renderProps(props: SchemaTableCellProps) {
const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props;
let protoCount = 0;
@@ -49,14 +61,14 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
break;
}
protoCount++;
- doc = doc.proto;
+ doc = DocCast(doc.proto);
}
const parenCount = Math.max(0, protoCount - 1);
const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue';
const textDecoration = color !== 'black' && parenCount ? 'underline' : '';
const fieldProps: FieldViewProps = {
- docFilters: returnEmptyFilter,
- docRangeFilters: returnEmptyFilter,
+ childFilters: returnEmptyFilter,
+ childFiltersByRanges: returnEmptyFilter,
searchFilterDocs: returnEmptyDoclist,
styleProvider: DefaultStyleProvider,
docViewPath: returnEmptyDoclist,
@@ -64,19 +76,19 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
isSelected: returnFalse,
setHeight: returnFalse,
select: emptyFunction,
- dropAction: 'alias' as dropActionType,
+ dropAction: 'embed',
bringToFront: emptyFunction,
renderDepth: 1,
isContentActive: returnFalse,
whenChildContentsActiveChanged: emptyFunction,
ScreenToLocalTransform: Transform.Identity,
focus: emptyFunction,
- addDocTab: returnFalse,
+ addDocTab: SchemaTableCell.addFieldDoc,
pinToPres: returnZero,
Document,
- fieldKey,
+ fieldKey: fieldKey,
PanelWidth: columnWidth,
- PanelHeight: SchemaTableCell.colRowHeightFunc,
+ PanelHeight: props.rowHeight,
};
const readOnly = getFinfo(fieldKey)?.readOnly ?? false;
const cursor = !readOnly ? 'text' : 'default';
@@ -98,32 +110,37 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
style={{
color,
textDecoration,
+ width: '100%',
}}>
<EditableView
+ oneLine={this.props.oneLine}
+ allowCRs={this.props.allowCRs}
contents={<FieldView {...fieldProps} />}
editing={this.selected ? undefined : false}
GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
- SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
+ SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
}
- return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
- })}
+ const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+ this.props.finishEdit?.();
+ return ret;
+ }, 'edit schema cell')}
/>
</div>
);
}
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') return ColumnType.Any;
- if (typeof cellValue === 'boolean') return ColumnType.Any;
+ if (typeof cellValue === 'string' && columnTypeStr !== 'enumeration') return ColumnType.Any;
+ if (typeof cellValue === 'boolean') return ColumnType.Boolean;
- const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
if (columnTypeStr && columnTypeStr in FInfotoColType) {
return FInfotoColType[columnTypeStr];
}
@@ -138,6 +155,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
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;
}
@@ -148,7 +166,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
<div
className="schema-table-cell"
onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))}
- style={{ width: this.props.columnWidth(), border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+ style={{ padding: this.props.padding, maxWidth: this.props.maxWidth?.(), width: this.props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
{this.content}
</div>
);
@@ -211,8 +229,8 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
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
+ 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} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
}
@@ -254,7 +272,7 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
fieldProps.isContentActive = this.selectedFunc;
return (
<div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox {...fieldProps} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ {this.selected ? <FormattedTextBox allowScroll={true} {...fieldProps} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
@@ -288,10 +306,47 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
if (shiftDown && enterKey) {
this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
}
- return KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+ const set = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
+ this.props.finishEdit?.();
+ return set;
})}
/>
</div>
);
}
}
+@observer
+export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> {
+ @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 options = this.props.options?.map(facet => ({ value: facet, label: facet }));
+ return (
+ <div className="schemaSelectionCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
+ <div style={{ width: '100%' }}>
+ <Select
+ styles={{
+ menuPortal: base => ({
+ ...base,
+ left: 0,
+ top: 0,
+ transform: `translate(${this.props.transform().TranslateX}px, ${this.props.transform().TranslateY}px)`,
+ width: Number(base.width) * this.props.transform().Scale,
+ zIndex: 9999,
+ }),
+ }}
+ menuPortalTarget={this.props.menuTarget}
+ menuPosition={'absolute'}
+ 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 ?? ''}"`)}
+ />
+ </div>
+ </div>
+ );
+ }
+}