Fixed following issues in ERD:

1) After opening an existing project, the first table is already selected but the edit, clone, delete buttons are disabled.
 2) ERD project title gets changed when 2 ERD projects are open & anyone of it edited.
 3) Closing the ERD tab does not ask for a confirmation pop-up.
 4) Shortcut for 'Show more/Fewer details' is missing.
 5) Deleting the primary key does not delete associated links.
 6) The long table & schema name are getting out of the box.
 7) The long table name in the notes pop-up needs re-alignment.
 8) The same table name present in ERD/canvas is allowed in Add Table dialogue. Added validation in the dialog.
 9) Download image option is added, but it is not perfect yet. Image icons (table, schema, etc.) are not showing up.
 10) Rename panel option should be disabled by default. It should be enabled for the tools which implement rename functionality.
 11) The Toolbar is not visible in Safari for the ERD tool.

refs #1802
pull/39/head
Aditya Toshniwal 2021-01-25 17:29:54 +05:30 committed by Akshay Joshi
parent 446c2a19a5
commit 13db981445
20 changed files with 589 additions and 185 deletions

View File

@ -46,6 +46,7 @@
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"raw-loader": "^3.1.0", "raw-loader": "^3.1.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"resolve-url-loader": "^3.1.2",
"sass": "^1.24.4", "sass": "^1.24.4",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"sass-resources-loader": "^2.0.0", "sass-resources-loader": "^2.0.0",
@ -87,6 +88,7 @@
"dagre": "^0.8.4", "dagre": "^0.8.4",
"dropzone": "^5.5.1", "dropzone": "^5.5.1",
"exports-loader": "~0.7.0", "exports-loader": "~0.7.0",
"html2canvas": "^1.0.0-rc.7",
"immutability-helper": "^3.0.0", "immutability-helper": "^3.0.0",
"imports-loader": "^0.8.0", "imports-loader": "^0.8.0",
"ip-address": "^5.8.9", "ip-address": "^5.8.9",

View File

@ -30,7 +30,7 @@ define([
height: 600, height: 600,
showTitle: true, showTitle: true,
isClosable: true, isClosable: true,
isRenamable: true, isRenamable: false,
isPrivate: false, isPrivate: false,
url: '', url: '',
icon: '', icon: '',

View File

@ -41,11 +41,11 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
options: optionsMerged, options: optionsMerged,
}); });
props.onInit && props.onInit(chartObj.current); props.onInit && props.onInit(chartObj.current);
} };
const destroyChart = function() { const destroyChart = function() {
chartObj.current && chartObj.current.destroy(); chartObj.current && chartObj.current.destroy();
} };
useEffect(()=>{ useEffect(()=>{
initChart(); initChart();
@ -72,7 +72,7 @@ export default function BaseChart({type='line', id, options, data, redraw=false,
destroyChart(); destroyChart();
initChart(); initChart();
} }
}, [redraw]) }, [redraw]);
return ( return (
<canvas id={id} ref={chartRef}></canvas> <canvas id={id} ref={chartRef}></canvas>

View File

@ -180,7 +180,7 @@ define('pgadmin.datagrid', [
name: 'frm_datagrid', name: 'frm_datagrid',
showTitle: true, showTitle: true,
isCloseable: true, isCloseable: true,
isRenameable: true, isRenamable: true,
isPrivate: true, isPrivate: true,
url: 'about:blank', url: 'about:blank',
}); });

View File

@ -78,11 +78,11 @@ export function setQueryToolDockerTitle(panel, is_query_tool, panel_title, is_fi
export function set_renamable_option(panel, is_file) { export function set_renamable_option(panel, is_file) {
if(is_file || is_file == 'true') { if(is_file || is_file == 'true') {
panel._isRenamable = false; panel.renamable(false);
$('.conn-info-dd').hide(); $('.conn-info-dd').hide();
$('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'}); $('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'});
} else { } else {
panel._isRenamable = true; panel.renamable(true);
} }
} }

View File

@ -288,6 +288,24 @@ class ERDModule(PgAdminModule):
fields=shortcut_fields fields=shortcut_fields
) )
self.preference.register(
'keyboard_shortcuts',
'show_details',
gettext('Show more/fewer details'),
'keyboardshortcut',
{
'alt': True,
'shift': False,
'control': True,
'key': {
'key_code': 84,
'char': 't'
}
},
category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
fields=shortcut_fields
)
self.preference.register( self.preference.register(
'keyboard_shortcuts', 'keyboard_shortcuts',
'zoom_to_fit', 'zoom_to_fit',

View File

@ -212,14 +212,14 @@ export default class ERDCore {
let sourcePort = sourceNode.getPort(portName); let sourcePort = sourceNode.getPort(portName);
/* Create the port if not there */ /* Create the port if not there */
if(!sourcePort) { if(!sourcePort) {
sourcePort = sourceNode.addPort(this.getNewPort(type, null, {name:portName, alignment:PortModelAlignment.RIGHT})); sourcePort = sourceNode.addPort(this.getNewPort(type, null, {name:portName, subtype: 'one', alignment:PortModelAlignment.RIGHT}));
} }
portName = targetNode.getPortName(data.local_column_attnum); portName = targetNode.getPortName(data.local_column_attnum);
let targetPort = targetNode.getPort(portName); let targetPort = targetNode.getPort(portName);
/* Create the port if not there */ /* Create the port if not there */
if(!targetPort) { if(!targetPort) {
targetPort = targetNode.addPort(this.getNewPort(type, null, {name:portName, alignment:PortModelAlignment.RIGHT})); targetPort = targetNode.addPort(this.getNewPort(type, null, {name:portName, subtype: 'many', alignment:PortModelAlignment.RIGHT}));
} }
/* Link the ports */ /* Link the ports */

View File

@ -50,7 +50,7 @@ export default class TableDialog {
return 'entity_dialog'; return 'entity_dialog';
} }
getDataModel(attributes, colTypes, schemas, sVersion) { getDataModel(attributes, existingTables, colTypes, schemas, sVersion) {
let dialogObj = this; let dialogObj = this;
let columnsModel = this.pgBrowser.DataModel.extend({ let columnsModel = this.pgBrowser.DataModel.extend({
idAttribute: 'attnum', idAttribute: 'attnum',
@ -694,6 +694,10 @@ export default class TableDialog {
msg = gettext('Table name cannot be empty.'); msg = gettext('Table name cannot be empty.');
this.errorModel.set('name', msg); this.errorModel.set('name', msg);
return msg; return msg;
} else if(_.findIndex(existingTables, (table)=>table[0]==schema&&table[1]==name) >= 0) {
msg = gettext('Table name already exists.');
this.errorModel.set('name', msg);
return msg;
} }
this.errorModel.unset('name'); this.errorModel.unset('name');
if ( if (
@ -705,6 +709,8 @@ export default class TableDialog {
return msg; return msg;
} }
this.errorModel.unset('schema'); this.errorModel.unset('schema');
return null; return null;
}, },
}); });
@ -731,9 +737,9 @@ export default class TableDialog {
return Alertify[dialogName]; return Alertify[dialogName];
} }
show(title, attributes, colTypes, schemas, sVersion, callback) { show(title, attributes, existingTables, colTypes, schemas, sVersion, callback) {
let dialogTitle = title || gettext('Unknown'); let dialogTitle = title || gettext('Unknown');
const dialog = this.createOrGetDialog('table_dialog'); const dialog = this.createOrGetDialog('table_dialog');
dialog(dialogTitle, this.getDataModel(attributes, colTypes, schemas, sVersion), callback).resizeTo(this.pgBrowser.stdW.md, this.pgBrowser.stdH.md); dialog(dialogTitle, this.getDataModel(attributes, existingTables, colTypes, schemas, sVersion), callback).resizeTo(this.pgBrowser.stdW.md, this.pgBrowser.stdH.md);
} }
} }

View File

