diff --git a/packages/d3-state-visualizer/.babelrc b/packages/d3-state-visualizer/.babelrc index 06050b00..5259cd24 100644 --- a/packages/d3-state-visualizer/.babelrc +++ b/packages/d3-state-visualizer/.babelrc @@ -1,7 +1,4 @@ { - "presets": ["@babel/preset-env"], - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-export-default-from" - ] + "presets": ["@babel/preset-env", "@babel/preset-typescript"], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/d3-state-visualizer/.eslintignore b/packages/d3-state-visualizer/.eslintignore new file mode 100644 index 00000000..1d149abd --- /dev/null +++ b/packages/d3-state-visualizer/.eslintignore @@ -0,0 +1,3 @@ +examples +lib +dist diff --git a/packages/d3-state-visualizer/.eslintrc.js b/packages/d3-state-visualizer/.eslintrc.js new file mode 100644 index 00000000..f815f2cb --- /dev/null +++ b/packages/d3-state-visualizer/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + extends: '../../.eslintrc', + overrides: [ + { + files: ['*.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + }, + { + files: ['webpack.config.umd.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.webpack.json'], + }, + }, + ], +}; diff --git a/packages/d3-state-visualizer/package.json b/packages/d3-state-visualizer/package.json index 589db98a..4ab6245f 100644 --- a/packages/d3-state-visualizer/package.json +++ b/packages/d3-state-visualizer/package.json @@ -2,24 +2,6 @@ "name": "d3-state-visualizer", "version": "1.3.4", "description": "Visualize your app state with a range of reusable charts", - "main": "lib/index.js", - "files": [ - "dist", - "lib", - "src" - ], - "scripts": { - "clean": "rimraf lib dist", - "build": "babel src --out-dir lib", - "build:umd": "webpack src/index.js -o dist/d3-state-visualizer.js --config webpack.config.development.js", - "build:umd:min": "webpack src/index.js -o dist/d3-state-visualizer.min.js --config webpack.config.production.js", - "prepare": "npm run build", - "prepublishOnly": "npm run clean && npm run build && npm run build:umd && npm run build:umd:min" - }, - "repository": { - "type": "git", - "url": "https://github.com/reduxjs/redux-devtools.git" - }, "keywords": [ "d3", "state", @@ -27,27 +9,46 @@ "tree", "visualization" ], - "author": "romseguy", - "license": "MIT", + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/d3-state-visualizer", "bugs": { "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "homepage": "https://github.com/reduxjs/redux-devtools", - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-export-default-from": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "babel-loader": "^8.1.0", - "rimraf": "^3.0.2", - "webpack": "^4.44.1" + "license": "MIT", + "author": "romseguy", + "files": [ + "dist", + "lib", + "src" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/reduxjs/redux-devtools.git" + }, + "scripts": { + "build": "npm run build:types && npm run build:js && npm run build:umd && npm run build:umd:min", + "build:types": "tsc --emitDeclarationOnly", + "build:js": "babel src --out-dir lib --extensions \".ts\" --source-maps inline", + "build:umd": "webpack --env.production --progress --config webpack.config.umd.ts", + "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.ts", + "clean": "rimraf lib dist", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch", + "preversion": "npm run type-check && npm run lint", + "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { + "@types/d3": "^3.5.43", "d3": "^3.5.17", "d3tooltip": "^1.2.3", "deepmerge": "^4.2.2", "map2tree": "^1.4.2", "ramda": "^0.27.1" + }, + "devDependencies": { + "@types/ramda": "^0.27.17" } } diff --git a/packages/d3-state-visualizer/src/charts/index.js b/packages/d3-state-visualizer/src/charts/index.js deleted file mode 100644 index d2f1835e..00000000 --- a/packages/d3-state-visualizer/src/charts/index.js +++ /dev/null @@ -1 +0,0 @@ -export tree from './tree/tree'; diff --git a/packages/d3-state-visualizer/src/charts/index.ts b/packages/d3-state-visualizer/src/charts/index.ts new file mode 100644 index 00000000..33abb024 --- /dev/null +++ b/packages/d3-state-visualizer/src/charts/index.ts @@ -0,0 +1 @@ +export { default as tree } from './tree/tree'; diff --git a/packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.js b/packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.ts similarity index 57% rename from packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.js rename to packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.ts index ff36ef6f..270eab40 100644 --- a/packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.js +++ b/packages/d3-state-visualizer/src/charts/tree/sortAndSerialize.ts @@ -1,4 +1,4 @@ -function sortObject(obj, strict) { +function sortObject(obj: unknown, strict?: boolean) { if (obj instanceof Array) { let ary; if (strict) { @@ -10,16 +10,16 @@ function sortObject(obj, strict) { } if (obj && typeof obj === 'object') { - const tObj = {}; + const tObj: { [key: string]: unknown } = {}; Object.keys(obj) .sort() - .forEach((key) => (tObj[key] = sortObject(obj[key]))); + .forEach((key) => (tObj[key] = sortObject(obj[key as keyof typeof obj]))); return tObj; } return obj; } -export default function sortAndSerialize(obj) { +export default function sortAndSerialize(obj: unknown) { return JSON.stringify(sortObject(obj, true), undefined, 2); } diff --git a/packages/d3-state-visualizer/src/charts/tree/tree.js b/packages/d3-state-visualizer/src/charts/tree/tree.ts similarity index 64% rename from packages/d3-state-visualizer/src/charts/tree/tree.js rename to packages/d3-state-visualizer/src/charts/tree/tree.ts index 5c97a6b1..246d2df8 100644 --- a/packages/d3-state-visualizer/src/charts/tree/tree.js +++ b/packages/d3-state-visualizer/src/charts/tree/tree.ts @@ -1,4 +1,4 @@ -import d3 from 'd3'; +import d3, { ZoomEvent, Primitive } from 'd3'; import { isEmpty } from 'ramda'; import map2tree from 'map2tree'; import deepmerge from 'deepmerge'; @@ -10,7 +10,63 @@ import { } from './utils'; import d3tooltip from 'd3tooltip'; -const defaultOptions = { +interface Options { + // eslint-disable-next-line @typescript-eslint/ban-types + state?: {}; + // eslint-disable-next-line @typescript-eslint/ban-types + tree?: NodeWithId | {}; + + rootKeyName: string; + pushMethod: 'push' | 'unshift'; + id: string; + style: { + node: { + colors: { + default: string; + collapsed: string; + parent: string; + }; + radius: number; + }; + text: { + colors: { + default: string; + hover: string; + }; + }; + link: { + stroke: string; + fill: string; + }; + }; + size: number; + aspectRatio: number; + initialZoom: number; + margin: { + top: number; + right: number; + bottom: number; + left: number; + }; + isSorted: boolean; + heightBetweenNodesCoeff: number; + widthBetweenNodesCoeff: number; + transitionDuration: number; + blinkDuration: number; + onClickText: () => void; + tooltipOptions: { + disabled: boolean; + left: number | undefined; + top: number | undefined; + offset: { + left: number; + top: number; + }; + style: { [key: string]: Primitive } | undefined; + }; +} + +const defaultOptions: Options = { state: undefined, rootKeyName: 'state', pushMethod: 'push', @@ -50,11 +106,13 @@ const defaultOptions = { widthBetweenNodesCoeff: 1, transitionDuration: 750, blinkDuration: 100, - onClickText: () => {}, + onClickText: () => { + // noop + }, tooltipOptions: { disabled: false, left: undefined, - right: undefined, + top: undefined, offset: { left: 0, top: 0, @@ -63,7 +121,27 @@ const defaultOptions = { }, }; -export default function (DOMNode, options = {}) { +export interface NodeWithId { + name: string; + children?: NodeWithId[] | null; + _children?: NodeWithId[] | null; + value?: unknown; + id: string; + + parent?: NodeWithId; + depth?: number; + x?: number; + y?: number; +} + +interface NodePosition { + parentId: string | null | undefined; + id: string; + x: number | undefined; + y: number | undefined; +} + +export default function (DOMNode: HTMLElement, options: Partial = {}) { const { id, style, @@ -89,16 +167,19 @@ export default function (DOMNode, options = {}) { const fullWidth = size; const fullHeight = size * aspectRatio; - const attr = { + const attr: { [key: string]: Primitive } = { id, preserveAspectRatio: 'xMinYMin slice', }; - if (!style.width) { + if (!((style as unknown) as { [key: string]: Primitive }).width) { attr.width = fullWidth; } - if (!style.width || !style.height) { + if ( + !((style as unknown) as { [key: string]: Primitive }).width || + !((style as unknown) as { [key: string]: Primitive }).height + ) { attr.viewBox = `0 0 ${fullWidth} ${fullHeight}`; } @@ -107,11 +188,16 @@ export default function (DOMNode, options = {}) { const vis = root .append('svg') .attr(attr) - .style({ cursor: '-webkit-grab', ...style }) + .style(({ cursor: '-webkit-grab', ...style } as unknown) as { + [key: string]: Primitive; + }) .call( zoom.on('zoom', () => { - const { translate, scale } = d3.event; - vis.attr('transform', `translate(${translate})scale(${scale})`); + const { translate, scale } = d3.event as ZoomEvent; + vis.attr( + 'transform', + `translate(${translate.toString()})scale(${scale})` + ); }) ) .append('g') @@ -122,18 +208,21 @@ export default function (DOMNode, options = {}) { }); let layout = d3.layout.tree().size([width, height]); - let data; + let data: NodeWithId; if (isSorted) { layout.sort((a, b) => - b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1 + (b as NodeWithId).name.toLowerCase() < + (a as NodeWithId).name.toLowerCase() + ? 1 + : -1 ); } // previousNodePositionsById stores node x and y // as well as hierarchy (id / parentId); // helps animating transitions - let previousNodePositionsById = { + let previousNodePositionsById: { [nodeId: string]: NodePosition } = { root: { id: 'root', parentId: null, @@ -145,10 +234,14 @@ export default function (DOMNode, options = {}) { // traverses a map with node positions by going through the chain // of parent ids; once a parent that matches the given filter is found, // the parent position gets returned - function findParentNodePosition(nodePositionsById, nodeId, filter) { + function findParentNodePosition( + nodePositionsById: { [nodeId: string]: NodePosition }, + nodeId: string, + filter: (nodePosition: NodePosition) => boolean + ) { let currentPosition = nodePositionsById[nodeId]; while (currentPosition) { - currentPosition = nodePositionsById[currentPosition.parentId]; + currentPosition = nodePositionsById[currentPosition.parentId!]; if (!currentPosition) { return null; } @@ -160,14 +253,18 @@ export default function (DOMNode, options = {}) { return function renderChart(nextState = tree || state) { data = !tree - ? map2tree(nextState, { key: rootKeyName, pushMethod }) - : nextState; + ? // eslint-disable-next-line @typescript-eslint/ban-types + (map2tree(nextState as {}, { + key: rootKeyName, + pushMethod, + }) as NodeWithId) + : (nextState as NodeWithId); if (isEmpty(data) || !data.name) { - data = { + data = ({ name: 'error', message: 'Please provide a state map or a tree structure', - }; + } as unknown) as NodeWithId; } let nodeIndex = 0; @@ -191,13 +288,13 @@ export default function (DOMNode, options = {}) { : null ); - /*eslint-disable*/ update(); - /*eslint-enable*/ function update() { // path generator for links - const diagonal = d3.svg.diagonal().projection((d) => [d.y, d.x]); + const diagonal = d3.svg + .diagonal() + .projection((d) => [d.y!, d.x!]); // set tree dimensions and spacing between branches and nodes const maxNodeCountByLevel = Math.max(...getNodeGroupByDepthCount(data)); @@ -206,12 +303,12 @@ export default function (DOMNode, options = {}) { width, ]); - let nodes = layout.nodes(data); - let links = layout.links(nodes); + const nodes = layout.nodes(data as d3.layout.tree.Node) as NodeWithId[]; + const links = layout.links(nodes as d3.layout.tree.Node[]); nodes.forEach( (node) => - (node.y = node.depth * (maxLabelLength * 7 * widthBetweenNodesCoeff)) + (node.y = node.depth! * (maxLabelLength * 7 * widthBetweenNodesCoeff)) ); const nodePositions = nodes.map((n) => ({ @@ -220,15 +317,18 @@ export default function (DOMNode, options = {}) { x: n.x, y: n.y, })); - const nodePositionsById = {}; + const nodePositionsById: { [nodeId: string]: NodePosition } = {}; nodePositions.forEach((node) => (nodePositionsById[node.id] = node)); // process the node selection - let node = vis + const node = vis .selectAll('g.node') - .property('__oldData__', (d) => d) - .data(nodes, (d) => d.id || (d.id = ++nodeIndex)); - let nodeEnter = node + .property('__oldData__', (d: NodeWithId) => d) + .data( + nodes, + (d) => d.id || (d.id = (++nodeIndex as unknown) as string) + ); + const nodeEnter = node .enter() .append('g') .attr({ @@ -237,35 +337,39 @@ export default function (DOMNode, options = {}) { const position = findParentNodePosition( nodePositionsById, d.id, - (n) => previousNodePositionsById[n.id] + (n) => !!previousNodePositionsById[n.id] ); const previousPosition = (position && previousNodePositionsById[position.id]) || previousNodePositionsById.root; - return `translate(${previousPosition.y},${previousPosition.x})`; + return `translate(${previousPosition.y!},${previousPosition.x!})`; }, }) .style({ fill: style.text.colors.default, cursor: 'pointer', }) - .on({ - mouseover: function mouseover() { - d3.select(this).style({ - fill: style.text.colors.hover, - }); - }, - mouseout: function mouseout() { - d3.select(this).style({ - fill: style.text.colors.default, - }); - }, + .on('mouseover', function mouseover(this: any) { + d3.select(this).style({ + fill: style.text.colors.hover, + }); + }) + .on('mouseout', function mouseout(this: any) { + d3.select(this).style({ + fill: style.text.colors.default, + }); }); if (!tooltipOptions.disabled) { nodeEnter.call( d3tooltip(d3, 'tooltip', { ...tooltipOptions, root }) - .text((d, i) => getTooltipString(d, i, tooltipOptions)) + .text((d, i) => + getTooltipString( + d, + i, + (tooltipOptions as unknown) as { indentationSize: number } + ) + ) .style(tooltipOptions.style) ); } @@ -279,12 +383,10 @@ export default function (DOMNode, options = {}) { class: 'nodeCircle', r: 0, }) - .on({ - click: (clickedNode) => { - if (d3.event.defaultPrevented) return; - toggleChildren(clickedNode); - update(); - }, + .on('click', (clickedNode) => { + if ((d3.event as Event).defaultPrevented) return; + toggleChildren(clickedNode); + update(); }); nodeEnterInnerGroup @@ -299,9 +401,7 @@ export default function (DOMNode, options = {}) { 'fill-opacity': 0, }) .text((d) => d.name) - .on({ - click: onClickText, - }); + .on('click', onClickText); // update the text to reflect whether node has children or not node.select('text').text((d) => d.name); @@ -319,11 +419,11 @@ export default function (DOMNode, options = {}) { }); // transition nodes to their new position - let nodeUpdate = node + const nodeUpdate = node .transition() .duration(transitionDuration) .attr({ - transform: (d) => `translate(${d.y},${d.x})`, + transform: (d) => `translate(${d.y!},${d.x!})`, }); // ensure circle radius is correct @@ -334,7 +434,7 @@ export default function (DOMNode, options = {}) { .select('text') .style('fill-opacity', 1) .attr({ - transform: function transform(d) { + transform: function transform(this: SVGGraphicsElement, d) { const x = (d.children || d._children ? -1 : 1) * (this.getBBox().width / 2 + style.node.radius + 5); @@ -344,7 +444,7 @@ export default function (DOMNode, options = {}) { // blink updated nodes node - .filter(function flick(d) { + .filter(function flick(this: any, d) { // test whether the relevant properties of d match // the equivalent property of the oldData // also test whether the old data exists, @@ -358,7 +458,7 @@ export default function (DOMNode, options = {}) { .style('opacity', '1'); // transition exiting nodes to the parent's new position - let nodeExit = node + const nodeExit = node .exit() .transition() .duration(transitionDuration) @@ -367,12 +467,12 @@ export default function (DOMNode, options = {}) { const position = findParentNodePosition( previousNodePositionsById, d.id, - (n) => nodePositionsById[n.id] + (n) => !!nodePositionsById[n.id] ); const futurePosition = (position && nodePositionsById[position.id]) || nodePositionsById.root; - return `translate(${futurePosition.y},${futurePosition.x})`; + return `translate(${futurePosition.y!},${futurePosition.x!})`; }, }) .remove(); @@ -382,7 +482,9 @@ export default function (DOMNode, options = {}) { nodeExit.select('text').style('fill-opacity', 0); // update the links - let link = vis.selectAll('path.link').data(links, (d) => d.target.id); + const link = vis + .selectAll('path.link') + .data(links, (d) => (d.target as NodeWithId).id); // enter any new links at the parent's previous position link @@ -393,8 +495,8 @@ export default function (DOMNode, options = {}) { d: (d) => { const position = findParentNodePosition( nodePositionsById, - d.target.id, - (n) => previousNodePositionsById[n.id] + (d.target as NodeWithId).id, + (n) => !!previousNodePositionsById[n.id] ); const previousPosition = (position && previousNodePositionsById[position.id]) || @@ -402,15 +504,18 @@ export default function (DOMNode, options = {}) { return diagonal({ source: previousPosition, target: previousPosition, - }); + } as d3.svg.diagonal.Link); }, }) .style(style.link); // transition links to their new position - link.transition().duration(transitionDuration).attr({ - d: diagonal, - }); + link + .transition() + .duration(transitionDuration) + .attr({ + d: (diagonal as unknown) as Primitive, + }); // transition exiting nodes to the parent's new position link @@ -421,8 +526,8 @@ export default function (DOMNode, options = {}) { d: (d) => { const position = findParentNodePosition( previousNodePositionsById, - d.target.id, - (n) => nodePositionsById[n.id] + (d.target as NodeWithId).id, + (n) => !!nodePositionsById[n.id] ); const futurePosition = (position && nodePositionsById[position.id]) || diff --git a/packages/d3-state-visualizer/src/charts/tree/utils.js b/packages/d3-state-visualizer/src/charts/tree/utils.ts similarity index 57% rename from packages/d3-state-visualizer/src/charts/tree/utils.js rename to packages/d3-state-visualizer/src/charts/tree/utils.ts index 7b76058d..dcd4a82c 100644 --- a/packages/d3-state-visualizer/src/charts/tree/utils.js +++ b/packages/d3-state-visualizer/src/charts/tree/utils.ts @@ -1,7 +1,8 @@ import { is, join, pipe, replace } from 'ramda'; import sortAndSerialize from './sortAndSerialize'; +import { NodeWithId } from './tree'; -export function collapseChildren(node) { +export function collapseChildren(node: NodeWithId) { if (node.children) { node._children = node.children; node._children.forEach(collapseChildren); @@ -9,7 +10,7 @@ export function collapseChildren(node) { } } -export function expandChildren(node) { +export function expandChildren(node: NodeWithId) { if (node._children) { node.children = node._children; node.children.forEach(expandChildren); @@ -17,7 +18,7 @@ export function expandChildren(node) { } } -export function toggleChildren(node) { +export function toggleChildren(node: NodeWithId) { if (node.children) { node._children = node.children; node.children = null; @@ -28,16 +29,20 @@ export function toggleChildren(node) { return node; } -export function visit(parent, visitFn, childrenFn) { +export function visit( + parent: NodeWithId, + visitFn: (parent: NodeWithId) => void, + childrenFn: (parent: NodeWithId) => NodeWithId[] | null | undefined +) { if (!parent) { return; } visitFn(parent); - let children = childrenFn(parent); + const children = childrenFn(parent); if (children) { - let count = children.length; + const count = children.length; for (let i = 0; i < count; i++) { visit(children[i], visitFn, childrenFn); @@ -45,10 +50,10 @@ export function visit(parent, visitFn, childrenFn) { } } -export function getNodeGroupByDepthCount(rootNode) { - let nodeGroupByDepthCount = [1]; +export function getNodeGroupByDepthCount(rootNode: NodeWithId) { + const nodeGroupByDepthCount = [1]; - const traverseFrom = function traverseFrom(node, depth = 0) { + const traverseFrom = function traverseFrom(node: NodeWithId, depth = 0) { if (!node.children || node.children.length === 0) { return 0; } @@ -68,7 +73,11 @@ export function getNodeGroupByDepthCount(rootNode) { return nodeGroupByDepthCount; } -export function getTooltipString(node, i, { indentationSize = 4 }) { +export function getTooltipString( + node: unknown, + i: number | undefined, + { indentationSize = 4 } +) { if (!is(Object, node)) return ''; const spacer = join('  '); @@ -76,10 +85,13 @@ export function getTooltipString(node, i, { indentationSize = 4 }) { const spaces2nbsp = replace(/\s{2}/g, spacer(new Array(indentationSize))); const json2html = pipe(sortAndSerialize, cr2br, spaces2nbsp); - const children = node.children || node._children; + const children = (node as any).children || (node as any)._children; - if (typeof node.value !== 'undefined') return json2html(node.value); - if (typeof node.object !== 'undefined') return json2html(node.object); - if (children && children.length) return 'childrenCount: ' + children.length; + if (typeof (node as any).value !== 'undefined') + return json2html((node as any).value); + if (typeof (node as any).object !== 'undefined') + return json2html((node as any).object); + if (children && children.length) + return `childrenCount: ${(children as unknown[]).length}`; return 'empty'; } diff --git a/packages/d3-state-visualizer/src/index.js b/packages/d3-state-visualizer/src/index.ts similarity index 100% rename from packages/d3-state-visualizer/src/index.js rename to packages/d3-state-visualizer/src/index.ts diff --git a/packages/d3-state-visualizer/tsconfig.json b/packages/d3-state-visualizer/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/d3-state-visualizer/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/d3-state-visualizer/tsconfig.webpack.json b/packages/d3-state-visualizer/tsconfig.webpack.json new file mode 100644 index 00000000..655c4644 --- /dev/null +++ b/packages/d3-state-visualizer/tsconfig.webpack.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["webpack.config.umd.ts"] +} diff --git a/packages/d3-state-visualizer/webpack.config.base.js b/packages/d3-state-visualizer/webpack.config.base.js deleted file mode 100644 index f7ac921b..00000000 --- a/packages/d3-state-visualizer/webpack.config.base.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -module.exports = { - module: { - rules: [ - { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }, - ], - }, - output: { - library: 'd3-state-visualizer', - libraryExport: 'default', - libraryTarget: 'umd', - }, - resolve: { - extensions: ['.js'], - }, -}; diff --git a/packages/d3-state-visualizer/webpack.config.development.js b/packages/d3-state-visualizer/webpack.config.development.js deleted file mode 100644 index aa9d02ee..00000000 --- a/packages/d3-state-visualizer/webpack.config.development.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var webpack = require('webpack'); -var baseConfig = require('./webpack.config.base'); - -var config = Object.assign({}, baseConfig); -config.mode = 'development'; -config.plugins = [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('development'), - }), -]; - -module.exports = config; diff --git a/packages/d3-state-visualizer/webpack.config.production.js b/packages/d3-state-visualizer/webpack.config.production.js deleted file mode 100644 index b5310704..00000000 --- a/packages/d3-state-visualizer/webpack.config.production.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var webpack = require('webpack'); -var baseConfig = require('./webpack.config.base'); - -var config = Object.assign({}, baseConfig); -config.mode = 'production'; -config.plugins = [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), -]; - -module.exports = config; diff --git a/packages/d3-state-visualizer/webpack.config.umd.ts b/packages/d3-state-visualizer/webpack.config.umd.ts new file mode 100644 index 00000000..a9643899 --- /dev/null +++ b/packages/d3-state-visualizer/webpack.config.umd.ts @@ -0,0 +1,28 @@ +import * as path from 'path'; + +export default (env: { production?: boolean } = {}) => ({ + mode: env.production ? 'production' : 'development', + entry: { + app: ['./src/index'], + }, + output: { + library: 'd3-state-visualizer', + libraryTarget: 'umd', + path: path.resolve(__dirname, 'dist'), + filename: env.production + ? 'd3-state-visualizer.min.js' + : 'd3-state-visualizer.js', + }, + module: { + rules: [ + { + test: /\.(js|ts)$/, + loader: 'babel-loader', + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, +}); diff --git a/packages/d3tooltip/package.json b/packages/d3tooltip/package.json index 67355f4d..27791a44 100644 --- a/packages/d3tooltip/package.json +++ b/packages/d3tooltip/package.json @@ -34,14 +34,17 @@ "lint:fix": "eslint . --ext .ts --fix", "type-check": "tsc --noEmit", "type-check:watch": "npm run type-check -- --watch", - "preversion": "npm run type-check && npm run lint && npm run test", + "preversion": "npm run type-check && npm run lint", "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { "ramda": "^0.27.1" }, "devDependencies": { - "@types/d3": "^3.5.43", "@types/ramda": "^0.27.17" + }, + "peerDependencies": { + "@types/d3": "^3.5.43", + "d3": "^3.5.17" } } diff --git a/packages/d3tooltip/src/index.ts b/packages/d3tooltip/src/index.ts index 13cac544..2f093242 100644 --- a/packages/d3tooltip/src/index.ts +++ b/packages/d3tooltip/src/index.ts @@ -3,7 +3,17 @@ import { is } from 'ramda'; import utils from './utils'; const { prependClass, functor } = utils; -const defaultOptions = { +interface Options { + left: number | undefined; + top: number | undefined; + offset: { + left: number; + top: number; + }; + root: Selection | undefined; +} + +const defaultOptions: Options = { left: undefined, // mouseX top: undefined, // mouseY offset: { left: 0, top: 0 }, @@ -13,9 +23,12 @@ const defaultOptions = { export default function tooltip( d3: typeof d3Package, className = 'tooltip', - options = {} + options: Partial> = {} ) { - const { left, top, offset, root } = { ...defaultOptions, ...options }; + const { left, top, offset, root } = { + ...defaultOptions, + ...options, + } as Options; let attrs = { class: className }; let text: (datum: Datum, index?: number, outerIndex?: number) => string = ( @@ -44,7 +57,7 @@ export default function tooltip( top: `${y}px`, ...styles, }) - .html(() => text(node)); + .html(() => text(node)) as Selection; }); selection.on('mousemove.tip', (node) => { diff --git a/packages/d3tooltip/webpack.config.umd.ts b/packages/d3tooltip/webpack.config.umd.ts index 9913c7f5..e52a1b34 100644 --- a/packages/d3tooltip/webpack.config.umd.ts +++ b/packages/d3tooltip/webpack.config.umd.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -module.exports = (env: { production?: boolean } = {}) => ({ +export default (env: { production?: boolean } = {}) => ({ mode: env.production ? 'production' : 'development', entry: { app: ['./src/index'], diff --git a/packages/map2tree/package.json b/packages/map2tree/package.json index 516f5da6..04d1eb48 100755 --- a/packages/map2tree/package.json +++ b/packages/map2tree/package.json @@ -38,7 +38,7 @@ "lint:fix": "eslint . --ext .ts --fix", "type-check": "tsc --noEmit", "type-check:watch": "npm run type-check -- --watch", - "preversion": "npm run type-check && npm run lint", + "preversion": "npm run type-check && npm run lint && npm run test", "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { diff --git a/packages/map2tree/src/index.ts b/packages/map2tree/src/index.ts index 958eef3d..4239fdb6 100644 --- a/packages/map2tree/src/index.ts +++ b/packages/map2tree/src/index.ts @@ -2,16 +2,16 @@ import isArray from 'lodash/isArray'; import isPlainObject from 'lodash/isPlainObject'; import mapValues from 'lodash/mapValues'; -interface Node { +export interface Node { name: string; - children?: Node[]; + children?: Node[] | null; value?: unknown; } function visit( parent: Node, visitFn: (parent: Node) => void, - childrenFn: (parent: Node) => Node[] | undefined + childrenFn: (parent: Node) => Node[] | undefined | null ) { if (!parent) return; @@ -47,17 +47,18 @@ export default function map2tree( root: {}, options: { key?: string; pushMethod?: 'push' | 'unshift' } = {}, tree: Node = { name: options.key || 'state', children: [] } -): Node { + // eslint-disable-next-line @typescript-eslint/ban-types +): Node | {} { // eslint-disable-next-line @typescript-eslint/ban-types if (!isPlainObject(root) && root && !(root as { toJS: () => {} }).toJS) { - return {} as Node; + return {}; } const { key: rootNodeKey = 'state', pushMethod = 'push' } = options; const currentNode = getNode(tree, rootNodeKey); if (currentNode === null) { - return {} as Node; + return {}; } mapValues( diff --git a/packages/map2tree/test/map2tree.spec.ts b/packages/map2tree/test/map2tree.spec.ts index e1cc3787..2a3cc7af 100644 --- a/packages/map2tree/test/map2tree.spec.ts +++ b/packages/map2tree/test/map2tree.spec.ts @@ -1,11 +1,11 @@ -import map2tree from '../src'; +import map2tree, { Node } from '../src'; import * as immutable from 'immutable'; test('# rootNodeKey', () => { const map = {}; const options = { key: 'foo' }; - expect(map2tree(map, options).name).toBe('foo'); + expect((map2tree(map, options) as Node).name).toBe('foo'); }); describe('# shallow map', () => { diff --git a/packages/map2tree/webpack.config.umd.ts b/packages/map2tree/webpack.config.umd.ts index 37e91464..86adef24 100644 --- a/packages/map2tree/webpack.config.umd.ts +++ b/packages/map2tree/webpack.config.umd.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -module.exports = (env: { production?: boolean } = {}) => ({ +export default (env: { production?: boolean } = {}) => ({ mode: env.production ? 'production' : 'development', entry: { app: ['./src/index'], diff --git a/packages/redux-devtools-log-monitor/src/LogMonitor.tsx b/packages/redux-devtools-log-monitor/src/LogMonitor.tsx index f224987a..b0d1f538 100644 --- a/packages/redux-devtools-log-monitor/src/LogMonitor.tsx +++ b/packages/redux-devtools-log-monitor/src/LogMonitor.tsx @@ -4,6 +4,7 @@ import { Action, Dispatch } from 'redux'; import * as themes from 'redux-devtools-themes'; import { Base16Theme } from 'redux-devtools-themes'; import { ActionCreators, LiftedAction, LiftedState } from 'redux-devtools'; +import debounce from 'lodash.debounce'; import { updateScrollTop, startConsecutiveToggle, @@ -12,9 +13,6 @@ import { import reducer, { LogMonitorState } from './reducers'; import LogMonitorButtonBar from './LogMonitorButtonBar'; import LogMonitorEntryList from './LogMonitorEntryList'; -import debounce from 'lodash.debounce'; -import { DockMonitorState } from 'redux-devtools-dock-monitor/lib/reducers'; -import { DockMonitorAction } from 'redux-devtools-dock-monitor/lib/actions'; // eslint-disable-next-line @typescript-eslint/unbound-method const { toggleAction, setActionsActive } = ActionCreators; @@ -276,8 +274,8 @@ export default (LogMonitor as unknown) as React.ComponentType< > & { update( monitorProps: ExternalProps>, - state: DockMonitorState | undefined, - action: DockMonitorAction - ): DockMonitorState; + state: LogMonitorState | undefined, + action: LogMonitorAction + ): LogMonitorState; defaultProps: DefaultProps; };