This commit is contained in:
Nathan Bierema 2020-05-04 18:18:55 -04:00
parent 469cf7cb2c
commit 460b20369a
20 changed files with 379 additions and 86 deletions

3
.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

View File

@ -0,0 +1 @@
lib

View File

@ -12,6 +12,8 @@
"build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
"build:umd": "rimraf ./umd && webpack --progress --config webpack.config.umd.js",
"build:umd:min": "webpack --env.minimize --progress --config webpack.config.umd.js",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"test": "jest",
"prepare": "npm run build",
"prepublishOnly": "npm run test && npm run clean && npm run build && npm run build:umd && npm run build:umd:min",
@ -49,11 +51,12 @@
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.9.0",
"@types/react": "^15.0.0 || ^16.0.0",
"@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0",
"babel-loader": "^8.0.5",
"jest": "^24.1.0",
"react": "^16.0.0",
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^16.0.0",
"react-test-renderer": "^16.0.0",
"rimraf": "^2.5.2",

View File

@ -1,8 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import JSONArrow from './JSONArrow';
import { CircularPropsPassedThroughItemRange } from './types';
export default class ItemRange extends React.Component {
interface Props extends CircularPropsPassedThroughItemRange {
data: any;
nodeType: string;
from: number;
to: number;
renderChildNodes: (props: Props, from: number, to: number) => React.ReactNode;
}
interface State {
expanded: boolean;
}
export default class ItemRange extends React.Component<Props, State> {
static propTypes = {
styling: PropTypes.func.isRequired,
from: PropTypes.number.isRequired,
@ -11,7 +24,7 @@ export default class ItemRange extends React.Component {
nodeType: PropTypes.string.isRequired
};
constructor(props) {
constructor(props: Props) {
super(props);
this.state = { expanded: false };
@ -42,7 +55,7 @@ export default class ItemRange extends React.Component {
);
}
handleClick() {
handleClick = () => {
this.setState({ expanded: !this.state.expanded });
}
};
}

View File

@ -1,15 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import JSONNestedNode from './JSONNestedNode';
import { CircularPropsPassedThroughJSONNode } from './types';
// Returns the "n Items" string for this node,
// generating and caching it if it hasn't been created yet.
function createItemString(data) {
function createItemString(data: any) {
return `${data.length} ${data.length !== 1 ? 'items' : 'item'}`;
}
interface Props extends CircularPropsPassedThroughJSONNode {
data: any;
nodeType: string;
}
// Configures <JSONNestedNode> to render an Array
const JSONArrayNode = ({ data, ...props }) => (
const JSONArrayNode: React.FunctionComponent<Props> = ({ data, ...props }) => (
<JSONNestedNode
{...props}
data={data}

View File

@ -1,7 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StylingFunction } from 'react-base16-styling';
const JSONArrow = ({ styling, arrowStyle, expanded, nodeType, onClick }) => (
interface Props {
styling: StylingFunction;
arrowStyle?: 'single' | 'double';
expanded: boolean;
nodeType: string;
onClick: React.MouseEventHandler<HTMLDivElement>;
}
const JSONArrow: React.FunctionComponent<Props> = ({
styling,
arrowStyle,
expanded,
nodeType,
onClick
}) => (
<div {...styling('arrowContainer', arrowStyle)} onClick={onClick}>
<div {...styling(['arrow', 'arrowSign'], nodeType, expanded, arrowStyle)}>
{'\u25B6'}

View File

@ -1,9 +1,10 @@
import React from 'react';
import JSONNestedNode from './JSONNestedNode';
import { CircularPropsPassedThroughJSONNode } from './types';
// Returns the "n Items" string for this node,
// generating and caching it if it hasn't been created yet.
function createItemString(data, limit) {
function createItemString(data: any, limit: number) {
let count = 0;
let hasMore = false;
if (Number.isSafeInteger(data.size)) {
@ -21,8 +22,13 @@ function createItemString(data, limit) {
return `${hasMore ? '>' : ''}${count} ${count !== 1 ? 'entries' : 'entry'}`;
}
interface Props extends CircularPropsPassedThroughJSONNode {
data: any;
nodeType: string;
}
// Configures <JSONNestedNode> to render an iterable
export default function JSONIterableNode({ ...props }) {
const JSONIterableNode: React.FunctionComponent<Props> = ({ ...props }) => {
return (
<JSONNestedNode
{...props}
@ -31,4 +37,6 @@ export default function JSONIterableNode({ ...props }) {
createItemString={createItemString}
/>
);
}
};
export default JSONIterableNode;

View File

@ -4,12 +4,40 @@ import JSONArrow from './JSONArrow';
import getCollectionEntries from './getCollectionEntries';
import JSONNode from './JSONNode';
import ItemRange from './ItemRange';
import {
CircularPropsPassedThroughJSONNestedNode,
CircularPropsPassedThroughRenderChildNodes
} from './types';
/**
* Renders nested values (eg. objects, arrays, lists, etc.)
*/
function renderChildNodes(props, from, to) {
export interface RenderChildNodesProps
extends CircularPropsPassedThroughRenderChildNodes {
data: any;
nodeType: string;
}
interface Range {
from: number;
to: number;
}
interface Entry {
key: string | number;
value: any;
}
function isRange(rangeOrEntry: Range | Entry): rangeOrEntry is Range {
return (rangeOrEntry as Range).to !== undefined;
}
function renderChildNodes(
props: RenderChildNodesProps,
from?: number,
to?: number
) {
const {
nodeType,
data,
@ -19,7 +47,7 @@ function renderChildNodes(props, from, to) {
postprocessValue,
sortObjectKeys
} = props;
const childNodes = [];
const childNodes: React.ReactNode[] = [];
getCollectionEntries(
nodeType,
@ -29,7 +57,7 @@ function renderChildNodes(props, from, to) {
from,
to
).forEach(entry => {
if (entry.to) {
if (isRange(entry)) {
childNodes.push(
<ItemRange
{...props}
@ -41,9 +69,9 @@ function renderChildNodes(props, from, to) {
);
} else {
const { key, value } = entry;
const isCircular = circularCache.indexOf(value) !== -1;
const isCircular = circularCache.includes(value);
const node = (
childNodes.push(
<JSONNode
{...props}
{...{ postprocessValue, collectionLimit }}
@ -55,17 +83,25 @@ function renderChildNodes(props, from, to) {
hideRoot={false}
/>
);
if (node !== false) {
childNodes.push(node);
}
}
});
return childNodes;
}
function getStateFromProps(props) {
interface Props extends CircularPropsPassedThroughJSONNestedNode {
data: any;
nodeType: string;
nodeTypeIndicator: string;
createItemString: (data: any, collectionLimit: number) => string;
expandable: boolean;
}
interface State {
expanded: boolean;
}
function getStateFromProps(props: Props) {
// calculate individual node expansion if necessary
const expanded =
props.shouldExpandNode && !props.isCircular
@ -76,7 +112,7 @@ function getStateFromProps(props) {
};
}
export default class JSONNestedNode extends React.Component {
export default class JSONNestedNode extends React.Component<Props, State> {
static propTypes = {
getItemString: PropTypes.func.isRequired,
nodeTypeIndicator: PropTypes.any,
@ -104,26 +140,26 @@ export default class JSONNestedNode extends React.Component {
expandable: true
};
constructor(props) {
constructor(props: Props) {
super(props);
this.state = getStateFromProps(props);
}
UNSAFE_componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps: Props) {
const nextState = getStateFromProps(nextProps);
if (getStateFromProps(this.props).expanded !== nextState.expanded) {
this.setState(nextState);
}
}
shouldComponentUpdate(nextProps, nextState) {
shouldComponentUpdate(nextProps: Props, nextState: State) {
return (
!!Object.keys(nextProps).find(
key =>
key !== 'circularCache' &&
(key === 'keyPath'
? nextProps[key].join('/') !== this.props[key].join('/')
: nextProps[key] !== this.props[key])
: nextProps[key as keyof Props] !== this.props[key as keyof Props])
) || nextState.expanded !== this.state.expanded
);
}
@ -159,7 +195,7 @@ export default class JSONNestedNode extends React.Component {
itemType,
createItemString(data, collectionLimit)
);
const stylingArgs = [keyPath, nodeType, expanded, expandable];
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
return hideRoot ? (
<li {...styling('rootNode', ...stylingArgs)}>

View File

@ -5,8 +5,15 @@ import JSONObjectNode from './JSONObjectNode';
import JSONArrayNode from './JSONArrayNode';
import JSONIterableNode from './JSONIterableNode';
import JSONValueNode from './JSONValueNode';
import { CircularPropsPassedThroughJSONNode } from './types';
const JSONNode = ({
interface Props extends CircularPropsPassedThroughJSONNode {
keyPath: (string | number)[];
value: any;
isCustomNode: (value: any) => boolean;
}
const JSONNode: React.FunctionComponent<Props> = ({
getItemString,
keyPath,
labelRenderer,
@ -97,7 +104,7 @@ const JSONNode = ({
JSONNode.propTypes = {
getItemString: PropTypes.func.isRequired,
keyPath: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
).isRequired,
labelRenderer: PropTypes.func.isRequired,
styling: PropTypes.func.isRequired,

View File

@ -1,16 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import JSONNestedNode from './JSONNestedNode';
import { CircularPropsPassedThroughJSONNode } from './types';
// Returns the "n Items" string for this node,
// generating and caching it if it hasn't been created yet.
function createItemString(data) {
function createItemString(data: any) {
const len = Object.getOwnPropertyNames(data).length;
return `${len} ${len !== 1 ? 'keys' : 'key'}`;
}
interface Props extends CircularPropsPassedThroughJSONNode {
data: any;
nodeType: string;
}
// Configures <JSONNestedNode> to render an Object
const JSONObjectNode = ({ data, ...props }) => (
const JSONObjectNode: React.FunctionComponent<Props> = ({ data, ...props }) => (
<JSONNestedNode
{...props}
data={data}
@ -23,7 +29,7 @@ const JSONObjectNode = ({ data, ...props }) => (
JSONObjectNode.propTypes = {
data: PropTypes.object,
nodeType: PropTypes.string
nodeType: PropTypes.string.isRequired
};
export default JSONObjectNode;

View File

@ -1,18 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import { JSONValueNodeCircularPropsProvidedByJSONNode } from './types';
/**
* Renders simple values (eg. strings, numbers, booleans, etc)
*/
const JSONValueNode = ({
interface Props extends JSONValueNodeCircularPropsProvidedByJSONNode {
nodeType: string;
value: any;
valueGetter?: (value: any) => any;
}
const JSONValueNode: React.FunctionComponent<Props> = ({
nodeType,
styling,
labelRenderer,
keyPath,
valueRenderer,
value,
valueGetter
valueGetter = value => value
}) => (
<li {...styling('value', nodeType, keyPath)}>
<label {...styling(['label', 'valueLabel'], nodeType, keyPath)}>
@ -29,15 +36,11 @@ JSONValueNode.propTypes = {
styling: PropTypes.func.isRequired,
labelRenderer: PropTypes.func.isRequired,
keyPath: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
).isRequired,
valueRenderer: PropTypes.func.isRequired,
value: PropTypes.any,
valueGetter: PropTypes.func
};
JSONValueNode.defaultProps = {
valueGetter: value => value
};
export default JSONValueNode;

View File

@ -1,7 +1,11 @@
import { createStyling } from 'react-base16-styling';
import {
createStyling,
Base16Theme,
StylingConfig
} from 'react-base16-styling';
import solarized from './themes/solarized';
const colorMap = theme => ({
const colorMap = (theme: Base16Theme) => ({
BACKGROUND_COLOR: theme.base00,
TEXT_COLOR: theme.base07,
STRING_COLOR: theme.base0B,
@ -18,7 +22,12 @@ const colorMap = theme => ({
ITEM_STRING_EXPANDED_COLOR: theme.base03
});
const valueColorMap = colors => ({
type Color = keyof ReturnType<typeof colorMap>;
type Colors = {
[color in Color]: string;
};
const valueColorMap = (colors: Colors) => ({
String: colors.STRING_COLOR,
Date: colors.DATE_COLOR,
Number: colors.NUMBER_COLOR,
@ -29,7 +38,7 @@ const valueColorMap = colors => ({
Symbol: colors.SYMBOL_COLOR
});
const getDefaultThemeStyling = theme => {
const getDefaultThemeStyling = (theme: Base16Theme): StylingConfig => {
const colors = colorMap(theme);
return {
@ -73,7 +82,9 @@ const getDefaultThemeStyling = theme => {
valueText: ({ style }, nodeType) => ({
style: {
...style,
color: valueColorMap(colors)[nodeType]
color: valueColorMap(colors)[
nodeType as keyof ReturnType<typeof valueColorMap>
]
}
}),

View File

@ -1,4 +1,4 @@
function getLength(type, collection) {
function getLength(type: string, collection: any) {
if (type === 'Object') {
return Object.keys(collection).length;
} else if (type === 'Array') {
@ -8,11 +8,17 @@ function getLength(type, collection) {
return Infinity;
}
function isIterableMap(collection) {
function isIterableMap(collection: any) {
return typeof collection.set === 'function';
}
function getEntries(type, collection, sortObjectKeys, from = 0, to = Infinity) {
function getEntries(
type: string,
collection: any,
sortObjectKeys?: ((a: any, b: any) => number) | boolean | undefined,
from = 0,
to = Infinity
): { entries: { key: string | number; value: any }[]; hasMore?: boolean } {
let res;
if (type === 'Object') {
@ -31,7 +37,7 @@ function getEntries(type, collection, sortObjectKeys, from = 0, to = Infinity) {
res = {
entries: collection
.slice(from, to + 1)
.map((val, idx) => ({ key: idx + from, value: val }))
.map((val: any, idx: number) => ({ key: idx + from, value: val }))
};
} else {
let idx = 0;
@ -74,7 +80,7 @@ function getEntries(type, collection, sortObjectKeys, from = 0, to = Infinity) {
return res;
}
function getRanges(from, to, limit) {
function getRanges(from: number, to: number, limit: number) {
const ranges = [];
while (to - from > limit * limit) {
limit = limit * limit;
@ -87,10 +93,10 @@ function getRanges(from, to, limit) {
}
export default function getCollectionEntries(
type,
collection,
sortObjectKeys,
limit,
type: string,
collection: any,
sortObjectKeys: ((a: any, b: any) => number) | boolean | undefined,
limit: number,
from = 0,
to = Infinity
) {

View File

@ -7,19 +7,47 @@ import React from 'react';
import PropTypes from 'prop-types';
import JSONNode from './JSONNode';
import createStylingFromTheme from './createStylingFromTheme';
import { invertTheme } from 'react-base16-styling';
import {
Base16Theme,
invertTheme,
StylingConfig,
StylingFunction,
Theme
} from 'react-base16-styling';
import { CircularPropsPassedThroughJSONTree } from './types';
const identity = value => value;
const expandRootNode = (keyName, data, level) => level === 0;
const defaultItemString = (type, data, itemType, itemString) => (
interface Props extends CircularPropsPassedThroughJSONTree {
theme: Theme;
invertTheme: boolean;
data: any;
}
interface State {
styling: StylingFunction;
}
const identity = (value: any) => value;
const expandRootNode = (
keyPath: (string | number)[],
data: any,
level: number
) => level === 0;
const defaultItemString = (
type: string,
data: any,
itemType: React.ReactNode,
itemString: string
) => (
<span>
{itemType} {itemString}
</span>
);
const defaultLabelRenderer = ([label]) => <span>{label}:</span>;
const defaultLabelRenderer = ([label]: (string | number)[]) => (
<span>{label}:</span>
);
const noCustomNode = () => false;
function checkLegacyTheming(theme, props) {
function checkLegacyTheming(theme: Theme, props: Props) {
const deprecatedStylingMethodsMap = {
getArrowStyle: 'arrow',
getListStyle: 'nestedNodeChildren',
@ -30,7 +58,7 @@ function checkLegacyTheming(theme, props) {
const deprecatedStylingMethods = Object.keys(
deprecatedStylingMethodsMap
).filter(name => props[name]);
).filter(name => props[name as keyof Props]);
if (deprecatedStylingMethods.length > 0) {
if (typeof theme === 'string') {
@ -47,10 +75,14 @@ function checkLegacyTheming(theme, props) {
`Styling method "${name}" is deprecated, use "theme" property instead`
);
theme[deprecatedStylingMethodsMap[name]] = ({ style }, ...args) => ({
(theme as StylingConfig)[
deprecatedStylingMethodsMap[
name as keyof typeof deprecatedStylingMethodsMap
]
] = ({ style }, ...args) => ({
style: {
...style,
...props[name](...args)
...props[name as keyof Props](...args)
}
});
});
@ -59,19 +91,28 @@ function checkLegacyTheming(theme, props) {
return theme;
}
function getStateFromProps(props) {
function isStylingConfig(
themeOrStyling: Base16Theme | StylingConfig
): themeOrStyling is StylingConfig {
return (themeOrStyling as StylingConfig).extend !== undefined;
}
function getStateFromProps(props: Props) {
let theme = checkLegacyTheming(props.theme, props);
if (props.invertTheme) {
if (typeof theme === 'string') {
theme = `${theme}:inverted`;
} else if (theme && theme.extend) {
if (typeof theme === 'string') {
} else if (theme && isStylingConfig(theme) && theme.extend) {
if (typeof theme.extend === 'string') {
theme = { ...theme, extend: `${theme.extend}:inverted` };
} else {
theme = { ...theme, extend: invertTheme(theme.extend) };
theme = {
...theme,
extend: invertTheme(theme.extend)
} as StylingConfig;
}
} else if (theme) {
theme = invertTheme(theme);
theme = invertTheme(theme as Base16Theme);
}
}
return {
@ -79,7 +120,7 @@ function getStateFromProps(props) {
};
}
export default class JSONTree extends React.Component {
export default class JSONTree extends React.Component<Props, State> {
static propTypes = {
data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
hideRoot: PropTypes.bool,
@ -105,22 +146,26 @@ export default class JSONTree extends React.Component {
invertTheme: true
};
constructor(props) {
constructor(props: Props) {
super(props);
this.state = getStateFromProps(props);
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (['theme', 'invertTheme'].find(k => nextProps[k] !== this.props[k])) {
UNSAFE_componentWillReceiveProps(nextProps: Props) {
if (
['theme', 'invertTheme'].find(
k => nextProps[k as keyof Props] !== this.props[k as keyof Props]
)
) {
this.setState(getStateFromProps(nextProps));
}
}
shouldComponentUpdate(nextProps) {
shouldComponentUpdate(nextProps: Props) {
return !!Object.keys(nextProps).find(k =>
k === 'keyPath'
? nextProps[k].join('/') !== this.props[k].join('/')
: nextProps[k] !== this.props[k]
: nextProps[k as keyof Props] !== this.props[k as keyof Props]
);
}

View File

@ -1,4 +1,4 @@
export default function objType(obj) {
export default function objType(obj: any) {
const type = Object.prototype.toString.call(obj).slice(8, -1);
if (type === 'Object' && typeof obj[Symbol.iterator] === 'function') {
return 'Iterable';

View File

@ -0,0 +1,52 @@
declare module 'react-base16-styling' {
import React from 'react';
export interface Styling {
style?: React.CSSProperties;
className?: string;
}
export interface Base16Theme {
scheme?: string;
author?: string;
base00: string;
base01: string;
base02: string;
base03: string;
base04: string;
base05: string;
base06: string;
base07: string;
base08: string;
base09: string;
base0A: string;
base0B: string;
base0C: string;
base0D: string;
base0E: string;
base0F: string;
}
export type StylingValue =
| string
| React.CSSProperties
| ((styling: Styling, ...rest: any[]) => Styling);
export type StylingConfig = { [name: string]: StylingValue } & {
extend?: string | Base16Theme;
};
export type Theme = string | Base16Theme | StylingConfig;
export type StylingFunction = (
keys: string | Array<string | undefined | null>,
...rest: any[]
) => Styling;
export function invertTheme(base16Theme: Base16Theme): Base16Theme;
export function createStyling(
getDefaultStyling: (base16Theme: Base16Theme) => StylingConfig,
options?: { defaultBase16?: Theme; base16Themes?: Theme[] }
): (theme: Theme, invertTheme?: boolean) => StylingFunction;
}

View File

@ -0,0 +1,74 @@
import React from 'react';
import { StylingFunction } from 'react-base16-styling';
interface SharedCircularPropsPassedThroughJSONTree {
keyPath: (string | number)[];
labelRenderer: (
keyPath: (string | number)[],
nodeType: string,
expanded: boolean,
expandable: boolean
) => React.ReactNode;
}
interface SharedCircularPropsProvidedByJSONTree
extends SharedCircularPropsPassedThroughJSONTree {
styling: StylingFunction;
}
interface JSONValueNodeCircularPropsPassedThroughJSONTree {
valueRenderer: (
valueAsString: any,
value: any,
...keyPath: (string | number)[]
) => React.ReactNode;
}
export type JSONValueNodeCircularPropsProvidedByJSONNode = SharedCircularPropsProvidedByJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree;
interface JSONNestedNodeCircularPropsPassedThroughJSONTree {
shouldExpandNode: (
keyPath: (string | number)[],
data: any,
level: number
) => boolean;
hideRoot: boolean;
getItemString: (
nodeType: string,
data: any,
itemType: React.ReactNode,
itemString: string
) => React.ReactNode;
postprocessValue: (value: any) => any;
isCustomNode: (value: any) => boolean;
collectionLimit: number;
sortObjectKeys?: (a: any, b: any) => number | boolean;
}
export type CircularPropsPassedThroughJSONTree = SharedCircularPropsPassedThroughJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree &
JSONNestedNodeCircularPropsPassedThroughJSONTree;
interface JSONNestedNodeCircularPropsPassedThroughJSONNode
extends JSONNestedNodeCircularPropsPassedThroughJSONTree {
circularCache?: any[];
isCircular?: boolean;
level?: number;
}
export type CircularPropsPassedThroughJSONNode = SharedCircularPropsProvidedByJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree &
JSONNestedNodeCircularPropsPassedThroughJSONNode;
export interface JSONNestedNodeCircularPropsPassedThroughJSONNestedNode
extends JSONNestedNodeCircularPropsPassedThroughJSONNode {
circularCache: any[];
level: number;
}
export type CircularPropsPassedThroughJSONNestedNode = SharedCircularPropsProvidedByJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree &
JSONNestedNodeCircularPropsPassedThroughJSONNestedNode;
export type CircularPropsPassedThroughRenderChildNodes = SharedCircularPropsProvidedByJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree &
JSONNestedNodeCircularPropsPassedThroughJSONNestedNode;
export type CircularPropsPassedThroughItemRange = SharedCircularPropsProvidedByJSONTree &
JSONValueNodeCircularPropsPassedThroughJSONTree &
JSONNestedNodeCircularPropsPassedThroughJSONNestedNode;

View File

@ -1,10 +0,0 @@
export default function(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
: null;
}

View File

@ -2,6 +2,7 @@
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"jsx": "react",
"declaration": true,
"outDir": "lib",
"strict": true,

View File

@ -2652,6 +2652,19 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react@^15.0.0 || ^16.0.0":
version "16.9.34"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.34.tgz#f7d5e331c468f53affed17a8a4d488cd44ea9349"
integrity sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
@ -5625,7 +5638,7 @@ cssstyle@^1.0.0:
dependencies:
cssom "0.3.x"
csstype@^2.5.7:
csstype@^2.2.0, csstype@^2.5.7:
version "2.6.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
@ -13857,7 +13870,7 @@ react-treebeard@^3.1.0:
shallowequal "^1.1.0"
velocity-react "^1.4.1"
react@^16.0.0, react@^16.4.0, react@^16.4.2, react@^16.7.0:
"react@^15.0.0 || ^16.0.0", react@^16.0.0, react@^16.4.0, react@^16.4.2, react@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==