@ -9,10 +9,13 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import _ from 'lodash';
import BodyWidget from './ui_components/BodyWidget'; import BodyWidget from './ui_components/BodyWidget';
import getDialog, {transformToSupported} from './dialogs'; import getDialog, {transformToSupported} from './dialogs';
import Alertify from 'pgadmin.alertifyjs'; import Alertify from 'pgadmin.alertifyjs';
import pgWindow from 'sources/window'; import pgWindow from 'sources/window';
import pgAdmin from 'sources/pgadmin';
export default class ERDTool { export default class ERDTool {
constructor(container, params) { constructor(container, params) {
@ -20,10 +23,28 @@ export default class ERDTool {
this.params = params; this.params = params;
} }
getPreferencesForModule() {
}
render() { render() {
/* Mount the React ERD tool to the container */ /* Mount the React ERD tool to the container */
let panel = null;
_.each(pgWindow.pgAdmin.Browser.docker.findPanels('frm_erdtool'), function(p) {
if (p.isVisible()) {
panel = p;
}
});
ReactDOM.render( ReactDOM.render(
<BodyWidget params={this.params} getDialog={getDialog} transformToSupported={transformToSupported} pgAdmin={pgWindow.pgAdmin} alertify={Alertify} />, <BodyWidget
params={this.params}
getDialog={getDialog}
transformToSupported={transformToSupported}
pgWindow={pgWindow}
pgAdmin={pgAdmin}
panel={panel}
alertify={Alertify} />,
this.container this.container
); );
} }

View File

@ -9,12 +9,12 @@
import React from 'react'; import React from 'react';
import { import {
RightAngleLinkModel, RightAngleLinkModel,
RightAngleLinkWidget, RightAngleLinkWidget,
DefaultLinkFactory, DefaultLinkFactory,
PortModelAlignment, PortModelAlignment,
LinkWidget, LinkWidget,
PointModel PointModel,
} from '@projectstorm/react-diagrams'; } from '@projectstorm/react-diagrams';
import {Point} from '@projectstorm/geometry'; import {Point} from '@projectstorm/geometry';
import _ from 'lodash'; import _ from 'lodash';
@ -24,7 +24,7 @@ export const OneToManyModel = {
local_column_attnum: undefined, local_column_attnum: undefined,
referenced_table_uid: undefined, referenced_table_uid: undefined,
referenced_column_attnum: undefined, referenced_column_attnum: undefined,
} };
export class OneToManyLinkModel extends RightAngleLinkModel { export class OneToManyLinkModel extends RightAngleLinkModel {
constructor({data, ...options}) { constructor({data, ...options}) {
@ -33,7 +33,7 @@ export class OneToManyLinkModel extends RightAngleLinkModel {
width: 1, width: 1,
class: 'link-onetomany', class: 'link-onetomany',
locked: true, locked: true,
...options ...options,
}); });
this._data = { this._data = {
@ -62,13 +62,13 @@ export class OneToManyLinkModel extends RightAngleLinkModel {
'local_column': _.find(target.columns, (col)=>data.local_column_attnum == col.attnum).name, 'local_column': _.find(target.columns, (col)=>data.local_column_attnum == col.attnum).name,
'referenced': _.find(source.columns, (col)=>data.referenced_column_attnum == col.attnum).name, 'referenced': _.find(source.columns, (col)=>data.referenced_column_attnum == col.attnum).name,
}], }],
} };
} }
serialize() { serialize() {
return { return {
...super.serialize(), ...super.serialize(),
data: this.getData() data: this.getData(),
}; };
} }
} }
@ -83,13 +83,13 @@ const CustomLinkEndWidget = props => {
<circle className="svg-link-ele svg-otom-circle" cx="0" cy="16" r={props.width*1.75} strokeWidth={props.width} /> <circle className="svg-link-ele svg-otom-circle" cx="0" cy="16" r={props.width*1.75} strokeWidth={props.width} />
<polyline className="svg-link-ele" points="-8,0 0,15 0,0 0,30 0,15 8,0" fill="none" strokeWidth={props.width} /> <polyline className="svg-link-ele" points="-8,0 0,15 0,0 0,30 0,15 8,0" fill="none" strokeWidth={props.width} />
</> </>
) );
} else if (type == 'one') { } else if (type == 'one') {
return ( return (
<polyline className="svg-link-ele" points="-8,15 0,15 0,0 0,30 0,15 8,15" fill="none" strokeWidth={props.width} /> <polyline className="svg-link-ele" points="-8,15 0,15 0,0 0,30 0,15 8,15" fill="none" strokeWidth={props.width} />
) );
} }
} };
return ( return (
<g transform={'translate(' + point.getPosition().x + ', ' + point.getPosition().y + ')'}> <g transform={'translate(' + point.getPosition().x + ', ' + point.getPosition().y + ')'}>
@ -111,21 +111,21 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
let degree = 0; let degree = 0;
let tx = 0, ty = 0; let tx = 0, ty = 0;
switch(alignment) { switch(alignment) {
case PortModelAlignment.BOTTOM: case PortModelAlignment.BOTTOM:
ty = -offset; ty = -offset;
break; break;
case PortModelAlignment.LEFT: case PortModelAlignment.LEFT:
degree = 90; degree = 90;
tx = offset tx = offset;
break; break;
case PortModelAlignment.TOP: case PortModelAlignment.TOP:
degree = 180; degree = 180;
ty = offset; ty = offset;
break; break;
case PortModelAlignment.RIGHT: case PortModelAlignment.RIGHT:
degree = -90; degree = -90;
tx = -offset; tx = -offset;
break; break;
} }
return [degree, tx, ty]; return [degree, tx, ty];
} }
@ -146,8 +146,8 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
point: point, point: point,
rotation: rotation, rotation: rotation,
tx: tx, tx: tx,
ty: ty ty: ty,
} };
} }
generateCustomEndWidget({type, point, rotation, tx, ty}) { generateCustomEndWidget({type, point, rotation, tx, ty}) {
@ -183,7 +183,7 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
} }
handleMove = function(event) { handleMove = function(event) {
this.props.link.getTargetPort() this.props.link.getTargetPort();
this.draggingEvent(event, this.dragging_index); this.draggingEvent(event, this.dragging_index);
this.props.link.fireEvent({}, 'positionChanged'); this.props.link.fireEvent({}, 'positionChanged');
}.bind(this); }.bind(this);
@ -220,7 +220,7 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
this.props.link.addPoint( this.props.link.addPoint(
new PointModel({ new PointModel({
link: this.props.link, link: this.props.link,
position: new Point(onePoint.point.getX(), manyPoint.point.getY()) position: new Point(onePoint.point.getX(), manyPoint.point.getY()),
}) })
); );
} }
@ -246,7 +246,7 @@ export class OneToManyLinkWidget extends RightAngleLinkWidget {
onMouseEnter: (event) => { onMouseEnter: (event) => {
this.setState({ selected: true }); this.setState({ selected: true });
this.props.link.lastHoverIndexOfPath = j; this.props.link.lastHoverIndexOfPath = j;
} },
}, },
j j
) )

View File

