[inspector-monitor] Replace jss with Emotion (#1560)

* Setup Emotion

* Fix setup

* Start conversion

* actionList

* actionListHeader

* actionListRows

* actionListHeaderSelector

* actionListItem

* actionListItemTime

* actionListItemSelector

* actionListItemName

* actionListHeaderSearch

* actionListHeaderWrapper

* actionPreview

* Remaining css

* Format

* Propagate Emotion dependencies

* Fix tests

* Remove styling prop

* Remove jss

* Remove themeState

* Use color map as Emotion theme

* Rework theme resolution

* Inline CSS

* Remove usage of className

* Fix warning

* Create large-spoons-yell.md
This commit is contained in:
Nathan Bierema 2023-12-11 23:02:35 -05:00 committed by GitHub
parent b54bc75cbb
commit 158ba2ce12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 609 additions and 685 deletions

View File

@ -0,0 +1,8 @@
---
'@redux-devtools/app': major
'@redux-devtools/inspector-monitor-test-tab': major
'@redux-devtools/inspector-monitor-trace-tab': major
'@redux-devtools/inspector-monitor': major
---
Replace jss with Emotion in inspector-monitor. `@emotion/react` is now a required peer dependency.

View File

@ -24,6 +24,7 @@
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"@emotion/react": "^11.11.1",
"@redux-devtools/app": "^4.0.1",
"@redux-devtools/core": "^3.13.2",
"@redux-devtools/instrument": "^2.1.0",

View File

@ -40,16 +40,14 @@ describe('Chrome extension', function () {
it("should contain inspector monitor's component", async () => {
await delay(1000);
const val = await driver
.findElement(webdriver.By.xpath('//div[contains(@class, "inspector-")]'))
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
.getText();
expect(val).toBeDefined();
});
it('should contain an empty actions list', async () => {
const val = await driver
.findElement(
webdriver.By.xpath('//div[contains(@class, "actionListRows-")]'),
)
.findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]'))
.getText();
expect(val).toBe('');
});
@ -72,9 +70,7 @@ describe('Chrome extension', function () {
const result = await driver.wait(
driver
.findElement(
webdriver.By.xpath('//div[contains(@class, "actionListRows-")]'),
)
.findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]'))
.getText()
.then((val) => {
return actionsPattern.test(val);

View File

@ -89,7 +89,7 @@ describe('DevTools panel for Electron', function () {
it('should contain INIT action', async () => {
const element = await driver.wait(
webdriver.until.elementLocated(
webdriver.By.xpath('//div[contains(@class, "actionListRows-")]'),
webdriver.By.xpath('//div[@data-testid="actionListRows"]'),
),
5000,
'Element not found',
@ -100,7 +100,7 @@ describe('DevTools panel for Electron', function () {
it("should contain Inspector monitor's component", async () => {
const val = await driver
.findElement(webdriver.By.xpath('//div[contains(@class, "inspector-")]'))
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
.getText();
expect(val).toBeDefined();
});

View File

@ -72,6 +72,7 @@
"@babel/preset-env": "^7.23.5",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@emotion/react": "^11.11.1",
"@rjsf/core": "^4.2.3",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "^14.1.2",
@ -115,6 +116,7 @@
"webpack-dev-server": "^4.15.1"
},
"peerDependencies": {
"@emotion/react": "^11.0.0",
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"@types/styled-components": "^5.1.34",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",

View File

@ -42,6 +42,7 @@
},
"dependencies": {
"@apollo/server": "^4.9.5",
"@emotion/react": "^11.11.1",
"@redux-devtools/app": "^4.0.0",
"@types/react": "^18.2.43",
"body-parser": "^1.20.2",

View File

@ -10,6 +10,7 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@redux-devtools/core": "^3.13.2",
"@redux-devtools/dock-monitor": "^3.0.2",
"@redux-devtools/inspector-monitor": "^4.0.1",

View File

@ -1,10 +1,7 @@
import React from 'react';
import { createDevTools } from '@redux-devtools/core';
import {
InspectorMonitor,
base16Themes,
Tab,
} from '@redux-devtools/inspector-monitor';
import { InspectorMonitor, Tab } from '@redux-devtools/inspector-monitor';
import type { Base16ThemeName } from '@redux-devtools/inspector-monitor';
import { DockMonitor } from '@redux-devtools/dock-monitor';
import { useLocation } from 'react-router-dom';
import getOptions from './getOptions';
@ -20,7 +17,7 @@ export const getDevTools = (location: { search: string }) =>
changeMonitorKey="ctrl-m"
>
<InspectorMonitor
theme={getOptions(location).theme as keyof typeof base16Themes}
theme={getOptions(location).theme as Base16ThemeName}
invertTheme={!getOptions(location).dark}
supportImmutable={getOptions(location).supportImmutable}
tabs={(defaultTabs) =>

View File

@ -60,6 +60,7 @@
"@babel/preset-env": "^7.23.5",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@emotion/react": "^11.11.1",
"@redux-devtools/core": "^3.14.0",
"@redux-devtools/inspector-monitor": "^4.1.0",
"@testing-library/react": "^14.1.2",
@ -85,6 +86,7 @@
"typescript": "~5.2.2"
},
"peerDependencies": {
"@emotion/react": "^11.0.0",
"@redux-devtools/inspector-monitor": "^4.0.0",
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"@types/styled-components": "^5.1.34",

View File

@ -47,6 +47,7 @@
"@babel/preset-env": "^7.23.5",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@emotion/react": "^11.11.1",
"@redux-devtools/core": "^3.14.0",
"@redux-devtools/inspector-monitor": "^4.1.0",
"@testing-library/react": "^14.1.2",
@ -76,6 +77,7 @@
"typescript": "~5.2.2"
},
"peerDependencies": {
"@emotion/react": "^11.0.0",
"@redux-devtools/inspector-monitor": "^4.0.0",
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",

View File

@ -8,6 +8,9 @@ module.exports = {
tsconfigRootDir: __dirname,
project: true,
},
rules: {
'react/no-unknown-property': ['error', { ignore: ['css'] }],
},
},
{
files: ['demo/**/*.ts', 'demo/**/*.tsx'],

View File

@ -2,7 +2,8 @@
"presets": [
["@babel/preset-env", { "targets": "defaults", "modules": false }],
"@babel/preset-react",
"@babel/preset-typescript"
"@babel/preset-typescript",
"@emotion/babel-preset-css-prop"
],
"plugins": ["@babel/plugin-transform-runtime"]
}

View File

@ -2,7 +2,8 @@
"presets": [
["@babel/preset-env", { "targets": "defaults" }],
"@babel/preset-react",
"@babel/preset-typescript"
"@babel/preset-typescript",
"@emotion/babel-preset-css-prop"
],
"plugins": ["@babel/plugin-transform-runtime"]
}

View File

@ -10,6 +10,7 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@redux-devtools/core": "^3.13.0",
"@redux-devtools/dock-monitor": "^3.0.0",
"@redux-devtools/inspector-monitor": "^4.0.0",

View File

@ -1,10 +1,8 @@
import React from 'react';
import { createDevTools } from '@redux-devtools/core';
import { DockMonitor } from '@redux-devtools/dock-monitor';
import {
InspectorMonitor,
base16Themes,
} from '@redux-devtools/inspector-monitor';
import { InspectorMonitor } from '@redux-devtools/inspector-monitor';
import type { Base16ThemeName } from '@redux-devtools/inspector-monitor';
import { useLocation } from 'react-router-dom';
import getOptions from './getOptions';
@ -32,7 +30,7 @@ export const getDevTools = (location: { search: string }) =>
changeMonitorKey="ctrl-m"
>
<InspectorMonitor
theme={getOptions(location).theme as keyof typeof base16Themes}
theme={getOptions(location).theme as Base16ThemeName}
invertTheme={!getOptions(location).dark}
supportImmutable={getOptions(location).supportImmutable}
tabs={(defaultTabs) => [

View File

@ -47,8 +47,6 @@
"immutable": "^4.3.4",
"javascript-stringify": "^2.1.0",
"jsondiffpatch": "^0.5.0",
"jss": "^10.10.0",
"jss-preset-default": "^10.10.0",
"lodash.debounce": "^4.0.8",
"react-base16-styling": "^0.9.1",
"react-json-tree": "^0.18.0",
@ -62,6 +60,8 @@
"@babel/preset-env": "^7.23.5",
"@babel/preset-react": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@emotion/babel-preset-css-prop": "^11.11.0",
"@emotion/react": "^11.11.1",
"@redux-devtools/core": "^3.14.0",
"@types/dateformat": "^5.0.2",
"@types/hex-rgba": "^1.0.3",
@ -80,6 +80,7 @@
"typescript": "~5.2.2"
},
"peerDependencies": {
"@emotion/react": "^11.0.0",
"@redux-devtools/core": "^3.0.0",
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",

View File

@ -1,7 +1,6 @@
import React, { ReactNode, useCallback, useLayoutEffect, useRef } from 'react';
import { Action } from 'redux';
import { PerformAction } from '@redux-devtools/core';
import { StylingFunction } from 'react-base16-styling';
import {
closestCenter,
DndContext,
@ -51,7 +50,6 @@ interface Props<A extends Action<string>> {
draggableActions: boolean;
hideMainButtons: boolean | undefined;
hideActionButtons: boolean | undefined;
styling: StylingFunction;
onSearch: (value: string) => void;
onSelect: (e: React.MouseEvent<HTMLDivElement>, actionId: number) => void;
onToggleAction: (actionId: number) => void;
@ -64,7 +62,6 @@ interface Props<A extends Action<string>> {
}
export default function ActionList<A extends Action<string>>({
styling,
actions,
actionIds,
isWideLayout,
@ -150,13 +147,29 @@ export default function ActionList<A extends Action<string>>({
<div
key="actionList"
data-testid="actionList"
{...styling(
['actionList', isWideLayout && 'actionListWide'],
isWideLayout,
)}
css={[
(theme) => ({
flexBasis: '40%',
flexShrink: 0,
overflowX: 'hidden',
overflowY: 'auto',
borderBottomWidth: '3px',
borderBottomStyle: 'double',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.BACKGROUND_COLOR,
borderColor: theme.LIST_BORDER_COLOR,
}),
isWideLayout && {
flexBasis: '40%',
borderBottom: 'none',
borderRightWidth: '3px',
borderRightStyle: 'double',
},
]}
>
<ActionListHeader
styling={styling}
onSearch={onSearch}
onCommit={onCommit}
onSweep={onSweep}
@ -167,7 +180,7 @@ export default function ActionList<A extends Action<string>>({
/>
<div
data-testid="actionListRows"
{...styling('actionListRows')}
css={{ overflow: 'auto' }}
ref={setNodeRef}
>
<DndContext
@ -183,7 +196,6 @@ export default function ActionList<A extends Action<string>>({
{filteredActionIds.map((actionId) => (
<SortableItem key={actionId} actionId={actionId}>
<ActionListRow
styling={styling}
actionId={actionId}
isInitAction={!actionId}
isSelected={

View File

@ -1,6 +1,9 @@
import React, { FunctionComponent } from 'react';
import { StylingFunction } from 'react-base16-styling';
import RightSlider from './RightSlider';
import {
selectorButtonCss,
selectorButtonSmallCss,
} from './utils/selectorButtonStyles';
const getActiveButtons = (hasSkippedActions: boolean): ('Sweep' | 'Commit')[] =>
[hasSkippedActions && 'Sweep', 'Commit'].filter(
@ -8,7 +11,6 @@ const getActiveButtons = (hasSkippedActions: boolean): ('Sweep' | 'Commit')[] =>
);
interface Props {
styling: StylingFunction;
onSearch: (value: string) => void;
onCommit: () => void;
onSweep: () => void;
@ -19,7 +21,6 @@ interface Props {
}
const ActionListHeader: FunctionComponent<Props> = ({
styling,
onSearch,
hasSkippedActions,
hasStagedActions,
@ -28,17 +29,45 @@ const ActionListHeader: FunctionComponent<Props> = ({
hideMainButtons,
searchValue,
}) => (
<div {...styling('actionListHeader')}>
<div
css={(theme) => ({
display: 'flex',
flex: '0 0 auto',
alignItems: 'center',
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
borderColor: theme.LIST_BORDER_COLOR,
})}
>
<input
{...styling('actionListHeaderSearch')}
css={(theme) => ({
outline: 'none',
border: 'none',
width: '100%',
padding: '5px 10px',
fontSize: '1em',
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
backgroundColor: theme.BACKGROUND_COLOR,
color: theme.TEXT_COLOR,
'&::-webkit-input-placeholder': {
color: theme.TEXT_PLACEHOLDER_COLOR,
},
'&::-moz-placeholder': {
color: theme.TEXT_PLACEHOLDER_COLOR,
},
})}
onChange={(e) => onSearch(e.target.value)}
placeholder="filter..."
value={searchValue}
/>
{!hideMainButtons && (
<div {...styling('actionListHeaderWrapper')}>
<RightSlider shown={hasStagedActions} styling={styling}>
<div {...styling('actionListHeaderSelector')}>
<div css={{ position: 'relative', height: '20px' }}>
<RightSlider shown={hasStagedActions}>
<div css={{ display: 'inline-flex', marginRight: '10px' }}>
{getActiveButtons(hasSkippedActions).map((btn) => (
<div
key={btn}
@ -48,11 +77,7 @@ const ActionListHeader: FunctionComponent<Props> = ({
Sweep: onSweep,
})[btn]()
}
{...styling(
['selectorButton', 'selectorButtonSmall'],
false,
true,
)}
css={[selectorButtonCss, selectorButtonSmallCss]}
>
{btn}
</div>

View File

@ -2,17 +2,33 @@ import React, { MouseEvent, MouseEventHandler, PureComponent } from 'react';
import dateformat from 'dateformat';
import type { DebouncedFunc } from 'lodash';
import debounce from 'lodash.debounce';
import { StylingFunction } from 'react-base16-styling';
import { Action } from 'redux';
import type { Interpolation, Theme } from '@emotion/react';
import RightSlider from './RightSlider';
import {
selectorButtonCss,
selectorButtonSelectedCss,
selectorButtonSmallCss,
} from './utils/selectorButtonStyles';
const BUTTON_SKIP = 'Skip';
const BUTTON_JUMP = 'Jump';
type Button = typeof BUTTON_SKIP | typeof BUTTON_JUMP;
const actionListItemTimeCss: Interpolation<Theme> = (theme) => ({
display: 'inline',
padding: '4px 6px',
borderRadius: '3px',
fontSize: '0.8em',
lineHeight: '1em',
flexShrink: 0,
backgroundColor: theme.ACTION_TIME_BACK_COLOR,
color: theme.ACTION_TIME_COLOR,
});
interface Props<A extends Action<string>> {
styling: StylingFunction;
actionId: number;
isInitAction: boolean;
isSelected: boolean;
@ -38,7 +54,6 @@ export default class ActionListRow<
render() {
const {
styling,
isSelected,
action,
actionId,
@ -74,28 +89,44 @@ export default class ActionListRow<
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseEnter}
data-id={actionId}
{...styling(
[
'actionListItem',
isSelected && 'actionListItemSelected',
isSkipped && 'actionListItemSkipped',
isInFuture && 'actionListFromFuture',
],
isSelected,
action,
)}
css={[
(theme) => ({
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
display: 'flex',
justifyContent: 'space-between',
padding: '5px 10px',
cursor: 'pointer',
userSelect: 'none',
borderBottomColor: theme.BORDER_COLOR,
}),
isSelected &&
((theme) => ({
backgroundColor: theme.SELECTED_BACKGROUND_COLOR,
})),
isSkipped &&
((theme) => ({
backgroundColor: theme.SKIPPED_BACKGROUND_COLOR,
})),
isInFuture && { opacity: '0.6' },
]}
>
<div
{...styling([
'actionListItemName',
isSkipped && 'actionListItemNameSkipped',
])}
css={[
{
overflow: 'hidden',
textOverflow: 'ellipsis',
lineHeight: '20px',
},
isSkipped && { textDecoration: 'line-through', opacity: 0.3 },
]}
>
{actionType}
</div>
{hideActionButtons ? (
<RightSlider styling={styling} shown>
<div {...styling('actionListItemTime')}>
<RightSlider shown>
<div css={actionListItemTimeCss}>
{timeDelta === 0
? '+00:00:00'
: dateformat(
@ -105,9 +136,9 @@ export default class ActionListRow<
</div>
</RightSlider>
) : (
<div {...styling('actionListItemButtons')}>
<RightSlider styling={styling} shown={!showButtons} rotate>
<div {...styling('actionListItemTime')}>
<div css={{ position: 'relative', height: '20px', display: 'flex' }}>
<RightSlider shown={!showButtons} rotate>
<div css={actionListItemTimeCss}>
{timeDelta === 0
? '+00:00:00'
: dateformat(
@ -116,23 +147,20 @@ export default class ActionListRow<
)}
</div>
</RightSlider>
<RightSlider styling={styling} shown={showButtons} rotate>
<div {...styling('actionListItemSelector')}>
<RightSlider shown={showButtons} rotate>
<div css={{ display: 'inline-flex' }}>
{([BUTTON_JUMP, BUTTON_SKIP] as const).map(
(btn) =>
(!isInitAction || btn !== BUTTON_SKIP) && (
<div
key={btn}
onClick={(e) => this.handleButtonClick(btn, e)}
{...styling(
[
'selectorButton',
isButtonSelected(btn) && 'selectorButtonSelected',
'selectorButtonSmall',
],
isButtonSelected(btn),
true,
)}
css={[
selectorButtonCss,
isButtonSelected(btn) && selectorButtonSelectedCss,
selectorButtonSmallCss,
]}
data-isselectorbutton={true}
>
{btn}
</div>
@ -177,12 +205,7 @@ export default class ActionListRow<
}, 100);
handleMouseDown = (e: MouseEvent<HTMLDivElement>) => {
if (
(e.target as unknown as { className: string[] }).className.indexOf(
'selectorButton',
) === 0
)
return;
if ((e.target as HTMLElement).dataset.isselectorbutton) return;
this.handleMouseLeave();
};
}

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { Base16Theme } from 'redux-devtools-themes';
import { Action } from 'redux';
import type { StylingFunction } from 'react-base16-styling';
import type { LabelRenderer } from 'react-json-tree';
import { PerformAction } from '@redux-devtools/core';
import { Delta } from 'jsondiffpatch';
@ -13,7 +12,6 @@ import ActionTab from './tabs/ActionTab';
export interface TabComponentProps<S, A extends Action<string>> {
labelRenderer: LabelRenderer;
styling: StylingFunction;
computedStates: { state: S; error?: string }[];
actions: { [actionId: number]: PerformAction<A> };
selectedActionId: number | null;
@ -68,7 +66,6 @@ interface Props<S, A extends Action<string>> {
dataTypeKey: string | symbol | undefined;
monitorState: DevtoolsInspectorState;
updateMonitorState: (monitorState: Partial<DevtoolsInspectorState>) => void;
styling: StylingFunction;
onInspectPath: (path: (string | number)[]) => void;
inspectedPath: (string | number)[];
onSelectTab: (tabName: string) => void;
@ -85,7 +82,6 @@ class ActionPreview<S, A extends Action<string>> extends Component<
render() {
const {
styling,
delta,
error,
nextState,
@ -121,17 +117,34 @@ class ActionPreview<S, A extends Action<string>> extends Component<
renderedTabs.find((tab) => tab.name === DEFAULT_STATE.tabName)!;
return (
<div key="actionPreview" {...styling('actionPreview')}>
<div
key="actionPreview"
css={(theme) => ({
flex: 1,
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflowY: 'hidden',
'& pre': {
border: 'inherit',
borderRadius: '3px',
lineHeight: 'inherit',
color: 'inherit',
},
backgroundColor: theme.BACKGROUND_COLOR,
})}
>
<ActionPreviewHeader
tabs={renderedTabs as unknown as Tab<unknown, Action<string>>[]}
{...{ styling, inspectedPath, onInspectPath, tabName, onSelectTab }}
{...{ inspectedPath, onInspectPath, tabName, onSelectTab }}
/>
{!error && (
<div key="actionPreviewContent" {...styling('actionPreviewContent')}>
<div key="actionPreviewContent" css={{ flex: 1, overflowY: 'auto' }}>
<TabComponent
labelRenderer={this.labelRenderer}
{...{
styling,
computedStates,
actions,
selectedActionId,
@ -151,19 +164,40 @@ class ActionPreview<S, A extends Action<string>> extends Component<
/>
</div>
)}
{error && <div {...styling('stateError')}>{error}</div>}
{error && (
<div
css={(theme) => ({
padding: '10px',
marginLeft: '14px',
fontWeight: 'bold',
color: theme.ERROR_COLOR,
})}
>
{error}
</div>
)}
</div>
);
}
labelRenderer: LabelRenderer = ([key, ...rest], nodeType, expanded) => {
const { styling, onInspectPath, inspectedPath } = this.props;
const { onInspectPath, inspectedPath } = this.props;
return (
<span>
<span {...styling('treeItemKey')}>{key}</span>
<span>{key}</span>
<span
{...styling('treeItemPin')}
css={(theme) => ({
fontSize: '0.7em',
paddingLeft: '5px',
cursor: 'pointer',
'&:hover': {
textDecoration: 'underline',
},
color: theme.PIN_COLOR,
})}
onClick={() =>
onInspectPath([
...inspectedPath.slice(0, inspectedPath.length - 1),

View File

@ -1,11 +1,30 @@
import React, { FunctionComponent } from 'react';
import { Action } from 'redux';
import { StylingFunction } from 'react-base16-styling';
import { css } from '@emotion/react';
import type { Interpolation, Theme } from '@emotion/react';
import { Tab } from './ActionPreview';
import {
selectorButtonCss,
selectorButtonSelectedCss,
} from './utils/selectorButtonStyles';
const inspectedPathKeyCss = css({
'&:not(:last-child):after': {
content: '" > "',
},
});
const inspectedPathKeyLinkCss: Interpolation<Theme> = (theme) => ({
cursor: 'pointer',
color: theme.LINK_COLOR,
'&:hover': {
textDecoration: 'underline',
color: theme.LINK_HOVER_COLOR,
},
});
interface Props<S, A extends Action<string>> {
tabs: Tab<S, A>[];
styling: StylingFunction;
inspectedPath: (string | number)[];
onInspectPath: (path: (string | number)[]) => void;
tabName: string;
@ -14,32 +33,38 @@ interface Props<S, A extends Action<string>> {
const ActionPreviewHeader: FunctionComponent<
Props<unknown, Action<string>>
> = ({ styling, inspectedPath, onInspectPath, tabName, onSelectTab, tabs }) => (
<div key="previewHeader" {...styling('previewHeader')}>
<div {...styling('tabSelector')}>
> = ({ inspectedPath, onInspectPath, tabName, onSelectTab, tabs }) => (
<div
key="previewHeader"
css={(theme) => ({
flex: '0 0 30px',
padding: '5px 10px',
alignItems: 'center',
borderBottomWidth: '1px',
borderBottomStyle: 'solid',
backgroundColor: theme.HEADER_BACKGROUND_COLOR,
borderBottomColor: theme.HEADER_BORDER_COLOR,
})}
>
<div css={{ position: 'relative', display: 'inline-flex', float: 'right' }}>
{tabs.map((tab) => (
<div
onClick={() => onSelectTab(tab.name)}
key={tab.name}
{...styling(
[
'selectorButton',
tab.name === tabName && 'selectorButtonSelected',
],
tab.name === tabName,
)}
css={[
selectorButtonCss,
tab.name === tabName && selectorButtonSelectedCss,
]}
>
{tab.name}
</div>
))}
</div>
<div {...styling('inspectedPath')}>
<div css={{ padding: '6px 0' }}>
{inspectedPath.length ? (
<span {...styling('inspectedPathKey')}>
<a
onClick={() => onInspectPath([])}
{...styling('inspectedPathKeyLink')}
>
<span css={inspectedPathKeyCss}>
<a onClick={() => onInspectPath([])} css={inspectedPathKeyLinkCss}>
{tabName}
</a>
</span>
@ -50,10 +75,10 @@ const ActionPreviewHeader: FunctionComponent<
idx === inspectedPath.length - 1 ? (
<span key={key}>{key}</span>
) : (
<span key={key} {...styling('inspectedPathKey')}>
<span key={key} css={inspectedPathKeyCss}>
<a
onClick={() => onInspectPath(inspectedPath.slice(0, idx + 1))}
{...styling('inspectedPathKeyLink')}
css={inspectedPathKeyLinkCss}
>
{key}
</a>

View File

@ -1,10 +1,5 @@
import React, { PureComponent } from 'react';
import { Base16Theme } from 'redux-devtools-themes';
import {
getBase16Theme,
invertTheme,
StylingFunction,
} from 'react-base16-styling';
import {
ActionCreators,
LiftedAction,
@ -13,9 +8,10 @@ import {
import { Action, Dispatch } from 'redux';
import { Delta, DiffContext } from 'jsondiffpatch';
import {
createStylingFromTheme,
base16Themes,
} from './utils/createStylingFromTheme';
createInspectorMonitorThemeFromBase16Theme,
resolveBase16Theme,
} from './utils/themes';
import type { Base16ThemeName } from './utils/themes';
import ActionList from './ActionList';
import ActionPreview, { Tab } from './ActionPreview';
import getInspectedState from './utils/getInspectedState';
@ -26,6 +22,7 @@ import {
reducer,
updateMonitorState,
} from './redux';
import { ThemeProvider } from '@emotion/react';
const {
// eslint-disable-next-line @typescript-eslint/unbound-method
@ -125,17 +122,6 @@ function createIntermediateState<S, A extends Action<string>>(
};
}
function createThemeState<S, A extends Action<string>>(
props: DevtoolsInspectorProps<S, A>,
) {
const base16Theme = getBase16Theme(props.theme, base16Themes)!;
const theme = props.invertTheme ? invertTheme(props.theme) : props.theme;
const styling = createStylingFromTheme(theme);
return { base16Theme, styling };
}
export interface ExternalProps<S, A extends Action<string>> {
dispatch: Dispatch<
DevtoolsInspectorAction | LiftedAction<S, A, DevtoolsInspectorState>
@ -143,7 +129,7 @@ export interface ExternalProps<S, A extends Action<string>> {
preserveScrollTop?: boolean;
draggableActions: boolean;
select: (state: S) => unknown;
theme: keyof typeof base16Themes | Base16Theme;
theme: Base16ThemeName | Base16Theme;
supportImmutable: boolean;
diffObjectHash?: (item: unknown, index: number) => string;
diffPropertyFilter?: (name: string, context: DiffContext) => boolean;
@ -160,7 +146,7 @@ interface DefaultProps {
select: (state: unknown) => unknown;
supportImmutable: boolean;
draggableActions: boolean;
theme: keyof typeof base16Themes;
theme: Base16ThemeName;
invertTheme: boolean;
}
@ -172,7 +158,7 @@ export interface DevtoolsInspectorProps<S, A extends Action<string>>
preserveScrollTop?: boolean;
draggableActions: boolean;
select: (state: S) => unknown;
theme: keyof typeof base16Themes | Base16Theme;
theme: Base16ThemeName | Base16Theme;
supportImmutable: boolean;
diffObjectHash?: (item: unknown, index: number) => string;
diffPropertyFilter?: (name: string, context: DiffContext) => boolean;
@ -191,7 +177,6 @@ interface State<S, A extends Action<string>> {
action: A;
error: string | undefined;
isWideLayout: boolean;
themeState: { base16Theme: Base16Theme; styling: StylingFunction };
}
class DevtoolsInspector<S, A extends Action<string>> extends PureComponent<
@ -201,7 +186,6 @@ class DevtoolsInspector<S, A extends Action<string>> extends PureComponent<
state: State<S, A> = {
...createIntermediateState(this.props, this.props.monitorState),
isWideLayout: false,
themeState: createThemeState(this.props),
};
static update = reducer;
@ -257,13 +241,6 @@ class DevtoolsInspector<S, A extends Action<string>> extends PureComponent<
) {
this.setState(createIntermediateState(nextProps, nextMonitorState));
}
if (
this.props.theme !== nextProps.theme ||
this.props.invertTheme !== nextProps.invertTheme
) {
this.setState({ themeState: createThemeState(nextProps) });
}
}
inspectorCreateRef: React.RefCallback<HTMLDivElement> = (node) => {
@ -277,6 +254,7 @@ class DevtoolsInspector<S, A extends Action<string>> extends PureComponent<
computedStates,
draggableActions,
tabs,
theme,
invertTheme,
skippedActionIds,
currentStateIndex,
@ -291,73 +269,88 @@ class DevtoolsInspector<S, A extends Action<string>> extends PureComponent<
monitorState;
const inspectedPathType =
tabName === 'Action' ? 'inspectedActionPath' : 'inspectedStatePath';
const { themeState, isWideLayout, action, nextState, delta, error } =
this.state;
const { base16Theme, styling } = themeState;
const { isWideLayout, action, nextState, delta, error } = this.state;
const base16Theme = resolveBase16Theme(theme)!;
const inspectorMonitorTheme = createInspectorMonitorThemeFromBase16Theme(
base16Theme,
invertTheme,
);
return (
<div
key="inspector"
data-testid="inspector"
ref={this.inspectorCreateRef}
{...styling(
['inspector', isWideLayout && 'inspectorWide'],
isWideLayout,
)}
>
<ActionList
{...{
actions,
actionIds,
isWideLayout,
searchValue,
selectedActionId,
startActionId,
skippedActionIds,
draggableActions,
hideMainButtons,
hideActionButtons,
styling,
}}
onSearch={this.handleSearch}
onSelect={this.handleSelectAction}
onToggleAction={this.handleToggleAction}
onJumpToState={this.handleJumpToState}
onCommit={this.handleCommit}
onSweep={this.handleSweep}
onReorderAction={this.handleReorderAction}
currentActionId={actionIds[currentStateIndex]}
lastActionId={getLastActionId(this.props)}
/>
<ActionPreview
{...{
base16Theme,
invertTheme,
isWideLayout,
tabs,
tabName,
delta,
error,
nextState,
computedStates,
action,
actions,
selectedActionId,
startActionId,
dataTypeKey,
sortStateTreeAlphabetically,
disableStateTreeCollection,
}}
monitorState={this.props.monitorState}
updateMonitorState={this.updateMonitorState}
styling={styling}
onInspectPath={(path: (string | number)[]) =>
this.handleInspectPath(inspectedPathType, path)
}
inspectedPath={monitorState[inspectedPathType]}
onSelectTab={this.handleSelectTab}
/>
</div>
<ThemeProvider theme={inspectorMonitorTheme}>
<div
key="inspector"
data-testid="inspector"
ref={this.inspectorCreateRef}
css={[
(theme) => ({
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
fontSize: '12px',
WebkitFontSmoothing: 'antialiased',
lineHeight: '1.5em',
backgroundColor: theme.BACKGROUND_COLOR,
color: theme.TEXT_COLOR,
}),
isWideLayout && { flexDirection: 'row' },
]}
>
<ActionList
{...{
actions,
actionIds,
isWideLayout,
searchValue,
selectedActionId,
startActionId,
skippedActionIds,
draggableActions,
hideMainButtons,
hideActionButtons,
}}
onSearch={this.handleSearch}
onSelect={this.handleSelectAction}
onToggleAction={this.handleToggleAction}
onJumpToState={this.handleJumpToState}
onCommit={this.handleCommit}
onSweep={this.handleSweep}
onReorderAction={this.handleReorderAction}
currentActionId={actionIds[currentStateIndex]}
lastActionId={getLastActionId(this.props)}
/>
<ActionPreview
{...{
base16Theme,
invertTheme,
isWideLayout,
tabs,
tabName,
delta,
error,
nextState,
computedStates,
action,
actions,
selectedActionId,
startActionId,
dataTypeKey,
sortStateTreeAlphabetically,
disableStateTreeCollection,
}}
monitorState={this.props.monitorState}
updateMonitorState={this.updateMonitorState}
onInspectPath={(path: (string | number)[]) =>
this.handleInspectPath(inspectedPathType, path)
}
inspectedPath={monitorState[inspectedPathType]}
onSelectTab={this.handleSelectTab}
/>
</div>
</ThemeProvider>
);
}

View File

@ -1,26 +1,35 @@
import React, { FunctionComponent } from 'react';
import { StylingFunction } from 'react-base16-styling';
interface Props {
styling: StylingFunction;
shown?: boolean;
children: React.ReactNode;
rotate?: boolean;
}
const RightSlider: FunctionComponent<Props> = ({
styling,
shown,
children,
rotate,
}) => (
const RightSlider: FunctionComponent<Props> = ({ shown, children, rotate }) => (
<div
{...styling([
'rightSlider',
shown && 'rightSliderShown',
rotate && 'rightSliderRotate',
rotate && shown && 'rightSliderRotateShown',
])}
css={[
{
WebkitFontSmoothing: 'subpixel-antialiased', // http://stackoverflow.com/a/21136111/4218591
position: 'absolute',
right: 0,
transform: 'translateX(150%)',
transition: 'transform 0.2s ease-in-out',
},
shown && {
position: 'static',
transform: 'translateX(0)',
},
rotate && {
transform: 'rotateX(90deg)',
transition: 'transform 0.2s ease-in-out 0.08s',
},
rotate &&
shown && {
transform: 'rotateX(0)',
transition: 'transform 0.2s ease-in-out 0.18s',
},
]}
>
{children}
</div>

View File

@ -1,9 +1,8 @@
export type { StylingFunction } from 'react-base16-styling';
export type { LabelRenderer } from 'react-json-tree';
export { default as InspectorMonitor } from './DevtoolsInspector';
export type { Tab, TabComponentProps } from './ActionPreview';
export type { DevtoolsInspectorState } from './redux';
export { base16Themes } from './utils/createStylingFromTheme';
export type { Base16ThemeName } from './utils/themes';
export * as inspectorThemes from './themes/index';
export { default as ActionTab } from './tabs/ActionTab';
export { default as DiffTab } from './tabs/DiffTab';

View File

@ -9,7 +9,6 @@ const ActionTab: FunctionComponent<
TabComponentProps<unknown, Action<string>>
> = ({
action,
styling,
base16Theme,
invertTheme,
labelRenderer,
@ -21,7 +20,7 @@ const ActionTab: FunctionComponent<
theme={getJsonTreeTheme(base16Theme)}
data={action}
getItemString={(type, data) =>
getItemString(styling, type, data, dataTypeKey, isWideLayout)
getItemString(type, data, dataTypeKey, isWideLayout)
}
invertTheme={invertTheme}
hideRoot

View File

@ -7,7 +7,6 @@ const DiffTab: FunctionComponent<
TabComponentProps<unknown, Action<string>>
> = ({
delta,
styling,
base16Theme,
invertTheme,
labelRenderer,
@ -17,7 +16,6 @@ const DiffTab: FunctionComponent<
<JSONDiff
{...{
delta,
styling,
base16Theme,
invertTheme,
labelRenderer,

View File

@ -3,8 +3,9 @@ import { JSONTree } from 'react-json-tree';
import type { LabelRenderer, ShouldExpandNodeInitially } from 'react-json-tree';
import { stringify } from 'javascript-stringify';
import { Delta } from 'jsondiffpatch';
import { StylingFunction } from 'react-base16-styling';
import { Base16Theme } from 'redux-devtools-themes';
import { css } from '@emotion/react';
import type { Interpolation, Theme } from '@emotion/react';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
@ -46,9 +47,18 @@ function prepareDelta(value: any) {
return value;
}
const diffCss: Interpolation<Theme> = (theme) => ({
padding: '2px 3px',
borderRadius: '3px',
position: 'relative',
color: theme.TEXT_COLOR,
});
const diffWrapCss = css({ position: 'relative', zIndex: 1 });
interface Props {
delta: Delta | null | undefined | false;
styling: StylingFunction;
base16Theme: Base16Theme;
invertTheme: boolean;
labelRenderer: LabelRenderer;
@ -82,10 +92,19 @@ export default class JSONDiff extends Component<Props, State> {
}
render() {
const { styling, base16Theme, ...props } = this.props;
const { base16Theme, ...props } = this.props;
if (!this.state.data) {
return <div {...styling('stateDiffEmpty')}>(states are equal)</div>;
return (
<div
css={(theme) => ({
padding: '10px',
color: theme.TEXT_PLACEHOLDER_COLOR,
})}
>
(states are equal)
</div>
);
}
return (
@ -105,7 +124,6 @@ export default class JSONDiff extends Component<Props, State> {
getItemString = (type: string, data: any) =>
getItemString(
this.props.styling,
type,
data,
this.props.dataTypeKey,
@ -114,45 +132,71 @@ export default class JSONDiff extends Component<Props, State> {
);
valueRenderer = (raw: any, value: any) => {
const { styling, isWideLayout } = this.props;
function renderSpan(name: string, body: string) {
return (
<span key={name} {...styling(['diff', name])}>
{body}
</span>
);
}
const { isWideLayout } = this.props;
if (Array.isArray(value)) {
switch (value.length) {
case 1:
return (
<span {...styling('diffWrap')}>
{renderSpan(
'diffAdd',
stringifyAndShrink(value[0], isWideLayout),
)}
<span css={diffWrapCss}>
<span
key="diffAdd"
css={[
diffCss,
(theme) => ({ backgroundColor: theme.DIFF_ADD_COLOR }),
]}
>
{stringifyAndShrink(value[0], isWideLayout)}
</span>
</span>
);
case 2:
return (
<span {...styling('diffWrap')}>
{renderSpan(
'diffUpdateFrom',
stringifyAndShrink(value[0], isWideLayout),
)}
{renderSpan('diffUpdateArrow', ' => ')}
{renderSpan(
'diffUpdateTo',
stringifyAndShrink(value[1], isWideLayout),
)}
<span css={diffWrapCss}>
<span
key="diffUpdateFrom"
css={[
diffCss,
(theme) => ({
textDecoration: 'line-through',
backgroundColor: theme.DIFF_REMOVE_COLOR,
}),
]}
>
{stringifyAndShrink(value[0], isWideLayout)}
</span>
<span
key="diffUpdateArrow"
css={[diffCss, (theme) => ({ color: theme.DIFF_ARROW_COLOR })]}
>
{' => '}
</span>
<span
key="diffUpdateTo"
css={[
diffCss,
(theme) => ({ backgroundColor: theme.DIFF_ADD_COLOR }),
]}
>
{stringifyAndShrink(value[1], isWideLayout)}
</span>
</span>
);
case 3:
return (
<span {...styling('diffWrap')}>
{renderSpan('diffRemove', stringifyAndShrink(value[0]))}
<span css={diffWrapCss}>
<span
key="diffRemove"
css={[
diffCss,
(theme) => ({
textDecoration: 'line-through',
backgroundColor: theme.DIFF_REMOVE_COLOR,
}),
]}
>
{stringifyAndShrink(value[0])}
</span>
</span>
);
}

View File

@ -9,7 +9,6 @@ const StateTab: React.FunctionComponent<
TabComponentProps<any, Action<string>>
> = ({
nextState,
styling,
base16Theme,
invertTheme,
labelRenderer,
@ -23,7 +22,7 @@ const StateTab: React.FunctionComponent<
theme={getJsonTreeTheme(base16Theme)}
data={nextState}
getItemString={(type, data) =>
getItemString(styling, type, data, dataTypeKey, isWideLayout)
getItemString(type, data, dataTypeKey, isWideLayout)
}
invertTheme={invertTheme}
hideRoot

View File

@ -1,6 +1,5 @@
import React from 'react';
import { isCollection, isIndexed, isKeyed } from 'immutable';
import { StylingFunction } from 'react-base16-styling';
import isIterable from '../utils/isIterable';
const IS_IMMUTABLE_KEY = '@@__IS_IMMUTABLE__@@';
@ -72,14 +71,13 @@ function getText(
}
const getItemString = (
styling: StylingFunction,
type: string,
data: any,
dataTypeKey: string | symbol | undefined,
isWideLayout: boolean,
isDiff?: boolean,
) => (
<span {...styling('treeItemHint')}>
<span css={(theme) => ({ color: theme.ITEM_HINT_COLOR })}>
{data[IS_IMMUTABLE_KEY] ? 'Immutable' : ''}
{dataTypeKey && data[dataTypeKey] ? `${data[dataTypeKey] as string} ` : ''}
{getText(type, data, isWideLayout, isDiff)}

View File

@ -1,394 +0,0 @@
import jss, { StyleSheet } from 'jss';
import preset from 'jss-preset-default';
import { createStyling, StylingFunction, Theme } from 'react-base16-styling';
import rgba from 'hex-rgba';
import { Base16Theme } from 'redux-devtools-themes';
import type { CurriedFunction1 } from 'lodash';
import inspector from '../themes/inspector';
import * as reduxThemes from 'redux-devtools-themes';
import * as inspectorThemes from '../themes';
jss.setup(preset());
const colorMap = (theme: Base16Theme) => ({
TEXT_COLOR: theme.base06,
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
BACKGROUND_COLOR: theme.base00,
SELECTED_BACKGROUND_COLOR: rgba(theme.base03, 20),
SKIPPED_BACKGROUND_COLOR: rgba(theme.base03, 10),
HEADER_BACKGROUND_COLOR: rgba(theme.base03, 30),
HEADER_BORDER_COLOR: rgba(theme.base03, 20),
BORDER_COLOR: rgba(theme.base03, 50),
LIST_BORDER_COLOR: rgba(theme.base03, 50),
ACTION_TIME_BACK_COLOR: rgba(theme.base03, 20),
ACTION_TIME_COLOR: theme.base04,
PIN_COLOR: theme.base04,
ITEM_HINT_COLOR: rgba(theme.base0F, 90),
TAB_BACK_SELECTED_COLOR: rgba(theme.base03, 20),
TAB_BACK_COLOR: rgba(theme.base00, 70),
TAB_BACK_HOVER_COLOR: rgba(theme.base03, 40),
TAB_BORDER_COLOR: rgba(theme.base03, 50),
DIFF_ADD_COLOR: rgba(theme.base0B, 40),
DIFF_REMOVE_COLOR: rgba(theme.base08, 40),
DIFF_ARROW_COLOR: theme.base0E,
LINK_COLOR: rgba(theme.base0E, 90),
LINK_HOVER_COLOR: theme.base0E,
ERROR_COLOR: theme.base08,
});
type Color = keyof ReturnType<typeof colorMap>;
type ColorMap = {
[color in Color]: string;
};
const getSheetFromColorMap = (map: ColorMap) => ({
inspector: {
display: 'flex',
'flex-direction': 'column',
width: '100%',
height: '100%',
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
'font-size': '12px',
'font-smoothing': 'antialiased',
'line-height': '1.5em',
'background-color': map.BACKGROUND_COLOR,
color: map.TEXT_COLOR,
},
inspectorWide: {
'flex-direction': 'row',
},
actionList: {
'flex-basis': '40%',
'flex-shrink': 0,
'overflow-x': 'hidden',
'overflow-y': 'auto',
'border-bottom-width': '3px',
'border-bottom-style': 'double',
display: 'flex',
'flex-direction': 'column',
'background-color': map.BACKGROUND_COLOR,
'border-color': map.LIST_BORDER_COLOR,
},
actionListHeader: {
display: 'flex',
flex: '0 0 auto',
'align-items': 'center',
'border-bottom-width': '1px',
'border-bottom-style': 'solid',
'border-color': map.LIST_BORDER_COLOR,
},
actionListRows: {
overflow: 'auto',
},
actionListHeaderSelector: {
display: 'inline-flex',
'margin-right': '10px',
},
actionListWide: {
'flex-basis': '40%',
'border-bottom': 'none',
'border-right-width': '3px',
'border-right-style': 'double',
},
actionListItem: {
'border-bottom-width': '1px',
'border-bottom-style': 'solid',
display: 'flex',
'justify-content': 'space-between',
padding: '5px 10px',
cursor: 'pointer',
'user-select': 'none',
'border-bottom-color': map.BORDER_COLOR,
},
actionListItemSelected: {
'background-color': map.SELECTED_BACKGROUND_COLOR,
},
actionListItemSkipped: {
'background-color': map.SKIPPED_BACKGROUND_COLOR,
},
actionListFromFuture: {
opacity: '0.6',
},
actionListItemButtons: {
position: 'relative',
height: '20px',
display: 'flex',
},
actionListItemTime: {
display: 'inline',
padding: '4px 6px',
'border-radius': '3px',
'font-size': '0.8em',
'line-height': '1em',
'flex-shrink': 0,
'background-color': map.ACTION_TIME_BACK_COLOR,
color: map.ACTION_TIME_COLOR,
},
actionListItemSelector: {
display: 'inline-flex',
},
actionListItemName: {
overflow: 'hidden',
'text-overflow': 'ellipsis',
'line-height': '20px',
},
actionListItemNameSkipped: {
'text-decoration': 'line-through',
opacity: 0.3,
},
actionListHeaderSearch: {
outline: 'none',
border: 'none',
width: '100%',
padding: '5px 10px',
'font-size': '1em',
'font-family': 'monaco, Consolas, "Lucida Console", monospace',
'background-color': map.BACKGROUND_COLOR,
color: map.TEXT_COLOR,
'&::-webkit-input-placeholder': {
color: map.TEXT_PLACEHOLDER_COLOR,
},
'&::-moz-placeholder': {
color: map.TEXT_PLACEHOLDER_COLOR,
},
},
actionListHeaderWrapper: {
position: 'relative',
height: '20px',
},
actionPreview: {
flex: 1,
display: 'flex',
'flex-direction': 'column',
'flex-grow': 1,
'overflow-y': 'hidden',
'& pre': {
border: 'inherit',
'border-radius': '3px',
'line-height': 'inherit',
color: 'inherit',
},
'background-color': map.BACKGROUND_COLOR,
},
actionPreviewContent: {
flex: 1,
'overflow-y': 'auto',
},
stateDiff: {
padding: '5px 0',
},
stateDiffEmpty: {
padding: '10px',
color: map.TEXT_PLACEHOLDER_COLOR,
},
stateError: {
padding: '10px',
'margin-left': '14px',
'font-weight': 'bold',
color: map.ERROR_COLOR,
},
inspectedPath: {
padding: '6px 0',
},
inspectedPathKey: {
'&:not(:last-child):after': {
content: '" > "',
},
},
inspectedPathKeyLink: {
cursor: 'pointer',
color: map.LINK_COLOR,
'&:hover': {
'text-decoration': 'underline',
color: map.LINK_HOVER_COLOR,
},
},
treeItemPin: {
'font-size': '0.7em',
'padding-left': '5px',
cursor: 'pointer',
'&:hover': {
'text-decoration': 'underline',
},
color: map.PIN_COLOR,
},
treeItemHint: {
color: map.ITEM_HINT_COLOR,
},
previewHeader: {
flex: '0 0 30px',
padding: '5px 10px',
'align-items': 'center',
'border-bottom-width': '1px',
'border-bottom-style': 'solid',
'background-color': map.HEADER_BACKGROUND_COLOR,
'border-bottom-color': map.HEADER_BORDER_COLOR,
},
tabSelector: {
position: 'relative',
display: 'inline-flex',
float: 'right',
},
selectorButton: {
cursor: 'pointer',
position: 'relative',
padding: '5px 10px',
'border-style': 'solid',
'border-width': '1px',
'border-left-width': 0,
'&:first-child': {
'border-left-width': '1px',
'border-top-left-radius': '3px',
'border-bottom-left-radius': '3px',
},
'&:last-child': {
'border-top-right-radius': '3px',
'border-bottom-right-radius': '3px',
},
'background-color': map.TAB_BACK_COLOR,
'&:hover': {
'background-color': map.TAB_BACK_HOVER_COLOR,
},
'border-color': map.TAB_BORDER_COLOR,
},
selectorButtonSmall: {
padding: '0px 8px',
'font-size': '0.8em',
},
selectorButtonSelected: {
'background-color': map.TAB_BACK_SELECTED_COLOR,
},
diff: {
padding: '2px 3px',
'border-radius': '3px',
position: 'relative',
color: map.TEXT_COLOR,
},
diffWrap: {
position: 'relative',
'z-index': 1,
},
diffAdd: {
'background-color': map.DIFF_ADD_COLOR,
},
diffRemove: {
'text-decoration': 'line-through',
'background-color': map.DIFF_REMOVE_COLOR,
},
diffUpdateFrom: {
'text-decoration': 'line-through',
'background-color': map.DIFF_REMOVE_COLOR,
},
diffUpdateTo: {
'background-color': map.DIFF_ADD_COLOR,
},
diffUpdateArrow: {
color: map.DIFF_ARROW_COLOR,
},
rightSlider: {
'font-smoothing': 'subpixel-antialiased', // http://stackoverflow.com/a/21136111/4218591
position: 'absolute',
right: 0,
transform: 'translateX(150%)',
transition: 'transform 0.2s ease-in-out',
},
rightSliderRotate: {
transform: 'rotateX(90deg)',
transition: 'transform 0.2s ease-in-out 0.08s',
},
rightSliderShown: {
position: 'static',
transform: 'translateX(0)',
},
rightSliderRotateShown: {
transform: 'rotateX(0)',
transition: 'transform 0.2s ease-in-out 0.18s',
},
});
let themeSheet: StyleSheet;
const getDefaultThemeStyling = (theme: Base16Theme) => {
if (themeSheet) {
themeSheet.detach();
}
themeSheet = jss
.createStyleSheet(getSheetFromColorMap(colorMap(theme)))
.attach();
return themeSheet.classes;
};
export const base16Themes = { ...reduxThemes, ...inspectorThemes };
export const createStylingFromTheme: CurriedFunction1<
Theme | undefined,
StylingFunction
> = createStyling(getDefaultThemeStyling, {
defaultBase16: inspector,
base16Themes,
});

View File

@ -0,0 +1,39 @@
import { css } from '@emotion/react';
import type { Interpolation, Theme } from '@emotion/react';
export const selectorButtonCss: Interpolation<Theme> = (theme) => ({
cursor: 'pointer',
position: 'relative',
padding: '5px 10px',
borderStyle: 'solid',
borderWidth: '1px',
borderLeftWidth: 0,
'&:first-of-type': {
borderLeftWidth: '1px',
borderTopLeftRadius: '3px',
borderBottomLeftRadius: '3px',
},
'&:last-of-type': {
borderTopRightRadius: '3px',
borderBottomRightRadius: '3px',
},
backgroundColor: theme.TAB_BACK_COLOR,
'&:hover': {
backgroundColor: theme.TAB_BACK_HOVER_COLOR,
},
borderColor: theme.TAB_BORDER_COLOR,
});
export const selectorButtonSmallCss = css({
padding: '0px 8px',
fontSize: '0.8em',
});
export const selectorButtonSelectedCss: Interpolation<Theme> = (theme) => ({
backgroundColor: theme.TAB_BACK_SELECTED_COLOR,
});

View File

@ -0,0 +1,76 @@
import rgba from 'hex-rgba';
import { Base16Theme } from 'redux-devtools-themes';
import * as reduxThemes from 'redux-devtools-themes';
import * as inspectorThemes from '../themes';
import { getBase16Theme, invertBase16Theme } from 'react-base16-styling';
const base16Themes = { ...reduxThemes, ...inspectorThemes };
export type Base16ThemeName = keyof typeof base16Themes;
export function resolveBase16Theme(theme: Base16ThemeName | Base16Theme) {
return getBase16Theme(theme, base16Themes);
}
declare module '@emotion/react' {
export interface Theme {
TEXT_COLOR: string;
TEXT_PLACEHOLDER_COLOR: string;
BACKGROUND_COLOR: string;
SELECTED_BACKGROUND_COLOR: string;
SKIPPED_BACKGROUND_COLOR: string;
HEADER_BACKGROUND_COLOR: string;
HEADER_BORDER_COLOR: string;
BORDER_COLOR: string;
LIST_BORDER_COLOR: string;
ACTION_TIME_BACK_COLOR: string;
ACTION_TIME_COLOR: string;
PIN_COLOR: string;
ITEM_HINT_COLOR: string;
TAB_BACK_SELECTED_COLOR: string;
TAB_BACK_COLOR: string;
TAB_BACK_HOVER_COLOR: string;
TAB_BORDER_COLOR: string;
DIFF_ADD_COLOR: string;
DIFF_REMOVE_COLOR: string;
DIFF_ARROW_COLOR: string;
LINK_COLOR: string;
LINK_HOVER_COLOR: string;
ERROR_COLOR: string;
}
}
const colorMap = (theme: Base16Theme) => ({
TEXT_COLOR: theme.base06,
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
BACKGROUND_COLOR: theme.base00,
SELECTED_BACKGROUND_COLOR: rgba(theme.base03, 20),
SKIPPED_BACKGROUND_COLOR: rgba(theme.base03, 10),
HEADER_BACKGROUND_COLOR: rgba(theme.base03, 30),
HEADER_BORDER_COLOR: rgba(theme.base03, 20),
BORDER_COLOR: rgba(theme.base03, 50),
LIST_BORDER_COLOR: rgba(theme.base03, 50),
ACTION_TIME_BACK_COLOR: rgba(theme.base03, 20),
ACTION_TIME_COLOR: theme.base04,
PIN_COLOR: theme.base04,
ITEM_HINT_COLOR: rgba(theme.base0F, 90),
TAB_BACK_SELECTED_COLOR: rgba(theme.base03, 20),
TAB_BACK_COLOR: rgba(theme.base00, 70),
TAB_BACK_HOVER_COLOR: rgba(theme.base03, 40),
TAB_BORDER_COLOR: rgba(theme.base03, 50),
DIFF_ADD_COLOR: rgba(theme.base0B, 40),
DIFF_REMOVE_COLOR: rgba(theme.base08, 40),
DIFF_ARROW_COLOR: theme.base0E,
LINK_COLOR: rgba(theme.base0E, 90),
LINK_HOVER_COLOR: theme.base0E,
ERROR_COLOR: theme.base08,
});
export function createInspectorMonitorThemeFromBase16Theme(
base16Theme: Base16Theme,
invertTheme: boolean,
) {
const finalBase16Theme = invertTheme
? invertBase16Theme(base16Theme)
: base16Theme;
return colorMap(finalBase16Theme);
}

View File

@ -2,7 +2,9 @@
"extends": "../../tsconfig.react.base.json",
"compilerOptions": {
"outDir": "lib/types",
"resolveJsonModule": true
"resolveJsonModule": true,
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react"
},
"include": ["src"]
}

View File

@ -59,6 +59,9 @@ importers:
'@babel/polyfill':
specifier: ^7.12.1
version: 7.12.1
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/app':
specifier: ^4.0.1
version: link:../packages/redux-devtools-app
@ -1088,6 +1091,9 @@ importers:
'@babel/preset-typescript':
specifier: ^7.23.3
version: 7.23.3(@babel/core@7.23.5)
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@rjsf/core':
specifier: ^4.2.3
version: 4.2.3(react@18.2.0)
@ -1293,6 +1299,9 @@ importers:
'@apollo/server':
specifier: ^4.9.5
version: 4.9.5(graphql@16.8.1)
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/app':
specifier: ^4.0.0
version: link:../redux-devtools-app
@ -1605,12 +1614,6 @@ importers:
jsondiffpatch:
specifier: ^0.5.0
version: 0.5.0
jss:
specifier: ^10.10.0
version: 10.10.0
jss-preset-default:
specifier: ^10.10.0
version: 10.10.0
lodash.debounce:
specifier: ^4.0.8
version: 4.0.8
@ -1648,6 +1651,12 @@ importers:
'@babel/preset-typescript':
specifier: ^7.23.3
version: 7.23.3(@babel/core@7.23.5)
'@emotion/babel-preset-css-prop':
specifier: ^11.11.0
version: 11.11.0(@babel/core@7.23.5)
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/core':
specifier: ^3.14.0
version: link:../redux-devtools
@ -1751,6 +1760,9 @@ importers:
'@babel/preset-typescript':
specifier: ^7.23.3
version: 7.23.3(@babel/core@7.23.5)
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/core':
specifier: ^3.14.0
version: link:../redux-devtools
@ -1823,6 +1835,9 @@ importers:
packages/redux-devtools-inspector-monitor-test-tab/demo:
dependencies:
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/core':
specifier: ^3.13.2
version: link:../../redux-devtools
@ -2002,6 +2017,9 @@ importers:
'@babel/preset-typescript':
specifier: ^7.23.3
version: 7.23.3(@babel/core@7.23.5)
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/core':
specifier: ^3.14.0
version: link:../redux-devtools
@ -2086,6 +2104,9 @@ importers:
packages/redux-devtools-inspector-monitor/demo:
dependencies:
'@emotion/react':
specifier: ^11.11.1
version: 11.11.1(@types/react@18.2.43)(react@18.2.0)
'@redux-devtools/core':
specifier: ^3.13.0
version: link:../../redux-devtools
@ -6429,6 +6450,15 @@ packages:
transitivePeerDependencies:
- supports-color
/@emotion/babel-plugin-jsx-pragmatic@0.2.1(@babel/core@7.23.5):
resolution: {integrity: sha512-xy1SlgEJygAAIvIuC2idkGKJYa6v5iwoyILkvNKgk347bV+IImXrUat5Z86EmLGyWhEoTplVT9EHqTnHZG4HFw==}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.5
'@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5)
dev: true
/@emotion/babel-plugin@11.11.0:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies:
@ -6443,7 +6473,18 @@ packages:
find-root: 1.1.0
source-map: 0.5.7
stylis: 4.2.0
dev: false
/@emotion/babel-preset-css-prop@11.11.0(@babel/core@7.23.5):
resolution: {integrity: sha512-+1Cba68IyBeltWzvbBSXcBWqP2eKQuQcSUpIu3ma4pOUeRol4EvwWrYS2Rv51aIVqg066fLB+Z9O/8NKR7uUlQ==}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.23.5
'@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.5)
'@babel/runtime': 7.23.5
'@emotion/babel-plugin': 11.11.0
'@emotion/babel-plugin-jsx-pragmatic': 0.2.1(@babel/core@7.23.5)
dev: true
/@emotion/cache@11.11.0:
resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==}
@ -6453,11 +6494,9 @@ packages:
'@emotion/utils': 1.2.1
'@emotion/weak-memoize': 0.3.1
stylis: 4.2.0
dev: false
/@emotion/hash@0.9.1:
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
dev: false
/@emotion/is-prop-valid@0.8.8:
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
@ -6500,7 +6539,6 @@ packages:
'@types/react': 18.2.43
hoist-non-react-statics: 3.3.2
react: 18.2.0
dev: false
/@emotion/serialize@1.1.2:
resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==}
@ -6510,11 +6548,9 @@ packages:
'@emotion/unitless': 0.8.1
'@emotion/utils': 1.2.1
csstype: 3.1.2
dev: false
/@emotion/sheet@1.2.2:
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
dev: false
/@emotion/styled@11.11.0(@emotion/react@11.11.1)(@types/react@18.2.43)(react@18.2.0):
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
@ -6545,7 +6581,6 @@ packages:
/@emotion/unitless@0.8.1:
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
dev: false
/@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0):
resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
@ -6556,11 +6591,9 @@ packages:
/@emotion/utils@1.2.1:
resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==}
dev: false
/@emotion/weak-memoize@0.3.1:
resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==}
dev: false
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
@ -11565,7 +11598,6 @@ packages:
'@babel/runtime': 7.23.5
cosmiconfig: 7.1.0
resolve: 1.22.8
dev: false
/babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.5):
resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
@ -12371,7 +12403,6 @@ packages:
/convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: false
/convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@ -14279,7 +14310,6 @@ packages:
/find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
dev: false
/find-up@3.0.0:
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
@ -19835,7 +19865,6 @@ packages:
/source-map@0.5.7:
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
engines: {node: '>=0.10.0'}
dev: false
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
@ -20280,7 +20309,6 @@ packages:
/stylis@4.2.0:
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
dev: false
/sumchecker@3.0.1:
resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==}