mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-22 06:00:07 +03:00
Merge branch 'main' into renovate/nanoid-4.x
This commit is contained in:
commit
2ade969514
8
.changeset/friendly-coats-trade.md
Normal file
8
.changeset/friendly-coats-trade.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
'd3-state-visualizer': major
|
||||
---
|
||||
|
||||
- Remove UMD build.
|
||||
- Split `style` option into `chartStyles`, `nodeStyleOptions`, `textStyleOptions`, and `linkStyles`.
|
||||
- The shape of the argument passed to the `onClickText` option has been updated.
|
||||
- Rename `InputOptions` to `Options`, `Primitive` to `StyleValue`, and `NodeWithId` to `HierarchyPointNode<Node>`.
|
6
.changeset/slimy-elephants-flash.md
Normal file
6
.changeset/slimy-elephants-flash.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
'@redux-devtools/chart-monitor': major
|
||||
---
|
||||
|
||||
- Split `style` option into `chartStyles`, `nodeStyleOptions`, `textStyleOptions`, and `linkStyles`.
|
||||
- The shape of the argument passed to the `onClickText` option has been updated.
|
10
.changeset/spicy-olives-compete.md
Normal file
10
.changeset/spicy-olives-compete.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
'd3tooltip': major
|
||||
---
|
||||
|
||||
- Remove UMD build.
|
||||
- Upgrade d3 peer dependency from v3 to v7.
|
||||
- Remove `attr` configuration method.
|
||||
- Rename `style` configuration method to `styles` and move to options.
|
||||
- Move `text` configuration method to options.
|
||||
- Remove d3 parameter as first parameter for `tooltip`.
|
5
.changeset/weak-kings-brake.md
Normal file
5
.changeset/weak-kings-brake.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'map2tree': major
|
||||
---
|
||||
|
||||
- Remove UMD build.
|
8
.github/workflows/CI.yml
vendored
8
.github/workflows/CI.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: nrwl/nx-set-shas@v2
|
||||
- uses: nrwl/nx-set-shas@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
|
@ -25,10 +25,10 @@ jobs:
|
|||
- name: Check formatting
|
||||
run: pnpm run format:check
|
||||
- name: Build
|
||||
run: pnpm exec nx affected --target=build --parallel=3
|
||||
run: pnpm exec nx affected --target=build --parallel=1
|
||||
- name: Lint
|
||||
run: pnpm exec nx affected --target=lint --parallel=3
|
||||
run: pnpm exec nx affected --target=lint --parallel=1
|
||||
- name: Test
|
||||
uses: GabrielBB/xvfb-action@v1
|
||||
with:
|
||||
run: pnpm exec nx affected --target=test --parallel=3
|
||||
run: pnpm exec nx affected --target=test --parallel=1
|
||||
|
|
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
|
@ -5,6 +5,8 @@ on:
|
|||
branches:
|
||||
- main
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
|
@ -36,3 +38,21 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Archive Chrome Extension
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: chrome
|
||||
path: extension/chrome/dist
|
||||
|
||||
- name: Archive Edge Extension
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: edge
|
||||
path: extension/edge/dist
|
||||
|
||||
- name: Archive Firefox Extension
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: firefox
|
||||
path: extension/firefox/dist
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
node_modules
|
||||
build
|
||||
dev
|
||||
webpack/replace
|
||||
dist
|
||||
examples
|
||||
npm-package
|
||||
_book
|
||||
|
|
9
extension/.gitignore
vendored
9
extension/.gitignore
vendored
|
@ -1,9 +1,2 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.idea/
|
||||
dist/
|
||||
build/
|
||||
dev/
|
||||
tmp/
|
||||
_book
|
||||
dist
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
# remotedev-redux-devtools-extension
|
||||
|
||||
## 3.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1aa6c4f7: Fix remounting root for devpanel
|
||||
|
||||
## 3.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 20ebf725: Remove source map from page wrap bundle
|
||||
|
||||
## 3.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 24f60a7a: bump min popup window width to 760px #1126 #1129
|
||||
|
||||
## 3.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -57,7 +57,7 @@ const composeEnhancers =
|
|||
compose;
|
||||
```
|
||||
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtools-extension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtoolsextension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
|
||||
```js
|
||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"version": "3.0.11",
|
||||
"version": "3.0.17",
|
||||
"name": "Redux DevTools",
|
||||
"short_name": "Redux DevTools",
|
||||
"description": "Redux DevTools for debugging application's state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"manifest_version": 2,
|
|
@ -16,6 +16,8 @@ If you develop on your local filesystem, make sure to allow Redux DevTools acces
|
|||
|
||||
Most likely you mutate the state. Check it by [adding `redux-immutable-state-invariant` middleware](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/counter/store/configureStore.js#L3).
|
||||
|
||||
Another cause could be that you are creating multiple stores, which means that the devtools get attached to one but the application uses another. See [https://github.com/reduxjs/redux-toolkit/issues/2753](this issue).
|
||||
|
||||
### @@INIT or REPLACE action resets the state of the app or last actions RE-APPLIED
|
||||
|
||||
`@@redux/REPLACE` (or `@@INIT`) is used internally when the application is hot reloaded. When you use `store.replaceReducer` the effect will be the same as for hot-reloading, where the extension is recomputing all the history again. To avoid that set [`shouldHotReload`](/docs/API/Arguments.md#shouldhotreload) parameter to `false`.
|
||||
|
|
67
extension/edge/manifest.json
Normal file
67
extension/edge/manifest.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"version": "3.0.17",
|
||||
"name": "Redux DevTools",
|
||||
"description": "Redux DevTools for debugging application's state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"manifest_version": 2,
|
||||
"page_action": {
|
||||
"default_icon": "img/logo/gray.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "window.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-left": {
|
||||
"description": "DevTools window to left"
|
||||
},
|
||||
"devtools-right": {
|
||||
"description": "DevTools window to right"
|
||||
},
|
||||
"devtools-bottom": {
|
||||
"description": "DevTools window to bottom"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
},
|
||||
"_execute_page_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+E"
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "img/logo/16x16.png",
|
||||
"48": "img/logo/48x48.png",
|
||||
"128": "img/logo/128x128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"chrome_style": true
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["background.bundle.js"],
|
||||
"persistent": false
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
}
|
||||
],
|
||||
"devtools_page": "devtools.html",
|
||||
"web_accessible_resources": ["page.bundle.js"],
|
||||
"externally_connectable": {
|
||||
"ids": ["*"]
|
||||
},
|
||||
"permissions": [
|
||||
"notifications",
|
||||
"contextMenus",
|
||||
"storage",
|
||||
"file:///*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "3.0.11",
|
||||
"version": "3.0.17",
|
||||
"name": "Redux DevTools",
|
||||
"manifest_version": 2,
|
||||
"description": "Redux Developer Tools for debugging application state changes.",
|
|
@ -5,4 +5,7 @@ module.exports = {
|
|||
moduleNameMapper: {
|
||||
'\\.css$': '<rootDir>/test/__mocks__/styleMock.ts',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!.pnpm|d3|dateformat|delaunator|nanoid|robust-predicates|uuid)',
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "remotedev-redux-devtools-extension",
|
||||
"version": "3.0.13",
|
||||
"version": "3.0.17",
|
||||
"description": "Redux Developer Tools for debugging application state changes.",
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/extension",
|
||||
"license": "MIT",
|
||||
|
@ -11,20 +11,18 @@
|
|||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack --config webpack/dev.config.babel.js",
|
||||
"build": "pnpm run build:extension && pnpm run build:firefox",
|
||||
"build:extension": "rimraf build/extension && webpack --config webpack/wrap.config.babel.js && webpack --config webpack/prod.config.babel.js",
|
||||
"build:firefox": "webpack --config webpack/prod.firefox.config.babel.js",
|
||||
"start": "webpack --env development --watch",
|
||||
"build": "pnpm run build:extension && pnpm run build:chrome && pnpm run build:edge && pnpm run build:firefox",
|
||||
"build:extension": "webpack --env production && webpack --config wrap.webpack.config.js",
|
||||
"build:chrome": "cpy . ../chrome/dist --cwd dist && cpy manifest.json dist --cwd chrome",
|
||||
"build:edge": "cpy . ../edge/dist --cwd dist && cpy manifest.json dist --cwd edge",
|
||||
"build:firefox": "cpy . ../firefox/dist --cwd dist && cpy manifest.json dist --cwd firefox",
|
||||
"build:examples": "babel-node examples/buildAll.js",
|
||||
"precompress:extension": "pnpm run lint && pnpm run test:app && pnpm run build:extension && pnpm run test:chrome && pnpm run test:electron",
|
||||
"precompress:firefox": "pnpm run lint && pnpm run build:firefox && pnpm run test:app",
|
||||
"compress:extension": "bestzip build/extension.zip build/extension",
|
||||
"compress:firefox": "bestzip build/extension.zip build/extension",
|
||||
"clean": "rimraf build && rimraf dev",
|
||||
"clean": "rimraf dist && rimraf chrome/dist && rimraf edge/dist && rimraf firefox/dist",
|
||||
"test:app": "cross-env BABEL_ENV=test jest test/app",
|
||||
"test:chrome": "jest test/chrome",
|
||||
"test:electron": "pnpm run build:test:electron:fixture && jest test/electron",
|
||||
"test": "pnpm run test:app && pnpm run build:extension && pnpm run test:chrome && pnpm run test:electron",
|
||||
"test": "pnpm run test:app && pnpm run test:chrome && pnpm run test:electron",
|
||||
"build:test:electron:fixture": "webpack --config test/electron/fixture/webpack.config.js",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
|
@ -43,56 +41,56 @@
|
|||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-is": "^18.2.0",
|
||||
"react-json-tree": "^0.17.0",
|
||||
"react-redux": "^8.0.2",
|
||||
"react-redux": "^8.0.5",
|
||||
"redux": "^4.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"styled-components": "^5.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.17.12",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@babel/register": "^7.17.7",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@types/chrome": "^0.0.190",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/react": "^18.0.14",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"babel-loader": "^8.2.5",
|
||||
"bestzip": "^2.2.1",
|
||||
"chromedriver": "^102.0.0",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@babel/register": "^7.18.9",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@types/chrome": "^0.0.206",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/styled-components": "^5.1.26",
|
||||
"babel-loader": "^9.1.0",
|
||||
"chromedriver": "^108.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"cpy-cli": "^4.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.1",
|
||||
"electron": "^19.0.4",
|
||||
"eslint": "^8.18.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"electron": "^22.0.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.14",
|
||||
"immutable": "^4.1.0",
|
||||
"jest": "^27.5.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"pug-html-loader": "^1.1.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-transform-catch-errors": "^1.0.2",
|
||||
"react-transform-hmr": "^1.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"selenium-webdriver": "^4.2.0",
|
||||
"selenium-webdriver": "^4.7.1",
|
||||
"sinon-chrome": "^3.0.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.1.5",
|
||||
"typescript": "~4.7.4",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "~4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ import {
|
|||
} from '@redux-devtools/app';
|
||||
import { GoRadioTower } from 'react-icons/go';
|
||||
import { MdBorderBottom, MdBorderLeft, MdBorderRight } from 'react-icons/md';
|
||||
import { Position } from '../api/openWindow';
|
||||
import { SingleMessage } from '../middlewares/api';
|
||||
import type { Position } from '../pageScript/api/openWindow';
|
||||
import type { SingleMessage } from '../background/store/apiMiddleware';
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
Action,
|
||||
createStore,
|
||||
PreloadedState,
|
||||
Reducer,
|
||||
StoreEnhancer,
|
||||
} from 'redux';
|
||||
|
||||
export default function configureStore<S, A extends Action<unknown>>(
|
||||
reducer: Reducer<S, A>,
|
||||
initialState: PreloadedState<S> | undefined,
|
||||
enhance: () => StoreEnhancer
|
||||
) {
|
||||
return createStore(reducer, initialState, enhance());
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
import { Store } from 'redux';
|
||||
import configureStore, {
|
||||
BackgroundAction,
|
||||
} from '../../../app/stores/backgroundStore';
|
||||
import configureStore, { BackgroundAction } from './store/backgroundStore';
|
||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||
import { createMenu, removeMenu } from './contextMenus';
|
||||
import syncOptions from '../options/syncOptions';
|
||||
import { BackgroundState } from '../../../app/reducers/background';
|
||||
import { BackgroundState } from './store/backgroundReducer';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
|
@ -15,28 +15,23 @@ import syncOptions, {
|
|||
Options,
|
||||
OptionsMessage,
|
||||
SyncOptions,
|
||||
} from '../../browser/extension/options/syncOptions';
|
||||
import openDevToolsWindow, {
|
||||
DevToolsPosition,
|
||||
} from '../../browser/extension/background/openWindow';
|
||||
import { getReport } from '../../browser/extension/background/logging';
|
||||
} from '../../options/syncOptions';
|
||||
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
||||
import { getReport } from '../logging';
|
||||
import { Action, Dispatch, MiddlewareAPI } from 'redux';
|
||||
import {
|
||||
import type {
|
||||
ContentScriptToBackgroundMessage,
|
||||
SplitMessage,
|
||||
} from '../../browser/extension/inject/contentScript';
|
||||
import {
|
||||
} from '../../contentScript';
|
||||
import type {
|
||||
ErrorMessage,
|
||||
PageScriptToContentScriptMessageForwardedToMonitors,
|
||||
PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance,
|
||||
} from '../api';
|
||||
} from '../../pageScript/api';
|
||||
import { LiftedState } from '@redux-devtools/instrument';
|
||||
import {
|
||||
BackgroundAction,
|
||||
LiftedActionAction,
|
||||
} from '../stores/backgroundStore';
|
||||
import { Position } from '../api/openWindow';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
||||
import type { Position } from '../../pageScript/api/openWindow';
|
||||
import type { BackgroundState } from './backgroundReducer';
|
||||
|
||||
interface TabMessageBase {
|
||||
readonly type: string;
|
|
@ -1,6 +1,6 @@
|
|||
import { combineReducers, Reducer } from 'redux';
|
||||
import { instances, InstancesState } from '@redux-devtools/app';
|
||||
import { BackgroundAction } from '../../stores/backgroundStore';
|
||||
import type { BackgroundAction } from './backgroundStore';
|
||||
|
||||
export interface BackgroundState {
|
||||
readonly instances: InstancesState;
|
|
@ -5,8 +5,8 @@ import {
|
|||
LIFTED_ACTION,
|
||||
StoreActionWithoutLiftedAction,
|
||||
} from '@redux-devtools/app';
|
||||
import rootReducer, { BackgroundState } from '../reducers/background';
|
||||
import api, { CONNECTED, DISCONNECTED } from '../middlewares/api';
|
||||
import rootReducer, { BackgroundState } from './backgroundReducer';
|
||||
import api, { CONNECTED, DISCONNECTED } from './apiMiddleware';
|
||||
|
||||
interface LiftedActionActionBase {
|
||||
action?: DispatchAction | string | CustomAction;
|
|
@ -3,12 +3,12 @@ import {
|
|||
getOptionsFromBg,
|
||||
isAllowed,
|
||||
} from '../options/syncOptions';
|
||||
import { TabMessage } from '../../../app/middlewares/api';
|
||||
import {
|
||||
import type { TabMessage } from '../background/store/apiMiddleware';
|
||||
import type {
|
||||
PageScriptToContentScriptMessage,
|
||||
PageScriptToContentScriptMessageWithoutDisconnect,
|
||||
PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance,
|
||||
} from '../../../app/api';
|
||||
} from '../pageScript/api';
|
||||
import { Action } from 'redux';
|
||||
import {
|
||||
CustomAction,
|
|
@ -4,7 +4,7 @@ html
|
|||
head
|
||||
meta(charset='UTF-8')
|
||||
title Redux DevTools
|
||||
include ./includes/style.pug
|
||||
include ../style.pug
|
||||
style.
|
||||
body {
|
||||
min-height: 100px;
|
|
@ -1,15 +1,15 @@
|
|||
import React, { CSSProperties } from 'react';
|
||||
import React, { CSSProperties, ReactNode } from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Persistor } from 'redux-persist';
|
||||
import { REMOVE_INSTANCE, StoreAction } from '@redux-devtools/app';
|
||||
import App from '../../../app/containers/App';
|
||||
import configureStore from '../../../app/stores/panelStore';
|
||||
import App from '../app/App';
|
||||
import configureStore from './store/panelStore';
|
||||
|
||||
import '../../views/devpanel.pug';
|
||||
import './devpanel.pug';
|
||||
import { Action, Store } from 'redux';
|
||||
import { PanelMessage } from '../../../app/middlewares/api';
|
||||
import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
|
||||
import type { PanelMessage } from '../background/store/apiMiddleware';
|
||||
import type { StoreStateWithoutSocket } from './store/panelReducer';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
||||
const position = location.hash;
|
||||
|
@ -20,6 +20,7 @@ const messageStyle: CSSProperties = {
|
|||
};
|
||||
|
||||
let rendered: boolean | undefined;
|
||||
let currentRoot: Root | undefined;
|
||||
let store: Store<StoreStateWithoutSocket, StoreAction> | undefined;
|
||||
let persistor: Persistor | undefined;
|
||||
let bgConnection: chrome.runtime.Port;
|
||||
|
@ -27,11 +28,16 @@ let naTimeout: NodeJS.Timeout;
|
|||
|
||||
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
||||
|
||||
function renderDevTools(root: Root) {
|
||||
root.unmount();
|
||||
function renderNodeAtRoot(node: ReactNode) {
|
||||
if (currentRoot) currentRoot.unmount();
|
||||
currentRoot = createRoot(document.getElementById('root')!);
|
||||
currentRoot.render(node);
|
||||
}
|
||||
|
||||
function renderDevTools() {
|
||||
clearTimeout(naTimeout);
|
||||
({ store, persistor } = configureStore(position, bgConnection));
|
||||
root.render(
|
||||
renderNodeAtRoot(
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
|
@ -41,7 +47,7 @@ function renderDevTools(root: Root) {
|
|||
rendered = true;
|
||||
}
|
||||
|
||||
function renderNA(root: Root) {
|
||||
function renderNA() {
|
||||
if (rendered === false) return;
|
||||
rendered = false;
|
||||
naTimeout = setTimeout(() => {
|
||||
|
@ -74,31 +80,28 @@ function renderNA(root: Root) {
|
|||
);
|
||||
}
|
||||
|
||||
root.unmount();
|
||||
root.render(message);
|
||||
renderNodeAtRoot(message);
|
||||
store = undefined;
|
||||
});
|
||||
} else {
|
||||
root.unmount();
|
||||
root.render(message);
|
||||
renderNodeAtRoot(message);
|
||||
store = undefined;
|
||||
}
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
function init(id: number) {
|
||||
const root = createRoot(document.getElementById('root')!);
|
||||
renderNA(root);
|
||||
renderNA();
|
||||
bgConnection = chrome.runtime.connect({
|
||||
name: id ? id.toString() : undefined,
|
||||
});
|
||||
bgConnection.onMessage.addListener(
|
||||
<S, A extends Action<unknown>>(message: PanelMessage<S, A>) => {
|
||||
if (message.type === 'NA') {
|
||||
if (message.id === id) renderNA(root);
|
||||
if (message.id === id) renderNA();
|
||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||
} else {
|
||||
if (!rendered) renderDevTools(root);
|
||||
if (!rendered) renderDevTools();
|
||||
store!.dispatch(message);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ import { createStore, applyMiddleware, Reducer } from 'redux';
|
|||
import localForage from 'localforage';
|
||||
import { persistReducer, persistStore } from 'redux-persist';
|
||||
import { exportStateMiddleware, StoreAction } from '@redux-devtools/app';
|
||||
import panelDispatcher from '../middlewares/panelSync';
|
||||
import rootReducer, { StoreStateWithoutSocket } from '../reducers/panel';
|
||||
import panelDispatcher from './panelSyncMiddleware';
|
||||
import rootReducer, { StoreStateWithoutSocket } from './panelReducer';
|
||||
|
||||
const persistConfig = {
|
||||
key: 'redux-devtools',
|
|
@ -1,4 +1,4 @@
|
|||
import '../../views/devtools.pug';
|
||||
import './devtools.pug';
|
||||
|
||||
function createPanel(url: string) {
|
||||
chrome.devtools.panels.create(
|
|
@ -23,7 +23,7 @@ export default ({ options, saveOption }: OptionsProps) => {
|
|||
<label className="option__label" htmlFor="editor-browser">
|
||||
{navigator.userAgent.indexOf('Firefox') !== -1
|
||||
? "Don't open in external editor"
|
||||
: "Use browser's debugger (from Chrome devpanel only)"}
|
||||
: "Use browser's debugger (from browser devpanel only)"}
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { FilterState } from '../../../app/api/filters';
|
||||
import { FilterState } from '../pageScript/api/filters';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
|
@ -2,10 +2,6 @@ import React from 'react';
|
|||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
const browserName = navigator.userAgent.includes('Firefox')
|
||||
? 'Firefox'
|
||||
: 'Chrome';
|
||||
|
||||
return (
|
||||
<fieldset className="option-group">
|
||||
<legend className="option-group__title">Miscellaneous</legend>
|
||||
|
@ -46,7 +42,7 @@ export default ({ options, saveOption }: OptionsProps) => {
|
|||
Show errors
|
||||
</label>
|
||||
<div className="option__hint">
|
||||
Show the {browserName} notifications when errors occur in the app
|
||||
Show the browser notifications when errors occur in the app
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
|
@ -3,7 +3,7 @@ import { createRoot } from 'react-dom/client';
|
|||
import OptionsComponent from './Options';
|
||||
import { Options } from './syncOptions';
|
||||
|
||||
import '../../views/options.pug';
|
||||
import './options.pug';
|
||||
|
||||
chrome.runtime.getBackgroundPage((background) => {
|
||||
const syncOptions = background!.syncOptions;
|
|
@ -1,4 +1,4 @@
|
|||
import { FilterState, FilterStateValue } from '../../../app/api/filters';
|
||||
import { FilterState, FilterStateValue } from '../pageScript/api/filters';
|
||||
|
||||
export interface Options {
|
||||
readonly useEditor: number;
|
|
@ -1,9 +1,6 @@
|
|||
import jsan from 'jsan';
|
||||
import { immutableSerialize } from '@redux-devtools/serialize';
|
||||
import {
|
||||
Config,
|
||||
SerializeWithImmutable,
|
||||
} from '../../browser/extension/inject/pageScript';
|
||||
import type { Config, SerializeWithImmutable } from '../index';
|
||||
import Immutable from 'immutable';
|
||||
import { LiftedState } from '@redux-devtools/instrument';
|
||||
import { Action } from 'redux';
|
|
@ -5,15 +5,15 @@ import { getActionsArray, getLocalFilter } from '@redux-devtools/utils';
|
|||
import { isFiltered, PartialLiftedState } from './filters';
|
||||
import importState from './importState';
|
||||
import generateId from './generateInstanceId';
|
||||
import { Config } from '../../browser/extension/inject/pageScript';
|
||||
import type { Config } from '../index';
|
||||
import { Action } from 'redux';
|
||||
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
||||
import { LibConfig } from '@redux-devtools/app';
|
||||
import {
|
||||
import type {
|
||||
ContentScriptToPageScriptMessage,
|
||||
ListenerMessage,
|
||||
} from '../../browser/extension/inject/contentScript';
|
||||
import { Position } from './openWindow';
|
||||
} from '../../contentScript';
|
||||
import type { Position } from './openWindow';
|
||||
|
||||
const listeners: {
|
||||
[instanceId: string]:
|
|
@ -1,5 +1,5 @@
|
|||
import { Action } from 'redux';
|
||||
import { PageScriptToContentScriptMessage } from './index';
|
||||
import type { PageScriptToContentScriptMessage } from './index';
|
||||
|
||||
export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Action, compose, Reducer, StoreEnhancerStoreCreator } from 'redux';
|
||||
import { instrument } from '@redux-devtools/instrument';
|
||||
import { persistState } from '@redux-devtools/core';
|
||||
import { ConfigWithExpandedMaxAge } from '../../browser/extension/inject/pageScript';
|
||||
import type { ConfigWithExpandedMaxAge } from './index';
|
||||
|
||||
export function getUrlParam(key: string) {
|
||||
const matches = window.location.href.match(
|
|
@ -27,19 +27,19 @@ import {
|
|||
LibConfig,
|
||||
Features,
|
||||
} from '@redux-devtools/app';
|
||||
import configureStore, { getUrlParam } from '../../../app/stores/enhancerStore';
|
||||
import configureStore, { getUrlParam } from './enhancerStore';
|
||||
import { isAllowed, Options } from '../options/syncOptions';
|
||||
import Monitor from '../../../app/service/Monitor';
|
||||
import Monitor from './Monitor';
|
||||
import {
|
||||
noFiltersApplied,
|
||||
isFiltered,
|
||||
filterState,
|
||||
startingFrom,
|
||||
} from '../../../app/api/filters';
|
||||
import notifyErrors from '../../../app/api/notifyErrors';
|
||||
import importState from '../../../app/api/importState';
|
||||
import openWindow, { Position } from '../../../app/api/openWindow';
|
||||
import generateId from '../../../app/api/generateInstanceId';
|
||||
} from './api/filters';
|
||||
import notifyErrors from './api/notifyErrors';
|
||||
import importState from './api/importState';
|
||||
import openWindow, { Position } from './api/openWindow';
|
||||
import generateId from './api/generateInstanceId';
|
||||
import {
|
||||
toContentScript,
|
||||
sendMessage,
|
||||
|
@ -51,8 +51,8 @@ import {
|
|||
Serialize,
|
||||
StructuralPerformAction,
|
||||
ConnectResponse,
|
||||
} from '../../../app/api';
|
||||
import { ContentScriptToPageScriptMessage } from './contentScript';
|
||||
} from './api';
|
||||
import type { ContentScriptToPageScriptMessage } from '../contentScript';
|
||||
|
||||
type EnhancedStoreWithInitialDispatch<
|
||||
S,
|
||||
|
@ -539,10 +539,14 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<unknown>>(
|
|||
return next(reducer_, initialState_);
|
||||
}
|
||||
|
||||
store = stores[instanceId] = configureStore(next, monitor.reducer, {
|
||||
store = stores[instanceId] = configureStore(
|
||||
next as StoreEnhancerStoreCreator,
|
||||
monitor.reducer,
|
||||
{
|
||||
...config,
|
||||
maxAge: getMaxAge as any,
|
||||
})(reducer_, initialState_) as any;
|
||||
}
|
||||
)(reducer_, initialState_) as any;
|
||||
|
||||
if (isInIframe()) setTimeout(init, 3000);
|
||||
else init();
|
|
@ -2,7 +2,7 @@ let s = document.createElement('script');
|
|||
s.type = 'text/javascript';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const { default: script } = require('raw-loader!tmp/page.bundle.js');
|
||||
const { default: script } = require('raw-loader!../dist/page.bundle.js');
|
||||
s.appendChild(document.createTextNode(script));
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
s.parentNode!.removeChild(s);
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { createRoot } from 'react-dom/client';
|
||||
import { Root } from '@redux-devtools/app';
|
||||
|
||||
import '../../views/remote.pug';
|
||||
import './remote.pug';
|
||||
|
||||
chrome.storage.local.get(
|
||||
{
|
|
@ -4,7 +4,7 @@ html
|
|||
head
|
||||
meta(charset='UTF-8')
|
||||
title RemoteDev
|
||||
include ./includes/style.pug
|
||||
include ../style.pug
|
||||
|
||||
body
|
||||
#root
|
|
@ -7,7 +7,7 @@ style.
|
|||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-width: 350px;
|
||||
min-width: 760px;
|
||||
min-height: 400px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -17,6 +17,7 @@ style.
|
|||
color: #fff;
|
||||
}
|
||||
#root {
|
||||
min-width: 760px;
|
||||
height: 100%;
|
||||
}
|
||||
#root > div {
|
|
@ -3,11 +3,11 @@ import { createRoot } from 'react-dom/client';
|
|||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { UPDATE_STATE } from '@redux-devtools/app';
|
||||
import App from '../../../app/containers/App';
|
||||
import configureStore from '../../../app/stores/windowStore';
|
||||
import { MonitorMessage } from '../../../app/middlewares/api';
|
||||
import App from '../app/App';
|
||||
import configureStore from './store/windowStore';
|
||||
import type { MonitorMessage } from '../background/store/apiMiddleware';
|
||||
|
||||
import '../../views/window.pug';
|
||||
import './window.pug';
|
||||
|
||||
const position = location.hash;
|
||||
|
|
@ -6,10 +6,10 @@ import {
|
|||
LIFTED_ACTION,
|
||||
SET_PERSIST,
|
||||
} from '@redux-devtools/app';
|
||||
import {
|
||||
import type {
|
||||
ExpandedUpdateStateAction,
|
||||
WindowStoreAction,
|
||||
} from '../../stores/windowStore';
|
||||
} from './windowStore';
|
||||
|
||||
export default function instances(
|
||||
state = instancesInitialState,
|
|
@ -9,8 +9,8 @@ import {
|
|||
theme,
|
||||
StoreState,
|
||||
} from '@redux-devtools/app';
|
||||
import instances from './instances';
|
||||
import { WindowStoreAction } from '../../stores/windowStore';
|
||||
import instances from './instancesReducer';
|
||||
import type { WindowStoreAction } from './windowStore';
|
||||
|
||||
const rootReducer: Reducer<StoreState, WindowStoreAction> =
|
||||
combineReducers<StoreState>({
|
|
@ -17,12 +17,15 @@ import {
|
|||
StoreState,
|
||||
UpdateStateAction,
|
||||
} from '@redux-devtools/app';
|
||||
import syncStores from '../middlewares/windowSync';
|
||||
import instanceSelector from '../middlewares/instanceSelector';
|
||||
import rootReducer from '../reducers/window';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import { BackgroundAction } from './backgroundStore';
|
||||
import { EmptyUpdateStateAction, NAAction } from '../middlewares/api';
|
||||
import syncStores from './windowSyncMiddleware';
|
||||
import instanceSelector from './instanceSelectorMiddleware';
|
||||
import rootReducer from './windowReducer';
|
||||
import type { BackgroundState } from '../../background/store/backgroundReducer';
|
||||
import type { BackgroundAction } from '../../background/store/backgroundStore';
|
||||
import type {
|
||||
EmptyUpdateStateAction,
|
||||
NAAction,
|
||||
} from '../../background/store/apiMiddleware';
|
||||
|
||||
export interface ExpandedUpdateStateAction extends UpdateStateAction {
|
||||
readonly instances: InstancesState;
|
|
@ -7,9 +7,9 @@ import {
|
|||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app';
|
||||
import { Dispatch, MiddlewareAPI, Store } from 'redux';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import { WindowStoreAction } from '../stores/windowStore';
|
||||
import { BackgroundAction } from '../stores/backgroundStore';
|
||||
import type { BackgroundState } from '../../background/store/backgroundReducer';
|
||||
import type { WindowStoreAction } from './windowStore';
|
||||
import type { BackgroundAction } from '../../background/store/backgroundStore';
|
||||
|
||||
const syncStores =
|
||||
(baseStore: Store<BackgroundState, BackgroundAction>) =>
|
|
@ -4,7 +4,7 @@ html
|
|||
head
|
||||
meta(charset='UTF-8')
|
||||
title Redux DevTools
|
||||
include ./includes/style.pug
|
||||
include ../style.pug
|
||||
|
||||
body
|
||||
#root
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from '../../../src/app/stores/windowStore';
|
||||
import App from '../../../src/app/containers/App';
|
||||
import configureStore from '../../../src/window/store/windowStore';
|
||||
import App from '../../../src/app/App';
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
|
@ -18,7 +18,7 @@ Object.defineProperty(window, 'matchMedia', {
|
|||
})),
|
||||
});
|
||||
|
||||
const { store } = configureStore(store);
|
||||
const { store } = configureStore();
|
||||
|
||||
describe('App container', () => {
|
||||
it("should render inspector monitor's component", () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { insertScript, listenMessage } from '../../utils/inject';
|
||||
import '../../../src/browser/extension/inject/pageScript';
|
||||
import '../../../src/pageScript';
|
||||
|
||||
describe('API', () => {
|
||||
it('should get window.__REDUX_DEVTOOLS_EXTENSION__ function', () => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import '@babel/polyfill';
|
||||
import { createStore, compose } from 'redux';
|
||||
import { insertScript, listenMessage } from '../../utils/inject';
|
||||
import '../../../src/browser/extension/inject/pageScript';
|
||||
import '../../../src/pageScript';
|
||||
|
||||
function counter(state = 0, action) {
|
||||
switch (action.type) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import chromedriver from 'chromedriver';
|
|||
import { switchMonitorTests, delay } from '../utils/e2e';
|
||||
|
||||
const port = 9515;
|
||||
const path = resolve(__dirname, '..', '..', 'build', 'extension');
|
||||
const path = resolve(__dirname, '..', '..', 'dist');
|
||||
const extensionId = 'lmhkpmbekcpmknklioeibfkpmmfibljd';
|
||||
const actionsPattern =
|
||||
/^@@INIT(.|\n)+@@reduxReactRouter\/routerDidChange(.|\n)+@@reduxReactRouter\/initRoutes(.|\n)+$/;
|
||||
|
|
|
@ -4,7 +4,7 @@ const { app, BrowserWindow, session } = require('electron');
|
|||
app.on('window-all-closed', app.quit);
|
||||
app.whenReady().then(async () => {
|
||||
await session.defaultSession.loadExtension(
|
||||
path.join(__dirname, '../../../build/extension'),
|
||||
path.join(__dirname, '../../../dist'),
|
||||
{ allowFileAccess: true }
|
||||
);
|
||||
|
||||
|
|
81
extension/webpack.config.js
Normal file
81
extension/webpack.config.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
|
||||
module.exports = function (env) {
|
||||
return {
|
||||
mode: env.production ? 'production' : 'development',
|
||||
devtool: env.production ? undefined : 'eval-source-map',
|
||||
entry: {
|
||||
background: [
|
||||
path.resolve(__dirname, 'src/chromeApiMock'),
|
||||
path.resolve(__dirname, 'src/background/index'),
|
||||
],
|
||||
options: [
|
||||
path.resolve(__dirname, 'src/chromeApiMock'),
|
||||
path.resolve(__dirname, 'src/options/index'),
|
||||
],
|
||||
window: [path.resolve(__dirname, 'src/window/index')],
|
||||
remote: [path.resolve(__dirname, 'src/remote/index')],
|
||||
devpanel: [
|
||||
path.resolve(__dirname, 'src/chromeApiMock'),
|
||||
path.resolve(__dirname, 'src/devpanel/index'),
|
||||
],
|
||||
devtools: path.resolve(__dirname, 'src/devtools/index'),
|
||||
content: [
|
||||
path.resolve(__dirname, 'src/chromeApiMock'),
|
||||
path.resolve(__dirname, 'src/contentScript/index'),
|
||||
],
|
||||
page: path.join(__dirname, 'src/pageScript'),
|
||||
...(env.production
|
||||
? {}
|
||||
: { pagewrap: path.resolve(__dirname, 'src/pageScriptWrap') }),
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.BABEL_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
}),
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
configFile: 'tsconfig.json',
|
||||
},
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join(__dirname, 'chrome/manifest.json'),
|
||||
to: path.join(__dirname, 'dist/manifest.json'),
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, 'src/assets'),
|
||||
to: path.join(__dirname, 'dist'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|ts)x?$/,
|
||||
use: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.css?$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.pug$/,
|
||||
use: ['file-loader?name=[name].html', 'pug-html-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,91 +0,0 @@
|
|||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import CopyPlugin from 'copy-webpack-plugin';
|
||||
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
|
||||
const extpath = path.join(__dirname, '../src/browser/extension/');
|
||||
const mock = `${extpath}chromeAPIMock`;
|
||||
|
||||
const baseConfig = (params) => ({
|
||||
// devtool: 'source-map',
|
||||
mode: params.mode,
|
||||
entry: params.input || {
|
||||
background: [mock, `${extpath}background/index`],
|
||||
options: [mock, `${extpath}options/index`],
|
||||
window: [`${extpath}window/index`],
|
||||
remote: [`${extpath}window/remote`],
|
||||
devpanel: [mock, `${extpath}devpanel/index`],
|
||||
devtools: [`${extpath}devtools/index`],
|
||||
content: [mock, `${extpath}inject/contentScript`],
|
||||
pagewrap: [`${extpath}inject/pageScriptWrap`],
|
||||
...params.inputExtra,
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
chunkFilename: '[id].chunk.js',
|
||||
...params.output,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(params.globals),
|
||||
...(params.plugins
|
||||
? params.plugins
|
||||
: [
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
configFile: 'tsconfig.json',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
].concat(
|
||||
params.copy
|
||||
? new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: params.manifestJsonPath,
|
||||
to: path.join(params.output.path, 'manifest.json'),
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../src/assets/'),
|
||||
to: params.output.path,
|
||||
},
|
||||
],
|
||||
})
|
||||
: []
|
||||
),
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
app: path.join(__dirname, '../src/app'),
|
||||
tmp: path.join(__dirname, '../build/tmp'),
|
||||
},
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
fallback: {
|
||||
path: require.resolve('path-browserify'),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
...(params.loaders
|
||||
? params.loaders
|
||||
: [
|
||||
{
|
||||
test: /\.(js|ts)x?$/,
|
||||
use: 'babel-loader',
|
||||
exclude: /(node_modules|tmp\/page\.bundle)/,
|
||||
},
|
||||
]),
|
||||
{
|
||||
test: /\.css?$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.pug$/,
|
||||
use: ['file-loader?name=[name].html', 'pug-html-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export default baseConfig;
|
|
@ -1,26 +0,0 @@
|
|||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import baseConfig from './base.config';
|
||||
|
||||
let config = baseConfig({
|
||||
mode: 'development',
|
||||
inputExtra: {
|
||||
page: [path.join(__dirname, '../src/browser/extension/inject/pageScript')],
|
||||
},
|
||||
output: { path: path.join(__dirname, '../dev') },
|
||||
globals: {
|
||||
'process.env': {
|
||||
NODE_ENV: '"development"',
|
||||
},
|
||||
},
|
||||
plugins: [new webpack.NoEmitOnErrorsPlugin()],
|
||||
copy: true,
|
||||
manifestJsonPath: path.join(
|
||||
__dirname,
|
||||
'../src/browser/extension/manifest.json'
|
||||
),
|
||||
});
|
||||
|
||||
config.watch = true;
|
||||
|
||||
export default config;
|
|
@ -1,20 +0,0 @@
|
|||
import path from 'path';
|
||||
import baseConfig from './base.config';
|
||||
|
||||
export default baseConfig({
|
||||
mode: 'production',
|
||||
inputExtra: {
|
||||
page: [path.join(__dirname, '../src/browser/extension/inject/pageScript')],
|
||||
},
|
||||
output: { path: path.join(__dirname, '../build/extension') },
|
||||
globals: {
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"',
|
||||
},
|
||||
},
|
||||
copy: true,
|
||||
manifestJsonPath: path.join(
|
||||
__dirname,
|
||||
'../src/browser/extension/manifest.json'
|
||||
),
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import path from 'path';
|
||||
import baseConfig from './base.config';
|
||||
|
||||
export default baseConfig({
|
||||
mode: 'production',
|
||||
output: { path: path.join(__dirname, '../build/extension') },
|
||||
globals: {
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"',
|
||||
},
|
||||
},
|
||||
copy: true,
|
||||
manifestJsonPath: path.join(
|
||||
__dirname,
|
||||
'../src/browser/firefox/manifest.json'
|
||||
),
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import path from 'path';
|
||||
import baseConfig from './base.config';
|
||||
|
||||
export default baseConfig({
|
||||
mode: 'production',
|
||||
input: {
|
||||
page: [path.join(__dirname, '../src/browser/extension/inject/pageScript')],
|
||||
},
|
||||
output: { path: path.join(__dirname, '../build/tmp') },
|
||||
globals: {
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"',
|
||||
},
|
||||
},
|
||||
});
|
31
extension/wrap.webpack.config.js
Normal file
31
extension/wrap.webpack.config.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
const path = require('path');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
pagewrap: path.resolve(__dirname, 'src/pageScriptWrap'),
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
},
|
||||
plugins: [
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
configFile: 'tsconfig.json',
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|ts)x?$/,
|
||||
use: 'babel-loader',
|
||||
exclude: /(node_modules|dist\/page\.bundle)/,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
35
package.json
35
package.json
|
@ -1,28 +1,29 @@
|
|||
{
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/eslint-parser": "^7.18.2",
|
||||
"@changesets/cli": "^2.23.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"eslint": "^8.18.0",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@changesets/cli": "^2.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-jest": "^26.5.3",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"jest": "^27.5.1",
|
||||
"prettier": "2.7.1",
|
||||
"typescript": "~4.7.4",
|
||||
"nx": "^14.3.6",
|
||||
"@nrwl/nx-cloud": "^14.1.2"
|
||||
"jest": "^29.3.1",
|
||||
"prettier": "2.8.1",
|
||||
"typescript": "~4.9.4",
|
||||
"nx": "^15.3.3",
|
||||
"@nrwl/nx-cloud": "^15.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"build:all": "nx run-many --target=build --all",
|
||||
"lint:all": "nx run-many --target=lint --all",
|
||||
"test:all": "nx run-many --target=test --all",
|
||||
"build:all": "nx run-many --target=build --all --parallel=1",
|
||||
"lint:all": "nx run-many --target=lint --all --parallel=1",
|
||||
"test:all": "nx run-many --target=test --all --parallel=1",
|
||||
"clean:all": "nx run-many --target=clean --all --parallel=1",
|
||||
"release": "pnpm build:all && changeset publish"
|
||||
},
|
||||
"workspaces": [
|
||||
|
@ -38,7 +39,7 @@
|
|||
"packages/redux-devtools-rtk-query-monitor/demo",
|
||||
"packages/redux-devtools-slider-monitor/examples/todomvc"
|
||||
],
|
||||
"packageManager": "pnpm@7.3.0",
|
||||
"packageManager": "pnpm@7.19.0",
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@babel/highlight>chalk": "Methuselah96/chalk#v2-without-process"
|
||||
|
|
|
@ -35,7 +35,7 @@ const render = tree(document.getElementById('root'), {
|
|||
isSorted: false,
|
||||
widthBetweenNodesCoeff: 1.5,
|
||||
heightBetweenNodesCoeff: 2,
|
||||
style: { border: '1px solid black' },
|
||||
chartStyles: { border: '1px solid black' },
|
||||
tooltipOptions: { offset: { left: 30, top: 10 }, indentationSize: 2 },
|
||||
});
|
||||
|
||||
|
@ -61,7 +61,7 @@ Other options are listed below and have reasonable default values if you want to
|
|||
| Option | Type | Default | Description |
|
||||
| ------------------------- | ------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | String | `'d3svg'` | Sets the identifier of the SVG element —i.e your chart— that will be added to the DOM element you passed as first argument |
|
||||
| `style` | Object | `{}` | Sets the CSS style of the chart |
|
||||
| `chartStyles` | Object | `{}` | Sets the CSS style of the chart |
|
||||
| `size` | Number | `500` | Sets size of the chart in pixels |
|
||||
| `aspectRatio` | Float | `1.0` | Sets the chart height to `size * aspectRatio` and [viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) in order to preserve the aspect ratio of the chart. [Great video](https://www.youtube.com/watch?v=FCOeMy7HrBc) if you want to learn more about how SVG works |
|
||||
| `widthBetweenNodesCoeff` | Float | `1.0` | Alters the horizontal space between each node |
|
||||
|
@ -74,12 +74,6 @@ Other options are listed below and have reasonable default values if you want to
|
|||
|
||||
More to come...
|
||||
|
||||
## Bindings
|
||||
|
||||
### React
|
||||
|
||||
[example](https://github.com/reduxjs/redux-devtools/tree/master/packages/d3-state-visualizer/examples/react-tree) implementation.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Threshold for large arrays so only a single node is displayed instead of all the children. That single node would be exclude from searching until selected.
|
||||
|
|
|
@ -29,22 +29,22 @@
|
|||
"map2tree": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@types/node": "^16.11.41",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"babel-loader": "^8.2.5",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/node": "^18.11.17",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"babel-loader": "^9.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.14",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "~4.7.4",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.9.2"
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.11.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ const render = tree(document.getElementById('root')!, {
|
|||
isSorted: false,
|
||||
widthBetweenNodesCoeff: 1.5,
|
||||
heightBetweenNodesCoeff: 2,
|
||||
style: { border: '1px solid black' },
|
||||
chartStyles: { border: '1px solid black' },
|
||||
tooltipOptions: { offset: { left: 30, top: 10 }, indentationSize: 2 },
|
||||
});
|
||||
|
||||
|
|
|
@ -23,18 +23,16 @@
|
|||
"main": "lib/cjs/index.js",
|
||||
"module": "lib/esm/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"unpkg": "dist/d3-state-visualizer.umd.js",
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types && pnpm run build:umd",
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types",
|
||||
"build:cjs": "babel src --extensions \".ts\" --out-dir lib/cjs",
|
||||
"build:esm": "babel src --config-file ./babel.config.esm.json --extensions \".ts\" --out-dir lib/esm",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:umd": "rollup -c",
|
||||
"clean": "rimraf lib",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"type-check": "tsc --noEmit",
|
||||
|
@ -42,35 +40,26 @@
|
|||
"prepublish": "pnpm run type-check && pnpm run lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@types/d3": "^3.5.47",
|
||||
"d3": "^3.5.17",
|
||||
"@babel/runtime": "^7.20.6",
|
||||
"@types/d3": "^7.4.0",
|
||||
"d3": "^7.8.0",
|
||||
"d3tooltip": "^2.1.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"map2tree": "^2.1.0",
|
||||
"ramda": "^0.28.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/eslint-parser": "^7.18.2",
|
||||
"@babel/plugin-transform-runtime": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@types/node": "^16.11.41",
|
||||
"@types/ramda": "^0.28.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"eslint": "^8.18.0",
|
||||
"@babel/cli": "^7.19.3",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/ramda": "^0.28.20",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.75.6",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.32.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.7.4"
|
||||
"typescript": "~4.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import typescript from 'rollup-plugin-typescript2';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const config = [
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'd3-state-visualizer',
|
||||
file: 'lib/umd/d3-state-visualizer.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'd3-state-visualizer',
|
||||
file: 'lib/umd/d3-state-visualizer.min.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
terser(),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default config;
|
|
@ -1,2 +1,4 @@
|
|||
export type { HierarchyPointNode } from 'd3';
|
||||
export type { StyleValue } from 'd3tooltip';
|
||||
export { default as tree } from './tree/tree';
|
||||
export type { InputOptions, NodeWithId, Primitive } from './tree/tree';
|
||||
export type { Node, Options } from './tree/tree';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import d3, { ZoomEvent, Primitive } from 'd3';
|
||||
import * as d3 from 'd3';
|
||||
import type { D3ZoomEvent, HierarchyPointLink, HierarchyPointNode } from 'd3';
|
||||
import { isEmpty } from 'ramda';
|
||||
import { map2tree } from 'map2tree';
|
||||
import type { Node } from 'map2tree';
|
||||
import deepmerge from 'deepmerge';
|
||||
import {
|
||||
getTooltipString,
|
||||
|
@ -9,17 +11,33 @@ import {
|
|||
getNodeGroupByDepthCount,
|
||||
} from './utils';
|
||||
import { tooltip } from 'd3tooltip';
|
||||
import type { StyleValue } from 'd3tooltip';
|
||||
|
||||
export interface InputOptions {
|
||||
export interface Options {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
state?: {} | null;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
tree?: NodeWithId | {};
|
||||
tree?: Node | {};
|
||||
|
||||
rootKeyName: string;
|
||||
pushMethod: 'push' | 'unshift';
|
||||
id: string;
|
||||
style: { [key: string]: Primitive };
|
||||
chartStyles: { [key: string]: StyleValue };
|
||||
nodeStyleOptions: {
|
||||
colors: {
|
||||
default: string;
|
||||
collapsed: string;
|
||||
parent: string;
|
||||
};
|
||||
radius: number;
|
||||
};
|
||||
textStyleOptions: {
|
||||
colors: {
|
||||
default: string;
|
||||
hover: string;
|
||||
};
|
||||
};
|
||||
linkStyles: { [key: string]: StyleValue };
|
||||
size: number;
|
||||
aspectRatio: number;
|
||||
initialZoom: number;
|
||||
|
@ -34,7 +52,7 @@ export interface InputOptions {
|
|||
widthBetweenNodesCoeff: number;
|
||||
transitionDuration: number;
|
||||
blinkDuration: number;
|
||||
onClickText: (datum: NodeWithId) => void;
|
||||
onClickText: (datum: HierarchyPointNode<Node>) => void;
|
||||
tooltipOptions: {
|
||||
disabled?: boolean;
|
||||
left?: number | undefined;
|
||||
|
@ -43,64 +61,7 @@ export interface InputOptions {
|
|||
left: number;
|
||||
top: number;
|
||||
};
|
||||
style?: { [key: string]: Primitive } | undefined;
|
||||
indentationSize?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface Options {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
state?: {} | null;
|
||||
// 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;
|
||||
styles?: { [key: string]: StyleValue } | undefined;
|
||||
indentationSize?: number;
|
||||
};
|
||||
}
|
||||
|
@ -111,8 +72,8 @@ const defaultOptions: Options = {
|
|||
pushMethod: 'push',
|
||||
tree: undefined,
|
||||
id: 'd3svg',
|
||||
style: {
|
||||
node: {
|
||||
chartStyles: {},
|
||||
nodeStyleOptions: {
|
||||
colors: {
|
||||
default: '#ccc',
|
||||
collapsed: 'lightsteelblue',
|
||||
|
@ -120,17 +81,16 @@ const defaultOptions: Options = {
|
|||
},
|
||||
radius: 7,
|
||||
},
|
||||
text: {
|
||||
textStyleOptions: {
|
||||
colors: {
|
||||
default: 'black',
|
||||
hover: 'skyblue',
|
||||
},
|
||||
},
|
||||
link: {
|
||||
linkStyles: {
|
||||
stroke: '#000',
|
||||
fill: 'none',
|
||||
},
|
||||
},
|
||||
size: 500,
|
||||
aspectRatio: 1.0,
|
||||
initialZoom: 1,
|
||||
|
@ -156,37 +116,29 @@ const defaultOptions: Options = {
|
|||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
style: undefined,
|
||||
styles: undefined,
|
||||
},
|
||||
};
|
||||
} satisfies Options;
|
||||
|
||||
export interface NodeWithId {
|
||||
name: string;
|
||||
children?: NodeWithId[] | null;
|
||||
_children?: NodeWithId[] | null;
|
||||
value?: unknown;
|
||||
id: string;
|
||||
|
||||
parent?: NodeWithId;
|
||||
depth?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
export interface InternalNode extends Node {
|
||||
_children?: this[] | undefined;
|
||||
id: string | number;
|
||||
}
|
||||
|
||||
interface NodePosition {
|
||||
parentId: string | null | undefined;
|
||||
id: string;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
parentId: string | number | null;
|
||||
id: string | number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export default function (
|
||||
DOMNode: HTMLElement,
|
||||
options: Partial<InputOptions> = {}
|
||||
) {
|
||||
export default function (DOMNode: HTMLElement, options: Partial<Options> = {}) {
|
||||
const {
|
||||
id,
|
||||
style,
|
||||
chartStyles,
|
||||
nodeStyleOptions,
|
||||
textStyleOptions,
|
||||
linkStyles,
|
||||
size,
|
||||
aspectRatio,
|
||||
initialZoom,
|
||||
|
@ -202,64 +154,50 @@ export default function (
|
|||
tree,
|
||||
tooltipOptions,
|
||||
onClickText,
|
||||
} = deepmerge(defaultOptions, options) as Options;
|
||||
} = deepmerge(defaultOptions, options);
|
||||
|
||||
const width = size - margin.left - margin.right;
|
||||
const height = size * aspectRatio - margin.top - margin.bottom;
|
||||
const fullWidth = size;
|
||||
const fullHeight = size * aspectRatio;
|
||||
|
||||
const attr: { [key: string]: Primitive } = {
|
||||
id,
|
||||
preserveAspectRatio: 'xMinYMin slice',
|
||||
};
|
||||
|
||||
if (!(style as unknown as { [key: string]: Primitive }).width) {
|
||||
attr.width = fullWidth;
|
||||
}
|
||||
|
||||
if (
|
||||
!(style as unknown as { [key: string]: Primitive }).width ||
|
||||
!(style as unknown as { [key: string]: Primitive }).height
|
||||
) {
|
||||
attr.viewBox = `0 0 ${fullWidth} ${fullHeight}`;
|
||||
}
|
||||
|
||||
const root = d3.select(DOMNode);
|
||||
const zoom = d3.behavior.zoom().scaleExtent([0.1, 3]).scale(initialZoom);
|
||||
const vis = root
|
||||
const zoom = d3.zoom<SVGSVGElement, unknown>().scaleExtent([0.1, 3]);
|
||||
|
||||
const svgElement = root
|
||||
.append('svg')
|
||||
.attr(attr)
|
||||
.style({ cursor: '-webkit-grab', ...style } as unknown as {
|
||||
[key: string]: Primitive;
|
||||
})
|
||||
.attr('id', id)
|
||||
.attr('preserveAspectRatio', 'xMinYMin slice')
|
||||
.style('cursor', '-webkit-grab');
|
||||
|
||||
if (!chartStyles.width) {
|
||||
svgElement.attr('width', fullWidth);
|
||||
}
|
||||
|
||||
if (!chartStyles.width || !chartStyles.height) {
|
||||
svgElement.attr('viewBox', `0 0 ${fullWidth} ${fullHeight}`);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(chartStyles)) {
|
||||
svgElement.style(key, value);
|
||||
}
|
||||
|
||||
const vis = svgElement
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
.call(zoom.scaleTo, initialZoom)
|
||||
.call(
|
||||
zoom.on('zoom', () => {
|
||||
const { translate, scale } = d3.event as ZoomEvent;
|
||||
vis.attr(
|
||||
'transform',
|
||||
`translate(${translate.toString()})scale(${scale})`
|
||||
);
|
||||
zoom.on('zoom', (event) => {
|
||||
const { transform } = event as D3ZoomEvent<SVGSVGElement, unknown>;
|
||||
vis.attr('transform', transform.toString());
|
||||
})
|
||||
)
|
||||
.append('g')
|
||||
.attr({
|
||||
transform: `translate(${margin.left + style.node.radius}, ${
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(${margin.left + nodeStyleOptions.radius}, ${
|
||||
margin.top
|
||||
}) scale(${initialZoom})`,
|
||||
});
|
||||
|
||||
let layout = d3.layout.tree().size([width, height]);
|
||||
let data: NodeWithId;
|
||||
|
||||
if (isSorted) {
|
||||
layout.sort((a, b) =>
|
||||
(b as NodeWithId).name.toLowerCase() <
|
||||
(a as NodeWithId).name.toLowerCase()
|
||||
? 1
|
||||
: -1
|
||||
}) scale(${initialZoom})`
|
||||
);
|
||||
}
|
||||
|
||||
// previousNodePositionsById stores node x and y
|
||||
// as well as hierarchy (id / parentId);
|
||||
|
@ -277,8 +215,8 @@ export default function (
|
|||
// of parent ids; once a parent that matches the given filter is found,
|
||||
// the parent position gets returned
|
||||
function findParentNodePosition(
|
||||
nodePositionsById: { [nodeId: string]: NodePosition },
|
||||
nodeId: string,
|
||||
nodePositionsById: { [nodeId: string | number]: NodePosition },
|
||||
nodeId: string | number,
|
||||
filter: (nodePosition: NodePosition) => boolean
|
||||
) {
|
||||
let currentPosition = nodePositionsById[nodeId];
|
||||
|
@ -294,19 +232,18 @@ export default function (
|
|||
}
|
||||
|
||||
return function renderChart(nextState = tree || state) {
|
||||
data = !tree
|
||||
? // eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(map2tree(nextState as {}, {
|
||||
let data = !tree
|
||||
? (map2tree(nextState, {
|
||||
key: rootKeyName,
|
||||
pushMethod,
|
||||
}) as NodeWithId)
|
||||
: (nextState as NodeWithId);
|
||||
}) as InternalNode)
|
||||
: (nextState as InternalNode);
|
||||
|
||||
if (isEmpty(data) || !data.name) {
|
||||
data = {
|
||||
name: 'error',
|
||||
message: 'Please provide a state map or a tree structure',
|
||||
} as unknown as NodeWithId;
|
||||
} as unknown as InternalNode;
|
||||
}
|
||||
|
||||
let nodeIndex = 0;
|
||||
|
@ -334,76 +271,94 @@ export default function (
|
|||
|
||||
function update() {
|
||||
// path generator for links
|
||||
const diagonal = d3.svg
|
||||
.diagonal<NodePosition>()
|
||||
.projection((d) => [d.y!, d.x!]);
|
||||
const linkHorizontal = d3
|
||||
.linkHorizontal<
|
||||
{
|
||||
source: { x: number; y: number };
|
||||
target: { x: number; y: number };
|
||||
},
|
||||
{ x: number; y: number }
|
||||
>()
|
||||
.x((d) => d.y)
|
||||
.y((d) => d.x);
|
||||
// set tree dimensions and spacing between branches and nodes
|
||||
const maxNodeCountByLevel = Math.max(...getNodeGroupByDepthCount(data));
|
||||
|
||||
layout = layout.size([
|
||||
maxNodeCountByLevel * 25 * heightBetweenNodesCoeff,
|
||||
width,
|
||||
]);
|
||||
const layout = d3
|
||||
.tree<InternalNode>()
|
||||
.size([maxNodeCountByLevel * 25 * heightBetweenNodesCoeff, width]);
|
||||
|
||||
const nodes = layout.nodes(data as d3.layout.tree.Node) as NodeWithId[];
|
||||
const links = layout.links(nodes as d3.layout.tree.Node[]);
|
||||
const rootNode = d3.hierarchy(data);
|
||||
if (isSorted) {
|
||||
rootNode.sort((a, b) =>
|
||||
b.data.name.toLowerCase() < a.data.name.toLowerCase() ? 1 : -1
|
||||
);
|
||||
}
|
||||
|
||||
nodes.forEach(
|
||||
const rootPointNode = layout(rootNode);
|
||||
const links = rootPointNode.links();
|
||||
|
||||
rootPointNode.each(
|
||||
(node) =>
|
||||
(node.y = node.depth! * (maxLabelLength * 7 * widthBetweenNodesCoeff))
|
||||
(node.y = node.depth * (maxLabelLength * 7 * widthBetweenNodesCoeff))
|
||||
);
|
||||
|
||||
const nodes = rootPointNode.descendants();
|
||||
|
||||
const nodePositions = nodes.map((n) => ({
|
||||
parentId: n.parent && n.parent.id,
|
||||
id: n.id,
|
||||
parentId: n.parent && n.parent.data.id,
|
||||
id: n.data.id,
|
||||
x: n.x,
|
||||
y: n.y,
|
||||
}));
|
||||
const nodePositionsById: { [nodeId: string]: NodePosition } = {};
|
||||
const nodePositionsById: { [nodeId: string | number]: NodePosition } = {};
|
||||
nodePositions.forEach((node) => (nodePositionsById[node.id] = node));
|
||||
|
||||
// process the node selection
|
||||
const node = vis
|
||||
.selectAll('g.node')
|
||||
.property('__oldData__', (d: NodeWithId) => d)
|
||||
.data(nodes, (d) => d.id || (d.id = ++nodeIndex as unknown as string));
|
||||
.selectAll<SVGGElement, HierarchyPointNode<InternalNode>>('g.node')
|
||||
.property('__oldData__', (d) => d)
|
||||
.data(nodes, (d) => d.data.id || (d.data.id = ++nodeIndex));
|
||||
const nodeEnter = node
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr({
|
||||
class: 'node',
|
||||
transform: (d) => {
|
||||
.attr('class', 'node')
|
||||
.attr('transform', (d) => {
|
||||
const position = findParentNodePosition(
|
||||
nodePositionsById,
|
||||
d.id,
|
||||
d.data.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',
|
||||
.style('fill', textStyleOptions.colors.default)
|
||||
.style('cursor', 'pointer')
|
||||
.on('mouseover', function mouseover() {
|
||||
d3.select(this).style('fill', textStyleOptions.colors.hover);
|
||||
})
|
||||
.on('mouseover', function mouseover(this: EventTarget) {
|
||||
d3.select(this).style({
|
||||
fill: style.text.colors.hover,
|
||||
});
|
||||
})
|
||||
.on('mouseout', function mouseout(this: EventTarget) {
|
||||
d3.select(this).style({
|
||||
fill: style.text.colors.default,
|
||||
});
|
||||
.on('mouseout', function mouseout() {
|
||||
d3.select(this).style('fill', textStyleOptions.colors.default);
|
||||
});
|
||||
|
||||
if (!tooltipOptions.disabled) {
|
||||
nodeEnter.call(
|
||||
tooltip<NodeWithId>(d3, 'tooltip', { ...tooltipOptions, root })
|
||||
.text((d, i) => getTooltipString(d, i, tooltipOptions))
|
||||
.style(tooltipOptions.style)
|
||||
tooltip<
|
||||
SVGGElement,
|
||||
HierarchyPointNode<InternalNode>,
|
||||
SVGGElement,
|
||||
unknown,
|
||||
HTMLElement,
|
||||
unknown,
|
||||
null,
|
||||
undefined
|
||||
>('tooltip', {
|
||||
...tooltipOptions,
|
||||
root,
|
||||
text: (d) => getTooltipString(d.data, tooltipOptions),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -412,77 +367,81 @@ export default function (
|
|||
const nodeEnterInnerGroup = nodeEnter.append('g');
|
||||
nodeEnterInnerGroup
|
||||
.append('circle')
|
||||
.attr({
|
||||
class: 'nodeCircle',
|
||||
r: 0,
|
||||
})
|
||||
.on('click', (clickedNode) => {
|
||||
if ((d3.event as Event).defaultPrevented) return;
|
||||
toggleChildren(clickedNode);
|
||||
.attr('class', 'nodeCircle')
|
||||
.attr('r', 0)
|
||||
.on('click', (event, clickedNode) => {
|
||||
if ((event as Event).defaultPrevented) return;
|
||||
toggleChildren(clickedNode.data);
|
||||
update();
|
||||
});
|
||||
|
||||
nodeEnterInnerGroup
|
||||
.append('text')
|
||||
.attr({
|
||||
class: 'nodeText',
|
||||
'text-anchor': 'middle',
|
||||
transform: 'translate(0,0)',
|
||||
dy: '.35em',
|
||||
})
|
||||
.style({
|
||||
'fill-opacity': 0,
|
||||
})
|
||||
.text((d) => d.name)
|
||||
.on('click', onClickText);
|
||||
.attr('class', 'nodeText')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('transform', 'translate(0,0)')
|
||||
.attr('dy', '.35em')
|
||||
.style('fill-opacity', 0)
|
||||
.text((d) => d.data.name)
|
||||
.on('click', (_, datum) => {
|
||||
onClickText(datum);
|
||||
});
|
||||
|
||||
const nodeEnterAndUpdate = nodeEnter.merge(node);
|
||||
|
||||
// update the text to reflect whether node has children or not
|
||||
node.select('text').text((d) => d.name);
|
||||
nodeEnterAndUpdate.select('text').text((d) => d.data.name);
|
||||
|
||||
// change the circle fill depending on whether it has children and is collapsed
|
||||
node.select('circle').style({
|
||||
stroke: 'black',
|
||||
'stroke-width': '1.5px',
|
||||
fill: (d) =>
|
||||
d._children
|
||||
? style.node.colors.collapsed
|
||||
: d.children
|
||||
? style.node.colors.parent
|
||||
: style.node.colors.default,
|
||||
});
|
||||
nodeEnterAndUpdate
|
||||
.select('circle')
|
||||
.style('stroke', 'black')
|
||||
.style('stroke-width', '1.5px')
|
||||
.style('fill', (d) =>
|
||||
d.data._children && d.data._children.length > 0
|
||||
? nodeStyleOptions.colors.collapsed
|
||||
: d.data.children && d.data.children.length > 0
|
||||
? nodeStyleOptions.colors.parent
|
||||
: nodeStyleOptions.colors.default
|
||||
);
|
||||
|
||||
// transition nodes to their new position
|
||||
const nodeUpdate = node
|
||||
const nodeUpdate = nodeEnterAndUpdate
|
||||
.transition()
|
||||
.duration(transitionDuration)
|
||||
.attr({
|
||||
transform: (d) => `translate(${d.y!},${d.x!})`,
|
||||
});
|
||||
.attr('transform', (d) => `translate(${d.y},${d.x})`);
|
||||
|
||||
// ensure circle radius is correct
|
||||
nodeUpdate.select('circle').attr('r', style.node.radius);
|
||||
nodeUpdate.select('circle').attr('r', nodeStyleOptions.radius);
|
||||
|
||||
// fade the text in and align it
|
||||
nodeUpdate
|
||||
.select('text')
|
||||
.select<SVGTextElement>('text')
|
||||
.style('fill-opacity', 1)
|
||||
.attr({
|
||||
transform: function transform(this: SVGGraphicsElement, d) {
|
||||
.attr('transform', function transform(d) {
|
||||
const x =
|
||||
(d.children || d._children ? -1 : 1) *
|
||||
(this.getBBox().width / 2 + style.node.radius + 5);
|
||||
(((d.data.children ?? d.data._children)?.length ?? 0) > 0
|
||||
? -1
|
||||
: 1) *
|
||||
(this.getBBox().width / 2 + nodeStyleOptions.radius + 5);
|
||||
return `translate(${x},0)`;
|
||||
},
|
||||
});
|
||||
|
||||
// blink updated nodes
|
||||
node
|
||||
.filter(function flick(this: any, d) {
|
||||
nodeEnterAndUpdate
|
||||
.filter(function flick(
|
||||
this: SVGGElement & {
|
||||
__oldData__?: HierarchyPointNode<InternalNode>;
|
||||
},
|
||||
d
|
||||
) {
|
||||
// test whether the relevant properties of d match
|
||||
// the equivalent property of the oldData
|
||||
// also test whether the old data exists,
|
||||
// to catch the entering elements!
|
||||
return this.__oldData__ && d.value !== this.__oldData__.value;
|
||||
return (
|
||||
!!this.__oldData__ && d.data.value !== this.__oldData__.data.value
|
||||
);
|
||||
})
|
||||
.select('g')
|
||||
.style('opacity', '0.3')
|
||||
|
@ -492,21 +451,19 @@ export default function (
|
|||
|
||||
// transition exiting nodes to the parent's new position
|
||||
const nodeExit = node
|
||||
.exit()
|
||||
.exit<HierarchyPointNode<InternalNode>>()
|
||||
.transition()
|
||||
.duration(transitionDuration)
|
||||
.attr({
|
||||
transform: (d) => {
|
||||
.attr('transform', (d) => {
|
||||
const position = findParentNodePosition(
|
||||
previousNodePositionsById,
|
||||
d.id,
|
||||
d.data.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();
|
||||
|
||||
|
@ -516,65 +473,66 @@ export default function (
|
|||
|
||||
// update the links
|
||||
const link = vis
|
||||
.selectAll('path.link')
|
||||
.data(links, (d) => (d.target as NodeWithId).id);
|
||||
.selectAll<SVGPathElement, HierarchyPointLink<InternalNode>>(
|
||||
'path.link'
|
||||
)
|
||||
.data(links, (d) => d.target.data.id);
|
||||
|
||||
// enter any new links at the parent's previous position
|
||||
link
|
||||
const linkEnter = link
|
||||
.enter()
|
||||
.insert('path', 'g')
|
||||
.attr({
|
||||
class: 'link',
|
||||
d: (d) => {
|
||||
.attr('class', 'link')
|
||||
.attr('d', (d) => {
|
||||
const position = findParentNodePosition(
|
||||
nodePositionsById,
|
||||
(d.target as NodeWithId).id,
|
||||
d.target.data.id,
|
||||
(n) => !!previousNodePositionsById[n.id]
|
||||
);
|
||||
const previousPosition =
|
||||
(position && previousNodePositionsById[position.id]) ||
|
||||
previousNodePositionsById.root;
|
||||
return diagonal({
|
||||
return linkHorizontal({
|
||||
source: previousPosition,
|
||||
target: previousPosition,
|
||||
} as d3.svg.diagonal.Link<NodePosition>);
|
||||
},
|
||||
})
|
||||
.style(style.link);
|
||||
});
|
||||
});
|
||||
|
||||
for (const [key, value] of Object.entries(linkStyles)) {
|
||||
linkEnter.style(key, value);
|
||||
}
|
||||
|
||||
const linkEnterAndUpdate = linkEnter.merge(link);
|
||||
|
||||
// transition links to their new position
|
||||
link
|
||||
linkEnterAndUpdate
|
||||
.transition()
|
||||
.duration(transitionDuration)
|
||||
.attr({
|
||||
d: diagonal as unknown as Primitive,
|
||||
});
|
||||
.attr('d', linkHorizontal);
|
||||
|
||||
// transition exiting nodes to the parent's new position
|
||||
link
|
||||
.exit()
|
||||
.exit<HierarchyPointLink<InternalNode>>()
|
||||
.transition()
|
||||
.duration(transitionDuration)
|
||||
.attr({
|
||||
d: (d) => {
|
||||
.attr('d', (d) => {
|
||||
const position = findParentNodePosition(
|
||||
previousNodePositionsById,
|
||||
(d.target as NodeWithId).id,
|
||||
d.target.data.id,
|
||||
(n) => !!nodePositionsById[n.id]
|
||||
);
|
||||
const futurePosition =
|
||||
(position && nodePositionsById[position.id]) ||
|
||||
nodePositionsById.root;
|
||||
return diagonal({
|
||||
return linkHorizontal({
|
||||
source: futurePosition,
|
||||
target: futurePosition,
|
||||
});
|
||||
},
|
||||
})
|
||||
.remove();
|
||||
|
||||
// delete the old data once it's no longer needed
|
||||
node.property('__oldData__', null);
|
||||
nodeEnterAndUpdate.property('__oldData__', null);
|
||||
|
||||
// stash the old positions for transition
|
||||
previousNodePositionsById = nodePositionsById;
|
||||
|
@ -582,4 +540,4 @@ export default function (
|
|||
};
|
||||
}
|
||||
|
||||
export { Primitive };
|
||||
export type { Node };
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
import { is, join, pipe, replace } from 'ramda';
|
||||
import sortAndSerialize from './sortAndSerialize';
|
||||
import { NodeWithId } from './tree';
|
||||
import type { InternalNode } from './tree';
|
||||
|
||||
export function collapseChildren(node: NodeWithId) {
|
||||
export function collapseChildren(node: InternalNode) {
|
||||
if (node.children) {
|
||||
node._children = node.children;
|
||||
node._children.forEach(collapseChildren);
|
||||
node.children = null;
|
||||
node.children = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function expandChildren(node: NodeWithId) {
|
||||
export function expandChildren(node: InternalNode) {
|
||||
if (node._children) {
|
||||
node.children = node._children;
|
||||
node.children.forEach(expandChildren);
|
||||
node._children = null;
|
||||
node._children = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleChildren(node: NodeWithId) {
|
||||
export function toggleChildren(node: InternalNode) {
|
||||
if (node.children) {
|
||||
node._children = node.children;
|
||||
node.children = null;
|
||||
node.children = undefined;
|
||||
} else if (node._children) {
|
||||
node.children = node._children;
|
||||
node._children = null;
|
||||
node._children = undefined;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
export function visit(
|
||||
parent: NodeWithId,
|
||||
visitFn: (parent: NodeWithId) => void,
|
||||
childrenFn: (parent: NodeWithId) => NodeWithId[] | null | undefined
|
||||
parent: InternalNode,
|
||||
visitFn: (parent: InternalNode) => void,
|
||||
childrenFn: (parent: InternalNode) => InternalNode[] | null | undefined
|
||||
) {
|
||||
if (!parent) {
|
||||
return;
|
||||
|
@ -50,10 +50,10 @@ export function visit(
|
|||
}
|
||||
}
|
||||
|
||||
export function getNodeGroupByDepthCount(rootNode: NodeWithId) {
|
||||
export function getNodeGroupByDepthCount(rootNode: InternalNode) {
|
||||
const nodeGroupByDepthCount = [1];
|
||||
|
||||
const traverseFrom = function traverseFrom(node: NodeWithId, depth = 0) {
|
||||
const traverseFrom = function traverseFrom(node: InternalNode, depth = 0) {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -73,11 +73,7 @@ export function getNodeGroupByDepthCount(rootNode: NodeWithId) {
|
|||
return nodeGroupByDepthCount;
|
||||
}
|
||||
|
||||
export function getTooltipString(
|
||||
node: unknown,
|
||||
i: number | undefined,
|
||||
{ indentationSize = 4 }
|
||||
) {
|
||||
export function getTooltipString(node: InternalNode, { indentationSize = 4 }) {
|
||||
if (!is(Object, node)) return '';
|
||||
|
||||
const spacer = join(' ');
|
||||
|
@ -89,7 +85,6 @@ export function getTooltipString(
|
|||
|
||||
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 as unknown[]).length}`;
|
||||
if (children && children.length) return `childrenCount: ${children.length}`;
|
||||
return 'empty';
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export { tree } from './charts';
|
||||
export type { InputOptions, NodeWithId, Primitive } from './charts';
|
||||
export type { HierarchyPointNode, Node, Options, StyleValue } from './charts';
|
||||
|
|
|
@ -10,45 +10,45 @@ It was created by [@romseguy](https://github.com/romseguy) and merged from [`rom
|
|||
## Quick usage
|
||||
|
||||
```javascript
|
||||
import d3 from 'd3';
|
||||
import * as d3 from 'd3';
|
||||
import { tooltip } from 'd3tooltip';
|
||||
|
||||
const DOMNode = document.getElementById('chart');
|
||||
const root = d3.select(DOMNode);
|
||||
const vis = root.append('svg');
|
||||
|
||||
let options = {
|
||||
offset: {left: 30, top: 10}
|
||||
const options = {
|
||||
offset: { left: 30, top: 10 },
|
||||
styles: { 'min-width': '50px', 'border-radius': '5px' },
|
||||
};
|
||||
|
||||
vis.selectAll('circle').data(someData).enter()
|
||||
vis
|
||||
.selectAll('circle')
|
||||
.data(someData)
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('r', 10)
|
||||
.call(
|
||||
d3tooltip(d3, 'tooltipClassName', options)
|
||||
.text((d, i) => toStringOrHtml(d))
|
||||
.attr({ 'class': 'anotherClassName' })
|
||||
.style({ 'min-width': '50px', 'border-radius: 5px' })
|
||||
d3tooltip('tooltipClassName', {
|
||||
...options,
|
||||
text: (d) => toStringOrHtml(d),
|
||||
})
|
||||
)
|
||||
.on({
|
||||
mouseover(d, i) {
|
||||
d3.select(this).style({
|
||||
fill: 'skyblue'
|
||||
});
|
||||
},
|
||||
mouseout(d, i) {
|
||||
d3.select(this).style({
|
||||
fill: 'black'
|
||||
});
|
||||
}
|
||||
.on('mouseover', function () {
|
||||
d3.select(this).style('fill', 'skyblue');
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(this).style('fill', 'black');
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| -------- | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select) |
|
||||
| `left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element |
|
||||
| `top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element |
|
||||
| `offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option |
|
||||
| -------- | ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select). |
|
||||
| `left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element. |
|
||||
| `top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element. |
|
||||
| `offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option. |
|
||||
| `styles` | Object | `{}` | Sets the styles of the tooltip element. |
|
||||
| `text` | String or Function | `''` | Sets the text of the tooltip. Can be a constant `string` or a function that takes the datum and returns a `string`. |
|
||||
|
|
|
@ -19,18 +19,16 @@
|
|||
"main": "lib/cjs/index.js",
|
||||
"module": "lib/esm/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"unpkg": "lib/umd/d3tooltip.umd.js",
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types && pnpm run build:umd",
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types",
|
||||
"build:cjs": "babel src --extensions \".ts\" --out-dir lib/cjs",
|
||||
"build:esm": "babel src --config-file ./babel.config.esm.json --extensions \".ts\" --out-dir lib/esm",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:umd": "rollup -c",
|
||||
"clean": "rimraf lib",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"type-check": "tsc --noEmit",
|
||||
|
@ -38,36 +36,25 @@
|
|||
"prepublish": "pnpm run type-check && pnpm run lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"ramda": "^0.28.0"
|
||||
"@babel/runtime": "^7.20.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/eslint-parser": "^7.18.2",
|
||||
"@babel/plugin-transform-runtime": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@types/d3": "^3.5.47",
|
||||
"@types/node": "^16.11.41",
|
||||
"@types/ramda": "^0.28.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"d3": "^3.5.17",
|
||||
"eslint": "^8.18.0",
|
||||
"@babel/cli": "^7.19.3",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/d3": "^7.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"d3": "^7.8.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.75.6",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.32.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.7.4"
|
||||
"typescript": "~4.9.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/d3": "^3.5.47",
|
||||
"d3": "^3.5.17"
|
||||
"@types/d3": "^7.4.0",
|
||||
"d3": "^7.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import typescript from 'rollup-plugin-typescript2';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const config = [
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'd3tooltip',
|
||||
file: 'lib/umd/d3tooltip.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'd3tooltip',
|
||||
file: 'lib/umd/d3tooltip.min.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
terser(),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default config;
|
|
@ -1,161 +1,102 @@
|
|||
import d3Package, { Primitive, Selection } from 'd3';
|
||||
import { is } from 'ramda';
|
||||
import utils from './utils';
|
||||
const { prependClass, functor } = utils;
|
||||
import * as d3 from 'd3';
|
||||
import type { BaseType, Selection } from 'd3';
|
||||
|
||||
interface Options<Datum> {
|
||||
export type StyleValue = string | number | boolean;
|
||||
|
||||
interface Options<
|
||||
Datum,
|
||||
RootGElement extends BaseType,
|
||||
RootDatum,
|
||||
RootPElement extends BaseType,
|
||||
RootPDatum
|
||||
> {
|
||||
left: number | undefined;
|
||||
top: number | undefined;
|
||||
offset: {
|
||||
left: number;
|
||||
top: number;
|
||||
};
|
||||
root: Selection<Datum> | undefined;
|
||||
root:
|
||||
| Selection<RootGElement, RootDatum, RootPElement, RootPDatum>
|
||||
| undefined;
|
||||
styles: { [key: string]: StyleValue };
|
||||
text: string | ((datum: Datum) => string);
|
||||
}
|
||||
|
||||
const defaultOptions: Options<unknown> = {
|
||||
const defaultOptions: Options<unknown, BaseType, unknown, BaseType, unknown> = {
|
||||
left: undefined, // mouseX
|
||||
top: undefined, // mouseY
|
||||
offset: { left: 0, top: 0 },
|
||||
root: undefined,
|
||||
styles: {},
|
||||
text: '',
|
||||
};
|
||||
|
||||
interface Tip<Datum> {
|
||||
(selection: Selection<Datum>): void;
|
||||
attr: (
|
||||
this: this,
|
||||
d:
|
||||
| string
|
||||
| {
|
||||
[key: string]:
|
||||
| Primitive
|
||||
| ((datum: Datum, index: number, outerIndex: number) => Primitive);
|
||||
}
|
||||
) => this;
|
||||
style: (
|
||||
this: this,
|
||||
d:
|
||||
| string
|
||||
| {
|
||||
[key: string]:
|
||||
| Primitive
|
||||
| ((datum: Datum, index: number, outerIndex: number) => Primitive);
|
||||
}
|
||||
| undefined
|
||||
) => this;
|
||||
text: (
|
||||
this: this,
|
||||
d: string | ((datum: Datum, index?: number, outerIndex?: number) => string)
|
||||
) => this;
|
||||
}
|
||||
|
||||
export function tooltip<Datum>(
|
||||
d3: typeof d3Package,
|
||||
export function tooltip<
|
||||
GElement extends BaseType,
|
||||
Datum,
|
||||
PElement extends BaseType,
|
||||
PDatum,
|
||||
RootGElement extends BaseType,
|
||||
RootDatum,
|
||||
RootPElement extends BaseType,
|
||||
RootPDatum
|
||||
>(
|
||||
className = 'tooltip',
|
||||
options: Partial<Options<Datum>> = {}
|
||||
): Tip<Datum> {
|
||||
const { left, top, offset, root } = {
|
||||
options: Partial<
|
||||
Options<Datum, RootGElement, RootDatum, RootPElement, RootPDatum>
|
||||
> = {}
|
||||
) {
|
||||
const { left, top, offset, root, styles, text } = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
} as Options<Datum>;
|
||||
} as Options<Datum, RootGElement, RootDatum, RootPElement, RootPDatum>;
|
||||
|
||||
let attrs = { class: className };
|
||||
let text: (datum: Datum, index?: number, outerIndex?: number) => string = (
|
||||
node: Datum
|
||||
) => '';
|
||||
let styles = {};
|
||||
let el: Selection<HTMLDivElement, RootDatum, BaseType, unknown>;
|
||||
const anchor: Selection<
|
||||
RootGElement,
|
||||
RootDatum,
|
||||
RootPElement | HTMLElement,
|
||||
RootPDatum
|
||||
> = root || d3.select<RootGElement, RootDatum>('body');
|
||||
const rootNode = anchor.node()!;
|
||||
|
||||
let el: Selection<Datum>;
|
||||
const anchor = root || d3.select('body');
|
||||
const rootNode = anchor.node();
|
||||
|
||||
function tip(selection: Selection<Datum>) {
|
||||
selection.on('mouseover.tip', (node) => {
|
||||
const [mouseX, mouseY] = d3.mouse(rootNode);
|
||||
const [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top];
|
||||
return function tip(selection: Selection<GElement, Datum, PElement, PDatum>) {
|
||||
selection.on('mouseover.tip', (event, datum) => {
|
||||
const [pointerX, pointerY] = d3.pointer(event, rootNode);
|
||||
const [x, y] = [
|
||||
left || pointerX + offset.left,
|
||||
top || pointerY - 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)) as Selection<Datum>;
|
||||
.attr('class', className)
|
||||
.style('position', 'absolute')
|
||||
.style('z-index', 1001)
|
||||
.style('left', `${x}px`)
|
||||
.style('top', `${y}px`)
|
||||
.html(typeof text === 'function' ? () => text(datum) : () => text);
|
||||
|
||||
for (const [key, value] of Object.entries(styles)) {
|
||||
el.style(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
selection.on('mousemove.tip', (node) => {
|
||||
const [mouseX, mouseY] = d3.mouse(rootNode);
|
||||
const [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top];
|
||||
selection.on('mousemove.tip', (event, datum) => {
|
||||
const [pointerX, pointerY] = d3.pointer(event, rootNode);
|
||||
const [x, y] = [
|
||||
left || pointerX + offset.left,
|
||||
top || pointerY - offset.top,
|
||||
];
|
||||
|
||||
el.style({
|
||||
left: `${x}px`,
|
||||
top: `${y}px`,
|
||||
}).html(() => text(node));
|
||||
el.style('left', `${x}px`)
|
||||
.style('top', `${y}px`)
|
||||
.html(typeof text === 'function' ? () => text(datum) : () => text);
|
||||
});
|
||||
|
||||
selection.on('mouseout.tip', () => el.remove());
|
||||
}
|
||||
|
||||
tip.attr = function setAttr(
|
||||
this: typeof tip,
|
||||
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(
|
||||
this: typeof tip,
|
||||
d:
|
||||
| string
|
||||
| {
|
||||
[key: string]:
|
||||
| Primitive
|
||||
| ((datum: Datum, index: number, outerIndex: number) => Primitive);
|
||||
}
|
||||
| undefined
|
||||
) {
|
||||
if (is(Object, d)) {
|
||||
styles = {
|
||||
...styles,
|
||||
...(d as {
|
||||
[key: string]:
|
||||
| Primitive
|
||||
| ((datum: Datum, index: number, outerIndex: number) => Primitive);
|
||||
}),
|
||||
};
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
tip.text = function setText(
|
||||
this: typeof tip,
|
||||
d: string | ((datum: Datum, index?: number, outerIndex?: number) => string)
|
||||
) {
|
||||
text = functor(d);
|
||||
return this;
|
||||
};
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import { is } from 'ramda';
|
||||
import { Primitive } from 'd3';
|
||||
|
||||
export default function functor<Datum>(
|
||||
v: string | ((datum: Datum, index?: number, outerIndex?: number) => string)
|
||||
): (datum: Datum, index?: number, outerIndex?: number) => string;
|
||||
export default function functor<Datum>(
|
||||
v:
|
||||
| Primitive
|
||||
| ((datum: Datum, index: number, outerIndex?: number) => Primitive)
|
||||
): (datum: Datum, index?: number, outerIndex?: number) => Primitive;
|
||||
export default function functor<Datum>(
|
||||
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;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import prependClass from './prependClass';
|
||||
import functor from './functor';
|
||||
|
||||
export default {
|
||||
prependClass,
|
||||
functor,
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
import { mapObjIndexed, join } from 'ramda';
|
||||
import functor from './functor';
|
||||
import { Primitive } from 'd3';
|
||||
|
||||
export default function prependClass<Datum>(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;
|
||||
}
|
||||
);
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: 'tsconfig.test.json',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -22,18 +22,16 @@
|
|||
"main": "lib/cjs/index.js",
|
||||
"module": "lib/esm/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"unpkg": "lib/umd/map2tree.umd.js",
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types && pnpm run build:umd",
|
||||
"build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types",
|
||||
"build:cjs": "babel src --extensions \".ts\" --out-dir lib/cjs",
|
||||
"build:esm": "babel src --config-file ./babel.config.esm.json --extensions \".ts\" --out-dir lib/esm",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:umd": "rollup -c",
|
||||
"clean": "rimraf lib",
|
||||
"test": "jest",
|
||||
"lint": "eslint . --ext .ts",
|
||||
|
@ -42,35 +40,26 @@
|
|||
"prepublish": "pnpm run type-check && pnpm run lint && pnpm run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@babel/runtime": "^7.20.6",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/eslint-parser": "^7.18.2",
|
||||
"@babel/plugin-transform-runtime": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^22.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^16.11.41",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"eslint": "^8.18.0",
|
||||
"@babel/cli": "^7.19.3",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-jest": "^26.5.3",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"immutable": "^4.1.0",
|
||||
"jest": "^27.5.1",
|
||||
"jest": "^29.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.75.6",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.32.1",
|
||||
"ts-jest": "^27.1.5",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.7.4"
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "~4.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import typescript from 'rollup-plugin-typescript2';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const config = [
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'map2tree',
|
||||
file: 'lib/umd/map2tree.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
name: 'map2tree',
|
||||
file: 'lib/umd/map2tree.min.js',
|
||||
format: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
typescript({
|
||||
tsconfigOverride: { compilerOptions: { declaration: false } },
|
||||
}),
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
extensions: ['.ts'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
}),
|
||||
terser(),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default config;
|
|
@ -4,7 +4,8 @@ import mapValues from 'lodash/mapValues';
|
|||
|
||||
export interface Node {
|
||||
name: string;
|
||||
children?: Node[] | null;
|
||||
children?: this[];
|
||||
object?: unknown;
|
||||
value?: unknown;
|
||||
}
|
||||
|
||||
|
@ -43,7 +44,6 @@ function getNode(tree: Node, key: string): Node | null {
|
|||
}
|
||||
|
||||
export function map2tree(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
root: unknown,
|
||||
options: { key?: string; pushMethod?: 'push' | 'unshift' } = {},
|
||||
tree: Node = { name: options.key || 'state', children: [] }
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'jsdom',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: 'tsconfig.test.json',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -39,32 +39,33 @@
|
|||
"prepublish": "pnpm run type-check && pnpm run lint && pnpm run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@babel/runtime": "^7.20.6",
|
||||
"@types/base16": "^1.0.2",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"base16": "^1.0.0",
|
||||
"color": "^4.2.3",
|
||||
"csstype": "^3.1.0",
|
||||
"csstype": "^3.1.1",
|
||||
"lodash.curry": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.17.10",
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/eslint-parser": "^7.18.2",
|
||||
"@babel/plugin-transform-runtime": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@babel/cli": "^7.19.3",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@babel/plugin-transform-runtime": "^7.19.6",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/lodash.curry": "^4.1.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"eslint": "^8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-jest": "^26.5.3",
|
||||
"jest": "^27.5.1",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"jest": "^29.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^27.1.5",
|
||||
"typescript": "~4.7.4"
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "~4.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,36 +11,36 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.4.0",
|
||||
"react-bootstrap": "^2.7.0",
|
||||
"react-dock": "^0.6.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-is": "^18.2.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"styled-components": "^5.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.17.12",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@types/node": "^16.11.41",
|
||||
"@types/react": "^18.0.14",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||
"@typescript-eslint/parser": "^5.28.0",
|
||||
"babel-loader": "^8.2.5",
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/node": "^18.11.17",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/styled-components": "^5.1.26",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"babel-loader": "^9.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint": "^8.30.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.14",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "~4.7.4",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.9.2"
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "~4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.11.1"
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user