diff --git a/packages/console/package.json b/packages/console/package.json index 841fe66ae..8a9c1c432 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -20,7 +20,6 @@ "@fontsource/roboto-mono": "^4.5.7", "@logto/phrases": "^0.1.0", "@logto/react": "^0.1.14", - "@logto/shared": "^0.1.0", "@logto/schemas": "^0.1.0", "@logto/shared": "^0.1.0", "@mdx-js/react": "^1.6.22", @@ -69,6 +68,7 @@ "react-paginate": "^8.1.2", "react-router-dom": "^6.2.2", "react-syntax-highlighter": "^15.5.0", + "recharts": "^2.1.10", "remark-gfm": "^3.0.1", "stylelint": "^14.8.2", "swr": "^1.2.2", diff --git a/packages/console/src/pages/Dashboard/components/Block.module.scss b/packages/console/src/pages/Dashboard/components/Block.module.scss new file mode 100644 index 000000000..18426b85b --- /dev/null +++ b/packages/console/src/pages/Dashboard/components/Block.module.scss @@ -0,0 +1,63 @@ +@use '@/scss/underscore' as _; + +.number { + font: var(--font-headline-small); +} + +.delta { + font: var(--font-title-medium); + color: var(--color-success-50); + display: flex; + align-items: center; + + &.down { + color: var(--color-error-50); + } +} + +.block { + flex: 1; + + &:not(:last-child) { + margin-right: _.unit(4); + } + + &.bordered { + border: 1px solid var(--color-divider); + width: 360px; + flex: unset; + } + + .title { + font: var(--font-title-medium); + margin-bottom: 24px; + } + + .content { + display: flex; + align-items: baseline; + + .number { + flex: 1; + } + } + + &.plain { + padding: 0; + + .title { + font: var(--font-title-medium); + margin-bottom: _.unit(6); + } + + .content { + display: flex; + align-items: center; + + .number { + margin-right: _.unit(2); + flex: 0; + } + } + } +} diff --git a/packages/console/src/pages/Dashboard/components/Block.tsx b/packages/console/src/pages/Dashboard/components/Block.tsx new file mode 100644 index 000000000..3babf327d --- /dev/null +++ b/packages/console/src/pages/Dashboard/components/Block.tsx @@ -0,0 +1,42 @@ +import { AdminConsoleKey } from '@logto/phrases'; +import { conditionalString } from '@silverhand/essentials'; +import classNames from 'classnames'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import Card from '@/components/Card'; +import { ArrowDown, ArrowUp } from '@/icons/Arrow'; +import { formatNumberWithComma } from '@/utilities/number'; + +import * as styles from './Block.module.scss'; + +type Props = { + count: number; + delta?: number; + title: AdminConsoleKey; + varient?: 'bordered' | 'default' | 'plain'; +}; + +const Block = ({ varient = 'default', count, delta, title }: Props) => { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + const deltaLable = delta !== undefined && `${conditionalString(delta >= 0 && '+')}${delta}`; + + return ( + +
{t(title)}
+
+
{formatNumberWithComma(count)}
+ {delta !== undefined && ( +
+ (${deltaLable}) + {delta > 0 && } + {delta < 0 && } +
+ )} +
+
+ ); +}; + +export default Block; diff --git a/packages/console/src/pages/Dashboard/index.module.scss b/packages/console/src/pages/Dashboard/index.module.scss index 933ccaa22..3da389cf3 100644 --- a/packages/console/src/pages/Dashboard/index.module.scss +++ b/packages/console/src/pages/Dashboard/index.module.scss @@ -22,41 +22,14 @@ } } -.topBlocks { +.blocks { display: flex; align-items: center; - - .block { - flex: 1; - - &:not(:last-child) { - margin-right: _.unit(4); - } - - .title { - font: var(--font-title-medium); - margin-bottom: 24px; - } - - .content { - display: flex; - align-items: baseline; - - .number { - flex: 1; - font: var(--font-headline-small); - } - - .delta { - font: var(--font-title-medium); - color: var(--color-success-50); - display: flex; - align-items: center; - - &.down { - color: var(--color-error-50); - } - } - } - } + margin-bottom: _.unit(4); +} + +.curve { + margin: _.unit(10) 0 _.unit(6); + width: 100%; + height: 168px; } diff --git a/packages/console/src/pages/Dashboard/index.tsx b/packages/console/src/pages/Dashboard/index.tsx index d77a07f82..d96581c63 100644 --- a/packages/console/src/pages/Dashboard/index.tsx +++ b/packages/console/src/pages/Dashboard/index.tsx @@ -1,20 +1,29 @@ -import classNames from 'classnames'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { + Area, + AreaChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; import useSWR from 'swr'; import Card from '@/components/Card'; -import { ArrowDown, ArrowUp } from '@/icons/Arrow'; +import Block from './components/Block'; import * as styles from './index.module.scss'; -import { NewUsersResponse, TotalUsersResponse } from './types'; +import { ActiveUsersResponse, NewUsersResponse, TotalUsersResponse } from './types'; const Dashboard = () => { const { data: totalData } = useSWR('/api/dashboard/users/total'); const { data: newData } = useSWR('/api/dashboard/users/new'); + const { data: activeData } = useSWR('/api/dashboard/users/active'); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const isLoading = !totalData || !newData; + const isLoading = !totalData || !newData || !activeData; return (
@@ -23,35 +32,68 @@ const Dashboard = () => {
{t('dashboard.description')}
{!isLoading && ( -
- -
{t('dashboard.total_users')}
-
-
{totalData.totalUserCount}
+ <> +
+ + + +
+ + +
+ + ({ + ...item, + // Remove "year" for a compact label. + date: item.date.replace(/\d{4}-/, ''), + }))} + width={1100} + height={168} + > + + + + + + + +
+
+ +
- -
{t('dashboard.new_users_today')}
-
-
{newData.today.count}
-
- ({newData.today.delta > 0 && '+'} - {newData.today.delta}){newData.today.delta > 0 ? : } -
-
-
- -
{t('dashboard.new_users_7_days')}
-
-
{newData.last7Days.count}
-
- ({newData.last7Days.delta > 0 && '+'} - {newData.last7Days.delta}) - {newData.last7Days.delta > 0 ? : } -
-
-
-
+ )}
); diff --git a/packages/console/src/pages/Dashboard/types.ts b/packages/console/src/pages/Dashboard/types.ts index 1f74ac43a..4adfa3a54 100644 --- a/packages/console/src/pages/Dashboard/types.ts +++ b/packages/console/src/pages/Dashboard/types.ts @@ -11,3 +11,10 @@ export type NewUsersResponse = { today: CountAndDelta; last7Days: CountAndDelta; }; + +export type ActiveUsersResponse = { + dau: CountAndDelta; + wau: CountAndDelta; + mau: CountAndDelta; + dauCurve: Array<{ date: string; count: number }>; +}; diff --git a/packages/console/src/utilities/number.ts b/packages/console/src/utilities/number.ts new file mode 100644 index 000000000..5006042df --- /dev/null +++ b/packages/console/src/utilities/number.ts @@ -0,0 +1,2 @@ +export const formatNumberWithComma = (value: number): string => + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40de0bc75..4ac35c8ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -670,6 +670,7 @@ importers: react-paginate: ^8.1.2 react-router-dom: ^6.2.2 react-syntax-highlighter: ^15.5.0 + recharts: ^2.1.10 remark-gfm: ^3.0.1 stylelint: ^14.8.2 swr: ^1.2.2 @@ -727,6 +728,7 @@ importers: react-paginate: 8.1.2_react@17.0.2 react-router-dom: 6.2.2_sfoxds7t5ydpegc3knd667wn6m react-syntax-highlighter: 15.5.0_react@17.0.2 + recharts: 2.1.10_sfoxds7t5ydpegc3knd667wn6m remark-gfm: 3.0.1 stylelint: 14.8.2 swr: 1.2.2_react@17.0.2 @@ -7779,6 +7781,10 @@ packages: csstype: 3.0.11 dev: true + /@types/resize-observer-browser/0.1.7: + resolution: {integrity: sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==} + dev: true + /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: @@ -10526,6 +10532,10 @@ packages: source-map: 0.6.1 dev: true + /css-unit-converter/1.1.2: + resolution: {integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==} + dev: true + /css-what/2.1.3: resolution: {integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==} dev: true @@ -10646,6 +10656,58 @@ packages: resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} dev: true + /d3-array/2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + dependencies: + internmap: 1.0.1 + dev: true + + /d3-color/2.0.0: + resolution: {integrity: sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==} + dev: true + + /d3-format/2.0.0: + resolution: {integrity: sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==} + dev: true + + /d3-interpolate/2.0.1: + resolution: {integrity: sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==} + dependencies: + d3-color: 2.0.0 + dev: true + + /d3-path/2.0.0: + resolution: {integrity: sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==} + dev: true + + /d3-scale/3.3.0: + resolution: {integrity: sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==} + dependencies: + d3-array: 2.12.1 + d3-format: 2.0.0 + d3-interpolate: 2.0.1 + d3-time: 2.1.1 + d3-time-format: 3.0.0 + dev: true + + /d3-shape/2.1.0: + resolution: {integrity: sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==} + dependencies: + d3-path: 2.0.0 + dev: true + + /d3-time-format/3.0.0: + resolution: {integrity: sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==} + dependencies: + d3-time: 2.1.1 + dev: true + + /d3-time/2.1.1: + resolution: {integrity: sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==} + dependencies: + d3-array: 2.12.1 + dev: true + /dargs/7.0.0: resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} engines: {node: '>=8'} @@ -10737,6 +10799,10 @@ packages: engines: {node: '>=10'} dev: false + /decimal.js-light/2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + dev: true + /decimal.js/10.3.1: resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} dev: true @@ -11005,6 +11071,12 @@ packages: utila: 0.4.0 dev: true + /dom-helpers/3.4.0: + resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==} + dependencies: + '@babel/runtime': 7.18.3 + dev: true + /dom-serializer/0.1.1: resolution: {integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==} dependencies: @@ -11986,6 +12058,10 @@ packages: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true + /fast-equals/2.0.4: + resolution: {integrity: sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==} + dev: true + /fast-glob/3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -13591,6 +13667,10 @@ packages: side-channel: 1.0.4 dev: true + /internmap/1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + dev: true + /interpret/1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} @@ -16017,7 +16097,6 @@ packages: /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: true /mime-types/2.1.18: resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} @@ -16037,7 +16116,6 @@ packages: engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: true /mime/1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -17501,6 +17579,10 @@ packages: resolution: {integrity: sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==} dev: true + /performance-now/2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + dev: true + /pg-connection-string/2.5.0: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} dev: false @@ -18186,6 +18268,10 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /postcss-value-parser/3.3.1: + resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + dev: true + /postcss-value-parser/4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} dev: true @@ -18626,6 +18712,12 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + /raf/3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + dependencies: + performance-now: 2.1.0 + dev: true + /randombytes/2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -18962,6 +19054,19 @@ packages: engines: {node: '>=0.10.0'} dev: true + /react-resize-detector/6.7.8_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-0FaEcUBAbn+pq3PT5a9hHRebUfuS1SRLGLpIw8LydU7zX429I6XJgKerKAMPsJH0qWAl6o5bVKNqFJqr6tGPYw==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 + react-dom: ^16.0.0 || ^17.0.0 + dependencies: + '@types/resize-observer-browser': 0.1.7 + lodash: 4.17.21 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + resize-observer-polyfill: 1.5.1 + dev: true + /react-router-config/5.1.1_oyuskl3t7voyrff2xstzuy4hqu: resolution: {integrity: sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==} peerDependencies: @@ -19060,6 +19165,20 @@ packages: react: 17.0.2 dev: true + /react-smooth/2.0.0_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-wK4dBBR6P21otowgMT9toZk+GngMplGS1O5gk+2WSiHEXIrQgDvhR5IIlT74Vtu//qpTcipkgo21dD7a7AUNxw==} + peerDependencies: + prop-types: ^15.6.0 + react: ^15.0.0 || ^16.0.0 || ^17.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + fast-equals: 2.0.4 + raf: 3.4.1 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-transition-group: 2.9.0_sfoxds7t5ydpegc3knd667wn6m + dev: true + /react-string-replace/1.0.0: resolution: {integrity: sha512-+iLsyE4AeSmnfctgswXOf1PmKRgns6wJ4LVb+8ADMU6mDK3jvBE11QzfMQf7CYtPUUiBCDjZ9ZppzXOIYrzCRg==} engines: {node: '>=0.12.0'} @@ -19112,6 +19231,20 @@ packages: react-dom: 17.0.2_react@17.0.2 dev: true + /react-transition-group/2.9.0_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==} + peerDependencies: + react: '>=15.0.0' + react-dom: '>=15.0.0' + dependencies: + dom-helpers: 3.4.0 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-lifecycles-compat: 3.0.4 + dev: true + /react/17.0.2: resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} engines: {node: '>=0.10.0'} @@ -19259,6 +19392,36 @@ packages: resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==} dev: true + /recharts-scale/0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + dependencies: + decimal.js-light: 2.5.1 + dev: true + + /recharts/2.1.10_sfoxds7t5ydpegc3knd667wn6m: + resolution: {integrity: sha512-me6c8m2Gs88X/nuM2gDSTDIhpSLNMbiTrlE4Cu53hjZNegT3g3xLlTrbYSAQuBCFWuWJAZXCmEuMr6AwizLyaA==} + engines: {node: '>=12'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 + react-dom: ^16.0.0 || ^17.0.0 + dependencies: + classnames: 2.3.1 + d3-interpolate: 2.0.1 + d3-scale: 3.3.0 + d3-shape: 2.1.0 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + react-is: 16.13.1 + react-resize-detector: 6.7.8_sfoxds7t5ydpegc3knd667wn6m + react-smooth: 2.0.0_sfoxds7t5ydpegc3knd667wn6m + recharts-scale: 0.4.5 + reduce-css-calc: 2.1.8 + transitivePeerDependencies: + - prop-types + dev: true + /rechoir/0.6.2: resolution: {integrity: sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=} engines: {node: '>= 0.10'} @@ -19348,6 +19511,13 @@ packages: - webpack dev: true + /reduce-css-calc/2.1.8: + resolution: {integrity: sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==} + dependencies: + css-unit-converter: 1.1.2 + postcss-value-parser: 3.3.1 + dev: true + /redux/4.1.2: resolution: {integrity: sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==} dependencies: @@ -19575,6 +19745,10 @@ packages: /requires-port/1.0.0: resolution: {integrity: sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=} + /resize-observer-polyfill/1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + dev: true + /resolve-alpn/1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}