diff options
Diffstat (limited to 'src/client/views/collections/CollectionNoteTakingView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionNoteTakingView.tsx | 102 |
1 files changed, 74 insertions, 28 deletions
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 6318620e0..d8a0aebb1 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, makeObservable, observable, observe, reaction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, Field, Opt } from '../../../fields/Doc'; @@ -9,24 +9,25 @@ 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 { DivHeight, emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils'; +import { DivHeight, emptyFunction, lightOrDark, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch } from '../../util/UndoManager'; +import { undoable, undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; +import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { DocumentView } from '../nodes/DocumentView'; -import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView'; +import { FieldViewProps, FocusViewOptions } 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'; -import { JsxElement } from 'typescript'; +import { FieldsDropdown } from '../FieldsDropdown'; const _global = (window /* browser */ || global) /* node */ as any; /** @@ -41,7 +42,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { _disposers: { [key: string]: IReactionDisposer } = {}; _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef<HTMLDivElement>(); - notetakingCategoryField = 'NotetakingCategory'; + @computed get notetakingCategoryField() { + return StrCast(this.dataDoc.notetaking_column, StrCast(this.layoutDoc.pivotField, 'notetaking_column')); + } public DividerWidth = 16; @observable docsDraggedRowCol: number[] = []; @observable _scroll = 0; @@ -153,25 +156,34 @@ export class CollectionNoteTakingView extends CollectionSubView() { ); }; + @computed get allFieldValues() { + return new Set(this.childDocs.map(doc => StrCast(doc[this.notetakingCategoryField]))); + } + componentDidMount() { super.componentDidMount?.(); document.addEventListener('pointerup', this.removeDocDragHighlight, true); - this._disposers.layout_autoHeight = reaction( - () => this.layoutDoc._layout_autoHeight, - layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight))) + + this._disposers.autoColumns = reaction( + () => (this.layoutDoc._notetaking_columns_autoCreate ? Array.from(this.allFieldValues) : undefined), + columns => undoable(() => columns?.filter(col => !this.colHeaderData.some(h => h.heading === col)).forEach(col => this.addColumn(col)), 'adding columns')(), + { fireImmediately: true } ); this._disposers.refList = reaction( () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }), ({ refList, autoHeight }) => { - if (autoHeight) refList.forEach(r => this.observer.observe(r)); - else this.observer.disconnect(); + if (autoHeight) { + refList.forEach(r => this.observer.observe(r)); + this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight))); + } else this.observer.disconnect(); }, { fireImmediately: true } ); } componentWillUnmount() { + this.observer.disconnect(); document.removeEventListener('pointerup', this.removeDocDragHighlight, true); super.componentWillUnmount(); Object.keys(this._disposers).forEach(key => this._disposers[key]()); @@ -298,7 +310,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { getDocWidth(d: Doc) { const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field); const existingHeader = this.colHeaderData.find(sh => sh.heading === heading); - const existingWidth = existingHeader?.width ? existingHeader.width : 0; + const existingWidth = this.layoutDoc._notetaking_columns_autoSize ? 1 / (this.colHeaderData.length ?? 1) : existingHeader?.width ? existingHeader.width : 0; const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth; const width = d.layout_fitWidth ? maxWidth : NumCast(d._width); return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth); @@ -370,7 +382,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { // we alter the pivot fields of the docs in case they are moved to a new column. const colIndex = this.getColumnFromXCoord(xCoord); const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading); - DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader)); + DragManager.docsBeingDragged.map(doc => doc[DocData]).forEach(d => (d[this.notetakingCategoryField] = colHeader)); // used to notify sections to re-render this.docsDraggedRowCol.length = 0; const columnFromCoord = this.getColumnFromXCoord(xCoord); @@ -500,11 +512,12 @@ export class CollectionNoteTakingView extends CollectionSubView() { editableViewProps = () => ({ GetValue: () => '', - SetValue: this.addGroup, - contents: '+ New Column', + SetValue: this.addColumn, + contents: '+ Column', }); refList = () => this._refList; + backgroundColor = () => this._props.DocumentView?.().backgroundColor(); // sectionNoteTaking returns a CollectionNoteTakingViewColumn (which is an individual column) sectionNoteTaking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => ( @@ -512,7 +525,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { key={heading?.heading ?? 'unset'} PanelWidth={this._props.PanelWidth} refList={this._refList} + backgroundColor={this.backgroundColor} select={this._props.select} + isContentActive={this.isContentActive} addDocument={this.addDocument} chromeHidden={this.chromeHidden} colHeaderData={this.colHeaderData} @@ -538,18 +553,26 @@ export class CollectionNoteTakingView extends CollectionSubView() { /> ); + @undoBatch + remColumn = (value: SchemaHeaderField) => { + const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null)); + if (value) { + const index = colHdrData.indexOf(value); + index !== -1 && colHdrData.splice(index, 1); + this.resizeColumns(colHdrData); + } + }; + // addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of // columnHeaders and resizing the existing columns to make room for our new one. @undoBatch - addGroup = (value: string) => { - if (this.colHeaderData) { - for (const header of this.colHeaderData) { - if (header.heading === value) { - alert('You cannot use an existing column name. Please try a new column name'); - return value; - } + addColumn = (value: string) => { + this.colHeaderData.forEach(header => { + if (header.heading === value) { + alert('You cannot use an existing column name. Please try a new column name'); + return value; } - } + }); const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null)); const newColWidth = 1 / (this.numGroupColumns + 1); columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)); @@ -557,11 +580,25 @@ export class CollectionNoteTakingView extends CollectionSubView() { return true; }; + removeEmptyColumns = undoable(() => { + this.colHeaderData.filter(h => !this.allFieldValues.has(h.heading)).forEach(this.remColumn); + }, 'remove empty Columns'); + 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._notetaking_columns_autoCreate ? 'Manually' : 'Automatically'} Create columns`, + event: () => (this.layoutDoc._notetaking_columns_autoCreate = !this.layoutDoc._notetaking_columns_autoCreate), + icon: 'computer', + }); + subItems.push({ description: 'Remove Empty Columns', event: this.removeEmptyColumns, icon: 'computer' }); + subItems.push({ + description: `${this.layoutDoc._notetaking_columns_autoSize ? 'Variable Size' : 'Autosize'} Columns`, + event: () => (this.layoutDoc._notetaking_columns_autoSize = !this.layoutDoc._notetaking_columns_autoSize), + icon: 'plus', + }); subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_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' }); @@ -614,12 +651,11 @@ export class CollectionNoteTakingView extends CollectionSubView() { TraceMobx(); return ( <div - className="collectionNoteTakingView" + className={`collectionNoteTakingView ${lightOrDark(this.backgroundColor()) === Colors.WHITE ? 'collectionNoteTakingViewLight' : ''}`} ref={this.createRef} - key="notes" style={{ - overflowY: this._props.isContentActive() ? 'auto' : 'hidden', - background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor), + overflowY: this.isContentActive() ? 'auto' : 'hidden', + background: this.backgroundColor(), pointerEvents: this.backgroundEvents, }} onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))} @@ -630,6 +666,16 @@ export class CollectionNoteTakingView extends CollectionSubView() { onContextMenu={this.onContextMenu} onWheel={e => this._props.isContentActive() && e.stopPropagation()}> <>{this.renderedSections}</> + <div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}> + <FieldsDropdown + Document={this.Document} + selectFunc={undoable(fieldKey => { + this.layoutDoc._pivotField = fieldKey; + this.removeEmptyColumns(); + }, 'change pivot field')} + placeholder={StrCast(this.layoutDoc._pivotField)} + /> + </div> </div> ); } |