diff --git a/docs/en_US/erd_tool.rst b/docs/en_US/erd_tool.rst index 844cf29f5..39c7f1f26 100644 --- a/docs/en_US/erd_tool.rst +++ b/docs/en_US/erd_tool.rst @@ -111,6 +111,23 @@ Table Relationship Options | | tables and link them. | | +----------------------+---------------------------------------------------------------------------------------------------+----------------+ +Node Color Options +************************** + +.. table:: + :class: longtable + :widths: 1 5 + + +----------------------+----------------------------------------------------------------------------------------------------------+ + | Icon | Behavior | + +======================+==========================================================================================================+ + | *Fill Color* | Use Fill Color to change the background color of a table node. This is helpful if you want to | + | | identify a of group tables. Once set, all the newly added tables will take the same color. | + +----------------------+----------------------------------------------------------------------------------------------------------+ + | *Text Color* | Use Text Color to change the text color of a table node based on the fill color to make text | + | | easily readable. | + +----------------------+----------------------------------------------------------------------------------------------------------+ + Utility Options *************** diff --git a/docs/en_US/images/erd_tool_toolbar.png b/docs/en_US/images/erd_tool_toolbar.png index e6e88a9c0..9e70249a0 100644 Binary files a/docs/en_US/images/erd_tool_toolbar.png and b/docs/en_US/images/erd_tool_toolbar.png differ diff --git a/web/pgadmin/static/css/style.css b/web/pgadmin/static/css/style.css index db1bf87f1..9928a52ac 100644 --- a/web/pgadmin/static/css/style.css +++ b/web/pgadmin/static/css/style.css @@ -15,3 +15,5 @@ @import 'node_modules/jsoneditor/dist/jsoneditor.min.css'; @import 'node_modules/react-checkbox-tree/lib/react-checkbox-tree.css'; + +@import 'node_modules/@simonwep/pickr/dist/themes/monolith.min.css'; diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index 5bc215c1b..7702818be 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -20,6 +20,7 @@ import getStandardTheme from './standard'; import getDarkTheme from './dark'; import getHightContrastTheme from './high_contrast'; import { CssBaseline } from '@material-ui/core'; +import pickrOverride from './overrides/pickr.override'; /* Common settings across all themes */ let basicSettings = createMuiTheme(); @@ -309,7 +310,8 @@ function getFinalTheme(baseTheme) { listStyle: 'none', margin: 0, padding: 0, - } + }, + ...pickrOverride(baseTheme), }, }, MuiOutlinedInput: { diff --git a/web/pgadmin/static/js/Theme/overrides/pickr.override.js b/web/pgadmin/static/js/Theme/overrides/pickr.override.js new file mode 100644 index 000000000..b2759e63c --- /dev/null +++ b/web/pgadmin/static/js/Theme/overrides/pickr.override.js @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2022, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +export default function pickrOverride(theme) { + return { + '.pickr, .pcr-app': { + fontFamily: theme.typography.fontFamily, + '& *:focus': { + outline: 'none !important', + }, + '& .pcr-save': { + backgroundColor: theme.palette.primary.main + '!important', + borderRadius: theme.shape.borderRadius + 'px !important', + color: theme.palette.primary.contrastText + '!important', + border: '1px solid '+theme.palette.primary.main, + }, + '& .pcr-clear': { + backgroundColor: theme.palette.default.main + '!important', + borderRadius: theme.shape.borderRadius + 'px !important', + color: theme.palette.default.contrastText + '!important', + border: '1px solid '+theme.palette.default.borderColor, + }, + } + }; +} diff --git a/web/pgadmin/static/js/components/FormComponents.jsx b/web/pgadmin/static/js/components/FormComponents.jsx index 71de75158..c6f2bcf23 100644 --- a/web/pgadmin/static/js/components/FormComponents.jsx +++ b/web/pgadmin/static/js/components/FormComponents.jsx @@ -25,7 +25,6 @@ import DescriptionIcon from '@material-ui/icons/Description'; import AssignmentTurnedIn from '@material-ui/icons/AssignmentTurnedIn'; import Select, { components as RSComponents } from 'react-select'; import CreatableSelect from 'react-select/creatable'; -import Pickr from '@simonwep/pickr'; import clsx from 'clsx'; import PropTypes from 'prop-types'; import HTMLReactParse from 'html-react-parser'; @@ -42,6 +41,7 @@ import KeyboardShortcuts from './KeyboardShortcuts'; import QueryThresholds from './QueryThresholds'; import SelectThemes from './SelectThemes'; import { showFileManager } from '../helpers/showFileManager'; +import { withColorPicker } from '../helpers/withColorPicker'; const useStyles = makeStyles((theme) => ({ @@ -980,120 +980,17 @@ FormInputSelect.propTypes = { inputRef: CustomPropTypes.ref }; -/* React wrapper on color pickr */ +const ColorButton = withColorPicker(PgIconButton); export function InputColor({ value, controlProps, disabled, onChange, currObj }) { - const pickrOptions = { - showPalette: true, - allowEmpty: true, - colorFormat: 'HEX', - defaultColor: null, - position: 'right-middle', - clearText: gettext('No color'), - ...controlProps, - disabled: disabled, - }; - const eleRef = useRef(); - const pickrObj = useRef(); const classes = useStyles(); - const setColor = (newVal) => { - pickrObj.current && - pickrObj.current.setColor((_.isUndefined(newVal) || newVal == '') ? pickrOptions.defaultColor : newVal); - }; - - const destroyPickr = () => { - if (pickrObj.current) { - pickrObj.current.destroy(); - pickrObj.current = null; - } - }; - - const initPickr = () => { - /* pickr does not have way to update options, need to - destroy and recreate pickr to reflect options */ - destroyPickr(); - - pickrObj.current = new Pickr({ - el: eleRef.current, - useAsButton: true, - theme: 'monolith', - swatches: [ - '#000', '#666', '#ccc', '#fff', '#f90', '#ff0', '#0f0', - '#f0f', '#f4cccc', '#fce5cd', '#d0e0e3', '#cfe2f3', '#ead1dc', '#ea9999', - '#b6d7a8', '#a2c4c9', '#d5a6bd', '#e06666', '#93c47d', '#76a5af', '#c27ba0', - '#f1c232', '#6aa84f', '#45818e', '#a64d79', '#bf9000', '#0c343d', '#4c1130', - ], - position: pickrOptions.position, - strings: { - clear: pickrOptions.clearText, - }, - components: { - palette: pickrOptions.showPalette, - preview: true, - hue: pickrOptions.showPalette, - interaction: { - clear: pickrOptions.allowEmpty, - defaultRepresentation: pickrOptions.colorFormat, - disabled: pickrOptions.disabled, - }, - }, - }).on('init', instance => { - setColor(value); - disabled && instance.disable(); - - const { lastColor } = instance.getRoot().preview; - const { clear } = instance.getRoot().interaction; - - /* Cycle the keyboard navigation within the color picker */ - clear.addEventListener('keydown', (e) => { - if (e.keyCode === 9) { - e.preventDefault(); - e.stopPropagation(); - lastColor.focus(); - } - }); - - lastColor.addEventListener('keydown', (e) => { - if (e.keyCode === 9 && e.shiftKey) { - e.preventDefault(); - e.stopPropagation(); - clear.focus(); - } - }); - }).on('clear', () => { - onChange && onChange(''); - }).on('change', (color) => { - onChange && onChange(color.toHEXA().toString()); - }).on('show', (color, instance) => { - const { palette } = instance.getRoot().palette; - palette.focus(); - }).on('hide', (instance) => { - const button = instance.getRoot().button; - button.focus(); - }); - - if (currObj) { - currObj(pickrObj.current); - } - }; - - useEffect(() => { - initPickr(); - return () => { - destroyPickr(); - }; - }, [...Object.values(pickrOptions)]); - - useEffect(() => { - if (pickrObj.current) { - setColor(value); - } - }, [value]); - let btnStyles = { backgroundColor: value }; return ( - } + } options={{ + ...controlProps, + disabled: disabled + }} onChange={onChange} value={value} currObj={currObj} /> ); } diff --git a/web/pgadmin/static/js/helpers/withColorPicker.js b/web/pgadmin/static/js/helpers/withColorPicker.js new file mode 100644 index 000000000..58278baee --- /dev/null +++ b/web/pgadmin/static/js/helpers/withColorPicker.js @@ -0,0 +1,146 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2022, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +/* React HOC on color pickr */ +import Pickr from '@simonwep/pickr'; +import React, { useEffect, useRef } from 'react'; +import gettext from 'sources/gettext'; +import PropTypes from 'prop-types'; +import { fullHexColor } from '../utils'; + +export function withColorPicker(Component) { + // eslint-disable-next-line react/display-name + const HOCComponent = ({value, currObj, onChange, onSave, options, ...props})=>{ + const pickrOptions = { + showPalette: true, + allowEmpty: true, + allowSave: false, + colorFormat: 'HEX', + defaultColor: null, + position: 'right-middle', + clearText: gettext('Clear'), + ...options, + }; + const eleRef = useRef(); + const pickrObj = useRef(); + + const setColor = (newVal) => { + pickrObj.current?.setColor((_.isUndefined(newVal) || newVal == '') ? pickrOptions.defaultColor : newVal); + }; + + const destroyPickr = () => { + if (pickrObj.current) { + pickrObj.current.destroy(); + pickrObj.current = null; + } + }; + + const initPickr = () => { + /* pickr does not have way to update options, need to + destroy and recreate pickr to reflect options */ + destroyPickr(); + + pickrObj.current = new Pickr({ + el: eleRef.current, + useAsButton: true, + theme: 'monolith', + swatches: [ + '#000', '#666', '#ccc', '#fff', '#f90', '#ff0', '#0f0', + '#f0f', '#f4cccc', '#fce5cd', '#d0e0e3', '#cfe2f3', '#ead1dc', '#ea9999', + '#b6d7a8', '#a2c4c9', '#d5a6bd', '#e06666', '#93c47d', '#76a5af', '#c27ba0', + '#f1c232', '#6aa84f', '#45818e', '#a64d79', '#bf9000', '#0c343d', '#4c1130', + ], + position: pickrOptions.position, + strings: { + clear: pickrOptions.clearText, + }, + components: { + palette: pickrOptions.showPalette, + preview: true, + hue: pickrOptions.showPalette, + interaction: { + clear: pickrOptions.allowEmpty, + defaultRepresentation: pickrOptions.colorFormat, + disabled: pickrOptions.disabled, + save: pickrOptions.allowSave, + }, + }, + }).on('init', instance => { + setColor(value); + pickrOptions.disabled && instance.disable(); + + const { lastColor } = instance.getRoot().preview; + const { clear } = instance.getRoot().interaction; + + /* Cycle the keyboard navigation within the color picker */ + clear.addEventListener('keydown', (e) => { + if (e.keyCode === 9) { + e.preventDefault(); + e.stopPropagation(); + lastColor.focus(); + } + }); + + lastColor.addEventListener('keydown', (e) => { + if (e.keyCode === 9 && e.shiftKey) { + e.preventDefault(); + e.stopPropagation(); + clear.focus(); + } + }); + }).on('clear', () => { + onChange?.(''); + }).on('change', (color) => { + onChange?.(color.toHEXA().toString()); + }).on('show', (color, instance) => { + const { palette } = instance.getRoot().palette; + palette.focus(); + }).on('hide', (instance) => { + const button = instance.getRoot().button; + button.focus(); + }).on('save', (color, instance) => { + if(color) { + color.toHEXA().toString() != fullHexColor(value) && onSave?.(color.toHEXA().toString()); + instance?.hide(); + } else { + onSave?.(''); + } + }); + + if (currObj) { + currObj(pickrObj.current); + } + }; + + useEffect(() => { + initPickr(); + return () => { + destroyPickr(); + }; + }, [...Object.values(pickrOptions)]); + + useEffect(() => { + if (pickrObj.current && !pickrOptions.allowSave) { + setColor(value); + } + }, [value, pickrOptions.allowSave]); + + return ; + }; + + HOCComponent.propTypes = { + value: PropTypes.string, + currObj: PropTypes.func, + onChange: PropTypes.func, + onSave: PropTypes.func, + options: PropTypes.object, + }; + + return HOCComponent; +} diff --git a/web/pgadmin/static/js/utils.js b/web/pgadmin/static/js/utils.js index 7392abc73..34f697f27 100644 --- a/web/pgadmin/static/js/utils.js +++ b/web/pgadmin/static/js/utils.js @@ -658,3 +658,10 @@ export function pgHandleItemError(xhr, args) { } return false; } + +export function fullHexColor(shortHex) { + if(shortHex?.length == 4) { + return shortHex.replace(RegExp('#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])'), '#$1$1$2$2$3$3').toUpperCase(); + } + return shortHex; +} diff --git a/web/pgadmin/static/scss/_pickr.overrides.scss b/web/pgadmin/static/scss/_pickr.overrides.scss deleted file mode 100644 index 97cdce529..000000000 --- a/web/pgadmin/static/scss/_pickr.overrides.scss +++ /dev/null @@ -1,19 +0,0 @@ -$palette-soft-red: $color-ternary; -$border-radius-mid: $btn-border-radius; -$font-family: $font-family-base; -$box-shadow-app: $popover-box-shadow; - -@import "node_modules/@simonwep/pickr/src/scss/themes/monolith.scss"; - -.pickr, .pcr-app { - *:focus { - outline: none !important; - } -} - -.pickr .pcr-button{ - border: $input-border-width solid $input-border-color; - &:focus { - box-shadow: $input-btn-focus-box-shadow !important; - } -} diff --git a/web/pgadmin/static/scss/pgadmin.scss b/web/pgadmin/static/scss/pgadmin.scss index 4e6311a6c..2416b6782 100644 --- a/web/pgadmin/static/scss/pgadmin.scss +++ b/web/pgadmin/static/scss/pgadmin.scss @@ -27,7 +27,6 @@ $theme-colors: ( @import 'pgadmin.grid'; @import 'pgadmin.style'; @import 'bootstrap4-toggle.overrides'; -@import 'pickr.overrides'; @import 'jsoneditor.overrides'; @import 'pgadmin4-tree.overrides'; @import 'pgadmin4-tree/src/css/styles'; diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDConstants.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDConstants.js index 9b763ffe0..558a92711 100644 --- a/web/pgadmin/tools/erd/static/js/erd_tool/ERDConstants.js +++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDConstants.js @@ -13,6 +13,7 @@ export const ERD_EVENTS = { MANY_TO_MANY: 'MANY_TO_MANY', AUTO_DISTRIBUTE: 'AUTO_DISTRIBUTE', TOGGLE_DETAILS: 'TOGGLE_DETAILS', + CHANGE_COLORS: 'CHANGE_COLORS', ZOOM_FIT: 'ZOOM_FIT', ZOOM_IN: 'ZOOM_IN', ZOOM_OUT: 'ZOOM_OUT', diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js index 626f649cc..755e6daf0 100644 --- a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js +++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js @@ -202,10 +202,11 @@ export default class ERDCore { }); } - addNode(data, position=[50, 50]) { + addNode(data, position=[50, 50], metadata={}) { let newNode = this.getNewNode(data); this.clearSelection(); newNode.setPosition(position[0], position[1]); + newNode.setMetadata(metadata); this.getModel().addNode(newNode); return newNode; } diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx index 5d6ae3672..9d34895b1 100644 --- a/web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx +++ b/web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx @@ -118,6 +118,8 @@ class ERDTool extends React.Component { oto_dialog_open: true, otm_dialog_open: true, database: null, + fill_color: null, + text_color: null, }; this.diagram = new ERDCore(); /* Flag for checking if user has opted for save before close */ @@ -137,7 +139,7 @@ class ERDTool extends React.Component { _.bindAll(this, ['onLoadDiagram', 'onSaveDiagram', 'onSaveAsDiagram', 'onSQLClick', 'onImageClick', 'onAddNewNode', 'onEditTable', 'onCloneNode', 'onDeleteNode', 'onNoteClick', 'onNoteClose', 'onOneToManyClick', 'onManyToManyClick', 'onAutoDistribute', 'onDetailsToggle', - 'onDetailsToggle', 'onHelpClick', 'onDropNode', 'onBeforeUnload', + 'onDetailsToggle', 'onChangeColors', 'onHelpClick', 'onDropNode', 'onBeforeUnload', ]); this.diagram.zoomToFit = this.diagram.zoomToFit.bind(this.diagram); @@ -214,6 +216,7 @@ class ERDTool extends React.Component { this.eventBus.registerListener(ERD_EVENTS.MANY_TO_MANY, this.onManyToManyClick); this.eventBus.registerListener(ERD_EVENTS.AUTO_DISTRIBUTE, this.onAutoDistribute); this.eventBus.registerListener(ERD_EVENTS.TOGGLE_DETAILS, this.onDetailsToggle); + this.eventBus.registerListener(ERD_EVENTS.CHANGE_COLORS, this.onChangeColors); this.eventBus.registerListener(ERD_EVENTS.ZOOM_FIT, this.diagram.zoomToFit); this.eventBus.registerListener(ERD_EVENTS.ZOOM_IN, this.diagram.zoomIn); this.eventBus.registerListener(ERD_EVENTS.ZOOM_OUT, this.diagram.zoomOut); @@ -427,7 +430,10 @@ class ERDTool extends React.Component { if(this.diagram.anyDuplicateNodeName(newData)) { return gettext('Table name already exists'); } - let newNode = this.diagram.addNode(newData); + let newNode = this.diagram.addNode(newData, [50, 50], { + fillColor: this.state.fill_color, + textColor: this.state.text_color, + }); this.diagram.syncTableLinks(newNode); newNode.setSelected(true); }); @@ -461,7 +467,10 @@ class ERDTool extends React.Component { }); }); const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e); - this.diagram.addNode(dataPromise, [x, y]).setSelected(true); + this.diagram.addNode(dataPromise, [x, y], { + fillColor: this.state.fill_color, + textColor: this.state.text_color, + }).setSelected(true); } } } @@ -484,6 +493,7 @@ class ERDTool extends React.Component { if(newData) { let {x, y} = selected[0].getPosition(); let newNode = this.diagram.addNode(newData, [x+20, y+20]); + newNode.setMetadata(_.pick(selected[0].getMetadata(), ['fillColor', 'textColor'])); newNode.setSelected(true); } } @@ -511,6 +521,16 @@ class ERDTool extends React.Component { this.diagram.dagreDistributeNodes(); } + onChangeColors(fillColor, textColor) { + this.setState({ + fill_color: fillColor, + text_color: textColor, + }); + this.diagram.getSelectedNodes().forEach((node)=>{ + node.fireEvent({fillColor: fillColor, textColor: textColor}, 'changeColors'); + }); + } + onDetailsToggle() { this.setState((prevState)=>({ show_details: !prevState.show_details, @@ -903,7 +923,9 @@ class ERDTool extends React.Component { - +
{e.preventDefault();}}> diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/components/MainToolBar.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/components/MainToolBar.jsx index 22122046c..ff28cfae7 100644 --- a/web/pgadmin/tools/erd/static/js/erd_tool/components/MainToolBar.jsx +++ b/web/pgadmin/tools/erd/static/js/erd_tool/components/MainToolBar.jsx @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////// import React, {useCallback, useEffect, useState} from 'react'; import { makeStyles } from '@material-ui/styles'; -import { Box } from '@material-ui/core'; +import { Box, useTheme } from '@material-ui/core'; import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons'; import FolderRoundedIcon from '@material-ui/icons/FolderRounded'; import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; @@ -25,6 +25,8 @@ import NoteRoundedIcon from '@material-ui/icons/NoteRounded'; import VisibilityRoundedIcon from '@material-ui/icons/VisibilityRounded'; import VisibilityOffRoundedIcon from '@material-ui/icons/VisibilityOffRounded'; import ImageRoundedIcon from '@material-ui/icons/ImageRounded'; +import FormatColorFillRoundedIcon from '@material-ui/icons/FormatColorFillRounded'; +import FormatColorTextRoundedIcon from '@material-ui/icons/FormatColorTextRounded'; import { PgMenu, PgMenuItem, usePgMenuGroup } from '../../../../../../static/js/components/Menu'; import gettext from 'sources/gettext'; @@ -33,6 +35,7 @@ import PropTypes from 'prop-types'; import { ERD_EVENTS } from '../ERDConstants'; import { MagicIcon, SQLFileIcon } from '../../../../../../static/js/components/ExternalIcon'; import { useModal } from '../../../../../../static/js/helpers/ModalProvider'; +import { withColorPicker } from '../../../../../../static/js/helpers/withColorPicker'; const useStyles = makeStyles((theme)=>({ root: { @@ -54,8 +57,9 @@ const useStyles = makeStyles((theme)=>({ }, })); -export function MainToolBar({preferences, eventBus}) { +export function MainToolBar({preferences, eventBus, fillColor, textColor}) { const classes = useStyles(); + const theme = useTheme(); const [buttonsDisabled, setButtonsDisabled] = useState({ 'save': true, 'edit-table': true, @@ -196,7 +200,7 @@ export function MainToolBar({preferences, eventBus}) { } shortcut={preferences.add_table} onClick={()=>{ - eventBus.fireEvent(ERD_EVENTS.ADD_NODE); + eventBus.fireEvent(ERD_EVENTS.ADD_NODE, {fillColor: fillColor, textColor: textColor}); }} /> } shortcut={preferences.edit_table} disabled={buttonsDisabled['edit-table']} @@ -226,6 +230,30 @@ export function MainToolBar({preferences, eventBus}) { eventBus.fireEvent(ERD_EVENTS.MANY_TO_MANY); }} /> + + } + value={fillColor ?? theme.palette.background.default} options={{ + allowSave: true, + }} + onSave={(val)=>{ + if(val) { + eventBus.fireEvent(ERD_EVENTS.CHANGE_COLORS, val, textColor); + } else { + eventBus.fireEvent(ERD_EVENTS.CHANGE_COLORS, null, textColor); + } + }}/> + } + value={textColor ?? theme.palette.text.primary} options={{ + allowSave: true, + }} + onSave={(val)=>{ + if(val) { + eventBus.fireEvent(ERD_EVENTS.CHANGE_COLORS, fillColor, val); + } else { + eventBus.fireEvent(ERD_EVENTS.CHANGE_COLORS, fillColor, null); + } + }}/> + } shortcut={preferences.add_edit_note} disabled={buttonsDisabled['show-note']} @@ -290,4 +318,8 @@ export function MainToolBar({preferences, eventBus}) { MainToolBar.propTypes = { preferences: PropTypes.object, eventBus: PropTypes.object, + fillColor: PropTypes.string, + textColor: PropTypes.string, }; + +const ColorButton = withColorPicker(PgIconButton); diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx index 58f97f487..bbb6d2bfb 100644 --- a/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx +++ b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx @@ -49,6 +49,7 @@ export class TableNodeModel extends DefaultNodeModel { /* Once the data is available, it is no more a promise */ this._data = data; this._metadata = { + ...this._metadata, data_failed: false, is_promise: false, }; @@ -56,6 +57,7 @@ export class TableNodeModel extends DefaultNodeModel { this.fireEvent({}, 'nodeUpdated'); }).catch(()=>{ this._metadata = { + ...this._metadata, data_failed: true, is_promise: true, }; @@ -85,6 +87,13 @@ export class TableNodeModel extends DefaultNodeModel { return this._metadata; } + setMetadata(metadata) { + this._metadata = { + ...this._metadata, + ...metadata, + }; + } + addColumn(col) { this._data.columns.push(col); } @@ -152,6 +161,7 @@ RowIcon.propTypes = { const styles = (theme)=>({ tableNode: { backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, ...theme.mixins.panelBorder.all, borderRadius: theme.shape.borderRadius, position: 'relative', @@ -202,6 +212,12 @@ class TableNodeWidgetRaw extends React.Component { toggleDetails: (event) => { this.setState({show_details: event.show_details}); }, + changeColors: (event)=>{ + this.props.node.setMetadata({ + fillColor: event.fillColor, textColor: event.textColor, + }); + this.setState({}); + }, dataAvaiable: ()=>{ /* Just re-render */ this.setState({}); @@ -271,8 +287,13 @@ class TableNodeWidgetRaw extends React.Component { localUkCols.push(...uk.columns.map((c)=>c.column)); }); const {classes} = this.props; + const styles = { + backgroundColor: tableMetaData.fillColor, + color: tableMetaData.textColor, + }; return ( -
{this.props.node.fireEvent({}, 'editTable');}}> +
{this.props.node.fireEvent({}, 'editTable');}} style={styles}>
: } onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} /> diff --git a/web/regression/javascript/erd/fake_item.js b/web/regression/javascript/erd/fake_item.js index 94b510c8d..9332ca73b 100644 --- a/web/regression/javascript/erd/fake_item.js +++ b/web/regression/javascript/erd/fake_item.js @@ -25,6 +25,7 @@ export class FakeNode { retVal.name = tabName; return retVal; } + setMetadata() {/* no-op */} getMetadata() { return { is_promise: false, diff --git a/web/regression/javascript/erd/ui_components/ERDTool.spec.js b/web/regression/javascript/erd/ui_components/ERDTool.spec.js index c65131113..a9dfdcd15 100644 --- a/web/regression/javascript/erd/ui_components/ERDTool.spec.js +++ b/web/regression/javascript/erd/ui_components/ERDTool.spec.js @@ -132,6 +132,7 @@ describe('ERDTool', ()=>{ body = erd.find('ERDTool'); bodyInstance = body.instance(); spyOn(bodyInstance, 'getDialog').and.callFake(getDialog); + spyOn(bodyInstance, 'onChangeColors').and.callFake(()=>{/*no op*/}); }); afterAll(() => { @@ -248,7 +249,7 @@ describe('ERDTool', ()=>{ let saveCallback = tableDialog.calls.mostRecent().args[3]; let newData = {key: 'value'}; saveCallback(newData); - expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith(newData); + expect(bodyInstance.diagram.addNode.calls.mostRecent().args[0]).toEqual(newData); /* Existing */ tableDialog.calls.reset();