import React = require('react'); import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } 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 { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; import './CollectionNoteTakingView.scss'; import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn'; import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider'; import { CollectionSubView } from './CollectionSubView'; const _global = (window /* browser */ || global) /* node */ as any; export type collectionNoteTakingViewProps = { chromeHidden?: boolean; viewType?: CollectionViewType; NativeWidth?: () => number; NativeHeight?: () => number; }; //TODO: somehow need to update the mapping and then have everything else rerender. Maybe with a refresh boolean like // in Hypermedia? @observer export class CollectionNoteTakingView extends CollectionSubView>() { _disposers: { [key: string]: IReactionDisposer } = {}; _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); // change to relative widths for deleting. change storage from columnStartXCoords to columnHeaders (schemaHeaderFields has a widgth alrady) @observable columnStartXCoords: number[] = []; // columnHeaders -- SchemaHeaderField -- widht @observable docsDraggedRowCol: number[] = []; @observable _cursor: CursorProperty = 'grab'; @observable _scroll = 0; // used to force the document decoration to update when scrolling @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); } @computed get columnHeaders() { const columnHeaders = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null)); const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset')); // @#Oberable draggedColIndex = ... //@observable cloneDivXYcoords // @observable clonedDiv... // render() { // { this.clonedDiv ?
pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout); } @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); } @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth())); } @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); } @computed get numGroupColumns() { return this.columnHeaders.length; } @computed get PanelWidth() { return this.props.PanelWidth(); } @computed get maxColWdith() { return this.props.PanelWidth() - 2 * this.xMargin; } // If the user has not yet created any docs (in another view), this will create a single column. Otherwise, // it will adjust according to the constructor(props: any) { super(props); if (this.columnHeaders === undefined) { this.dataDoc.columnHeaders = new List([new SchemaHeaderField('New Column')]); // add all of the docs that have not been added to a column to this new column } } // passed as a prop to the NoteTakingField, which uses this function // to render the docs you see within an individual column. children = (docs: Doc[]) => { TraceMobx(); return docs.map((d, i) => { const height = () => this.getDocHeight(d); const width = () => this.getDocWidth(d); const style = { width: width(), marginTop: this.gridGap, height: height() }; return (
{this.getDisplayDoc(d, width)}
); }); }; // [CAVEATS] (1) keep track of the offsetting // (2) documentView gets unmounted as you remove it from the list @computed get Sections() { TraceMobx(); const columnHeaders = this.columnHeaders; let docs = this.childDocs; const sections = new Map(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); const rowCol = this.docsDraggedRowCol; // filter out the currently dragged docs from the child docs, since we will insert them later if (rowCol.length && DragManager.docsBeingDragged.length) { const docIdsToRemove = new Set(); DragManager.docsBeingDragged.forEach(d => docIdsToRemove.add(d[Id])); docs = docs.filter(d => !docIdsToRemove.has(d[Id])); } // this will sort the docs into the correct columns (minus the ones you're currently dragging) docs.map(d => { const sectionValue = (d[this.notetakingCategoryField] as object) ?? `unset`; // look for if header exists already const existingHeader = columnHeaders.find(sh => sh.heading === sectionValue.toString()); if (existingHeader) { sections.get(existingHeader)!.push(d); } }); // now we add back in the docs that we're dragging if (rowCol.length && DragManager.docsBeingDragged.length) { const colHeader = columnHeaders[rowCol[1]]; // TODO: get the actual offset that occurs if the docs were in that column const offset = 0; sections.get(colHeader)?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged); } return sections; } removeDocDragHighlight = () => { setTimeout( action(() => (this.docsDraggedRowCol.length = 0)), 100 ); }; componentDidMount() { super.componentDidMount?.(); document.addEventListener('pointerup', this.removeDocDragHighlight, true); this._disposers.autoHeight = reaction( () => this.layoutDoc._autoHeight, autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))))) ); this._disposers.headers = reaction( () => this.columnHeaders.slice(), headers => this.resizeColumns(headers.length), { fireImmediately: true } ); } componentWillUnmount() { document.removeEventListener('pointerup', this.removeDocDragHighlight, true); super.componentWillUnmount(); Object.keys(this._disposers).forEach(key => this._disposers[key]()); } @action moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => { return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false; }; createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; this.createDashEventsTarget(ele!); //so the whole grid is the drop target? }; @computed get onChildClickHandler() { return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); } @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); } addDocTab = (doc: Doc, where: string) => { if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) { this.dataDoc[this.props.fieldKey] = new List([doc]); return true; } return this.props.addDocTab(doc, where); }; scrollToBottom = () => { smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight); }; // let's dive in and get the actual document we want to drag/move around focusDocument = (doc: Doc, options?: DocFocusOptions) => { Doc.BrushDoc(doc); let focusSpeed = 0; 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) { smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop); } } const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing); this.props.focus(this.rootDoc, { willZoom: options?.willZoom, scale: options?.scale, afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), }); }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { if (property === StyleProp.BoxShadow && doc && DragManager.docsBeingDragged.includes(doc)) { return `#9c9396 ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}`; } if (property === StyleProp.Opacity && doc) { if (this.props.childOpacity) { return this.props.childOpacity(); } if (this.Document._currentFrame !== undefined) { return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity; } } return this.props.styleProvider?.(doc, props, property); }; isContentActive = () => this.props.isSelected() || this.props.isContentActive(); // rules for displaying the documents getDisplayDoc(doc: Doc, width: () => number) { const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); let dref: Opt; const noteTakingDocTransform = () => this.getDocTransform(doc, dref); return ( (dref = r || undefined)} Document={doc} DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])} renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} styleProvider={this.styleProvider} docViewPath={this.props.docViewPath} fitWidth={this.props.childFitWidth} isContentActive={emptyFunction} onKey={this.onKeyDown} //TODO: change this from a prop to a parameter passed into a function dontHideOnDrag={true} isDocumentActive={this.isContentActive} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined} dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined} dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} rootSelected={this.rootSelected} showTitle={this.props.childShowTitle} dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={noteTakingDocTransform} focus={this.focusDocument} docFilters={this.childDocFilters} hideDecorationTitle={this.props.childHideDecorationTitle?.()} hideResizeHandles={this.props.childHideResizeHandles?.()} hideTitle={this.props.childHideTitle?.()} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)} whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} addDocTab={this.addDocTab} bringToFront={returnFalse} scriptContext={this.props.scriptContext} pinToPres={this.props.pinToPres} /> ); } // This is used to get the coordinates of a document when we go from a view like freeform to columns getDocTransform(doc: Doc, dref?: DocumentView) { const y = this._scroll; // required for document decorations to update when the text box container is scrolled const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined); // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale); } // how to get the width of a document. Currently returns the width of the column (minus margins) // if a note doc. Otherwise, returns the normal width (for graphs, images, etc...) getDocWidth(d: Doc) { const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field); const existingHeader = this.columnHeaders.find(sh => sh.heading === heading); const index = existingHeader ? this.columnHeaders.indexOf(existingHeader) : 0; const endColValue = index === this.columnHeaders.length - 1 || index > this.columnStartXCoords.length - 1 ? this.PanelWidth : this.columnStartXCoords[index + 1]; const maxWidth = index > this.columnStartXCoords.length - 1 ? this.PanelWidth : endColValue - this.columnStartXCoords[index] - 3 * this.xMargin; if (d.type === DocumentType.RTF) { return maxWidth; } const width = d[WidthSym](); return width < maxWidth ? width : maxWidth; } // how to get the height of a document. Nothing special here. getDocHeight(d?: Doc) { if (!d || d.hidden) return 0; const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc; const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1)); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); if (nw && nh) { // const colWid = this.columnWidth / this.numGroupColumns; // const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid); const docWid = this.getDocWidth(d); return Math.min(maxHeight, (docWid * nh) / nw); } const childHeight = NumCast(childLayoutDoc._height); const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin; return Math.min(childHeight, maxHeight, panelHeight); } // called when a column is either added or deleted. This function creates n evenly spaced columns @action resizeColumns = (n: number) => { const totalWidth = this.PanelWidth; const dividerWidth = 32; const totaldividerWidth = (n - 1) * dividerWidth; const colWidth = (totalWidth - totaldividerWidth) / n; const newColXCoords: number[] = []; let colStart = 0; for (let i = 0; i < n; i++) { newColXCoords.push(colStart); colStart += colWidth + dividerWidth; } this.columnStartXCoords = newColXCoords; }; // This function is used to preview where a document will drop in a column once a drag is complete. @action onPointerMove = (force: boolean, ex: number, ey: number) => { if (this.childDocList && (this.childDocList.includes(DragManager.DocDragData?.draggedDocuments.lastElement()!) || force || this.isContentActive())) { // get the current docs for the column based on the mouse's x coordinate // will use again later, which is why we're saving as local const xCoord = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[0] - 2 * this.gridGap; const colDocs = this.getDocsFromXCoord(xCoord); // get the index for where you need to insert the doc you are currently dragging const clientY = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[1]; let dropInd = -1; let pos0 = (this.refList.lastElement() as HTMLDivElement).children[0].getBoundingClientRect().height + this.yMargin * 2; colDocs.forEach((doc, i) => { let pos1 = this.getDocHeight(doc) + 2 * this.gridGap; pos1 += pos0; // updating drop position based on y coordinates const yCoordInBetween = clientY > pos0 && clientY < pos1; if (yCoordInBetween || (clientY < pos0 && i === 0)) { dropInd = i; } else if (i === colDocs.length - 1 && dropInd === -1) { dropInd = !colDocs.includes(DragManager.docsBeingDragged.lastElement()) ? i + 1 : i; } pos0 = pos1; }); // we alter the pivot fields of the docs in case they are moved to a new column. const colIndex = this.getColumnFromXCoord(xCoord); const colHeader = StrCast(this.columnHeaders[colIndex].heading); DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader)); // used to notify sections to re-render this.docsDraggedRowCol.length = 0; this.docsDraggedRowCol.push(dropInd, this.getColumnFromXCoord(xCoord)); } }; // returns the column index for a given x-coordinate getColumnFromXCoord = (xCoord: number): number => { const numColumns = this.columnHeaders.length; const coords = this.columnStartXCoords.slice(); coords.push(this.PanelWidth); let colIndex = 0; for (let i = 0; i < numColumns; i++) { if (xCoord > coords[i] && xCoord < coords[i + 1]) { colIndex = i; break; } } return colIndex; }; // returns the docs of a column based on the x-coordinate provided. getDocsFromXCoord = (xCoord: number): Doc[] => { const colIndex = this.getColumnFromXCoord(xCoord); const colHeader = StrCast(this.columnHeaders[colIndex].heading); // const docs = this.childDocList const docs = this.childDocs; const docsMatchingHeader: Doc[] = []; if (docs) { docs.map(d => { if (d instanceof Promise) return; const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset'; if (sectionValue.toString() == colHeader) { docsMatchingHeader.push(d); } }); } return docsMatchingHeader; }; @undoBatch @action onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { const docView = fieldProps.DocumentView?.(); if (docView && (e.ctrlKey || docView.rootDoc._singleLine) && ['Enter'].includes(e.key)) { e.stopPropagation?.(); const newDoc = Doc.MakeCopy(docView.rootDoc, true); Doc.GetProto(newDoc).text = undefined; FormattedTextBox.SelectOnLoad = newDoc[Id]; return this.addDocument?.(newDoc); } }; @undoBatch @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.docDragData) { if (super.onInternalDrop(e, de)) { // filter out the currently dragged docs from the child docs, since we will insert them later const rowCol = this.docsDraggedRowCol; const droppedDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= this.childDocs.length); // if the drop operation adds something to the end of the list, then use that as the new document (may be different than what was dropped e.g., in the case of a button which is dropped but which creates say, a note). const newDocs = droppedDocs.length ? droppedDocs : de.complete.docDragData.droppedDocuments; // const docs = this.childDocs const docs = this.childDocList; if (docs && newDocs.length) { // remove the dragged documents from the childDocList newDocs.filter(d => docs.indexOf(d) !== -1).forEach(d => docs.splice(docs.indexOf(d), 1)); // if the doc starts a columnm (or the drop index is undefined), we can just push it to the front. Otherwise we need to add it to the column properly //TODO: you need to update childDocList instead. It seems that childDocs is a copy of the actual array we want to modify if (rowCol[0] <= 0) { docs.splice(0, 0, ...newDocs); } else { const colDocs = this.getDocsFromXCoord(this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]); const previousDoc = colDocs[rowCol[0] - 1]; const previousDocIndex = docs.indexOf(previousDoc); docs.splice(previousDocIndex + 1, 0, ...newDocs); } } } } // it seems like we're creating a link here. Weird. I didn't know that you could establish links by dragging else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' }); this.props.addDocument?.(source); de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed e.stopPropagation(); } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); return false; }; @undoBatch internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) { const dropCreator = annoDragData.dropDocCreator; annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => { const dropDoc = dropCreator(annotationOn); return dropDoc || this.rootDoc; }; return true; } // when dropping outside of the current noteTaking context (like a new tab, freeform view, etc...) onExternalDrop = async (e: React.DragEvent): Promise => { const targInd = this.docsDraggedRowCol?.[0] || 0; const colInd = this.docsDraggedRowCol?.[1] || 0; super.onExternalDrop( e, {}, undoBatch( action(docus => { this.onPointerMove(true, e.clientX, e.clientY); docus?.map((doc: Doc) => this.addDocument(doc)); const newDoc = this.childDocs.lastElement(); const colHeader = StrCast(this.columnHeaders[colInd].heading); newDoc[this.notetakingCategoryField] = colHeader; const docs = this.childDocList; if (docs && targInd !== -1) { docs.splice(docs.length - 1, 1); docs.splice(targInd, 0, newDoc); } this.removeDocDragHighlight(); }) ) ); }; headings = () => Array.from(this.Sections); refList: any[] = []; editableViewProps = () => ({ GetValue: () => '', SetValue: this.addGroup, contents: '+ New Column', }); sectionNoteTaking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { const type = 'number'; return ( this.refList.splice(this.refList.indexOf(ref), 1)} observeHeight={ref => { if (ref) { this.refList.push(ref); this.observer = new _global.ResizeObserver( action((entries: any) => { if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))); if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) { this.props.setHeight?.(height); } } }) ); this.observer.observe(ref); } }} addDocument={this.addDocument} // docsByColumnHeader={this._docsByColumnHeader} // setDocsForColHeader={this.setDocsForColHeader} chromeHidden={this.chromeHidden} columnHeaders={this.columnHeaders} Document={this.props.Document} DataDoc={this.props.DataDoc} resizeColumns={this.resizeColumns} renderChildren={this.children} numGroupColumns={this.numGroupColumns} gridGap={this.gridGap} pivotField={this.notetakingCategoryField} columnStartXCoords={this.columnStartXCoords} maxColWidth={this.maxColWdith} PanelWidth={this.PanelWidth} key={heading?.heading ?? ''} headings={this.headings} heading={heading?.heading ?? ''} headingObject={heading} docList={docList} yMargin={this.yMargin} type={type} createDropTarget={this.createDashEventsTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} editableViewProps={this.editableViewProps} /> ); }; // called when adding a new columnHeader @undoBatch @action addGroup = (value: string) => { const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); return value && columnHeaders?.push(new SchemaHeaderField(value)) ? true : false; }; sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => { const descending = StrCast(this.layoutDoc._columnsSort) === 'descending'; const firstEntry = descending ? b : a; const secondEntry = descending ? a : b; return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1; }; onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped()) { const subItems: ContextMenuProps[] = []; subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' }); subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' }); ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' }); } }; // used to reset column sizes when using the drag handlers @action setColumnStartXCoords = (movementX: number, colIndex: number) => { const coords = [...this.columnStartXCoords]; coords[colIndex] += movementX; this.columnStartXCoords = coords; }; @computed get renderedSections() { TraceMobx(); // let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]; // if (this.pivotField) { // const entries = Array.from(this.Sections.entries()); // sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries; // } const entries = Array.from(this.Sections.entries()); const sections = entries; //.sort(this.sortFunc); const eles: JSX.Element[] = []; for (let i = 0; i < sections.length; i++) { const col = this.sectionNoteTaking(sections[i][0], sections[i][1]); eles.push(col); if (i < sections.length - 1) { eles.push(); } } return eles; } @computed get buttonMenu() { const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); // TODO:glr Allow support for multiple buttons if (menuDoc) { const width: number = NumCast(menuDoc._width, 30); const height: number = NumCast(menuDoc._height, 30); return (
35} PanelHeight={() => 35} renderDepth={this.props.renderDepth} focus={emptyFunction} styleProvider={this.props.styleProvider} docViewPath={returnEmptyDoclist} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={this.props.docFilters} docRangeFilters={this.props.docRangeFilters} searchFilterDocs={this.props.searchFilterDocs} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
); } } @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); } @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); } @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; } @computed get backgroundEvents() { return SnappingManager.GetIsDragging(); } observer: any; render() { TraceMobx(); const buttonMenu = this.rootDoc.buttonMenu; const noviceExplainer = StrCast(this.rootDoc.explainer); return ( <> {buttonMenu || noviceExplainer ? (
{buttonMenu ? this.buttonMenu : null} {Doc.UserDoc().noviceMode && noviceExplainer ?
{noviceExplainer}
: null}
) : null}
(this._scroll = e.currentTarget.scrollTop))} onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))} onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)} onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}> {this.renderedSections}
); } }