@ -19,7 +19,7 @@ export class TableNodeModel extends DefaultNodeModel {
constructor({otherInfo, ...options}) { constructor({otherInfo, ...options}) {
super({ super({
...options, ...options,
type: TYPE type: TYPE,
}); });
this._note = otherInfo.note || ''; this._note = otherInfo.note || '';
@ -60,22 +60,22 @@ export class TableNodeModel extends DefaultNodeModel {
cloneData(name) { cloneData(name) {
let newData = { let newData = {
...this.getData() ...this.getData(),
}; };
if(name) { if(name) {
newData['name'] = name newData['name'] = name;
} }
return newData; return newData;
} }
setData(data) { setData(data) {
let self = this; let self = this;
/* Remove the links if column dropped */ /* Remove the links if column dropped or primary key removed */
_.differenceWith(this._data.columns, data.columns, function(existing, incoming) { _.differenceWith(this._data.columns, data.columns, function(existing, incoming) {
return existing.attnum == incoming.attnum; return existing.attnum == incoming.attnum && incoming.is_primary_key == true;
}).forEach((col)=>{ }).forEach((col)=>{
let existPort = self.getPort(self.getPortName(col.attnum)); let existPort = self.getPort(self.getPortName(col.attnum));
if(existPort) { if(existPort && existPort.getSubtype() == 'one') {
existPort.removeAllLinks(); existPort.removeAllLinks();
self.removePort(existPort); self.removePort(existPort);
} }
@ -109,7 +109,7 @@ export class TableNodeModel extends DefaultNodeModel {
otherInfo: { otherInfo: {
data: this.getData(), data: this.getData(),
note: this.getNote(), note: this.getNote(),
} },
}; };
} }
} }
@ -119,8 +119,8 @@ export class TableNodeWidget extends React.Component {
super(props); super(props);
this.state = { this.state = {
show_details: true show_details: true,
} };
this.props.node.registerListener({ this.props.node.registerListener({
toggleDetails: (event) => { toggleDetails: (event) => {
@ -143,13 +143,13 @@ export class TableNodeWidget extends React.Component {
</div> </div>
<div className="ml-auto col-row-port">{this.generatePort(port)}</div> <div className="ml-auto col-row-port">{this.generatePort(port)}</div>
</div> </div>
) );
} }
generatePort = port => { generatePort = port => {
if(port) { if(port) {
return ( return (
<PortWidget engine={this.props.engine} port={port} key={port.getID()} className={"port-" + port.options.alignment} /> <PortWidget engine={this.props.engine} port={port} key={port.getID()} className={'port-' + port.options.alignment} />
); );
} }
return <></>; return <></>;
@ -163,21 +163,21 @@ export class TableNodeWidget extends React.Component {
render() { render() {
let node_data = this.props.node.getData(); let node_data = this.props.node.getData();
return ( return (
<div className={"table-node " + (this.props.node.isSelected() ? 'selected': '') } onDoubleClick={()=>{this.props.node.fireEvent({}, 'editNode')}}> <div className={'table-node ' + (this.props.node.isSelected() ? 'selected': '') } onDoubleClick={()=>{this.props.node.fireEvent({}, 'editNode');}}>
<div className="table-toolbar"> <div className="table-toolbar">
<DetailsToggleButton className='btn-xs' showDetails={this.state.show_details} onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} /> <DetailsToggleButton className='btn-xs' showDetails={this.state.show_details} onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} />
{this.props.node.getNote() && {this.props.node.getNote() &&
<IconButton icon="far fa-sticky-note" className="btn-xs btn-warning ml-auto" onClick={()=>{ <IconButton icon="far fa-sticky-note" className="btn-xs btn-warning ml-auto" onClick={()=>{
this.props.node.fireEvent({}, 'showNote') this.props.node.fireEvent({}, 'showNote');
}} title="Check note" />} }} title="Check note" />}
</div> </div>
<div className="table-schema"> <div className="d-flex table-schema-data">
<span className="wcTabIcon icon-schema"></span> <div className="table-icon-schema"></div>
{node_data.schema} <div className="table-schema">{node_data.schema}</div>
</div> </div>
<div className="table-name"> <div className="d-flex table-name-data">
<span className="wcTabIcon icon-table"></span> <div><span className="wcTabIcon icon-table"></span></div>
{node_data.name} <div className="table-name">{node_data.name}</div>
</div> </div>
<div className="table-cols"> <div className="table-cols">
{_.map(node_data.columns, (col)=>this.generateColumn(col))} {_.map(node_data.columns, (col)=>this.generateColumn(col))}

View File

@ -16,6 +16,7 @@ const TYPE = 'onetomany';
export default class OneToManyPortModel extends PortModel { export default class OneToManyPortModel extends PortModel {
constructor({options}) { constructor({options}) {
super({ super({
subtype: 'notset',
...options, ...options,
type: TYPE, type: TYPE,
}); });
@ -30,6 +31,22 @@ export default class OneToManyPortModel extends PortModel {
createLinkModel() { createLinkModel() {
return new OneToManyLinkModel({}); return new OneToManyLinkModel({});
} }
getSubtype() {
return this.options.subtype;
}
deserialize(event) {
super.deserialize(event);
this.options.subtype = event.data.subtype || 'notset';
}
serialize() {
return {
...super.serialize(),
subtype: this.options.subtype,
};
}
} }
export class OneToManyPortFactory extends AbstractModelFactory { export class OneToManyPortFactory extends AbstractModelFactory {

View File

@ -12,6 +12,8 @@ import { CanvasWidget } from '@projectstorm/react-canvas-core';
import axios from 'axios'; import axios from 'axios';
import { Action, InputType } from '@projectstorm/react-canvas-core'; import { Action, InputType } from '@projectstorm/react-canvas-core';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import _ from 'lodash';
import html2canvas from 'html2canvas';
import ERDCore from '../ERDCore'; import ERDCore from '../ERDCore';
import ToolBar, {IconButton, DetailsToggleButton, ButtonGroup} from './ToolBar'; import ToolBar, {IconButton, DetailsToggleButton, ButtonGroup} from './ToolBar';
@ -22,22 +24,25 @@ import {setPanelTitle} from '../../erd_module';
import gettext from 'sources/gettext'; import gettext from 'sources/gettext';
import url_for from 'sources/url_for'; import url_for from 'sources/url_for';
import {showERDSqlTool} from 'tools/datagrid/static/js/show_query_tool'; import {showERDSqlTool} from 'tools/datagrid/static/js/show_query_tool';
import 'wcdocker';
/* Custom react-diagram action for keyboard events */ /* Custom react-diagram action for keyboard events */
export class KeyboardShortcutAction extends Action { export class KeyboardShortcutAction extends Action {
constructor(shortcut_handlers=[]) { constructor(shortcut_handlers=[]) {
super({ super({
type: InputType.KEY_DOWN, type: InputType.KEY_DOWN,
fire: ({ event })=>{ fire: ({ event })=>{
this.callHandler(event); this.callHandler(event);
} },
}); });
this.shortcuts = {}; this.shortcuts = {};
for(let i=0; i<shortcut_handlers.length; i++){ for(let i=0; i<shortcut_handlers.length; i++){
let [key, handler] = shortcut_handlers[i]; let [key, handler] = shortcut_handlers[i];
if(key) {
this.shortcuts[this.shortcutKey(key.alt, key.control, key.shift, false, key.key.key_code)] = handler; this.shortcuts[this.shortcutKey(key.alt, key.control, key.shift, false, key.key.key_code)] = handler;
} }
}
} }
shortcutKey(altKey, ctrlKey, shiftKey, metaKey, keyCode) { shortcutKey(altKey, ctrlKey, shiftKey, metaKey, keyCode) {
@ -69,9 +74,12 @@ export default class BodyWidget extends React.Component {
current_file: null, current_file: null,
dirty: false, dirty: false,
show_details: true, show_details: true,
is_new_tab: false,
preferences: {}, preferences: {},
} };
this.diagram = new ERDCore(); this.diagram = new ERDCore();
/* Flag for checking if user has opted for save before close */
this.closeOnSave = React.createRef();
this.fileInputRef = React.createRef(); this.fileInputRef = React.createRef();
this.diagramContainerRef = React.createRef(); this.diagramContainerRef = React.createRef();
this.canvasEle = null; this.canvasEle = null;
@ -83,6 +91,7 @@ export default class BodyWidget extends React.Component {
this.onSaveDiagram = this.onSaveDiagram.bind(this); this.onSaveDiagram = this.onSaveDiagram.bind(this);
this.onSaveAsDiagram = this.onSaveAsDiagram.bind(this); this.onSaveAsDiagram = this.onSaveAsDiagram.bind(this);
this.onSQLClick = this.onSQLClick.bind(this); this.onSQLClick = this.onSQLClick.bind(this);
this.onImageClick = this.onImageClick.bind(this);
this.onAddNewNode = this.onAddNewNode.bind(this); this.onAddNewNode = this.onAddNewNode.bind(this);
this.onEditNode = this.onEditNode.bind(this); this.onEditNode = this.onEditNode.bind(this);
this.onCloneNode = this.onCloneNode.bind(this); this.onCloneNode = this.onCloneNode.bind(this);
@ -133,7 +142,7 @@ export default class BodyWidget extends React.Component {
}, },
'editNode': (event) => { 'editNode': (event) => {
this.addEditNode(event.node); this.addEditNode(event.node);
} },
}; };
Object.keys(diagramEvents).forEach(eventName => { Object.keys(diagramEvents).forEach(eventName => {
this.diagram.registerModelEvent(eventName, diagramEvents[eventName]); this.diagram.registerModelEvent(eventName, diagramEvents[eventName]);
@ -157,9 +166,10 @@ export default class BodyWidget extends React.Component {
[this.state.preferences.one_to_many, this.onOneToManyClick], [this.state.preferences.one_to_many, this.onOneToManyClick],
[this.state.preferences.many_to_many, this.onManyToManyClick], [this.state.preferences.many_to_many, this.onManyToManyClick],
[this.state.preferences.auto_align, this.onAutoDistribute], [this.state.preferences.auto_align, this.onAutoDistribute],
[this.state.preferences.show_details, this.onDetailsToggle],
[this.state.preferences.zoom_to_fit, this.diagram.zoomToFit], [this.state.preferences.zoom_to_fit, this.diagram.zoomToFit],
[this.state.preferences.zoom_in, this.diagram.zoomIn], [this.state.preferences.zoom_in, this.diagram.zoomIn],
[this.state.preferences.zoom_out, this.diagram.zoomOut] [this.state.preferences.zoom_out, this.diagram.zoomOut],
]); ]);
this.diagram.registerKeyAction(this.keyboardActionObj); this.diagram.registerKeyAction(this.keyboardActionObj);
@ -182,11 +192,16 @@ export default class BodyWidget extends React.Component {
} }
async componentDidMount() { async componentDidMount() {
this.setLoading('Preparing'); this.setLoading(gettext('Preparing...'));
this.setTitle(this.state.current_file);
this.setState({ this.setState({
preferences: this.props.pgAdmin.Browser.get_preferences_for_module('erd') preferences: this.props.pgWindow.pgAdmin.Browser.get_preferences_for_module('erd'),
}, this.registerKeyboardShortcuts); is_new_tab: (this.props.pgWindow.pgAdmin.Browser.get_preferences_for_module('browser').new_browser_tab_open || '')
.includes('erd_tool'),
}, ()=>{
this.registerKeyboardShortcuts();
this.setTitle(this.state.current_file);
});
this.registerModelEvents(); this.registerModelEvents();
this.realignGrid({ this.realignGrid({
backgroundSize: '45px 45px', backgroundSize: '45px 45px',
@ -197,10 +212,19 @@ export default class BodyWidget extends React.Component {
this.props.pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', this.saveFile, this); this.props.pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', this.saveFile, this);
this.props.pgAdmin.Browser.onPreferencesChange('erd', () => { this.props.pgAdmin.Browser.onPreferencesChange('erd', () => {
this.setState({ this.setState({
preferences: this.props.pgAdmin.Browser.get_preferences_for_module('erd') preferences: this.props.pgWindow.pgAdmin.Browser.get_preferences_for_module('erd'),
}, ()=>this.registerKeyboardShortcuts()); }, ()=>this.registerKeyboardShortcuts());
}); });
this.props.panel?.on(window.wcDocker?.EVENT.CLOSING, () => {
if(this.state.dirty) {
this.closeOnSave = false;
this.confirmBeforeClose();
return false;
}
return true;
});
let done = await this.initConnection(); let done = await this.initConnection();
if(!done) return; if(!done) return;
@ -218,18 +242,78 @@ export default class BodyWidget extends React.Component {
} }
} }
confirmBeforeClose() {
let bodyObj = this;
this.props.alertify.confirmSave || this.props.alertify.dialog('confirmSave', function() {
return {
main: function(title, message) {
this.setHeader(title);
this.setContent(message);
},
setup: function() {
return {
buttons: [{
text: gettext('Cancel'),
key: 27, // ESC
invokeOnClose: true,
className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button',
}, {
text: gettext('Don\'t save'),
className: 'btn btn-secondary fa fa-lg fa-trash-alt pg-alertify-button',
}, {
text: gettext('Save'),
className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
}],
focus: {
element: 0,
select: false,
},
options: {
maximizable: false,
resizable: false,
},
};
},
callback: function(closeEvent) {
switch (closeEvent.index) {
case 0: // Cancel
//Do nothing.
break;
case 1: // Don't Save
bodyObj.closePanel();
break;
case 2: //Save
bodyObj.onSaveDiagram(false, true);
break;
}
},
};
});
this.props.alertify.confirmSave(gettext('Save changes?'), gettext('The diagram has changed. Do you want to save changes?'));
return false;
}
closePanel() {
window.onbeforeunload = null;
this.props.panel.off(wcDocker.EVENT.CLOSING);
this.props.pgWindow.pgAdmin.Browser.docker.removePanel(this.props.panel);
}
getDialog(dialogName) { getDialog(dialogName) {
if(dialogName === 'entity_dialog') { if(dialogName === 'entity_dialog') {
let existingTables = this.diagram.getModel().getNodes().map((node)=>{
return node.getSchemaTableName();
});
return (title, attributes, callback)=>{ return (title, attributes, callback)=>{
this.props.getDialog(dialogName).show( this.props.getDialog(dialogName).show(
title, attributes, this.diagram.getCache('colTypes'), this.diagram.getCache('schemas'), this.state.server_version, callback title, attributes, existingTables, this.diagram.getCache('colTypes'), this.diagram.getCache('schemas'), this.state.server_version, callback
); );
}; };
} else if(dialogName === 'onetomany_dialog' || dialogName === 'manytomany_dialog') { } else if(dialogName === 'onetomany_dialog' || dialogName === 'manytomany_dialog') {
return (title, attributes, callback)=>{ return (title, attributes, callback)=>{
this.props.getDialog(dialogName).show( this.props.getDialog(dialogName).show(
title, attributes, this.diagram.getModel().getNodesDict(), this.state.server_version, callback title, attributes, this.diagram.getModel().getNodesDict(), this.state.server_version, callback
); );
}; };
} }
} }
@ -289,20 +373,20 @@ export default class BodyWidget extends React.Component {
gettext('Delete ?'), gettext('Delete ?'),
gettext('You have selected %s tables and %s links.', this.diagram.getSelectedNodes().length, this.diagram.getSelectedLinks().length) gettext('You have selected %s tables and %s links.', this.diagram.getSelectedNodes().length, this.diagram.getSelectedLinks().length)
+ '<br />' + gettext('Are you sure you want to delete ?'), + '<br />' + gettext('Are you sure you want to delete ?'),
() => { () => {
this.diagram.getSelectedNodes().forEach((node)=>{ this.diagram.getSelectedNodes().forEach((node)=>{
node.setSelected(false); node.setSelected(false);
node.remove(); node.remove();
}); });
this.diagram.getSelectedLinks().forEach((link)=>{ this.diagram.getSelectedLinks().forEach((link)=>{
link.getTargetPort().remove(); link.getTargetPort().remove();
link.getSourcePort().remove(); link.getSourcePort().remove();
link.setSelected(false); link.setSelected(false);
link.remove(); link.remove();
}); });
this.diagram.repaint(); this.diagram.repaint();
}, },
() => {} () => {}
); );
} }
@ -312,11 +396,11 @@ export default class BodyWidget extends React.Component {
onDetailsToggle() { onDetailsToggle() {
this.setState((prevState)=>({ this.setState((prevState)=>({
show_details: !prevState.show_details show_details: !prevState.show_details,
}), ()=>{ }), ()=>{
this.diagram.getModel().getNodes().forEach((node)=>{ this.diagram.getModel().getNodes().forEach((node)=>{
node.fireEvent({show_details: this.state.show_details}, 'toggleDetails'); node.fireEvent({show_details: this.state.show_details}, 'toggleDetails');
}) });
}); });
} }
@ -335,8 +419,9 @@ export default class BodyWidget extends React.Component {
} }
openFile(fileName) { openFile(fileName) {
this.setLoading(gettext('Loading project...'));
axios.post(url_for('sqleditor.load_file'), { axios.post(url_for('sqleditor.load_file'), {
'file_name': decodeURI(fileName) 'file_name': decodeURI(fileName),
}).then((res)=>{ }).then((res)=>{
this.setState({ this.setState({
current_file: fileName, current_file: fileName,
@ -344,13 +429,17 @@ export default class BodyWidget extends React.Component {
}); });
this.setTitle(fileName); this.setTitle(fileName);
this.diagram.deserialize(res.data); this.diagram.deserialize(res.data);
this.diagram.clearSelection();
this.registerModelEvents(); this.registerModelEvents();
}).catch((err)=>{ }).catch((err)=>{
this.handleAxiosCatch(err); this.handleAxiosCatch(err);
}).then(()=>{
this.setLoading(null);
}); });
} }
onSaveDiagram(isSaveAs=false) { onSaveDiagram(isSaveAs=false, closeOnSave=false) {
this.closeOnSave = closeOnSave;
if(this.state.current_file && !isSaveAs) { if(this.state.current_file && !isSaveAs) {
this.saveFile(this.state.current_file); this.saveFile(this.state.current_file);
} else { } else {
@ -370,9 +459,10 @@ export default class BodyWidget extends React.Component {
} }
saveFile(fileName) { saveFile(fileName) {
this.setLoading(gettext('Saving...'));
axios.post(url_for('sqleditor.save_file'), { axios.post(url_for('sqleditor.save_file'), {
'file_name': decodeURI(fileName), 'file_name': decodeURI(fileName),
'file_content': JSON.stringify(this.diagram.serialize(this.props.pgAdmin.Browser.utils.app_version_int)) 'file_content': JSON.stringify(this.diagram.serialize(this.props.pgAdmin.Browser.utils.app_version_int)),
}).then(()=>{ }).then(()=>{
this.props.alertify.success(gettext('Project saved successfully.')); this.props.alertify.success(gettext('Project saved successfully.'));
this.setState({ this.setState({
@ -380,7 +470,12 @@ export default class BodyWidget extends React.Component {
dirty: false, dirty: false,
}); });
this.setTitle(fileName); this.setTitle(fileName);
this.setLoading(null);
if(this.closeOnSave) {
this.closePanel.call(this);
}
}).catch((err)=>{ }).catch((err)=>{
this.setLoading(null);
this.handleAxiosCatch(err); this.handleAxiosCatch(err);
}); });
} }
@ -395,14 +490,10 @@ export default class BodyWidget extends React.Component {
title = 'Untitled'; title = 'Untitled';
} }
title = this.getCurrentProjectName(title) + (dirty ? '*': ''); title = this.getCurrentProjectName(title) + (dirty ? '*': '');
if (this.new_browser_tab) { if (this.state.is_new_tab) {
window.document.title = title; window.document.title = title;
} else { } else {
_.each(this.props.pgAdmin.Browser.docker.findPanels('frm_erdtool'), function(p) { setPanelTitle(this.props.panel, title);
if (p.isVisible()) {
setPanelTitle(p, title);
}
});
} }
} }
@ -414,7 +505,7 @@ export default class BodyWidget extends React.Component {
trans_id: this.props.params.trans_id, trans_id: this.props.params.trans_id,
sgid: this.props.params.sgid, sgid: this.props.params.sgid,
sid: this.props.params.sid, sid: this.props.params.sid,
did: this.props.params.did did: this.props.params.did,
}); });
this.setLoading(gettext('Preparing the SQL...')); this.setLoading(gettext('Preparing the SQL...'));
@ -428,18 +519,53 @@ export default class BodyWidget extends React.Component {
sid: this.props.params.sid, sid: this.props.params.sid,
did: this.props.params.did, did: this.props.params.did,
stype: this.props.params.server_type, stype: this.props.params.server_type,
} };
let sqlId = `erd${this.props.params.trans_id}`; let sqlId = `erd${this.props.params.trans_id}`;
localStorage.setItem(sqlId, sqlScript); localStorage.setItem(sqlId, sqlScript);
showERDSqlTool(parentData, sqlId, this.props.params.title, this.props.pgAdmin.DataGrid, this.props.alertify); showERDSqlTool(parentData, sqlId, this.props.params.title, this.props.pgWindow.pgAdmin.DataGrid, this.props.alertify);
}) })
.catch((error)=>{ .catch((error)=>{
this.handleAxiosCatch(error); this.handleAxiosCatch(error);
}) })
.then(()=>{ .then(()=>{
this.setLoading(null); this.setLoading(null);
}) });
}
onImageClick() {
this.setLoading(gettext('Preparing the image...'));
/* Change the styles for suiting html2canvas */
this.canvasEle.classList.add('html2canvas-reset');
this.canvasEle.style.width = this.canvasEle.scrollWidth + 'px';
this.canvasEle.style.height = this.canvasEle.scrollHeight + 'px';
html2canvas(this.canvasEle, {
width: this.canvasEle.scrollWidth + 10,
height: this.canvasEle.scrollHeight + 10,
scrollX: 0,
scrollY: 0,
useCORS: true,
allowTaint: true,
backgroundColor: window.getComputedStyle(this.canvasEle).backgroundColor,
}).then((canvas)=>{
let link = document.createElement('a');
link.setAttribute('href', canvas.toDataURL('image/png'));
link.setAttribute('download', this.getCurrentProjectName() + '.png');
link.click();
}).catch((err)=>{
console.error(err);
this.props.alertify.alert()
.set('title', gettext('Error'))
.set('message', err).show();
}).then(()=>{
/* Revert back to the original CSS styles */
this.canvasEle.classList.remove('html2canvas-reset');
this.canvasEle.style.width = '';
this.canvasEle.style.height = '';
this.setLoading(null);
});
} }
onOneToManyClick() { onOneToManyClick() {
@ -473,8 +599,8 @@ export default class BodyWidget extends React.Component {
'name': `${right_table.getData().name}_${right_table.getColumnAt(newData.right_table_column_attnum).name}`, 'name': `${right_table.getData().name}_${right_table.getColumnAt(newData.right_table_column_attnum).name}`,
'is_primary_key': false, 'is_primary_key': false,
'attnum': 1, 'attnum': 1,
}] }],
} };
let newNode = this.diagram.addNode(tableData); let newNode = this.diagram.addNode(tableData);
this.diagram.clearSelection(); this.diagram.clearSelection();
newNode.setSelected(true); newNode.setSelected(true);
@ -484,7 +610,7 @@ export default class BodyWidget extends React.Component {
local_column_attnum: newNode.getColumns()[0].attnum, local_column_attnum: newNode.getColumns()[0].attnum,
referenced_table_uid: newData.left_table_uid, referenced_table_uid: newData.left_table_uid,
referenced_column_attnum : newData.left_table_column_attnum, referenced_column_attnum : newData.left_table_column_attnum,
} };
this.diagram.addLink(linkData, 'onetomany'); this.diagram.addLink(linkData, 'onetomany');
linkData = { linkData = {
@ -492,7 +618,7 @@ export default class BodyWidget extends React.Component {
local_column_attnum: newNode.getColumns()[1].attnum, local_column_attnum: newNode.getColumns()[1].attnum,
referenced_table_uid: newData.right_table_uid, referenced_table_uid: newData.right_table_uid,
referenced_column_attnum : newData.right_table_column_attnum, referenced_column_attnum : newData.right_table_column_attnum,
} };
this.diagram.addLink(linkData, 'onetomany'); this.diagram.addLink(linkData, 'onetomany');
@ -505,7 +631,7 @@ export default class BodyWidget extends React.Component {
this.noteRefEle = this.diagram.getEngine().getNodeElement(noteNode); this.noteRefEle = this.diagram.getEngine().getNodeElement(noteNode);
this.setState({ this.setState({
note_node: noteNode, note_node: noteNode,
note_open: true note_open: true,
}); });
} }
} }
@ -528,14 +654,14 @@ export default class BodyWidget extends React.Component {
trans_id: this.props.params.trans_id, trans_id: this.props.params.trans_id,
sgid: this.props.params.sgid, sgid: this.props.params.sgid,
sid: this.props.params.sid, sid: this.props.params.sid,
did: this.props.params.did did: this.props.params.did,
}); });
try { try {
let response = await axios.post(initUrl); let response = await axios.post(initUrl);
this.setState({ this.setState({
conn_status: CONNECT_STATUS.CONNECTED, conn_status: CONNECT_STATUS.CONNECTED,
server_version: response.data.data.serverVersion server_version: response.data.data.serverVersion,
}); });
return true; return true;
} catch (error) { } catch (error) {
@ -556,7 +682,7 @@ export default class BodyWidget extends React.Component {
trans_id: this.props.params.trans_id, trans_id: this.props.params.trans_id,
sgid: this.props.params.sgid, sgid: this.props.params.sgid,
sid: this.props.params.sid, sid: this.props.params.sid,
did: this.props.params.did did: this.props.params.did,
}); });
try { try {
@ -579,7 +705,7 @@ export default class BodyWidget extends React.Component {
trans_id: this.props.params.trans_id, trans_id: this.props.params.trans_id,
sgid: this.props.params.sgid, sgid: this.props.params.sgid,
sid: this.props.params.sid, sid: this.props.params.sid,
did: this.props.params.did did: this.props.params.did,
}); });
try { try {
@ -604,7 +730,7 @@ export default class BodyWidget extends React.Component {
<ButtonGroup> <ButtonGroup>
<IconButton id="open-file" icon="fa fa-folder-open" onClick={this.onLoadDiagram} title={gettext('Load from file')} <IconButton id="open-file" icon="fa fa-folder-open" onClick={this.onLoadDiagram} title={gettext('Load from file')}
shortcut={this.state.preferences.open_project}/> shortcut={this.state.preferences.open_project}/>
<IconButton id="save-erd" icon="fa fa-save" onClick={()=>{this.onSaveDiagram()}} title={gettext('Save project')} <IconButton id="save-erd" icon="fa fa-save" onClick={()=>{this.onSaveDiagram();}} title={gettext('Save project')}
shortcut={this.state.preferences.save_project} disabled={!this.state.dirty}/> shortcut={this.state.preferences.save_project} disabled={!this.state.dirty}/>
<IconButton id="save-as-erd" icon="fa fa-share-square" onClick={this.onSaveAsDiagram} title={gettext('Save as')} <IconButton id="save-as-erd" icon="fa fa-share-square" onClick={this.onSaveAsDiagram} title={gettext('Save as')}
shortcut={this.state.preferences.save_project_as}/> shortcut={this.state.preferences.save_project_as}/>
@ -612,6 +738,8 @@ export default class BodyWidget extends React.Component {
<ButtonGroup> <ButtonGroup>
<IconButton id="save-sql" icon="fa fa-file-code" onClick={this.onSQLClick} title={gettext('Generate SQL')} <IconButton id="save-sql" icon="fa fa-file-code" onClick={this.onSQLClick} title={gettext('Generate SQL')}
shortcut={this.state.preferences.generate_sql}/> shortcut={this.state.preferences.generate_sql}/>
<IconButton id="save-image" icon="fa fa-file-image" onClick={this.onImageClick} title={gettext('Generate SQL')}
shortcut={this.state.preferences.generate_sql}/>
</ButtonGroup> </ButtonGroup>
<ButtonGroup> <ButtonGroup>
<IconButton id="add-node" icon="fa fa-plus-square" onClick={this.onAddNewNode} title={gettext('Add table')} <IconButton id="add-node" icon="fa fa-plus-square" onClick={this.onAddNewNode} title={gettext('Add table')}
@ -634,7 +762,8 @@ export default class BodyWidget extends React.Component {
shortcut={this.state.preferences.add_edit_note} disabled={!this.state.single_node_selected || this.state.single_link_selected}/> shortcut={this.state.preferences.add_edit_note} disabled={!this.state.single_node_selected || this.state.single_link_selected}/>
<IconButton id="auto-align" icon="fa fa-magic" onClick={this.onAutoDistribute} title={gettext('Auto align')} <IconButton id="auto-align" icon="fa fa-magic" onClick={this.onAutoDistribute} title={gettext('Auto align')}
shortcut={this.state.preferences.auto_align} /> shortcut={this.state.preferences.auto_align} />
<DetailsToggleButton id="more-details" onClick={this.onDetailsToggle} showDetails={this.state.show_details} /> <DetailsToggleButton id="more-details" onClick={this.onDetailsToggle} showDetails={this.state.show_details}
shortcut={this.state.preferences.show_details} />
</ButtonGroup> </ButtonGroup>
<ButtonGroup> <ButtonGroup>
<IconButton id="zoom-to-fit" icon="fa fa-compress" onClick={this.diagram.zoomToFit} title={gettext('Zoom to fit')} <IconButton id="zoom-to-fit" icon="fa fa-compress" onClick={this.diagram.zoomToFit} title={gettext('Zoom to fit')}
@ -654,7 +783,7 @@ export default class BodyWidget extends React.Component {
reference={this.noteRefEle} noteNode={this.state.note_node} appendTo={this.diagramContainerRef.current} rows={8}/> reference={this.noteRefEle} noteNode={this.state.note_node} appendTo={this.diagramContainerRef.current} rows={8}/>
<div className="diagram-container" ref={this.diagramContainerRef}> <div className="diagram-container" ref={this.diagramContainerRef}>
<Loader message={this.state.loading_msg} autoEllipsis={true}/> <Loader message={this.state.loading_msg} autoEllipsis={true}/>
<CanvasWidget className="diagram-canvas flex-grow-1" ref={(ele)=>{this.canvasEle = ele?.ref?.current}} engine={this.diagram.getEngine()} /> <CanvasWidget className="diagram-canvas flex-grow-1" ref={(ele)=>{this.canvasEle = ele?.ref?.current;}} engine={this.diagram.getEngine()} />
</div> </div>
</> </>
); );
@ -672,10 +801,11 @@ BodyWidget.propTypes = {
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
bgcolor: PropTypes.string, bgcolor: PropTypes.string,
fgcolor: PropTypes.string, fgcolor: PropTypes.string,
gen: PropTypes.bool.isRequired gen: PropTypes.bool.isRequired,
}), }),
getDialog: PropTypes.func.isRequired, getDialog: PropTypes.func.isRequired,
transformToSupported: PropTypes.func.isRequired, transformToSupported: PropTypes.func.isRequired,
pgWindow: PropTypes.object.isRequired,
pgAdmin: PropTypes.object.isRequired, pgAdmin: PropTypes.object.isRequired,
alertify: PropTypes.object.isRequired alertify: PropTypes.object.isRequired,
}; };

