import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { RichTextField } from '../../../fields/RichTextField'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import './CollectionNoteTakingView.scss'; import { listSpec } from '../../../fields/Schema'; import { Cast } from '../../../fields/Types'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; // So this is how we are storing a column interface CSVFieldColumnProps { Document: Doc; DataDoc: Opt; docList: Doc[]; heading: string; pivotField: string; chromeHidden?: boolean; columnHeaders: SchemaHeaderField[] | undefined; headingObject: SchemaHeaderField | undefined; yMargin: number; // columnWidth: number; numGroupColumns: number; gridGap: number; type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined; headings: () => object[]; renderChildren: (docs: Doc[]) => JSX.Element[]; addDocument: (doc: Doc | Doc[]) => boolean; createDropTarget: (ele: HTMLDivElement) => void; screenToLocalTransform: () => Transform; observeHeight: (myref: any) => void; unobserveHeight: (myref: any) => void; //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: ) editableViewProps: () => any; resizeColumns: (n: number) => void; columnStartXCoords: number[]; PanelWidth: number; maxColWidth: number; // docsByColumnHeader: Map // setDocsForColHeader: (key: string, docs: Doc[]) => void } @observer export class CollectionNoteTakingViewColumn extends React.Component { @observable private _background = 'inherit'; @computed get columnWidth() { // base cases if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length == 1) { return this.props.maxColWidth; } const i = this.props.columnHeaders.indexOf(this.props.headingObject); if (i < 0 || i > this.props.columnStartXCoords.length - 1) { return this.props.maxColWidth; } const endColValue = i == this.props.numGroupColumns - 1 ? this.props.PanelWidth : this.props.columnStartXCoords[i + 1]; // TODO make the math work here. 35 is half of 70, which is the current width of the divider return endColValue - this.props.columnStartXCoords[i] - 30; } private dropDisposer?: DragManager.DragDropDisposer; private _headerRef: React.RefObject = React.createRef(); @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading; @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb'; _ele: HTMLElement | null = null; // This is likely similar to what we will be doing. Why do we need to make these refs? // is that the only way to have drop targets? createColumnDropRef = (ele: HTMLDivElement | null) => { this.dropDisposer?.(); if (ele) { this._ele = ele; this.props.observeHeight(ele); this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this)); } }; componentWillUnmount() { this.props.unobserveHeight(this._ele); } @undoBatch columnDrop = action((e: Event, de: DragManager.DropEvent) => { const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) }; drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false)); }); getValue = (value: string): any => { const parsed = parseInt(value); if (!isNaN(parsed)) return parsed; if (value.toLowerCase().indexOf('true') > -1) return true; if (value.toLowerCase().indexOf('false') > -1) return false; return value; }; @action headingChanged = (value: string, shiftDown?: boolean) => { const castedValue = this.getValue(value); if (castedValue) { if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) { return false; } this.props.docList.forEach(d => (d[this.props.pivotField] = castedValue)); if (this.props.headingObject) { this.props.headingObject.setHeading(castedValue.toString()); this._heading = this.props.headingObject.heading; } return true; } return false; }; @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4'); @action pointerLeave = () => (this._background = 'inherit'); textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true); @action addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => { if (!value && !forceEmptyNote) return false; const key = this.props.pivotField; const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true }); const colValue = this.getValue(this.props.heading); newDoc[key] = colValue; FormattedTextBox.SelectOnLoad = newDoc[Id]; FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this.props.addDocument?.(newDoc) || false; }; @undoBatch @action deleteColumn = () => { const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); if (columnHeaders && this.props.headingObject) { const index = columnHeaders.indexOf(this.props.headingObject); this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset')); columnHeaders.splice(index, 1); } }; menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); const layoutItems: ContextMenuProps[] = []; const docItems: ContextMenuProps[] = []; const dataDoc = this.props.DataDoc || this.props.Document; const pivotValue = this.getValue(this.props.heading); DocUtils.addDocumentCreatorMenuItems( doc => { const key = this.props.pivotField; doc[key] = this.getValue(this.props.heading); FormattedTextBox.SelectOnLoad = doc[Id]; return this.props.addDocument?.(doc); }, this.props.addDocument, x, y, true, this.props.pivotField, pivotValue ); Array.from(Object.keys(Doc.GetProto(dataDoc))) .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string') .map(fieldKey => docItems.push({ description: ':' + fieldKey, event: () => { const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document)); if (created) { if (this.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.Document); } return this.props.addDocument?.(created); } }, icon: 'compress-arrows-alt', }) ); Array.from(Object.keys(Doc.GetProto(dataDoc))) .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length) .map(fieldKey => docItems.push({ description: ':' + fieldKey, event: () => { const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey }); if (created) { const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document; if (container.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, container); return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); } return this.props.addDocument?.(created) || false; } }, icon: 'compress-arrows-alt', }) ); !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' }); !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' }); ContextMenu.Instance.setDefaultItem('::', (name: string): void => { Doc.GetProto(this.props.Document)[name] = ''; const created = Docs.Create.TextDocument('', { title: name, _width: 250, _autoHeight: true }); if (created) { if (this.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.Document); } this.props.addDocument?.(created); } }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; @computed get innards() { TraceMobx(); const key = this.props.pivotField; const heading = this._heading; const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin; const evContents = heading ? heading : '25'; const headingView = this.props.headingObject ? (
evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
) : null; // const templatecols = `${this.props.columnWidth / this.props.numGroupColumns}px `; const templatecols = `${this.columnWidth}px `; const type = this.props.Document.type; return ( <> {headingView} {
{this.props.renderChildren(this.props.docList)}
{!this.props.chromeHidden && type !== DocumentType.PRES ? (
style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
{this.props.columnHeaders?.length && this.props.columnHeaders.length > 1 && ( )}
) : null}
} ); } render() { TraceMobx(); const heading = this._heading; return (
{this.innards}
); } }