diff options
| -rw-r--r-- | src/client/documents/Documents.ts | 1 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 15 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.scss | 214 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 130 |
4 files changed, 244 insertions, 116 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dabbf9591..dc649ec4e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -235,6 +235,7 @@ export class DocumentOptions { dataViz_pie?: string; dataViz_histogram?: string; dataViz?: string; + dataViz_savedTemplates?: LISTt; layout?: string | Doc; // default layout string or template document layout_isSvg?: BOOLt = new BoolInfo('whether document decorations and other selections should handle pointerEvents for svg content or use doc bounding box'); diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 765642891..e943dd2e3 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -33,12 +33,14 @@ import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import { LinkManager } from '../../../util/LinkManager'; -import { DataVizTemplateInfo, DocCreatorMenu, LayoutType } from './DocCreatorMenu'; +import { DataVizTemplateInfo, DataVizTemplateLayout, DocCreatorMenu, LayoutType } from './DocCreatorMenu'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; import { template } from 'lodash'; import { data } from 'jquery'; +import { listSpec } from '../../../../fields/Schema'; +import { ObjectField } from '../../../../fields/ObjectField'; export enum DataVizView { TABLE = 'table', @@ -532,14 +534,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } } - // applyImagesTo = (doc: Doc, cols: string[]) => { - // const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]); - // const imageFields = childDocs.filter(doc => doc.type === 'image'); - // const imageToKey: Map<Doc, string> = new Map(); - // imageFields.forEach(img => cols.forEach(col => {if (img[col]) imageToKey.set(img, col)})); - - // imageFields.forEach(doc => doc['data'] = String(doc[String(imageToKey.get(doc))]).replace(/"/g, '')); - + // @action addSavedLayout = (layout: DataVizTemplateLayout) => { + // const saved = Cast(this.layoutDoc.dataViz_savedTemplates, listSpec('RefField')); + // } @action diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index 0fc85ba5d..d18f75545 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -68,6 +68,13 @@ height: 0; width: 50px; } + + &.preview-toggle { + margin: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + border-left: 0px; + } } .docCreatorMenu-top-buttons-container { @@ -168,16 +175,44 @@ height: 25px; } +.docCreatorMenu-general-options-container { + display: flex; + justify-content: center; + align-items: center; + margin: 0px; + padding: 0px; + gap: 5px; +} + +.docCreatorMenu-save-layout-button { + display: flex; + justify-content: center; + align-items: center; + width: 40px; + height: 40px; + background-color: rgb(99, 148, 238); + border: 2px solid rgb(80, 107, 152); + border-radius: 5px; + margin-bottom: 20px; + font-size: 25px; + + &:hover{ + background-color: rgb(59, 128, 255); + border: 2px solid rgb(53, 80, 127); + } +} + .docCreatorMenu-create-docs-button { - width: 80px; + width: 40px; height: 40px; background-color: rgb(176, 229, 149); - border-radius: 5px; border: 2px solid rgb(126, 219, 80); + border-radius: 5px; padding: 0px; - font-size: 16px; - color: black; + font-size: 25px; + color: white; flex: 0 0 auto; + margin-bottom: 20px; //remove later !!! &:hover { background-color: rgb(129, 223, 83); @@ -185,6 +220,13 @@ } } +.docCreatorMenu-option-divider { + border-top: 1px solid rgb(180, 180, 180); + width: 225px; + margin-top: 10px; + margin-bottom: 10px; +} + //------------------------------------------------------------------------------------------------------------------------------------------ //DocCreatorMenu templates preview CSS //-------------------------------------------------------------------------------------------------------------------------------------------- @@ -202,29 +244,32 @@ height: calc(100% - 30px); border: 1px solid rgb(180, 180, 180); border-radius: 5px; +} - .docCreatorMenu-preview-window { - display: flex; - justify-content: center; - align-items: center; - width: 125px; - height: 125px; - margin-top: 10px; - margin-left: 10px; - border: 1px solid rgb(163, 163, 163); - border-radius: 5px; - box-shadow: 5px 5px rgb(29, 29, 31); +.docCreatorMenu-preview-window { + display: flex; + justify-content: center; + align-items: center; + width: 125px; + height: 125px; + margin-top: 10px; + margin-left: 10px; + border: 1px solid rgb(163, 163, 163); + border-radius: 5px; + box-shadow: 5px 5px rgb(29, 29, 31); - &:hover{ - background-color: rgb(72, 72, 73); - } + &:hover{ + background-color: rgb(72, 72, 73); + } - .docCreatorMenu-preview-image{ - width: 105px; - height: 105px; - border-radius: 5px; - } + .docCreatorMenu-preview-image{ + width: 105px; + height: 105px; + border-radius: 5px; + } + &.empty { + font-size: 35px; } } @@ -254,6 +299,8 @@ background: whitesmoke; background-color: rgb(34, 34, 37); border-radius: 5px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; border: 1px solid rgb(180, 180, 180); padding: 0px; font-size: 12px; @@ -412,75 +459,80 @@ } } +} - .docCreatorMenu-layout-preview-window-wrapper { - display: flex; - justify-content: center; - align-items: center; - width: 85%; - height: auto; - position: relative; - padding: 0px; +.docCreatorMenu-layout-preview-window-wrapper { + display: flex; + justify-content: center; + align-items: center; + width: 85%; + height: auto; + position: relative; + padding: 0px; - &:hover .docCreatorMenu-zoom-button-container { - display: block; - } + &:hover .docCreatorMenu-zoom-button-container { + display: block; + } - .docCreatorMenu-layout-preview-window { - padding: 5px; - flex: 0 0 auto; - overflow: scroll; - display: grid; - width: auto; - height: auto; - max-width: 240; - max-height: 240; - border: 1px solid rgb(180, 180, 180); - border-radius: 5px; - background-color: rgb(34, 34, 37); - -ms-overflow-style: none; - scrollbar-width: none; - - .docCreatorMenu-layout-preview-item { - display: flex; - justify-content: center; - align-items: center; - border-radius: 3px; - border: solid 1px lightblue; - - &:hover { - border: solid 2px rgb(68, 153, 233); - z-index: 2; - } - } + .docCreatorMenu-layout-preview-window { + padding: 5px; + flex: 0 0 auto; + overflow: scroll; + display: grid; + width: auto; + height: auto; + max-width: 240; + max-height: 240; + border: 1px solid rgb(180, 180, 180); + border-radius: 5px; + background-color: rgb(34, 34, 37); + -ms-overflow-style: none; + scrollbar-width: none; + + &.small { + max-width: 100; + max-height: 100; } - .docCreatorMenu-zoom-button-container { - position: absolute; - top: 0px; + .docCreatorMenu-layout-preview-item { display: flex; justify-content: center; align-items: center; - display: none; - z-index: 999; - } - - .docCreatorMenu-zoom-button{ - width: 15px; - height: 15px; - background: whitesmoke; - background-color: rgb(34, 34, 37); border-radius: 3px; - border: 1px solid rgb(180, 180, 180); - padding: 0px; - font-size: 10px; - z-index: 6; - margin-left: 0px; - margin-top: 0px; - margin-right: 0px; //225px - margin-bottom: 0px; + border: solid 1px lightblue; + + &:hover { + border: solid 2px rgb(68, 153, 233); + z-index: 2; + } } } + + .docCreatorMenu-zoom-button-container { + position: absolute; + top: 0px; + display: flex; + justify-content: center; + align-items: center; + display: none; + z-index: 999; + } + + .docCreatorMenu-zoom-button{ + width: 15px; + height: 15px; + background: whitesmoke; + background-color: rgb(34, 34, 37); + border-radius: 3px; + border: 1px solid rgb(180, 180, 180); + padding: 0px; + font-size: 10px; + z-index: 6; + margin-left: 0px; + margin-top: 0px; + margin-right: 0px; //225px + margin-bottom: 0px; + } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 67999fc39..fdf623150 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -40,6 +40,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _layout: {type: LayoutType, yMargin: number, xMargin: number, columns?: number, repeat: number} = {type: LayoutType.Grid, yMargin: 0, xMargin: 0, repeat: 0}; @observable _layoutPreview: boolean = true; @observable _layoutPreviewScale: number = 1; + @observable _savedLayouts: DataVizTemplateLayout[] = []; @observable _pageX: number = 0; @observable _pageY: number = 0; @@ -193,6 +194,20 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + @action updateSelectedSavedLayout = (layout: DataVizTemplateLayout) => { + this._layout.xMargin = layout.layout.xMargin; + this._layout.yMargin = layout.layout.yMargin; + this._layout.type = layout.layout.type; + this._layout.columns = layout.columns; + } + + isSelectedLayout = (layout: DataVizTemplateLayout) => { + return this._layout.xMargin === layout.layout.xMargin + && this._layout.yMargin === layout.layout.yMargin + && this._layout.type === layout.layout.type + && this._layout.columns === layout.columns; + } + get templatesPreviewContents(){ const renderedTemplates: Doc[] = []; return ( @@ -203,13 +218,35 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return (<div className='docCreatorMenu-preview-window' style={{ - background: this._selectedTemplate === info.doc ? Colors.MEDIUM_BLUE : '', + border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '' }} onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} /> </div> )})} + <div className='docCreatorMenu-preview-window empty'> + <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/> + </div> + </div> + ); + } + + get savedLayoutsPreviewContents(){ + return ( + <div className='docCreatorMenu-preview-container'> + {this._savedLayouts.map((layout, index) => + <div + className='docCreatorMenu-preview-window' + style={{ + border: this.isSelectedLayout(layout) ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', + boxShadow: this.isSelectedLayout(layout) ? `0 0 15px rgba(68, 118, 247, .8)` : '' + }} + onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedSavedLayout(layout)))} + > + {this.layoutPreviewContents(87, layout, false, true, index)} + </div> + )} </div> ); } @@ -259,20 +296,23 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + layoutPreviewContents = (outerSpan: number, altLayout?: DataVizTemplateLayout, zoomOption: boolean = true, small: boolean = false, id?: number) => { + const doc: Doc | undefined = altLayout ? altLayout.template : this._selectedTemplate; + if (!doc) return; + const layout = altLayout ? altLayout.layout : this._layout; - get layoutPreviewContents() { - const docWidth = Number(this._selectedTemplate?._width); - const docHeight = Number(this._selectedTemplate?._height); - const horizontalSpan: number = (docWidth + this._layout.xMargin) * this.columnsCount - this._layout.xMargin; - const verticalSpan: number = (docHeight + this._layout.yMargin) * this.rowsCount - this._layout.yMargin; + const docWidth: number = Number(doc._width); + const docHeight: number = Number(doc._height); + const horizontalSpan: number = (docWidth + layout.xMargin) * (altLayout ? altLayout.columns : this.columnsCount) - layout.xMargin;; + const verticalSpan: number = (docHeight + layout.yMargin) * (altLayout ? altLayout.rows : this.rowsCount) - layout.yMargin; const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan; - const scaledDown = (input: number) => {return input / (largerSpan / 225 * this._layoutPreviewScale)} + const scaledDown = (input: number) => {return input / (largerSpan / outerSpan * this._layoutPreviewScale)} const fontSize = Math.min(scaledDown(docWidth / 3), scaledDown(docHeight / 3)); return ( - <div className='docCreatorMenu-layout-preview-window-wrapper'> - <div className='docCreatorMenu-zoom-button-container'> + <div className='docCreatorMenu-layout-preview-window-wrapper' id={String(id) ?? undefined}> + {!zoomOption ? null : <div className='docCreatorMenu-zoom-button-container'> <button className='docCreatorMenu-zoom-button' onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= 1.25))}> @@ -283,15 +323,16 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= .75))}> <FontAwesomeIcon icon={'plus'}/> </button> - </div> + </div>} <div - className='docCreatorMenu-layout-preview-window' + id={String(id) ?? undefined} + className={`docCreatorMenu-layout-preview-window ${small ? 'small' : ''}`} style={{ - gridTemplateColumns: `repeat(${this.columnsCount}, ${scaledDown(docWidth)}px`, + gridTemplateColumns: `repeat(${altLayout ? altLayout.columns : this.columnsCount}, ${scaledDown(docWidth)}px`, gridTemplateRows: `${scaledDown(docHeight)}px`, gridAutoRows: `${scaledDown(docHeight)}px`, - rowGap: `${scaledDown(this._layout.yMargin)}px`, - columnGap: `${scaledDown(this._layout.xMargin)}px` + rowGap: `${scaledDown(layout.yMargin)}px`, + columnGap: `${scaledDown(layout.xMargin)}px` }}> {this._layout.type === LayoutType.Stacked ? <div @@ -365,32 +406,61 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { </div> </div> <button - className='docCreatorMenu-menu-button' + className='docCreatorMenu-menu-button preview-toggle' onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreview = !this._layoutPreview))}> <FontAwesomeIcon icon={this._layoutPreview ? 'minus' : 'magnifying-glass'}/> </button> </div> {this._layout.type ? this.layoutConfigOptions: null} - {this._layoutPreview ? this.layoutPreviewContents : null} + {this._layoutPreview ? this.layoutPreviewContents(225) : null} {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => <option onPointerDown={e => this._layout.repeat = num}>{`${num}x`}</option>))} - <button - className='docCreatorMenu-create-docs-button' - style={{backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'rgb(180, 180, 180)'}} - onPointerDown={e => setupMoveUpEvents( this, e, returnFalse, emptyFunction, + <hr className='docCreatorMenu-option-divider'/> + <div className='docCreatorMenu-general-options-container'> + <button + className='docCreatorMenu-save-layout-button' + onPointerDown={e => setupMoveUpEvents( this, e, returnFalse, emptyFunction, undoable(clickEv => { clickEv.stopPropagation(); if (!this._selectedTemplate) return; - const templateInfo: DataVizTemplateInfo = {doc: this._selectedTemplate, layout: this._layout, referencePos: {x: this._pageX + 450, y: this._pageY}, columns: this.columnsCount}; - this._dataViz?.createDocsFromTemplate(templateInfo); - }, 'make docs') + const layout: DataVizTemplateLayout = {template: this._selectedTemplate, layout: {type: this._layout.type, xMargin: this._layout.xMargin, yMargin:this._layout.yMargin, repeat: 0}, columns: this.columnsCount, rows: this.rowsCount, docsNumList: this.docsToRender}; + this._savedLayouts.push(layout); + }, 'make docs') ) }> - Create - </button> + <FontAwesomeIcon icon='floppy-disk'/> + </button> + <button + className='docCreatorMenu-create-docs-button' + style={{backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'solid 2px rgb(180, 180, 180)'}} + onPointerDown={e => setupMoveUpEvents( this, e, returnFalse, emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + if (!this._selectedTemplate) return; + const templateInfo: DataVizTemplateInfo = {doc: this._selectedTemplate, layout: this._layout, referencePos: {x: this._pageX + 450, y: this._pageY}, columns: this.columnsCount}; + this._dataViz?.createDocsFromTemplate(templateInfo); + }, 'make docs') + ) + }> + <FontAwesomeIcon icon='plus'/> + </button> + </div> </div> ); } + get renderSelectedViewType(){ + switch (this._menuContent){ + case 'templates': + return this.templatesPreviewContents; + case 'options': + return this.optionsMenuContents; + case 'saved': + return this.savedLayoutsPreviewContents; + default: + return undefined; + } + } + render() { const topButton = (icon: string, opt: string, func: Function, tag: string) => { return ( @@ -485,7 +555,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { <FontAwesomeIcon icon={'minus'}/> </button> </div> - {this._menuContent === 'templates' ? this.templatesPreviewContents : this.optionsMenuContents} + {this.renderSelectedViewType} </div> </> } @@ -499,4 +569,12 @@ export interface DataVizTemplateInfo { layout: {type: LayoutType, xMargin: number, yMargin: number, repeat: number}; columns: number; referencePos: {x: number, y: number}; +} + +export interface DataVizTemplateLayout { + template: Doc; + docsNumList: number[]; + layout: {type: LayoutType, xMargin: number, yMargin: number, repeat: number}; + columns: number; + rows: number; }
\ No newline at end of file |