View File

@ -16,7 +16,7 @@ export const STATUS = {
DISCONNECTED: 2, DISCONNECTED: 2,
CONNECTING: 3, CONNECTING: 3,
FAILED: 4, FAILED: 4,
} };
/* The connection bar component */ /* The connection bar component */
export default function ConnectionBar({statusId, status, bgcolor, fgcolor, title}) { export default function ConnectionBar({statusId, status, bgcolor, fgcolor, title}) {
@ -33,19 +33,19 @@ export default function ConnectionBar({statusId, status, bgcolor, fgcolor, title
+ (status == STATUS.CONNECTED ? 'icon-query-tool-connected' : '') + (status == STATUS.CONNECTED ? 'icon-query-tool-connected' : '')
+ (status == (STATUS.DISCONNECTED || STATUS.FAILED) ? 'icon-query-tool-disconnected ' : '') + (status == (STATUS.DISCONNECTED || STATUS.FAILED) ? 'icon-query-tool-disconnected ' : '')
+ (status == STATUS.CONNECTING ? 'obtaining-conn' : '')} + (status == STATUS.CONNECTING ? 'obtaining-conn' : '')}
aria-hidden="true" title="" role="img"> aria-hidden="true" title="" role="img">
</span> </span>
</div> </div>
<div className="connection-info btn-group" role="group" aria-label=""> <div className="connection-info btn-group" role="group" aria-label="">
<div className="editor-title" <div className="editor-title"
style={{backgroundColor: bgcolor, color: fgcolor}}> style={{backgroundColor: bgcolor, color: fgcolor}}>
{status == STATUS.CONNECTING ? '(' + gettext('Obtaining connection...') + ') ' : ''} {status == STATUS.CONNECTING ? '(' + gettext('Obtaining connection...') + ') ' : ''}
{status == STATUS.FAILED ? '(' + gettext('Connection failed') + ') ' : ''} {status == STATUS.FAILED ? '(' + gettext('Connection failed') + ') ' : ''}
{title} {title}
</div> </div>
</div> </div>
</div> </div>
) );
} }
ConnectionBar.propTypes = { ConnectionBar.propTypes = {

View File

@ -51,13 +51,13 @@ export default function FloatingNote({open, onClose, reference, rows, noteNode,
</div> </div>
</div> </div>
</div> </div>
)} )}
visible={open} visible={open}
interactive={true} interactive={true}
animation={false} animation={false}
reference={reference} reference={reference}
placement='auto-end' placement='auto-end'
{...tippyProps} {...tippyProps}
/> />
); );
} }

