import React = require('react'); import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTransparent, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils'; import { Docs, DocUtils } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SelectionManager } from '../../../util/SelectionManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; import { DocComponentView, DocFocusOptions, DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptField } from '../../../../fields/ScriptField'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { IconButton } from 'browndash-components'; import { KeyValueBox } from '../../nodes/KeyValueBox'; export enum ColumnType { Number, String, Boolean, Doc, Image, } const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', 'text']; @observer export class CollectionSchemaView extends CollectionSubView() { private _ref: HTMLDivElement | null = null; private _selectedDocSortedArray: Doc[] = []; private _closestDropIndex: number = 0; private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; public static _rowHeight: number = 40; public static _minColWidth: number = 25; public static _rowMenuWidth: number = 100; public static _previewDividerWidth: number = 4; @observable _lastSelectedRow: number | undefined; @observable _selectedDocs: ObservableSet = new ObservableSet(); @observable _rowEles: ObservableMap = new ObservableMap(); @observable _colEles: HTMLDivElement[] = []; @observable _isDragging: boolean = false; @observable _displayColumnWidths: number[] | undefined; @observable _columnMenuIndex: number | undefined; @observable _menuOptions: string[] = []; @observable _newFieldWarning: string = ''; @observable _makeNewField: boolean = false; @observable _newFieldDefault: any = 0; @observable _newFieldType: ColumnType = ColumnType.Number; @observable _menuValue: string = ''; @observable _filterColumnIndex: number | undefined; @observable _filterValue: string = ''; get documentKeys() { const docs = this.childDocs; const keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu // is displayed (unlikely) it won't show up until something else changes. //TODO Types untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))))); // this.columns.forEach(key => (keys[key.heading] = true)); return Array.from(Object.keys(keys)); } @computed get previewWidth() { return NumCast(this.layoutDoc.schemaPreviewWidth); } @computed get tableWidth() { return this.props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth); } @computed get columnKeys() { return Cast(this.layoutDoc.columnKeys, listSpec('string'), defaultColumnKeys); } @computed get storedColumnWidths() { let widths = Cast( this.layoutDoc.columnWidths, listSpec('number'), this.columnKeys.map(() => (this.tableWidth - CollectionSchemaView._rowMenuWidth) / this.columnKeys.length) ); const totalWidth = widths.reduce((sum, width) => sum + width, 0); if (totalWidth !== this.tableWidth - CollectionSchemaView._rowMenuWidth) { widths = widths.map(w => { const proportion = w / totalWidth; return proportion * (this.tableWidth - CollectionSchemaView._rowMenuWidth); }); } return widths; } @computed get displayColumnWidths() { return this._displayColumnWidths ?? this.storedColumnWidths; } @computed get sortField() { return StrCast(this.layoutDoc.sortField); } @computed get sortDesc() { return BoolCast(this.layoutDoc.sortDesc); } rowIndex(doc: Doc) { return this.childDocs.indexOf(doc); } componentDidMount() { this.props.setContentView?.(this as DocComponentView); document.addEventListener('keydown', this.onKeyDown); } componentWillUnmount() { document.removeEventListener('keydown', this.onKeyDown); } @action onKeyDown = (e: KeyboardEvent) => { if (this._selectedDocs.size > 0) { if (e.key == 'ArrowDown') { const lastDoc = Array.from(this._selectedDocs.values()).lastElement(); const lastIndex = this.rowIndex(lastDoc); if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) { !e.shiftKey && this.clearSelection(); const newDoc = this.childDocs[lastIndex + 1]; this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1); } } if (e.key == 'ArrowUp') { const firstDoc = Array.from(this._selectedDocs.values())[0]; const firstIndex = this.rowIndex(firstDoc); if (firstIndex > 0 && firstIndex < this.childDocs.length) { !e.shiftKey && this.clearSelection(); const newDoc = this.childDocs[firstIndex - 1]; this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1); } } } }; @undoBatch @action setSort = (field: string | undefined, desc: boolean = false) => { this.layoutDoc.sortField = field; this.layoutDoc.sortDesc = desc; if (field === undefined) return; this.childDocs.sort((docA, docB) => { const aStr = Field.toString(docA[field] as Field); const bStr = Field.toString(docB[field] as Field); var out = 0; if (aStr < bStr) out = -1; if (aStr > bStr) out = 1; if (desc) out *= -1; return out; }); }; addRow = (doc: Doc | Doc[]) => { const result: boolean = this.addDocument(doc); this.setSort(this.sortField, this.sortDesc); return result; }; @undoBatch @action changeColumnKey = (index: number, newKey: string, defaultVal?: any) => { if (!this.documentKeys.includes(newKey)) { this.addNewKey(newKey, defaultVal); } let currKeys = [...this.columnKeys]; currKeys[index] = newKey; this.layoutDoc.columnKeys = new List(currKeys); }; @undoBatch @action addColumn = (key: string, defaultVal?: any) => { if (!this.documentKeys.includes(key)) { this.addNewKey(key, defaultVal); } const newColWidth = this.tableWidth / (this.storedColumnWidths.length + 1); const currWidths = this.storedColumnWidths.slice(); currWidths.splice(0, 0, newColWidth); const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); this.layoutDoc.columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); let currKeys = this.columnKeys.slice(); currKeys.splice(0, 0, key); this.layoutDoc.columnKeys = new List(currKeys); }; @action addNewKey = (key: string, defaultVal: any) => { this.childDocs.forEach(doc => (doc[key] = defaultVal)); }; @undoBatch @action removeColumn = (index: number) => { if (this.columnKeys.length === 1) return; const currWidths = this.storedColumnWidths.slice(); currWidths.splice(index, 1); const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0); this.layoutDoc.columnWidths = new List(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth))); let currKeys = this.columnKeys.slice(); currKeys.splice(index, 1); this.layoutDoc.columnKeys = new List(currKeys); }; @action startResize = (e: any, index: number) => { this._displayColumnWidths = this.storedColumnWidths; setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index), this.finishResize, emptyFunction); }; @action resizeColumn = (e: PointerEvent, index: number) => { if (this._displayColumnWidths) { let shrinking; let growing; let change = e.movementX; if (index !== 0) { growing = change < 0 ? index : index - 1; shrinking = change < 0 ? index - 1 : index; } if (shrinking === undefined || growing === undefined) return true; change = Math.abs(change); if (this._displayColumnWidths[shrinking] - change < CollectionSchemaView._minColWidth) { change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth; } this._displayColumnWidths[shrinking] -= change * this.props.ScreenToLocalTransform().Scale; this._displayColumnWidths[growing] += change * this.props.ScreenToLocalTransform().Scale; return false; } return true; }; @action finishResize = () => { this.layoutDoc.columnWidths = new List(this._displayColumnWidths); this._displayColumnWidths = undefined; }; @undoBatch @action swapColumns = (index1: number, index2: number) => { const tempKey = this.columnKeys[index1]; const tempWidth = this.storedColumnWidths[index1]; let currKeys = this.columnKeys; currKeys[index1] = currKeys[index2]; currKeys[index2] = tempKey; this.layoutDoc.columnKeys = new List(currKeys); let currWidths = this.storedColumnWidths; currWidths[index1] = currWidths[index2]; currWidths[index2] = tempWidth; this.layoutDoc.columnWidths = new List(currWidths); }; @action dragColumn = (e: PointerEvent, index: number) => { const dragData = new DragManager.ColumnDragData(index); const dragEles = [this._colEles[index]]; this.childDocs.forEach(doc => { dragEles.push(this._rowEles.get(doc).children[1].children[index]); }); DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y); return true; }; @action addRowRef = (doc: Doc, ref: HTMLDivElement) => { this._rowEles.set(doc, ref); }; @action setColRef = (index: number, ref: HTMLDivElement) => { if (this._colEles.length <= index) { this._colEles.push(ref); } else { this._colEles[index] = ref; } }; @action addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => { this._selectedDocs.add(doc); const rowDocView = DocumentManager.Instance.getDocumentView(doc); if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection); this._lastSelectedRow = index; }; @action removeDocFromSelection = (doc: Doc) => { if (this._selectedDocs.has(doc)) this._selectedDocs.delete(doc); const rowDocView = DocumentManager.Instance.getDocumentView(doc); if (rowDocView) SelectionManager.DeselectView(rowDocView); if (this._selectedDocs.size === 0) { this._lastSelectedRow = undefined; } }; @action clearSelection = () => { this._selectedDocs.clear(); SelectionManager.DeselectAll(); this._lastSelectedRow = undefined; }; rowOnClickScript = ScriptField.MakeFunction('scriptContext.selectRow(self, shiftKey, ctrlKey || metaKey)', { scriptContext: 'any', shiftKey: 'boolean', ctrlKey: 'boolean', metaKey: 'boolean' })!; @action selectRow = (doc: Doc, shift: boolean, ctrl: boolean) => { const index = this.childDocs.indexOf(doc); if (index < 0) return; if (shift && this._lastSelectedRow !== undefined) { const startRow = Math.min(this._lastSelectedRow, index); const endRow = Math.max(this._lastSelectedRow, index); for (let i = startRow; i <= endRow; i++) { const currDoc = this.childDocs[i]; if (!this._selectedDocs.has(currDoc)) this.addDocToSelection(currDoc, true, i); } this._lastSelectedRow = index; } else if (ctrl) { if (!this._selectedDocs.has(doc)) { this.addDocToSelection(doc, true, index); } else { this.removeDocFromSelection(doc); } } else { if (!this._selectedDocs.has(doc)) { this.clearSelection(); this.addDocToSelection(doc, false, index); } } }; @action sortedSelectedDocs = (): Doc[] => { return this.childDocs.filter(doc => this._selectedDocs.has(doc)); }; setDropIndex = (index: number) => { this._closestDropIndex = index; }; @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.columnDragData) { e.stopPropagation(); const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]; let i = de.complete.columnDragData.colIndex; this.displayColumnWidths.reduce((total, curr, index) => { if (total <= mouseX && total + curr >= mouseX) { i = index; } return total + curr; }, CollectionSchemaView._rowMenuWidth); this.swapColumns(de.complete.columnDragData.colIndex, i); return true; } if (super.onInternalDrop(e, de)) { this._isDragging = false; const pushedDocs: Doc[] = this.childDocs.filter((doc: Doc, index: number) => index >= this._closestDropIndex && !this._selectedDocs.has(doc)); this.props.removeDocument?.(pushedDocs); this.props.removeDocument?.(this._selectedDocSortedArray); this.addDocument(this._selectedDocSortedArray); this.addDocument(pushedDocs); this.setSort(undefined); this.clearSelection(); return true; } return false; }; @action onExternalDrop = async (e: React.DragEvent): Promise => { super.onExternalDrop( e, {}, undoBatch( action(docus => { this._isDragging = false; docus.map((doc: Doc) => { this.addDocument(doc); }); }) ) ); this.setSort(undefined); }; @action startDrag = (e: PointerEvent, doc: Doc, index: number) => { if (!this._selectedDocs.has(doc)) { this.clearSelection(); this.addDocToSelection(doc, false, index); } this._isDragging = true; this._selectedDocSortedArray = this.sortedSelectedDocs(); const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move'); dragData.moveDocument = this.props.moveDocument; const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values()).map((doc: Doc) => this._rowEles.get(doc)); DragManager.StartDocumentDrag( dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined ); return true; }; onDividerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction); }; @action onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { const nativeWidth = this._previewRef!.getBoundingClientRect(); const minWidth = 40; const maxWidth = 1000; const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0]; const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; this.layoutDoc.schemaPreviewWidth = width; return false; }; @action addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => { if (!value && !forceEmptyNote) return false; const newDoc = Docs.Create.TextDocument(value, { title: value, _autoHeight: true }); FormattedTextBox.SelectOnLoad = newDoc[Id]; FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this.addRow(newDoc) || false; }; menuCallback = (x: number, y: number) => { ContextMenu.Instance.clearItems(); const layoutItems: ContextMenuProps[] = []; const docItems: ContextMenuProps[] = []; const dataDoc = this.props.DataDoc || this.props.Document; DocUtils.addDocumentCreatorMenuItems( doc => { FormattedTextBox.SelectOnLoad = StrCast(doc[Id]); return this.addRow(doc); }, this.addRow, x, y, true ); 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.addRow(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.addRow(created) || false; } }, icon: 'compress-arrows-alt', }) ); !Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' }); !Doc.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, _autoHeight: true }); if (created) { if (this.props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.Document); } this.addRow(created); } }); ContextMenu.Instance.displayMenu(x, y, undefined, true); }; focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { let focusSpeed = options.zoomTime ?? 500; smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); return focusSpeed; } } return undefined; }; isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; @computed get fieldDefaultInput() { switch (this._newFieldType) { case ColumnType.Number: return e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; case ColumnType.Boolean: return ( <> e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} /> {this._newFieldDefault ? 'true' : 'false'} ); case ColumnType.String: return e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; } } onSearchKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': this._menuOptions.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuOptions[0]) : action(() => (this._makeNewField = true))(); break; case 'Escape': this.closeColumnMenu(); break; } }; @action setKey = (key: string, defaultVal?: any) => { if (this._makeNewColumn) { this.addColumn(key, defaultVal); } else { this.changeColumnKey(this._columnMenuIndex!, key, defaultVal); } this.closeColumnMenu(); }; setColumnValues = (key: string, value: string) => { let success: boolean = true; this.childDocs.forEach(doc => success && KeyValueBox.SetField(doc, key, value)); return success; }; @action openColumnMenu = (index: number, newCol: boolean) => { this._makeNewColumn = false; this._columnMenuIndex = index; this._menuValue = ''; this._menuOptions = this.documentKeys; this._makeNewField = false; this._newFieldWarning = ''; this._makeNewField = false; this._makeNewColumn = newCol; }; @action closeColumnMenu = () => { this._columnMenuIndex = undefined; }; @action openFilterMenu = (index: number) => { this._filterColumnIndex = index; this._filterValue = this.getFieldFilters(this.columnKeys[this._filterColumnIndex!]).map(filter => filter.split(':')[1])[0]; }; @action closeFilterMenu = (setValue: boolean) => { if (setValue) { if (this._filterValue !== '') { Doc.setDocFilter(this.Document, this.columnKeys[this._filterColumnIndex!], this._filterValue, 'check', false, undefined, false); } else { this.removeFieldFilters(this.columnKeys[this._filterColumnIndex!]); } } this._filterColumnIndex = undefined; }; openContextMenu = (x: number, y: number, index: number) => { this.closeColumnMenu(); this.closeFilterMenu(false); ContextMenu.Instance.clearItems(); ContextMenu.Instance.addItem({ description: 'Change field', event: () => { this.openColumnMenu(index, false); }, icon: 'pencil-alt', }); ContextMenu.Instance.addItem({ description: 'Filter field', event: () => { this.openFilterMenu(index); }, icon: 'filter', }); ContextMenu.Instance.addItem({ description: 'Delete column', event: () => { this.removeColumn(index); }, icon: 'trash', }); ContextMenu.Instance.displayMenu(x, y, undefined, false); }; @action updateKeySearch = (e: React.ChangeEvent) => { this._menuValue = e.target.value; this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); }; getFieldFilters = (field: string) => { return StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); }; removeFieldFilters = (field: string) => { this.getFieldFilters(field).forEach(filter => { Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'); }); }; onFilterKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': this.closeFilterMenu(true); break; case 'Escape': this.closeFilterMenu(false); break; } }; @action updateFilterSearch = (e: React.ChangeEvent) => { this._filterValue = e.target.value; }; @computed get newFieldMenu() { return (
{ this._newFieldType = ColumnType.Number; this._newFieldDefault = 0; })} /> number
{ this._newFieldType = ColumnType.Boolean; this._newFieldDefault = false; })} /> boolean
{ this._newFieldType = ColumnType.String; this._newFieldDefault = ''; })} /> string
value: {this.fieldDefaultInput}
{this._newFieldWarning}
{ if (this.documentKeys.includes(this._menuValue)) { this._newFieldWarning = 'Field already exists'; } else if (this._menuValue.length === 0) { this._newFieldWarning = 'Field cannot be an empty string'; } else { this.setKey(this._menuValue, this._newFieldDefault); } })}> done
); } @computed get keysDropdown() { return (
{ e.stopPropagation(); this._makeNewField = true; })}> + new field
r?.addEventListener( 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) (e: WheelEvent) => { if (!r.scrollTop && e.deltaY <= 0) e.preventDefault(); e.stopPropagation(); }, { passive: false } ) }> {this._menuOptions.map(key => (
{ e.stopPropagation(); this.setKey(key); }}> {key}
))}
); } @computed get renderColumnMenu() { const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return (
e.stopPropagation()} /> {this._makeNewField ? this.newFieldMenu : this.keysDropdown}
{ e.stopPropagation(); this.closeColumnMenu(); })}> cancel
); } @computed get renderFilterOptions() { const keyOptions: string[] = []; const columnKey = this.columnKeys[this._filterColumnIndex!]; this.childDocs.forEach(doc => { const key = StrCast(doc[columnKey]); if (keyOptions.includes(key) === false && (key.includes(this._filterValue) || !this._filterValue) && key !== '') { keyOptions.push(key); } }); const filters = StrListCast(this.Document._docFilters); for (let i = 0; i < (filters?.length ?? 0) - 1; i++) { if (filters[i] === columnKey && keyOptions.includes(filters[i].split(':')[1]) === false) { keyOptions.push(filters[i + 1]); } } const options = keyOptions.map(key => { let bool = false; if (filters !== undefined) { const ind = filters.findIndex(filter => filter.split(':')[1] === key); const fields = ind === -1 ? undefined : filters[ind].split(':'); bool = fields ? fields[2] === 'check' : false; } return (
e.stopPropagation()} onClick={e => e.stopPropagation()} onChange={action(e => { if (e.target.checked) { Doc.setDocFilter(this.props.Document, columnKey, key, 'check'); } else { Doc.setDocFilter(this.props.Document, columnKey, key, 'remove'); } })} checked={bool} /> {key}
); }); return options; } @computed get renderFilterMenu() { const x = this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._filterColumnIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return (
e.stopPropagation()} /> {this.renderFilterOptions}
{ e.stopPropagation(); this.closeFilterMenu(true); })}> done
); } render() { return (
{ this._ref = ele; this.createDashEventsTarget(ele); }} onDrop={this.onExternalDrop.bind(this)}>
{ this.clearSelection(); })}>
{ this._columnMenuIndex && this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true); }}>
{this.columnKeys.map((key, index) => { return ( ); })}
{this._columnMenuIndex !== undefined && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu}
e.stopPropagation()}> {this.childDocs.map((doc: Doc, index: number) => { const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; let dref: Opt; return (
(dref = r || undefined)} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)} Document={doc} DataDoc={dataDoc} ContainingCollectionView={this.props.CollectionView} ContainingCollectionDoc={this.Document} PanelWidth={() => this.tableWidth} PanelHeight={() => CollectionSchemaView._rowHeight} styleProvider={DefaultStyleProvider} focus={this.focusDocument} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} rootSelected={this.rootSelected} ScreenToLocalTransform={Transform.Identity} bringToFront={emptyFunction} isContentActive={this.isChildContentActive} hideDecorations={true} hideTitle={true} hideDocumentButtonBar={true} hideLinkAnchors={true} fitWidth={returnTrue} onClick={() => this.rowOnClickScript} scriptContext={this} />
); })}
{this.previewWidth > 0 &&
} {this.previewWidth > 0 && (
(this._previewRef = ref)}> {this._lastSelectedRow !== undefined && ( this.previewWidth} PanelHeight={this.props.PanelHeight} isContentActive={returnTrue} isDocumentActive={returnFalse} ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0)} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} addDocument={this.addRow} removeDocument={this.props.removeDocument} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} /> )}
)}
); } }