From 3b580dad4cb36abc395f9be139b2c3f94e872d87 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 20 Sep 2020 14:21:59 -0400 Subject: [PATCH] feat(d3tooltip): convert to TypeScript (#639) * start d3tooltip * finish d3tooltip --- packages/d3tooltip/.babelrc | 3 +- packages/d3tooltip/.eslintignore | 3 + packages/d3tooltip/.eslintrc.js | 21 ++++ packages/d3tooltip/package.json | 56 ++++----- packages/d3tooltip/src/index.js | 78 ------------ packages/d3tooltip/src/index.ts | 115 ++++++++++++++++++ packages/d3tooltip/src/utils/functor.js | 5 - packages/d3tooltip/src/utils/functor.ts | 20 +++ .../src/utils/{index.js => index.ts} | 0 packages/d3tooltip/src/utils/prependClass.js | 20 --- packages/d3tooltip/src/utils/prependClass.ts | 28 +++++ packages/d3tooltip/tsconfig.json | 7 ++ packages/d3tooltip/tsconfig.webpack.json | 4 + ...ck.config.umd.js => webpack.config.umd.ts} | 11 +- packages/map2tree/package.json | 2 +- packages/map2tree/webpack.config.umd.ts | 2 +- yarn.lock | 17 +++ 17 files changed, 254 insertions(+), 138 deletions(-) create mode 100644 packages/d3tooltip/.eslintignore create mode 100644 packages/d3tooltip/.eslintrc.js delete mode 100644 packages/d3tooltip/src/index.js create mode 100644 packages/d3tooltip/src/index.ts delete mode 100644 packages/d3tooltip/src/utils/functor.js create mode 100644 packages/d3tooltip/src/utils/functor.ts rename packages/d3tooltip/src/utils/{index.js => index.ts} (100%) delete mode 100644 packages/d3tooltip/src/utils/prependClass.js create mode 100644 packages/d3tooltip/src/utils/prependClass.ts create mode 100644 packages/d3tooltip/tsconfig.json create mode 100644 packages/d3tooltip/tsconfig.webpack.json rename packages/d3tooltip/{webpack.config.umd.js => webpack.config.umd.ts} (64%) diff --git a/packages/d3tooltip/.babelrc b/packages/d3tooltip/.babelrc index 1320b9a3..5259cd24 100644 --- a/packages/d3tooltip/.babelrc +++ b/packages/d3tooltip/.babelrc @@ -1,3 +1,4 @@ { - "presets": ["@babel/preset-env"] + "presets": ["@babel/preset-env", "@babel/preset-typescript"], + "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/d3tooltip/.eslintignore b/packages/d3tooltip/.eslintignore new file mode 100644 index 00000000..1d149abd --- /dev/null +++ b/packages/d3tooltip/.eslintignore @@ -0,0 +1,3 @@ +examples +lib +dist diff --git a/packages/d3tooltip/.eslintrc.js b/packages/d3tooltip/.eslintrc.js new file mode 100644 index 00000000..f815f2cb --- /dev/null +++ b/packages/d3tooltip/.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/d3tooltip/package.json b/packages/d3tooltip/package.json index 1ac1190a..67355f4d 100644 --- a/packages/d3tooltip/package.json +++ b/packages/d3tooltip/package.json @@ -2,46 +2,46 @@ "name": "d3tooltip", "version": "1.2.3", "description": "A highly configurable tooltip for d3", - "main": "lib/index.js", - "scripts": { - "clean": "rimraf lib dist", - "build": "babel src --out-dir lib", - "build:umd": "webpack --progress --config webpack.config.umd.js", - "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.js", - "version": "npm run build", - "postversion": "git push && git push --tags && npm run clean", - "prepare": "npm run clean && npm run build", - "prepublishOnly": "npm run clean && npm run build && npm run build:umd && npm run build:umd:min" + "keywords": [ + "d3", + "tooltip" + ], + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/d3tooltip", + "bugs": { + "url": "https://github.com/reduxjs/redux-devtools/issues" }, + "license": "MIT", + "author": "romseguy", "files": [ "lib", "dist", "src" ], + "main": "lib/index.js", + "types": "lib/index.d.ts", "repository": { "type": "git", "url": "https://github.com/reduxjs/redux-devtools.git" }, - "keywords": [ - "d3", - "tooltip" - ], - "author": "romseguy", - "license": "MIT", - "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/preset-env": "^7.11.0", - "babel-loader": "^8.1.0", - "rimraf": "^3.0.2", - "webpack": "^4.44.1", - "webpack-cli": "^3.3.12" + "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 --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 && npm run test", + "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { "ramda": "^0.27.1" + }, + "devDependencies": { + "@types/d3": "^3.5.43", + "@types/ramda": "^0.27.17" } } diff --git a/packages/d3tooltip/src/index.js b/packages/d3tooltip/src/index.js deleted file mode 100644 index 19d84255..00000000 --- a/packages/d3tooltip/src/index.js +++ /dev/null @@ -1,78 +0,0 @@ -import { is } from 'ramda'; -import utils from './utils'; -const { prependClass, functor } = utils.default || utils; - -const defaultOptions = { - left: undefined, // mouseX - top: undefined, // mouseY - offset: { left: 0, top: 0 }, - root: undefined, -}; - -export default function tooltip(d3, className = 'tooltip', options = {}) { - const { left, top, offset, root } = { ...defaultOptions, ...options }; - - let attrs = { class: className }; - let text = () => ''; - let styles = {}; - - let el; - const anchor = root || d3.select('body'); - const rootNode = anchor.node(); - - function tip(selection) { - selection.on({ - 'mouseover.tip': (node) => { - let [mouseX, mouseY] = d3.mouse(rootNode); - let [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top]; - - anchor.selectAll(`div.${className}`).remove(); - - el = anchor - .append('div') - .attr(prependClass(className)(attrs)) - .style({ - position: 'absolute', - 'z-index': 1001, - left: x + 'px', - top: y + 'px', - ...styles, - }) - .html(() => text(node)); - }, - - 'mousemove.tip': (node) => { - let [mouseX, mouseY] = d3.mouse(rootNode); - let [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top]; - - el.style({ - left: x + 'px', - top: y + 'px', - }).html(() => text(node)); - }, - - 'mouseout.tip': () => el.remove(), - }); - } - - tip.attr = function setAttr(d) { - if (is(Object, d)) { - attrs = { ...attrs, ...d }; - } - return this; - }; - - tip.style = function setStyle(d) { - if (is(Object, d)) { - styles = { ...styles, ...d }; - } - return this; - }; - - tip.text = function setText(d) { - text = functor(d); - return this; - }; - - return tip; -} diff --git a/packages/d3tooltip/src/index.ts b/packages/d3tooltip/src/index.ts new file mode 100644 index 00000000..13cac544 --- /dev/null +++ b/packages/d3tooltip/src/index.ts @@ -0,0 +1,115 @@ +import d3Package, { Primitive, Selection } from 'd3'; +import { is } from 'ramda'; +import utils from './utils'; +const { prependClass, functor } = utils; + +const defaultOptions = { + left: undefined, // mouseX + top: undefined, // mouseY + offset: { left: 0, top: 0 }, + root: undefined, +}; + +export default function tooltip( + d3: typeof d3Package, + className = 'tooltip', + options = {} +) { + const { left, top, offset, root } = { ...defaultOptions, ...options }; + + let attrs = { class: className }; + let text: (datum: Datum, index?: number, outerIndex?: number) => string = ( + node: Datum + ) => ''; + let styles = {}; + + let el: Selection; + const anchor = root || d3.select('body'); + const rootNode = anchor.node(); + + function tip(selection: Selection) { + selection.on('mouseover.tip', (node) => { + const [mouseX, mouseY] = d3.mouse(rootNode); + const [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top]; + + anchor.selectAll(`div.${className}`).remove(); + + el = anchor + .append('div') + .attr(prependClass(className)(attrs)) + .style({ + position: 'absolute', + 'z-index': 1001, + left: `${x}px`, + top: `${y}px`, + ...styles, + }) + .html(() => text(node)); + }); + + selection.on('mousemove.tip', (node) => { + const [mouseX, mouseY] = d3.mouse(rootNode); + const [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top]; + + el.style({ + left: `${x}px`, + top: `${y}px`, + }).html(() => text(node)); + }); + + selection.on('mouseout.tip', () => el.remove()); + } + + tip.attr = function setAttr( + d: + | string + | { + [key: string]: + | Primitive + | ((datum: Datum, index: number, outerIndex: number) => Primitive); + } + ) { + if (is(Object, d)) { + attrs = { + ...attrs, + ...(d as { + [key: string]: + | Primitive + | ((datum: Datum, index: number, outerIndex: number) => Primitive); + }), + }; + } + return this; + }; + + tip.style = function setStyle( + d: + | string + | { + [key: string]: + | Primitive + | ((datum: Datum, index: number, outerIndex: number) => Primitive); + } + ) { + if (is(Object, d)) { + styles = { + ...styles, + ...(d as { + [key: string]: + | Primitive + | ((datum: Datum, index: number, outerIndex: number) => Primitive); + }), + }; + } + return this; + }; + + tip.text = function setText( + d: string | ((datum: Datum, index?: number, outerIndex?: number) => string) + ) { + text = functor(d); + return this; + }; + + return tip; +} diff --git a/packages/d3tooltip/src/utils/functor.js b/packages/d3tooltip/src/utils/functor.js deleted file mode 100644 index 007885d3..00000000 --- a/packages/d3tooltip/src/utils/functor.js +++ /dev/null @@ -1,5 +0,0 @@ -import { is } from 'ramda'; - -export default function functor(v) { - return is(Function, v) ? v : () => v; -} diff --git a/packages/d3tooltip/src/utils/functor.ts b/packages/d3tooltip/src/utils/functor.ts new file mode 100644 index 00000000..b0ba2146 --- /dev/null +++ b/packages/d3tooltip/src/utils/functor.ts @@ -0,0 +1,20 @@ +import { is } from 'ramda'; +import { Primitive } from 'd3'; + +export default function functor( + v: string | ((datum: Datum, index?: number, outerIndex?: number) => string) +): (datum: Datum, index?: number, outerIndex?: number) => string; +export default function functor( + v: + | Primitive + | ((datum: Datum, index: number, outerIndex?: number) => Primitive) +): (datum: Datum, index?: number, outerIndex?: number) => Primitive; +export default function functor( + v: + | Primitive + | ((datum: Datum, index: number, outerIndex?: number) => Primitive) +): (datum: Datum, index: number, outerIndex?: number) => Primitive { + return is(Function, v) + ? (v as (datum: Datum, index: number, outerIndex?: number) => Primitive) + : () => v as Primitive; +} diff --git a/packages/d3tooltip/src/utils/index.js b/packages/d3tooltip/src/utils/index.ts similarity index 100% rename from packages/d3tooltip/src/utils/index.js rename to packages/d3tooltip/src/utils/index.ts diff --git a/packages/d3tooltip/src/utils/prependClass.js b/packages/d3tooltip/src/utils/prependClass.js deleted file mode 100644 index 2194f865..00000000 --- a/packages/d3tooltip/src/utils/prependClass.js +++ /dev/null @@ -1,20 +0,0 @@ -import { mapObjIndexed, join } from 'ramda'; -import functor from './functor'; - -export default function prependClass(className) { - return mapObjIndexed((value, key) => { - if (key === 'class') { - const fn = functor(value); - - return (d, i) => { - const classNames = fn(d, i); - if (classNames !== className) { - return join(' ', [className, classNames]); - } - return classNames; - }; - } - - return value; - }); -} diff --git a/packages/d3tooltip/src/utils/prependClass.ts b/packages/d3tooltip/src/utils/prependClass.ts new file mode 100644 index 00000000..cc9bf058 --- /dev/null +++ b/packages/d3tooltip/src/utils/prependClass.ts @@ -0,0 +1,28 @@ +import { mapObjIndexed, join } from 'ramda'; +import functor from './functor'; +import { Primitive } from 'd3'; + +export default function prependClass(className: string) { + return mapObjIndexed( + ( + value: + | Primitive + | ((datum: Datum, index: number, outerIndex?: number) => Primitive), + key + ) => { + if (key === 'class') { + const fn = functor(value); + + return (d: Datum, i: number) => { + const classNames = fn(d, i); + if (classNames !== className) { + return join(' ', [className, classNames]); + } + return classNames; + }; + } + + return value; + } + ); +} diff --git a/packages/d3tooltip/tsconfig.json b/packages/d3tooltip/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/d3tooltip/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/d3tooltip/tsconfig.webpack.json b/packages/d3tooltip/tsconfig.webpack.json new file mode 100644 index 00000000..655c4644 --- /dev/null +++ b/packages/d3tooltip/tsconfig.webpack.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["webpack.config.umd.ts"] +} diff --git a/packages/d3tooltip/webpack.config.umd.js b/packages/d3tooltip/webpack.config.umd.ts similarity index 64% rename from packages/d3tooltip/webpack.config.umd.js rename to packages/d3tooltip/webpack.config.umd.ts index 37a3178a..9913c7f5 100644 --- a/packages/d3tooltip/webpack.config.umd.js +++ b/packages/d3tooltip/webpack.config.umd.ts @@ -1,9 +1,9 @@ -const path = require('path'); +import * as path from 'path'; -module.exports = (env = {}) => ({ +module.exports = (env: { production?: boolean } = {}) => ({ mode: env.production ? 'production' : 'development', entry: { - app: ['./src/index.js'], + app: ['./src/index'], }, output: { library: 'd3tooltip', @@ -14,10 +14,13 @@ module.exports = (env = {}) => ({ module: { rules: [ { - test: /\.js$/, + test: /\.(js|ts)$/, loader: 'babel-loader', exclude: /node_modules/, }, ], }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, }); diff --git a/packages/map2tree/package.json b/packages/map2tree/package.json index 04d1eb48..516f5da6 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 && npm run test", + "preversion": "npm run type-check && npm run lint", "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { diff --git a/packages/map2tree/webpack.config.umd.ts b/packages/map2tree/webpack.config.umd.ts index 6bb5dabc..37e91464 100644 --- a/packages/map2tree/webpack.config.umd.ts +++ b/packages/map2tree/webpack.config.umd.ts @@ -6,7 +6,7 @@ module.exports = (env: { production?: boolean } = {}) => ({ app: ['./src/index'], }, output: { - library: 'd3tooltip', + library: 'map2tree', libraryTarget: 'umd', path: path.resolve(__dirname, 'dist'), filename: env.production ? 'map2tree.min.js' : 'map2tree.js', diff --git a/yarn.lock b/yarn.lock index 8b678689..58a70246 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3355,6 +3355,11 @@ dependencies: "@types/node" "*" +"@types/d3@^3.5.43": + version "3.5.43" + resolved "https://registry.yarnpkg.com/@types/d3/-/d3-3.5.43.tgz#e9b4992817e0b6c5efaa7d6e5bb2cee4d73eab58" + integrity sha512-t9ZmXOcpVxywRw86YtIC54g7M9puRh8hFedRvVfHKf5YyOP6pSxA0TvpXpfseXSCInoW4P7bggTrSDiUOs4g5w== + "@types/dateformat@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/dateformat/-/dateformat-3.0.1.tgz#98d747a2e5e9a56070c6bf14e27bff56204e34cc" @@ -3675,6 +3680,13 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== +"@types/ramda@^0.27.17": + version "0.27.17" + resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.27.17.tgz#b4359d29614994dbb4c70e504b268bd9f0388bb0" + integrity sha512-AHVwr1YdFdxeabfC1g34ZuJ61dKOcfdXlG+sqGUweD+5VrD6A9emwmc2OZY+N8CdEKdwl29hwvtTMSJ6ZVVsiQ== + dependencies: + ts-toolbelt "^6.3.3" + "@types/range-parser@*": version "1.2.3" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" @@ -16929,6 +16941,11 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +ts-toolbelt@^6.3.3: + version "6.15.5" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz#cb3b43ed725cb63644782c64fbcad7d8f28c0a83" + integrity sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A== + tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"