View File

@ -34,7 +34,7 @@ BaseIconButton.propTypes = {
text: PropTypes.string, text: PropTypes.string,
className: PropTypes.string, className: PropTypes.string,
ref: CustomPropTypes.ref, ref: CustomPropTypes.ref,
} };
/* The tooltip content to show shortcut details */ /* The tooltip content to show shortcut details */
@ -47,10 +47,10 @@ export function Shortcut({shortcut}) {
return ( return (
<div style={{justifyContent: 'center', marginTop: '0.125rem'}} className="d-flex"> <div style={{justifyContent: 'center', marginTop: '0.125rem'}} className="d-flex">
{keys.map((key, i)=>{ {keys.map((key, i)=>{
return <div key={i} className="shortcut-key">{key}</div> return <div key={i} className="shortcut-key">{key}</div>;
})} })}
</div> </div>
) );
} }
const shortcutPropType = PropTypes.shape({ const shortcutPropType = PropTypes.shape({
@ -85,7 +85,7 @@ export const IconButton = forwardRef((props, ref) => {
</Tippy> </Tippy>
); );
} else { } else {
return <BaseIconButton ref={ref} className='btn btn-sm btn-primary-icon' {...otherProps}/> return <BaseIconButton ref={ref} className='btn btn-sm btn-primary-icon' {...otherProps}/>;
} }
}); });
@ -93,21 +93,21 @@ IconButton.propTypes = {
title: PropTypes.string, title: PropTypes.string,
shortcut: shortcutPropType, shortcut: shortcutPropType,
className: PropTypes.string, className: PropTypes.string,
} };
/* Toggle button, icon changes based on value */ /* Toggle button, icon changes based on value */
export function DetailsToggleButton({showDetails, ...props}) { export function DetailsToggleButton({showDetails, ...props}) {
return ( return (
<IconButton <IconButton
icon={showDetails ? 'far fa-eye' : 'fas fa-low-vision'} icon={showDetails ? 'far fa-eye' : 'fas fa-low-vision'}
title={showDetails ? gettext('Show fewer details') : gettext("Show more details") } title={showDetails ? gettext('Show fewer details') : gettext('Show more details') }
{...props} /> {...props} />
); );
} }
DetailsToggleButton.propTypes = { DetailsToggleButton.propTypes = {
showDetails: PropTypes.bool, showDetails: PropTypes.bool,
} };
/* Button group container */ /* Button group container */
export function ButtonGroup({className, children}) { export function ButtonGroup({className, children}) {
@ -115,12 +115,12 @@ export function ButtonGroup({className, children}) {
<div className={'btn-group mr-1 ' + (className ? className : '')} role="group" aria-label="save group"> <div className={'btn-group mr-1 ' + (className ? className : '')} role="group" aria-label="save group">
{children} {children}
</div> </div>
) );
} }
ButtonGroup.propTypes = { ButtonGroup.propTypes = {
className: PropTypes.string, className: PropTypes.string,
} };
/* Toolbar container */ /* Toolbar container */
export default function ToolBar({id, children}) { export default function ToolBar({id, children}) {
@ -128,9 +128,9 @@ export default function ToolBar({id, children}) {
<div id={id} className="editor-toolbar d-flex" role="toolbar" aria-label=""> <div id={id} className="editor-toolbar d-flex" role="toolbar" aria-label="">
{children} {children}
</div> </div>
) );
} }
ButtonGroup.propTypes = { ButtonGroup.propTypes = {
id: PropTypes.string, id: PropTypes.string,
} };

