diff --git a/.dumi/global.ts b/.dumi/global.ts index 53535b0aa..76104c1b6 100644 --- a/.dumi/global.ts +++ b/.dumi/global.ts @@ -1,3 +1,3 @@ import { webVitals } from './vitals'; -webVitals({ debug: true }); +webVitals({}); diff --git a/.dumirc.ts b/.dumirc.ts index 77c2e5b70..4513d3e4e 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -16,6 +16,9 @@ export default defineConfig({ }, extraBabelPresets: [require.resolve('@emotion/babel-preset-css-prop')], outputPath: 'site', + define: { + 'process.env.VERCEL_ANALYTICS_ID': process.env.VERCEL_ANALYTICS_ID, + }, analytics: { ga_v2: 'G-81Y5XPZY2E', }, diff --git a/package.json b/package.json index 3c50b07be..e2d904633 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "test:update": "NODE_OPTIONS=--max_old_space_size=4096 cross-env TZ=UTC jest --updateSnapshot", "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --cache --ext .js,.jsx,.ts,.tsx --fix --format=pretty", - "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", + "prettier": "prettier --write \"**/*.{js,jsx,ts,tsx,less,md,json}\"", "tsc": "tsc --noEmit", "tsc:clean": "tsc --build --clean", "locale:remove-useless": "ts-node scripts/remove-useless-locale.ts" diff --git a/packages/codemod/bin/cli.js b/packages/codemod/bin/cli.js index 71c000215..399d2bb91 100644 --- a/packages/codemod/bin/cli.js +++ b/packages/codemod/bin/cli.js @@ -8,7 +8,6 @@ const _ = require('lodash'); const chalk = require('chalk'); const isGitClean = require('is-git-clean'); const updateCheck = require('update-check'); -const findUp = require('find-up'); const semver = require('semver'); const { run: jscodeshift } = require('jscodeshift/src/Runner'); const execa = require('execa'); @@ -286,7 +285,7 @@ async function bootstrap() { console.log('[Prettier] format files running...'); try { const isDir = isDirectory.sync(dir); - const path = isDir ? '**/*.{js,jsx,tsx,ts,d.ts}' : dir; + const path = isDir ? path.join(dir, '**/*.{js,jsx,ts,tsx,d.ts}') : dir; const npxCommand = commandExistsSync('tnpx') ? 'tnpx' : 'npx'; await execa(npxCommand, ['prettier', '--write', path], { stdio: 'inherit' }); console.log('\n[Prettier] format files completed!\n'); diff --git a/packages/codemod/package.json b/packages/codemod/package.json index 309a6953e..5e493f473 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@oceanbase/codemod", - "version": "0.2.5", + "version": "0.2.6", "description": "Codemod for OceanBase Design upgrade", "keywords": [ "oceanbase", diff --git a/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.input.js b/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.input.js index 060137c00..ca9aea27f 100644 --- a/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.input.js +++ b/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.input.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Alert, Button, BackgroundTaskManager, BackgroundTaskManagerConstants, BasicLayout, Boundary, ConfigProvider, Login, PageContainer, theme } from '@alipay/ob-ui'; +import { Alert, Button, BackgroundTaskManager, BackgroundTaskManagerConstants, BasicLayout, Boundary, ConfigProvider, ContentWithQuestion, IconFont, Login, PageContainer, Ranger, theme, TreeSearch } from '@alipay/ob-ui'; import type { BackgroundTaskManagerRef, ITaskMgrPreset, ITaskMgrQueue, TaskMgrID } from '@alipay/ob-ui'; import type { BasicLayoutProps } from '@alipay/ob-ui/es/BasicLayout'; import type { LoginProps } from '@alipay/ob-ui/es/Login'; diff --git a/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.output.js b/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.output.js index fa517be15..807aaeb67 100644 --- a/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.output.js +++ b/packages/codemod/transforms/__testfixtures__/obui-to-oceanbase-design-and-ui/obui.output.js @@ -1,6 +1,6 @@ import React from 'react'; import { Alert, Button, ConfigProvider, theme } from '@oceanbase/design'; -import { BackgroundTaskManager, BackgroundTaskManagerConstants, BasicLayout, Boundary, Login, PageContainer } from '@oceanbase/ui'; +import { BackgroundTaskManager, BackgroundTaskManagerConstants, BasicLayout, Boundary, ContentWithQuestion, IconFont, Login, PageContainer, Ranger, TreeSearch } from '@oceanbase/ui'; import type { BackgroundTaskManagerRef, ITaskMgrPreset, ITaskMgrQueue, TaskMgrID } from '@oceanbase/ui'; import type { BasicLayoutProps } from '@oceanbase/ui/es/BasicLayout'; import type { LoginProps } from '@oceanbase/ui/es/Login'; diff --git a/packages/codemod/transforms/obui-to-oceanbase-design-and-ui.js b/packages/codemod/transforms/obui-to-oceanbase-design-and-ui.js index b2635b46a..751fd4764 100644 --- a/packages/codemod/transforms/obui-to-oceanbase-design-and-ui.js +++ b/packages/codemod/transforms/obui-to-oceanbase-design-and-ui.js @@ -19,14 +19,24 @@ module.exports = (file, api, options) => { 'ContentWithQuestion', 'Dialog', 'DocDialog', + 'FullscreenBox', + 'Highlight', 'GraphToolbar', + 'IconFont', 'Login', 'Lottie', 'NavMenu', + 'Password', + 'Ranger', + 'SideTip', + 'TaskGraph', + 'TreeSearch', + 'Welcome', ], types: [ 'PageContainerProps', 'ActionProps', + // BackgroundTaskManager 'BackgroundTaskManagerProps', 'BackgroundTaskManagerRef', 'ITaskMgrPreset', @@ -38,10 +48,24 @@ module.exports = (file, api, options) => { 'ContentWithQuestionProps', 'DialogProps', 'DocDialogProps', + 'FullscreenBoxProps', 'GraphToolbarProps', + 'HighlightProps', + 'IconFontProps', 'LoginProps', 'LottieProps', 'NavMenuProps', + 'PasswordProps', + // Ranger + 'RangerProps', + 'QuickPickerProps', + 'SideTipProps', + 'TaskGraphProps', + // TreeSearch + 'TreeSearchProps', + 'TreeSearchRef', + 'Node', + 'WelcomeProps', ], paths: ['/locale/', '/locale/'], }, diff --git a/packages/design/package.json b/packages/design/package.json index a612c2570..870e254b8 100644 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -1,6 +1,6 @@ { "name": "@oceanbase/design", - "version": "0.2.22", + "version": "0.2.23", "description": "The Design System of OceanBase", "keywords": [ "oceanbase", diff --git a/packages/design/src/table/demo/bordered.tsx b/packages/design/src/table/demo/bordered.tsx new file mode 100644 index 000000000..273747904 --- /dev/null +++ b/packages/design/src/table/demo/bordered.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Table } from '@oceanbase/design'; +import type { ColumnsType } from '@oceanbase/design/es/table'; + +interface DataType { + key: string; + name: string; + money: string; + address: string; +} + +const columns: ColumnsType = [ + { + title: 'Name', + dataIndex: 'name', + render: text => {text}, + }, + { + title: 'Cash Assets', + className: 'column-money', + dataIndex: 'money', + align: 'right', + }, + { + title: 'Address', + dataIndex: 'address', + }, +]; + +const data: DataType[] = [ + { + key: '1', + name: 'John Brown', + money: '¥300,000.00', + address: 'New York No. 1 Lake Park', + }, + { + key: '2', + name: 'Jim Green', + money: '¥1,256,000.00', + address: 'London No. 1 Lake Park', + }, + { + key: '3', + name: 'Joe Black', + money: '¥120,000.00', + address: 'Sydney No. 1 Lake Park', + }, +]; + +const App: React.FC = () => ( + 'Header'} + footer={() => 'Footer'} + /> +); + +export default App; diff --git a/packages/design/src/table/demo/edit-row.tsx b/packages/design/src/table/demo/edit-row.tsx new file mode 100644 index 000000000..ee09e7985 --- /dev/null +++ b/packages/design/src/table/demo/edit-row.tsx @@ -0,0 +1,182 @@ +import React, { useState } from 'react'; +import { Form, Input, InputNumber, Popconfirm, Table, Typography } from '@oceanbase/design'; + +interface Item { + key: string; + name: string; + age: number; + address: string; +} + +const originData: Item[] = []; +for (let i = 0; i < 100; i++) { + originData.push({ + key: i.toString(), + name: `Edward ${i}`, + age: 32, + address: `London Park no. ${i}`, + }); +} +interface EditableCellProps extends React.HTMLAttributes { + editing: boolean; + dataIndex: string; + title: any; + inputType: 'number' | 'text'; + record: Item; + index: number; + children: React.ReactNode; +} + +const EditableCell: React.FC = ({ + editing, + dataIndex, + title, + inputType, + record, + index, + children, + ...restProps +}) => { + const inputNode = inputType === 'number' ? : ; + + return ( + + ); +}; + +const App: React.FC = () => { + const [form] = Form.useForm(); + const [data, setData] = useState(originData); + const [editingKey, setEditingKey] = useState(''); + + const isEditing = (record: Item) => record.key === editingKey; + + const edit = (record: Partial & { key: React.Key }) => { + form.setFieldsValue({ name: '', age: '', address: '', ...record }); + setEditingKey(record.key); + }; + + const cancel = () => { + setEditingKey(''); + }; + + const save = async (key: React.Key) => { + try { + const row = (await form.validateFields()) as Item; + + const newData = [...data]; + const index = newData.findIndex(item => key === item.key); + if (index > -1) { + const item = newData[index]; + newData.splice(index, 1, { + ...item, + ...row, + }); + setData(newData); + setEditingKey(''); + } else { + newData.push(row); + setData(newData); + setEditingKey(''); + } + } catch (errInfo) { + console.log('Validate Failed:', errInfo); + } + }; + + const columns = [ + { + title: 'name', + dataIndex: 'name', + width: '25%', + editable: true, + }, + { + title: 'age', + dataIndex: 'age', + width: '15%', + editable: true, + }, + { + title: 'address', + dataIndex: 'address', + width: '40%', + editable: true, + }, + { + title: 'operation', + dataIndex: 'operation', + render: (_: any, record: Item) => { + const editable = isEditing(record); + return editable ? ( + + save(record.key)} style={{ marginRight: 8 }}> + Save + + + Cancel + + + ) : ( + edit(record)}> + Edit + + ); + }, + }, + ]; + + const mergedColumns = columns.map(col => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: (record: Item) => ({ + record, + inputType: col.dataIndex === 'age' ? 'number' : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditing(record), + }), + }; + }); + + return ( +
+
+ {editing ? ( + + {inputNode} + + ) : ( + children + )} +
+ + ); +}; + +export default App; diff --git a/packages/design/src/table/index.md b/packages/design/src/table/index.md index 8f5803a73..ff107580d 100644 --- a/packages/design/src/table/index.md +++ b/packages/design/src/table/index.md @@ -14,6 +14,8 @@ nav: + + @@ -24,6 +26,8 @@ nav: + + ## API diff --git a/packages/design/src/table/index.tsx b/packages/design/src/table/index.tsx index 4fd880b04..40b401042 100644 --- a/packages/design/src/table/index.tsx +++ b/packages/design/src/table/index.tsx @@ -68,7 +68,7 @@ function Table(props: TableProps) { const [currentSelectedRows, setCurrentSelectedRows] = useState([]); const [currentSelectedInfo, setCurrentSelectedInfo] = useState({}); - const newColumns = columns.map(item => { + const newColumns = columns?.map(item => { if (item.ellipsis) { return { ...item, diff --git a/packages/design/src/table/style/index.ts b/packages/design/src/table/style/index.ts index f2394e2b4..f6c763d60 100644 --- a/packages/design/src/table/style/index.ts +++ b/packages/design/src/table/style/index.ts @@ -171,11 +171,6 @@ export const genTableStyle: GenerateStyle = (token: TableToken): CSS }, }, }, - [`${componentCls}-pagination`]: { - [`&${antCls}-pagination`]: { - borderTop: 'none', - }, - }, }, // 分页器样式 @@ -202,6 +197,14 @@ export const genTableStyle: GenerateStyle = (token: TableToken): CSS }, }, }, + [`${componentCls}${componentCls}-bordered`]: { + [`&+${componentCls}-pagination`]: { + [`&${antCls}-pagination`]: { + // Remove pagination borderTop for bordered Table + borderTop: 'none', + }, + }, + }, }, // 批量操作栏中的弹出层样式 diff --git a/packages/ui/package.json b/packages/ui/package.json index 99359bd81..ab8e142f1 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@oceanbase/ui", - "version": "0.2.23", + "version": "0.2.24", "description": "The UI library based on OceanBase Design", "keywords": [ "oceanbase", diff --git a/packages/ui/src/BasicLayout/Header/index.tsx b/packages/ui/src/BasicLayout/Header.tsx similarity index 70% rename from packages/ui/src/BasicLayout/Header/index.tsx rename to packages/ui/src/BasicLayout/Header.tsx index 4910ed12a..5b7ef7c5d 100644 --- a/packages/ui/src/BasicLayout/Header/index.tsx +++ b/packages/ui/src/BasicLayout/Header.tsx @@ -4,28 +4,27 @@ import { CopyrightOutlined, ReadFilled, UserOutlined, + UserFilled, } from '@oceanbase/icons'; import { ConfigProvider, Button, Dropdown, Menu, Modal, Space, Tooltip } from '@oceanbase/design'; import classNames from 'classnames'; import moment from 'moment'; -import React, { useState,useContext } from 'react'; -import { OB_SITE_LINK } from '../../constant'; -import type { Locale } from '../../interface'; -import type { LocaleWrapperProps } from '../../locale/LocaleWrapper'; -import LocaleWrapper from '../../locale/LocaleWrapper'; -import { directTo, getPrefix } from '../../_util'; -import useNavigate from '../../_util/useNavigate'; -import zhCN from '../locale/zh-CN'; -// @ts-ignore -import logoImg from '../../assets/logo/oceanbase_logo.svg'; -// @ts-ignore -import logoImgDark from '../../assets/logo/oceanbase_logo_dark.svg'; +import React, { useState, useContext } from 'react'; +import { OB_SITE_LINK } from '../constant'; +import type { Locale } from '../interface'; +import type { LocaleWrapperProps } from '../locale/LocaleWrapper'; +import LocaleWrapper from '../locale/LocaleWrapper'; +import { directTo } from '../_util'; +import useNavigate from '../_util/useNavigate'; +import zhCN from './locale/zh-CN'; // @ts-ignore // 自定义 SVG 图标需要将其导入为图片,而不能是 ReactComponent,因为需要依赖 webpack 插件 // 虽然本地开发可以生效,但构建后的产物在上层项目中不会生效,导致 SVG 展示为空 -import UserSvg from '../../assets/user.svg'; -import LocaleDropdown from '../../LocaleDropdown'; -import useStyle from './style'; +import logoImg from '../assets/logo/oceanbase_logo.svg'; +// @ts-ignore +import logoImgDark from '../assets/logo/oceanbase_logo_dark.svg'; +import LocaleDropdown from '../LocaleDropdown'; +import useStyle from './style/Header'; export type OverlayFunc = () => React.ReactElement; @@ -48,6 +47,7 @@ export interface AppData { } export interface HeaderProps extends LocaleWrapperProps { + prefixCls?: string; // 单是否收起状态 菜单收起状态只展示:icon, 展开状态展示:icon 文字 showLabel?: boolean; style?: React.CSSProperties; @@ -78,9 +78,8 @@ export interface HeaderProps extends LocaleWrapperProps { langs?: Locale[]; } -const prefix = getPrefix('layout-header'); - const Header: React.FC = ({ + prefixCls: customizePrefixCls, showLabel = true, title, extra, @@ -102,13 +101,13 @@ const Header: React.FC = ({ langs, ...restProps }) => { - const { theme } = useContext(ConfigProvider.ConfigContext); + const { theme, getPrefixCls } = useContext(ConfigProvider.ConfigContext); + const prefixCls = getPrefixCls('pro-basic-layout-header', `${customizePrefixCls}-header`); + const { wrapSSR } = useStyle(prefixCls); + const navigate = useNavigate(); const [visible, setVisible] = useState(false); - const prefixCls = getPrefix('layout-header'); - const { wrapSSR } = useStyle(prefixCls); - // 是否为欢迎页 // 主要是为了处理与欢迎页搭配使用的场景 const isWelcome = pathname === welcomePath; @@ -143,27 +142,27 @@ const Header: React.FC = ({ return wrapSSR(
-
+
{ navigate?.('/'); }} - className={`${prefix}-logo`} + className={`${prefixCls}-logo`} /> - {title &&
{title}
} + {title &&
{title}
} {showLabel ? ( -
+
{extra} {showHelp && ( - + {locale.help} @@ -171,7 +170,7 @@ const Header: React.FC = ({ )} {showLocale && ( - + )} @@ -179,7 +178,7 @@ const Header: React.FC = ({ {userMenu ? (
) : ( -
- +
+ - + @@ -211,12 +210,12 @@ const Header: React.FC = ({ {docsPath && ( { directTo(docsPath); }} > - + @@ -224,36 +223,24 @@ const Header: React.FC = ({ )} {showLocale && ( - + )} {userMenu ? ( - + - - - - {username} + + + {username} ) : ( - - - - + + + {username} @@ -271,15 +258,15 @@ const Header: React.FC = ({ setVisible(false); }} > -
-
- -
-
+
+
+ +
+
{locale.version}: {appData.version}
{appData.releaseTime && ( -
+
{`${locale.releaseTime}: ${appData.releaseTime}`}
)} @@ -293,7 +280,7 @@ const Header: React.FC = ({
- + {locale.right} {moment().year()} {locale.company} diff --git a/packages/ui/src/BasicLayout/Header/style/Header.less b/packages/ui/src/BasicLayout/Header/style/Header.less deleted file mode 100644 index 2d9a28c15..000000000 --- a/packages/ui/src/BasicLayout/Header/style/Header.less +++ /dev/null @@ -1,134 +0,0 @@ -@import '../../variable.less'; - -@prefix: ob-layout-header; - -.@{prefix} { - position: fixed; - z-index: 10; - width: 100%; - height: 48px; - padding: 10px 24px; - line-height: 48px; - background-color: @colorBgLayout; - box-shadow: inset 0 -1px 0 0 #e2e8f3; - - .@{prefix}-content { - display: flex; - align-items: center; - justify-content: space-between; - max-width: @maxWidth; - height: 100%; - margin: 0 auto; - } - - .@{prefix}-logo { - height: 24px; - cursor: pointer; - } - - .@{prefix}-icon { - width: 52px; - height: 48px; - line-height: 48px; - text-align: center; - border-right: 1px solid #e2e8f3; - border-bottom: 1px solid #e2e8f3; - cursor: pointer; - img { - height: 32px; - margin-top: 8px; - } - } - - .@{prefix}-title { - /* 占据剩余的全部空间 */ - flex: 1; - margin: 0 16px; - } - - .@{prefix}-extra { - display: flex; - align-items: center; - justify-content: space-between; - .@{prefix}-extra-item { - display: inline-flex; - font-size: 12px; - cursor: pointer; - &:not(:last-child) { - margin-right: 24px; - } - .@{prefix}-extra-icon-wrapper { - width: 28px; - height: 28px; - line-height: 28px; - text-align: center; - border: 0.88px solid #ced4e1; - border-radius: 14px; - } - .@{prefix}-extra-user-wrapper { - height: 28px; - padding: 0 10px; - line-height: 28px; - border: 0.88px solid #ced4e1; - border-radius: 14px; - .@{prefix}-extra-user-icon { - margin-right: 6px; - margin-bottom: -2px; - } - } - } - } - .@{prefix}-extra-with-label { - .@{prefix}-extra-item { - &:not(:last-child) { - margin-right: 24px !important; - } - } - } -} - -.@{prefix}-welcome { - color: #fff; - background-color: transparent; - border-bottom: none; - - .@{prefix}-extra { - .@{prefixCls}-btn { - color: #fff; - background-color: rgba(255, 255, 255, 0.25); - border: 0.5px solid rgba(0, 0, 0, 0.1); - } - } -} - -.@{prefix}-about-wrapper { - .@{prefix}-about { - margin-top: 12px; - - .@{prefix}-logo { - height: 72px; - } - - .@{prefix}-release-info { - margin-top: 20px; - margin-bottom: 50px; - - // .@{prefix}-version { - // color: #000; - // } - - .@{prefix}-date { - // color: #000; - font-size: 12px; - font-family: Helvetica; - opacity: 0.45; - } - } - - .@{prefix}-copyright { - color: #000; - font-size: 12px; - opacity: 0.45; - } - } -} diff --git a/packages/ui/src/BasicLayout/index.less b/packages/ui/src/BasicLayout/index.less deleted file mode 100644 index 4a05b096b..000000000 --- a/packages/ui/src/BasicLayout/index.less +++ /dev/null @@ -1,396 +0,0 @@ -@import '../variable.less'; - -@prefix: ob-layout; - -// 弹出菜单样式 -.@{prefixCls}-menu-submenu-popup { - .@{prefixCls}-menu { - padding-left: 6px !important; - background-color: @colorBgLayout !important; - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu { - width: 100%; - background-color: transparent; - border: none; - margin-inline: 0; - &:not(:last-child) { - margin-bottom: 8px !important; - } - } - .@{prefixCls}-menu-item-active, - .@{prefixCls}-menu-submenu-active > .@{prefixCls}-menu-submenu-title { - color: @colorText !important; - font-weight: 600; - animation: activeGradientAnimation 0.1s; - .border-gradient(linear-gradient(to right, #E9EDF6, @colorBgLayout), linear-gradient(90deg, #C6CDD9, @colorBgLayout), 0.5px, solid, 8px 0 0 8px); - } - - .@{prefixCls}-menu-item-selected { - color: @colorPrimary !important; - font-weight: 600; - animation: selectedGradientAnimation 0.1s; - .border-gradient(linear-gradient(to right, #E5EEFF, #F4F8FF), linear-gradient(90deg, @colorPrimaryBorder, @colorBgLayout), 0.5px, solid, 8px 0 0 8px); - } - .@{prefixCls}-divider { - width: 60%; - min-width: 60%; - margin: 0 0 8px 16px !important; - } - } -} - -.@{prefix}-banner-wrapper { - position: fixed; - top: 0; - z-index: 20; - width: 100%; -} - -.@{prefix} { - height: 100%; - background-color: @colorBgLayout; - transition: all 0.1s; - - .@{prefix}-content-layout { - max-width: @maxWidth; - // 居中对齐 - margin: 0 auto; - .@{prefix}-sider { - position: fixed; - z-index: 10; - padding: 16px 0 16px 16px; - background-color: @colorBgLayout; - transition: all 0.3s; - - .@{prefix}-sider-border { - position: relative; - top: -16px; - width: 1px; - height: 100%; - background-color: #e2e8f3; - cursor: pointer; - opacity: 0; - // 左右两侧扩大热区 - &::after { - position: absolute; - top: 0; - right: -10px; - bottom: 0; - left: -10px; - content: ''; - } - &:hover { - opacity: 1; - // 仅在 hover 时增加过渡动画,避免展开/收起时 border 和 collapse 没有及时消失、影响整体效果 - transition: opacity 0.3s; - .@{prefix}-sider-collapse { - opacity: 1; - transition: opacity 0.3s; - } - } - - .@{prefix}-sider-collapse { - position: relative; - top: 245px; - right: 10px; - // 需要设置 z-index 以便叠加在分隔线上,否则点击的是分隔线的热区,无法触发 collapse 的点击事件 - z-index: 1; - width: 20px; - height: 42px; - line-height: 42px; - text-align: center; - background-color: #fff; - border: 1px solid @colorBorder; - border-radius: 10px; - cursor: pointer; - opacity: 0; - // 设置展开/收起按钮中的图标大小 - .@{iconPrefixCls} { - font-size: 12px; - } - } - } - - .@{prefix}-sider-wrapper { - display: flex; - height: calc(100vh - 48px); - // 菜单通用样式 - .@{prefixCls}-menu { - background-color: transparent; - border-right: none; - - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu { - width: 100%; - margin-top: 0; - margin-right: auto; - margin-left: auto; - color: @colorText; - background-color: transparent; - .@{iconPrefixCls} { - // 图标尺寸设为 18px,因为设计侧给到的图标内侧有间距,需要适当加大尺寸 - width: 18px; - height: 18px; - font-size: 18px; - } - } - .@{prefixCls}-menu-submenu > .@{prefixCls}-menu-submenu-title { - width: 100%; - margin-bottom: 4px !important; - margin-inline: 0; - margin-block: 0; - } - } - // 内嵌菜单样式 - .@{prefixCls}-menu-inline { - // 菜单项间距 - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu { - &:not(:last-child) { - margin-bottom: 16px; - // 子菜单展开时 margin-bottom 会变小,为了避免效果突兀,增加过渡效果 - transition: margin-bottom 0.2s; - } - } - .@{prefixCls}-menu-submenu-open { - &:not(:last-child) { - // 子菜单展开时,减小 margin-bottom - margin-bottom: 4px; - } - } - // 子菜单项间距 - .@{prefixCls}-menu-submenu { - .@{prefixCls}-menu-item:not(:last-child) { - margin-bottom: 4px; - } - } - // 菜单项缩进 - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu > .@{prefixCls}-menu-submenu-title { - padding-left: 16px !important; - } - // 子菜单项缩进 - .@{prefixCls}-menu-sub { - .@{prefixCls}-menu-item { - margin-left: 16px; - padding-left: 28px !important; - } - } - // 菜单项激活样式 - .@{prefixCls}-menu-item-active, - .@{prefixCls}-menu-submenu-active > .@{prefixCls}-menu-submenu-title { - color: @colorText !important; - font-weight: 600; - animation: activeGradientAnimation 0.1s; - .border-gradient(linear-gradient(to right, #E9EDF6, @colorBgLayout), linear-gradient(90deg, #C6CDD9, @colorBgLayout), 0.5px, solid, 8px 0 0 8px); - } - // 菜单项选中样式 - .@{prefixCls}-menu-item-selected { - color: @colorPrimary !important; - font-weight: 600; - animation: selectedGradientAnimation 0.5s; - .border-gradient(linear-gradient(to right, #E5EEFF, #F4F8FF), linear-gradient(90deg, @colorPrimaryBorder, @colorBgLayout), 0.5px, solid, 8px 0 0 8px); - &::after { - // 去掉菜单项的选中标记 - display: none; - } - } - .@{prefixCls}-menu-submenu-selected > .@{prefixCls}-menu-submenu-title { - color: @colorPrimary !important; - } - .@{prefixCls}-divider { - margin: 0 0 16px 0; - } - } - - // 垂直菜单样式 - .@{prefixCls}-menu-vertical { - overflow-x: hidden; - overflow-y: auto; - border-right: none; - - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu { - &:not(:last-child) { - margin-bottom: 4px; - } - } - - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu > .@{prefixCls}-menu-submenu-title { - width: 52px; - height: 52px; - padding: 0; - line-height: 52px; - text-align: center; - - .@{prefixCls}-menu-title-content { - display: inline-block; - width: 40px; - height: 40px; - line-height: 40px; - border-radius: 8px; - } - - .@{prefixCls}-menu-submenu-arrow { - display: none; - } - } - .@{prefixCls}-menu-item-active, - .@{prefixCls}-menu-submenu-active > .@{prefixCls}-menu-submenu-title { - .@{prefixCls}-menu-title-content { - background-color: #e9edf6; - border: 0.5px solid #c6cdd9; - } - } - .@{prefixCls}-menu-item-selected, - .@{prefixCls}-menu-submenu-selected > .@{prefixCls}-menu-submenu-title { - .@{prefixCls}-menu-title-content { - background-color: #e5eeff; - border: 0.5px solid @colorPrimaryBorder; - } - } - } - - .@{prefix}-sub-sider { - border-right: 1px solid #e2e8f3; - .@{prefixCls}-divider { - margin: 0 0 4px 0; - } - } - - .@{prefix}-sub-sider, - .@{prefix}-menu-collapsed { - width: 52px; - .@{prefixCls}-divider { - margin: 0 0 4px 0; - } - } - - .@{prefix}-sider-content { - display: flex; - flex: 1; - // 纵向排列 - flex-direction: column; - // 纵向两端对齐 - justify-content: space-between; - height: 100%; - - .@{prefix}-sider-header { - padding-top: 16px; - } - - .@{prefix}-menu-wrapper { - display: flex; - flex-direction: column; - // 保证垂直方向超出高度出现滚动 - flex-grow: 1; - justify-content: space-between; - overflow-x: hidden; - overflow-y: auto; - - .@{prefix}-menu { - margin-bottom: 32px; - } - } - } - } - } - - .@{prefix}-sider-collapsed { - padding-left: 0; - } - - .@{prefix}-sider-has-sub-sider { - padding: 0; - // 包含子侧边栏的菜单不支持收起,直接隐藏侧边栏 border 和 collapse - .@{prefix}-sider-border { - display: none; - .@{prefix}-sider-collapse { - display: none; - } - } - .@{prefixCls}-menu-inline { - padding-top: 16px; - .@{prefixCls}-menu-item, - .@{prefixCls}-menu-submenu { - &:not(:last-child) { - margin-bottom: 16px !important; - } - } - .@{prefixCls}-divider { - width: 60%; - min-width: 60%; - margin: -8px 0 8px 16px !important; - } - } - .@{prefixCls}-menu-vertical { - padding-top: 10px; - } - .@{prefix}-sider-content { - padding-left: 6px; - } - } - - .@{prefix}-content { - background-color: @colorBgLayout; - transition: all 0.3s; - } - - // .@{prefix}-content-0 { - // .@{prefixCls}-pro-page-container { - // .@{prefixCls}-pro-footer-bar { - // width: 100%; - // } - // } - // } - - // .@{prefix}-content-52 { - // .@{prefixCls}-pro-page-container { - // .@{prefixCls}-pro-footer-bar { - // width: calc(100% - 52px - 24px); - // } - // } - // } - - // .@{prefix}-content-104 { - // .@{prefixCls}-pro-page-container { - // .@{prefixCls}-pro-footer-bar { - // width: calc(100% - 104px - 24px); - // } - // } - // } - - // .@{prefix}-content-192 { - // .@{prefixCls}-pro-page-container { - // .@{prefixCls}-pro-footer-bar { - // width: calc(100% - 192px - 24px); - // } - // } - // } - } -} - -@media (min-width: @maxWidth) { - .@{prefixCls}-pro-footer-bar { - right: calc((100% - @maxWidth) / 2 + 24px); - width: calc(@maxWidth - 192px - 24px - 24px); - max-width: calc(@maxWidth - 192px - 24px - 24px); - } -} - -// 对于 Alert 类型的 banner,自动增加上间距 -// 对于其他类型的 banner,需要业务侧自行设置上间距 -.@{prefix}-with-banner { - margin-top: 38px; -} - -@media (min-width: @maxWidth) { - .@{prefix} { - .@{prefix}-content-layout { - .@{prefix}-sider { - padding-left: 0; - } - } - } -} diff --git a/packages/ui/src/BasicLayout/index.tsx b/packages/ui/src/BasicLayout/index.tsx index 3a6a4ddd5..d2670b39f 100644 --- a/packages/ui/src/BasicLayout/index.tsx +++ b/packages/ui/src/BasicLayout/index.tsx @@ -11,7 +11,7 @@ import { pathToRegexp } from 'path-to-regexp'; import React, { useEffect, useState, useContext } from 'react'; import type { LocaleWrapperProps } from '../locale/LocaleWrapper'; import LocaleWrapper from '../locale/LocaleWrapper'; -import { getPrefix, isEnglish, urlToList } from '../_util'; +import { isEnglish, urlToList } from '../_util'; import useNavigate from '../_util/useNavigate'; import type { HeaderProps } from './Header'; import Header from './Header'; @@ -73,8 +73,6 @@ export interface BasicLayoutProps extends LocaleWrapperProps { style?: React.CSSProperties; } - const prefix = getPrefix('layout'); - const BasicLayout: React.FC = ({ children, location: { pathname } = {}, @@ -94,16 +92,9 @@ const BasicLayout: React.FC = ({ prefixCls: customizePrefixCls, ...restProps }) => { - - // const prefixCls = getPrefix('layout'); - // const { wrapSSR, hashId } = useStyle(prefixCls); - // const basicLayoutCls = classNames(className); - const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('pro-basic-layout', customizePrefixCls); - const { wrapSSR, hashId } = useStyle(prefixCls); - const basicLayoutCls = classNames(prefixCls, className); -debugger + const { wrapSSR } = useStyle(prefixCls); const navigate = useNavigate(); // 侧边栏导航是否收起 const [collapsed, setCollapsed] = useState(defaultCollapsed); @@ -309,14 +300,19 @@ debugger <> {banner &&
{banner}
}
; - export const genHeaderStyle: GenerateStyle = ( token: HeaderToken ): CSSObject => { - const { componentCls } = token; + const { antCls, componentCls } = token; return { [`${componentCls}`]: { @@ -37,7 +36,7 @@ export const genHeaderStyle: GenerateStyle = ( [`${componentCls}-icon`]: { width: 52, height: 48, - lineHeight: 48, + lineHeight: '48px', textAlign: "center", borderRight: "1px solid #e2e8f3", borderBottom: "1px solid #e2e8f3", @@ -67,7 +66,7 @@ export const genHeaderStyle: GenerateStyle = ( [`${componentCls}-extra-icon-wrapper`]: { width: 28, height: 28, - lineHeight: 28, + lineHeight: '28px', textAlign: "center", border: "0.88px solid #ced4e1", borderRadius: 14, @@ -75,12 +74,11 @@ export const genHeaderStyle: GenerateStyle = ( [`${componentCls}-extra-user-wrapper`]: { height: 28, padding: "0 10px", - lineHeight: 28, + lineHeight: '28px', border: "0.88px solid #ced4e1", borderRadius: 14, [`${componentCls}-extra-user-icon`]: { marginRight: 6, - marginBottom: -2, }, }, }, @@ -101,7 +99,7 @@ export const genHeaderStyle: GenerateStyle = ( backgroundColor: "transparent", borderBottom: "none", [`${componentCls}-extra`]: { - "@{prefixCls}-btn": { + [`${antCls}-btn`]: { color: "#fff", backgroundColor: "rgba(255, 255, 255, 0.25)", border: "0.5px solid rgba(0, 0, 0, 0.1)", @@ -109,7 +107,6 @@ export const genHeaderStyle: GenerateStyle = ( }, }, - [`${componentCls}-about-wrapper`]: { [`${componentCls}-about`]: { marginTop: 12, diff --git a/packages/ui/src/BasicLayout/style/index.ts b/packages/ui/src/BasicLayout/style/index.ts index 297632157..9f4e7ed96 100644 --- a/packages/ui/src/BasicLayout/style/index.ts +++ b/packages/ui/src/BasicLayout/style/index.ts @@ -16,82 +16,24 @@ export const genBasicLayoutStyle: GenerateStyle = ( colorBorder, colorPrimaryBorder, colorPrimary, + motionDurationSlow, } = token; - debugger const maxWidth = '8192px' - return { - // 弹出菜单样式 - [`${antCls}-menu-submenu-popup`]: { - [`${antCls}-menu`]: { - paddingLeft: '6px !important', - backgroundColor: `${colorBgLayout} !important`, - [`${antCls}-menu-item`]: { - width: '100%', - backgroundColor: 'transparent', - border: 'none', - marginInline: 0, - '&:not(:last-child):': { - marginBottom: '8px !important', - } - }, - [`${antCls}-menu-submenu`]: { - width: '100%', - backgroundColor: 'transparent', - border: 'none', - marginInline: 0, - '&:not(:last-child):': { - marginBottom: '8px !important', - } - }, - [`${antCls}-menu-item-active`]: { - color: 'colorText !important', - fontWeight: 600, - animation: 'activeGradientAnimation 0.1s', - // .border-gradient(linear-gradient(to right, #E9EDF6, colorBgLayout), linear-gradient(90deg, #C6CDD9, colorBgLayout), 0.5px, solid, 8px 0 0 8px), - backgroundImage: `linear-gradient(to right, #E9EDF6, ${colorBgLayout}), linear-gradient(90deg, #C6CDD9, ${colorBgLayout})`, - backgroundClip: 'padding-box,border-box', - backgroundOrigin: 'padding-box,border-box', - border: '.5px solid transparent', - borderRadius: '8px 0 0 8px', - transition: 'border-width .3s' - + const siderWidthList = [0, 52, 52 * 2, 192, 208]; - - }, - [`${antCls}-menu-submenu-active > & ${antCls}-menu-submenu-title`]: { - color: 'colorText !important', - fontWeight: 600, - animation: 'activeGradientAnimation 0.1s', - // .border-gradient(linear-gradient(to right, #E9EDF6, colorBgLayout), linear-gradient(90deg, #C6CDD9, colorBgLayout), 0.5px, solid, 8px 0 0 8px), - backgroundImage: `linear-gradient(to right, #E9EDF6, ${colorBgLayout}), linear-gradient(90deg, #C6CDD9, ${colorBgLayout})`, - backgroundClip: 'padding-box,border-box', - backgroundOrigin: 'padding-box,border-box', - border: '.5px solid transparent', - borderRadius: '8px 0 0 8px', - transition: 'border-width .3s' - }, - - [`${antCls}-menu-item-selected`]: { - color: `${colorPrimary} !important`, - fontWeight: 600, - animation: 'selectedGradientAnimation 0.1s', - // .border-gradient(linear-gradient(to right, #E5EEFF, #F4F8FF), linear-gradient(90deg, @colorPrimaryBorder, colorBgLayout), 0.5px, solid, 8px 0 0 8px), - backgroundImage: `linear-gradient(to right,#E5EEFF,#F4F8FF),linear-gradient(90deg,${colorPrimaryBorder},${colorBgLayout})`, - backgroundClip: 'padding-box,border-box', - backgroundOrigin: 'padding-box,border-box', - border: '.5px solid transparent', - borderRadius: '8px 0 0 8px', - transition: 'border-width .3s' - }, - [`${antCls}-divider`]: { - width: "'60%'", - minWidth: '60%', - margin: '0 0 8px 16px !important' - }, + const footerBarStyle: CSSObject = {}; + siderWidthList.forEach(width => { + footerBarStyle[`${componentCls}${componentCls}-sider-${width}`] = { + [`${proComponentsCls}-footer-bar`]: { + // footer bar width adapt to sider width of BasicLayout + width: width === 0 ? '100%' : `calc(100% - ${width}px - 24px)`, + transition: `width ${motionDurationSlow}`, }, - }, + }; + }); + return { [`${componentCls}-banner-wrapper`]: { position: 'fixed', top: 0, @@ -99,11 +41,24 @@ export const genBasicLayoutStyle: GenerateStyle = ( width: '100%' }, + // 对于 Alert 类型的 banner,自动增加上间距 + // 对于其他类型的 banner,需要业务侧自行设置上间距 + [`${componentCls}-with-banner`]: { + marginTop: '38px', + }, + + ...footerBarStyle, [`${componentCls}`]: { height: '100%', backgroundColor: colorBgLayout, transition: 'all 0.1s', + // Set style of PageContainer in BasicLayout + [`${proComponentsCls}-page-container`]: { + // 48px is the height of BasicLayout header + minHeight: 'calc(100vh - 48px)', + }, + [`${componentCls}-content-layout`]: { maxWidth: maxWidth, // 居中对齐 @@ -419,6 +374,13 @@ export const genBasicLayoutStyle: GenerateStyle = ( }, [`@media (min-width: ${maxWidth})`]: { + [`${componentCls}`]: { + [`${componentCls}-content-layout`]: { + [`${componentCls}-sider`]: { + paddingLeft: 0 + } + } + }, [`${proComponentsCls}-footer-bar`]: { right: `calc((100% - ${maxWidth}) / 2 + 24px)`, width: `calc(${maxWidth} - 192px - 24px - 24px)`, @@ -426,21 +388,76 @@ export const genBasicLayoutStyle: GenerateStyle = ( } }, - // 对于 Alert 类型的 banner,自动增加上间距 - // 对于其他类型的 banner,需要业务侧自行设置上间距 - [`${componentCls}-with-banner`]: { - marginTop: '38px', - }, - - [`@media (min-width: ${maxWidth})`]: { - [`${componentCls}`]: { - [`${componentCls}-content-layout`]: { - [`${componentCls}-sider`]: { - paddingLeft: 0 + // 弹出菜单样式 + [`${antCls}-menu-submenu-popup`]: { + [`${antCls}-menu`]: { + paddingLeft: '6px !important', + backgroundColor: `${colorBgLayout} !important`, + [`${antCls}-menu-item`]: { + width: '100%', + backgroundColor: 'transparent', + border: 'none', + marginInline: 0, + '&:not(:last-child):': { + marginBottom: '8px !important', } - } - } - } + }, + [`${antCls}-menu-submenu`]: { + width: '100%', + backgroundColor: 'transparent', + border: 'none', + marginInline: 0, + '&:not(:last-child):': { + marginBottom: '8px !important', + } + }, + [`${antCls}-menu-item-active`]: { + color: 'colorText !important', + fontWeight: 600, + animation: 'activeGradientAnimation 0.1s', + // .border-gradient(linear-gradient(to right, #E9EDF6, colorBgLayout), linear-gradient(90deg, #C6CDD9, colorBgLayout), 0.5px, solid, 8px 0 0 8px), + backgroundImage: `linear-gradient(to right, #E9EDF6, ${colorBgLayout}), linear-gradient(90deg, #C6CDD9, ${colorBgLayout})`, + backgroundClip: 'padding-box,border-box', + backgroundOrigin: 'padding-box,border-box', + border: '.5px solid transparent', + borderRadius: '8px 0 0 8px', + transition: 'border-width .3s' + + + + }, + [`${antCls}-menu-submenu-active > & ${antCls}-menu-submenu-title`]: { + color: 'colorText !important', + fontWeight: 600, + animation: 'activeGradientAnimation 0.1s', + // .border-gradient(linear-gradient(to right, #E9EDF6, colorBgLayout), linear-gradient(90deg, #C6CDD9, colorBgLayout), 0.5px, solid, 8px 0 0 8px), + backgroundImage: `linear-gradient(to right, #E9EDF6, ${colorBgLayout}), linear-gradient(90deg, #C6CDD9, ${colorBgLayout})`, + backgroundClip: 'padding-box,border-box', + backgroundOrigin: 'padding-box,border-box', + border: '.5px solid transparent', + borderRadius: '8px 0 0 8px', + transition: 'border-width .3s' + }, + + [`${antCls}-menu-item-selected`]: { + color: `${colorPrimary} !important`, + fontWeight: 600, + animation: 'selectedGradientAnimation 0.1s', + // .border-gradient(linear-gradient(to right, #E5EEFF, #F4F8FF), linear-gradient(90deg, @colorPrimaryBorder, colorBgLayout), 0.5px, solid, 8px 0 0 8px), + backgroundImage: `linear-gradient(to right,#E5EEFF,#F4F8FF),linear-gradient(90deg,${colorPrimaryBorder},${colorBgLayout})`, + backgroundClip: 'padding-box,border-box', + backgroundOrigin: 'padding-box,border-box', + border: '.5px solid transparent', + borderRadius: '8px 0 0 8px', + transition: 'border-width .3s' + }, + [`${antCls}-divider`]: { + width: "'60%'", + minWidth: '60%', + margin: '0 0 8px 16px !important' + }, + }, + }, }; } diff --git a/packages/ui/src/PageContainer/demo/empty.tsx b/packages/ui/src/PageContainer/demo/empty.tsx index 2e5d64d71..5ee82a0b6 100644 --- a/packages/ui/src/PageContainer/demo/empty.tsx +++ b/packages/ui/src/PageContainer/demo/empty.tsx @@ -11,7 +11,6 @@ export default () => { header={{ title: '总览', }} - footer={[111]} > void; @@ -92,7 +92,7 @@ const RangeSelect = ({ selects, onChange, value, customable, locale = {}, size } ); }; -export default (props: IProps) => { +export default (props: QuickPickerProps) => { const { type = 'select', name, diff --git a/packages/ui/src/Ranger/Ranger.tsx b/packages/ui/src/Ranger/Ranger.tsx index 1eed762c9..990780166 100644 --- a/packages/ui/src/Ranger/Ranger.tsx +++ b/packages/ui/src/Ranger/Ranger.tsx @@ -31,7 +31,7 @@ export type RangeDateValue = { range: RangeValue; }; -interface IProps extends Omit { +interface RangerProps extends Omit { // 数据相关 selects?: RangeOption[]; defaultQuickValue?: string; @@ -49,7 +49,7 @@ interface IProps extends Omit { +const Ranger = (props: RangerProps) => { const { selects = [NEAR_1_MINUTES, NEAR_30_MINUTES, NEAR_1_HOURS], value, diff --git a/packages/ui/src/Ranger/index.ts b/packages/ui/src/Ranger/index.ts index 3097599de..41183816d 100644 --- a/packages/ui/src/Ranger/index.ts +++ b/packages/ui/src/Ranger/index.ts @@ -27,6 +27,9 @@ import { import QuickPicker from './QuickPicker'; import InternalRanger from './Ranger'; +export * from './QuickPicker'; +export * from './Ranger'; + const Ranger = InternalRanger; // 内置 ranges diff --git a/packages/ui/src/TreeSearch/index.tsx b/packages/ui/src/TreeSearch/index.tsx index 24f292a5d..516e906b0 100644 --- a/packages/ui/src/TreeSearch/index.tsx +++ b/packages/ui/src/TreeSearch/index.tsx @@ -32,7 +32,7 @@ export interface TreeSearchRef { invertSelect: () => void; } -interface IProps { +interface TreeSearchProps { treeData: Node[]; titleRender?: (nodeData: DataNode) => React.ReactNode; checkable?: boolean; @@ -50,7 +50,7 @@ interface IProps { searchStyle?: {}; } -export default forwardRef( +export default forwardRef( ( { treeData = [], diff --git a/packages/ui/src/assets/user.svg b/packages/ui/src/assets/user.svg deleted file mode 100644 index db1fdbf87..000000000 --- a/packages/ui/src/assets/user.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - 编组 2 - - - - - - - - - - - - - \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1cad26d8..4c9e9e961 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8271,6 +8271,7 @@ packages: /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + requiresBuild: true dev: true /binaryextensions@2.3.0: @@ -13787,6 +13788,7 @@ packages: /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + requiresBuild: true dependencies: binary-extensions: 2.2.0 dev: true @@ -20933,6 +20935,7 @@ packages: /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + requiresBuild: true dependencies: picomatch: 2.3.1 dev: true