View File

@ -28,6 +28,7 @@
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 0;
} }
.floating-note { .floating-note {
@ -56,6 +57,7 @@
} }
.note-body { .note-body {
word-break: break-all;
& textarea { & textarea {
width: 100%; width: 100%;
border: none; border: none;
@ -69,11 +71,21 @@
} }
} }
.html2canvas-reset {
background-image: none !important;
overflow: auto !important;
& > svg, & > div {
transform: none !important;
}
}
.diagram-canvas{ .diagram-canvas{
width: 100%; width: 100%;
height: 100%; height: 100%;
color: $color-fg; color: $color-fg;
font-family: sans-serif; font-family: sans-serif;
background-color: $erd-canvas-bg;
background-image: $erd-bg-grid; background-image: $erd-bg-grid;
cursor: unset; cursor: unset;
@ -85,6 +97,22 @@
width: 175px; width: 175px;
font-size: 0.8em; font-size: 0.8em;
.table-icon-schema {
background-image: url('~top/browser/server_groups/servers/databases/schemas/static/img/schema.svg') !important;
// background-repeat: no-repeat;
// // background-size: 20px !important;
// align-content: center;
// vertical-align: middle;
// height: 100%;
// background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIwLjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2NCA2NCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgNjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojOTk5OTk5O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTIxLjksMjIuMmMwLDMuNS0yLjksNi40LTYuNCw2LjRzLTYuNC0yLjktNi40LTYuNHMyLjktNi40LDYuNC02LjRTMjEuOSwxOC43LDIxLjksMjIuMnogTTQuMiw1MWwyMi4xLTE5CgkgTTQ2LjUsNTEuMUwyNi40LDMyIE0zNy42LDQyLjZsMTEuNi0xMCBNNjIuNCw0NS4xTDQ5LjIsMzIuNyBNNjMsMUgxdjYyaDYyVjF6IE0xLDUxLjRoNjIiLz4KPC9zdmc+Cg==');
width: 20px;
height: 20px;
// background: transparent url('~top/browser/server_groups/servers/databases/schemas/static/img/schema.svg') no-repeat center center;
// height: 100%;
// width: 20px;
}
&.selected { &.selected {
border-color: $input-focus-border-color; border-color: $input-focus-border-color;
box-shadow: $input-btn-focus-box-shadow; box-shadow: $input-btn-focus-box-shadow;
@ -105,16 +133,24 @@
} }
} }
.table-schema { .table-schema-data {
border-bottom: $border-width solid $erd-node-border-color; border-bottom: $border-width solid $erd-node-border-color;
padding: $erd-row-padding; padding: $erd-row-padding;
font-weight: bold;
& .table-schema {
font-weight: bold;
word-break: break-all;
}
} }
.table-name { .table-name-data {
border-bottom: $border-width*2 solid $erd-node-border-color; border-bottom: $border-width*2 solid $erd-node-border-color;
padding: $erd-row-padding; padding: $erd-row-padding;
font-weight: bold;
& .table-name {
font-weight: bold;
word-break: break-all;
}
} }
.table-cols { .table-cols {
@ -123,10 +159,7 @@
.col-row-data { .col-row-data {
padding: $erd-row-padding; padding: $erd-row-padding;
width: 100%; width: 100%;
word-break: break-all;
.col-name {
word-break: break-all;
}
} }
.col-row-port { .col-row-port {
padding: 0; padding: 0;

View File

@ -73,7 +73,10 @@ describe('ERD TableNodeModel', ()=>{
}); });
describe('setData', ()=>{ describe('setData', ()=>{
let existPort = jasmine.createSpyObj('port', ['removeAllLinks']); let existPort = jasmine.createSpyObj('port', {
'removeAllLinks': jasmine.createSpy('removeAllLinks'),
'getSubtype': 'notset',
});
beforeEach(()=>{ beforeEach(()=>{
modelObj._data.columns = [ modelObj._data.columns = [
@ -93,6 +96,7 @@ describe('ERD TableNodeModel', ()=>{
}); });
it('add columns', ()=>{ it('add columns', ()=>{
spyOn(existPort, 'getSubtype').and.returnValue('many');
existPort.removeAllLinks.calls.reset(); existPort.removeAllLinks.calls.reset();
modelObj.setData({ modelObj.setData({
name: 'noname', name: 'noname',
@ -118,29 +122,31 @@ describe('ERD TableNodeModel', ()=>{
}); });
it('update columns', ()=>{ it('update columns', ()=>{
spyOn(existPort, 'getSubtype').and.returnValue('many');
existPort.removeAllLinks.calls.reset(); existPort.removeAllLinks.calls.reset();
modelObj.setData({ modelObj.setData({
name: 'noname', name: 'noname',
schema: 'erd', schema: 'erd',
columns: [ columns: [
{name: 'col1', not_null:false, attnum: 0}, {name: 'col1', not_null:false, attnum: 0, is_primary_key: false},
{name: 'col2updated', not_null:false, attnum: 1}, {name: 'col2updated', not_null:false, attnum: 1, is_primary_key: false},
{name: 'col3', not_null:true, attnum: 2}, {name: 'col3', not_null:true, attnum: 2, is_primary_key: false},
], ],
}); });
expect(modelObj.getData()).toEqual({ expect(modelObj.getData()).toEqual({
name: 'noname', name: 'noname',
schema: 'erd', schema: 'erd',
columns: [ columns: [
{name: 'col1', not_null:false, attnum: 0}, {name: 'col1', not_null:false, attnum: 0, is_primary_key: false},
{name: 'col2updated', not_null:false, attnum: 1}, {name: 'col2updated', not_null:false, attnum: 1, is_primary_key: false},
{name: 'col3', not_null:true, attnum: 2}, {name: 'col3', not_null:true, attnum: 2, is_primary_key: false},
], ],
}); });
expect(existPort.removeAllLinks).not.toHaveBeenCalled(); expect(existPort.removeAllLinks).not.toHaveBeenCalled();
}); });
it('remove columns', ()=>{ it('remove columns', ()=>{
spyOn(existPort, 'getSubtype').and.returnValue('one');
existPort.removeAllLinks.calls.reset(); existPort.removeAllLinks.calls.reset();
modelObj.setData({ modelObj.setData({
name: 'noname', name: 'noname',

View File

@ -41,6 +41,10 @@ let pgAdmin = {
}, },
}; };
let pgWindow = {
pgAdmin: pgAdmin,
};
let alertify = jasmine.createSpyObj('alertify', { let alertify = jasmine.createSpyObj('alertify', {
'success': null, 'success': null,
'error': null, 'error': null,
@ -124,7 +128,7 @@ describe('ERD BodyWidget', ()=>{
beforeEach(()=>{ beforeEach(()=>{
jasmineEnzyme(); jasmineEnzyme();
body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} getDialog={getDialog} transformToSupported={()=>{}} alertify={alertify}/>); body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} getDialog={getDialog} transformToSupported={()=>{}} alertify={alertify}/>);
bodyInstance = body.instance(); bodyInstance = body.instance();
}); });
@ -248,7 +252,7 @@ describe('ERD BodyWidget', ()=>{
bodyInstance.addEditNode(); bodyInstance.addEditNode();
expect(tableDialog.show).toHaveBeenCalled(); expect(tableDialog.show).toHaveBeenCalled();
let saveCallback = tableDialog.show.calls.mostRecent().args[5]; let saveCallback = tableDialog.show.calls.mostRecent().args[6];
let newData = {key: 'value'}; let newData = {key: 'value'};
saveCallback(newData); saveCallback(newData);
expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith(newData); expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith(newData);
@ -263,7 +267,7 @@ describe('ERD BodyWidget', ()=>{
bodyInstance.addEditNode(node); bodyInstance.addEditNode(node);
expect(tableDialog.show).toHaveBeenCalled(); expect(tableDialog.show).toHaveBeenCalled();
saveCallback = tableDialog.show.calls.mostRecent().args[5]; saveCallback = tableDialog.show.calls.mostRecent().args[6];
newData = {key: 'value'}; newData = {key: 'value'};
saveCallback(newData); saveCallback(newData);
expect(node.setData).toHaveBeenCalledWith(newData); expect(node.setData).toHaveBeenCalledWith(newData);

View File

@ -1281,6 +1281,14 @@ acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
adjust-sourcemap-loader@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e"
integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==
dependencies:
loader-utils "^2.0.0"
regex-parser "^2.2.11"
after@0.8.2: after@0.8.2:
version "0.8.2" version "0.8.2"
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
@ -1432,6 +1440,11 @@ argparse@^1.0.6, argparse@^1.0.7:
dependencies: dependencies:
sprintf-js "~1.0.2" sprintf-js "~1.0.2"
arity-n@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745"
integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U=
arr-diff@^4.0.0: arr-diff@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@ -1813,6 +1826,11 @@ base64-arraybuffer@0.1.5:
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
base64-arraybuffer@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==
base64-js@^1.0.2, base64-js@^1.3.1: base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -2437,16 +2455,16 @@ camelcase-keys@^2.0.0:
camelcase "^2.0.0" camelcase "^2.0.0"
map-obj "^1.0.0" map-obj "^1.0.0"
camelcase@5.3.1, camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
camelcase@^2.0.0: camelcase@^2.0.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-api@^3.0.0: caniuse-api@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@ -2797,6 +2815,13 @@ component-inherit@0.0.3:
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=
compose-function@3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f"
integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=
dependencies:
arity-n "^1.0.4"
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -2857,13 +2882,18 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
convert-source-map@^1.1.3, convert-source-map@^1.5.0, convert-source-map@^1.7.0: convert-source-map@1.7.0, convert-source-map@^1.1.3, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
dependencies: dependencies:
safe-buffer "~5.1.1" safe-buffer "~5.1.1"
convert-source-map@^0.3.3:
version "0.3.5"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA=
convert-source-map@~1.1.0: convert-source-map@~1.1.0:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
@ -3051,6 +3081,13 @@ css-declaration-sorter@^4.0.1:
postcss "^7.0.1" postcss "^7.0.1"
timsort "^0.3.0" timsort "^0.3.0"
css-line-break@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==
dependencies:
base64-arraybuffer "^0.2.0"
css-loader@2.1.0: css-loader@2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.0.tgz#42952ac22bca5d076978638e9813abce49b8f0cc" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.0.tgz#42952ac22bca5d076978638e9813abce49b8f0cc"
@ -3119,6 +3156,16 @@ css-what@^4.0.0:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233"
integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==
css@^2.0.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==
dependencies:
inherits "^2.0.3"
source-map "^0.6.1"
source-map-resolve "^0.5.2"
urix "^0.1.0"
cssesc@^3.0.0: cssesc@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -3235,6 +3282,14 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
d@1, d@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
dependencies:
es5-ext "^0.10.50"
type "^1.0.1"
dagre@^0.8.4: dagre@^0.8.4:
version "0.8.5" version "0.8.5"
resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee"
@ -3664,6 +3719,11 @@ emoji-regex@^7.0.1:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
emojis-list@^3.0.0: emojis-list@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
@ -3870,6 +3930,32 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.50:
version "0.10.53"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1"
integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==
dependencies:
es6-iterator "~2.0.3"
es6-symbol "~3.1.3"
next-tick "~1.0.0"
es6-iterator@2.0.3, es6-iterator@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
dependencies:
d "1"
es5-ext "^0.10.35"
es6-symbol "^3.1.1"
es6-symbol@^3.1.1, es6-symbol@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
dependencies:
d "^1.0.1"
ext "^1.1.2"
escalade@^3.1.1: escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@ -4176,6 +4262,13 @@ ext-name@^5.0.0:
ext-list "^2.0.0" ext-list "^2.0.0"
sort-keys-length "^1.0.0" sort-keys-length "^1.0.0"
ext@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==
dependencies:
type "^2.0.0"
extend-shallow@^2.0.1: extend-shallow@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@ -4982,6 +5075,13 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
html2canvas@^1.0.0-rc.7:
version "1.0.0-rc.7"
resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.0.0-rc.7.tgz#70c159ce0e63954a91169531894d08ad5627ac98"
integrity sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==
dependencies:
css-line-break "1.1.1"
htmlescape@^1.1.0: htmlescape@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
@ -6167,6 +6267,15 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
loader-utils@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
dependencies:
big.js "^5.2.2"
emojis-list "^2.0.0"
json5 "^1.0.1"
loader-utils@1.4.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.1, loader-utils@^1.2.3, loader-utils@^1.4.0: loader-utils@1.4.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.1, loader-utils@^1.2.3, loader-utils@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
@ -6837,6 +6946,11 @@ neo-async@^2.5.0, neo-async@^2.6.1:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-tick@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
nice-try@^1.0.4: nice-try@^1.0.4:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@ -7854,6 +7968,15 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
postcss@7.0.21:
version "7.0.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17"
integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@7.0.27: postcss@7.0.27:
version "7.0.27" version "7.0.27"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
@ -8260,6 +8383,11 @@ regex-not@^1.0.0, regex-not@^1.0.2:
extend-shallow "^3.0.2" extend-shallow "^3.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
regex-parser@^2.2.11:
version "2.2.11"
resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58"
integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==
regexp.prototype.flags@^1.3.0: regexp.prototype.flags@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
@ -8374,6 +8502,22 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-url-loader@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08"
integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==
dependencies:
adjust-sourcemap-loader "3.0.0"
camelcase "5.3.1"
compose-function "3.0.3"
convert-source-map "1.7.0"
es6-iterator "2.0.3"
loader-utils "1.2.3"
postcss "7.0.21"
rework "1.0.1"
rework-visit "1.0.0"
source-map "0.6.1"
resolve-url@^0.2.1: resolve-url@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@ -8412,6 +8556,19 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
rework-visit@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a"
integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo=
rework@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7"
integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=
dependencies:
convert-source-map "^0.3.3"
css "^2.0.0"
rfdc@^1.1.4: rfdc@^1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2"
@ -8890,7 +9047,7 @@ source-list-map@^2.0.0:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-resolve@^0.5.0: source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
@ -8919,16 +9076,16 @@ source-map@0.5.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86"
integrity sha1-D+llA6yGpa213mP05BKuSHLNvoY= integrity sha1-D+llA6yGpa213mP05BKuSHLNvoY=
source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.3: source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.3:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.7.3: source-map@^0.7.3:
version "0.7.3" version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
@ -9653,6 +9810,16 @@ type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
type@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
type@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f"
integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==
typedarray@^0.0.6: typedarray@^0.0.6:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"