From 03a953652516458b7002bebbcddb60c50e52801a Mon Sep 17 00:00:00 2001 From: Jong Eun Lee Date: Tue, 11 Feb 2025 22:18:26 +0800 Subject: [PATCH] feat: introduce NEO style Tabs and RadioGroup --- react/src/components/BAICard.tsx | 16 ++ react/src/components/BAIRadioGroup.tsx | 65 +++++++ react/src/components/BAITable.tsx | 5 +- react/src/components/BAITabs.tsx | 32 ++++ .../SessionStatusTag.tsx | 43 +++-- .../EndpointList.tsx} | 167 +++++++++--------- .../src/components/SessionLauncherPreview.tsx | 5 + .../src/components/SessionOwnerSetterCard.tsx | 1 + .../SummaryItemInvitation.tsx | 2 +- react/src/pages/ComputeSessionListPage.tsx | 14 +- react/src/pages/EndpointDetailPage.tsx | 2 +- react/src/pages/ImportAndRunPage.tsx | 1 + react/src/pages/ServingPage.tsx | 8 +- react/src/pages/UserCredentialsPage.tsx | 1 + 14 files changed, 250 insertions(+), 112 deletions(-) create mode 100644 react/src/components/BAIRadioGroup.tsx create mode 100644 react/src/components/BAITabs.tsx rename react/src/{pages/EndpointListPage.tsx => components/EndpointList.tsx} (83%) diff --git a/react/src/components/BAICard.tsx b/react/src/components/BAICard.tsx index fb2b484714..bcb7075d9a 100644 --- a/react/src/components/BAICard.tsx +++ b/react/src/components/BAICard.tsx @@ -6,6 +6,7 @@ import React, { ReactNode } from 'react'; export interface BAICardProps extends CardProps { status?: 'success' | 'error' | 'warning' | 'default'; extraButtonTitle?: string | ReactNode; + showDivider?: boolean; onClickExtraButton?: () => void; ref?: React.LegacyRef | undefined; } @@ -16,6 +17,8 @@ const BAICard: React.FC = ({ onClickExtraButton, extra, style, + styles, + showDivider, ...cardProps }) => { const { token } = theme.useToken(); @@ -50,6 +53,19 @@ const BAICard: React.FC = ({ ? token.colorSuccess : style?.borderColor, // default })} + styles={_.merge( + showDivider + ? {} + : { + header: { + borderBottom: 'none', + }, + body: { + paddingTop: token.marginXS, + }, + }, + styles, + )} extra={_extra} {...cardProps} /> diff --git a/react/src/components/BAIRadioGroup.tsx b/react/src/components/BAIRadioGroup.tsx new file mode 100644 index 0000000000..1dc9037021 --- /dev/null +++ b/react/src/components/BAIRadioGroup.tsx @@ -0,0 +1,65 @@ +import { ConfigProvider, Radio, theme } from 'antd'; +import type { RadioGroupProps } from 'antd'; +import { createStyles } from 'antd-style'; +import classNames from 'classnames'; +import React from 'react'; + +interface BAIRadioGroupProps extends RadioGroupProps {} + +const useStyle = createStyles(({ css, token }) => ({ + baiRadioGroup: css` + // border version + .ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked)::before, + .ant-radio-button-wrapper:hover::before { + background-color: transparent; + } + .ant-radio-button-wrapper-checked:hover::before, + .ant-radio-button-wrapper-checked::before { + background-color: transparent; + /* background-color: ${`rgba(${parseInt(token.colorPrimary.slice(1, 3), 16)}, ${parseInt(token.colorPrimary.slice(3, 5), 16)}, ${parseInt(token.colorPrimary.slice(5, 7), 16)}, 0.30)`}; */ + } + + // original design version + /* .ant-radio-button-wrapper-checked::before, + .ant-radio-button-wrapper::before { + background-color: ${token.colorBorder}; + } + .ant-radio-button-wrapper-checked:hover::before, + .ant-radio-button-wrapper:hover::before { + background-color: ${token.colorBorder}; + } + + .ant-radio-button-wrapper-checked { + border-color: transparent !important; + } */ + `, +})); +const BAIRadioGroup: React.FC = ({ options, ...props }) => { + const { styles } = useStyle(); + const { token } = theme.useToken(); + const colorPrimaryWithAlpha = `rgba(${parseInt(token.colorPrimary.slice(1, 3), 16)}, ${parseInt(token.colorPrimary.slice(3, 5), 16)}, ${parseInt(token.colorPrimary.slice(5, 7), 16)}, 0.15)`; + const colorPrimaryWithLessAlpha = `rgba(${parseInt(token.colorPrimary.slice(1, 3), 16)}, ${parseInt(token.colorPrimary.slice(3, 5), 16)}, ${parseInt(token.colorPrimary.slice(5, 7), 16)}, 0.3)`; + return ( + + + + ); +}; + +export default BAIRadioGroup; diff --git a/react/src/components/BAITable.tsx b/react/src/components/BAITable.tsx index 960210bdb9..5e8f6804b8 100644 --- a/react/src/components/BAITable.tsx +++ b/react/src/components/BAITable.tsx @@ -1,6 +1,6 @@ import { useThemeMode } from '../hooks/useThemeMode'; import { useDebounce } from 'ahooks'; -import { ConfigProvider, GetProps, Table } from 'antd'; +import { ConfigProvider, GetProps, Table, theme } from 'antd'; import { createStyles } from 'antd-style'; import { ColumnsType, ColumnType } from 'antd/es/table'; import { TableProps } from 'antd/lib'; @@ -124,6 +124,7 @@ const BAITable = ({ ...tableProps }: BAITableProps) => { const { styles } = useStyles(); + const { token } = theme.useToken(); const { isDarkMode } = useThemeMode(); const [resizedColumnWidths, setResizedColumnWidths] = useState< Record @@ -163,6 +164,8 @@ const BAITable = ({ !isDarkMode && neoStyle ? { headerBg: '#E3E3E3', + headerSplitColor: token.colorTextQuaternary, + // headerSplitColor: token.colorTextQuaternary } : undefined, }, diff --git a/react/src/components/BAITabs.tsx b/react/src/components/BAITabs.tsx new file mode 100644 index 0000000000..591be1fbd8 --- /dev/null +++ b/react/src/components/BAITabs.tsx @@ -0,0 +1,32 @@ +import { Tabs, TabsProps } from 'antd'; +import { createStyles } from 'antd-style'; +import classNames from 'classnames'; +import React from 'react'; + +const useStyles = createStyles(({ token, css }) => ({ + baiTabs: css` + .ant-tabs-nav::before { + border-color: ${token.colorPrimary}; + } + .ant-tabs-tab:not(.ant-tabs-tab-active) { + border-bottom-color: ${token.colorPrimary}; + } + .ant-tabs-tab.ant-tabs-tab-active { + border-color: ${token.colorPrimary}; + } + `, +})); + +interface BAITabsProps extends TabsProps {} +const BAITabs: React.FC = ({ className, ...props }) => { + const { styles } = useStyles(); + return ( + + ); +}; + +export default BAITabs; diff --git a/react/src/components/ComputeSessionNodeItems/SessionStatusTag.tsx b/react/src/components/ComputeSessionNodeItems/SessionStatusTag.tsx index 9c6aed1673..e8c3f1ca74 100644 --- a/react/src/components/ComputeSessionNodeItems/SessionStatusTag.tsx +++ b/react/src/components/ComputeSessionNodeItems/SessionStatusTag.tsx @@ -4,7 +4,7 @@ import { SessionStatusTagFragment$key, } from './__generated__/SessionStatusTagFragment.graphql'; import { LoadingOutlined } from '@ant-design/icons'; -import { Tag, theme } from 'antd'; +import { Tag, theme, Tooltip } from 'antd'; import graphql from 'babel-plugin-relay/macro'; import _ from 'lodash'; import React from 'react'; @@ -69,24 +69,32 @@ const SessionStatusTag: React.FC = ({ return session ? ( _.isEmpty(session.status_info) || !showInfo ? ( - : undefined} - // Comment out to match the legacy tag style temporarily - // style={{ - // borderRadius: 11, - // paddingLeft: token.paddingSM, - // paddingRight: token.paddingSM, - // }} - > - {session.status || ' '} - + + : undefined} + // Comment out to match the legacy tag style temporarily + style={{ + borderRadius: 11, + paddingLeft: token.paddingSM, + paddingRight: token.paddingSM, + }} + > + {session.status || ' '} + + ) : ( = ({ margin: 0, marginLeft: -1, borderStyle: 'dashed', + paddingRight: token.paddingSM, + borderTopRightRadius: 11, + borderBottomRightRadius: 11, color: session.status_info && _.get(statusInfoTagColor, session.status_info) diff --git a/react/src/pages/EndpointListPage.tsx b/react/src/components/EndpointList.tsx similarity index 83% rename from react/src/pages/EndpointListPage.tsx rename to react/src/components/EndpointList.tsx index 8b8e9ffb40..1ca88da0aa 100644 --- a/react/src/pages/EndpointListPage.tsx +++ b/react/src/components/EndpointList.tsx @@ -1,8 +1,3 @@ -import BAIPropertyFilter from '../components/BAIPropertyFilter'; -import EndpointOwnerInfo from '../components/EndpointOwnerInfo'; -import EndpointStatusTag from '../components/EndpointStatusTag'; -import Flex from '../components/Flex'; -import TableColumnsSettingModal from '../components/TableColumnsSettingModal'; import { baiSignedRequestWithPromise, filterEmptyItem, @@ -20,24 +15,31 @@ import { useBAIPaginationOptionState } from '../hooks/reactPaginationQueryOption import { useTanMutation } from '../hooks/reactQueryAlias'; import { useCurrentProjectValue } from '../hooks/useCurrentProject'; import { useHiddenColumnKeysSetting } from '../hooks/useHiddenColumnKeysSetting'; +import BAIPropertyFilter from './BAIPropertyFilter'; +import BAIRadioGroup from './BAIRadioGroup'; +import BAITable from './BAITable'; +import EndpointOwnerInfo from './EndpointOwnerInfo'; +import EndpointStatusTag from './EndpointStatusTag'; +import Flex from './Flex'; +import TableColumnsSettingModal from './TableColumnsSettingModal'; import { - EndpointListPageQuery, - EndpointListPageQuery$data, -} from './__generated__/EndpointListPageQuery.graphql'; + EndpointListQuery, + EndpointListQuery$data, +} from './__generated__/EndpointListQuery.graphql'; import { CheckOutlined, CloseOutlined, DeleteOutlined, - LoadingOutlined, ReloadOutlined, SettingOutlined, } from '@ant-design/icons'; import { useRafInterval, useToggle } from 'ahooks'; -import { Button, Table, Typography, theme, Radio, App } from 'antd'; +import { Button, Typography, theme, App, Tooltip } from 'antd'; import { ColumnType } from 'antd/lib/table'; import graphql from 'babel-plugin-relay/macro'; import { default as dayjs } from 'dayjs'; import _ from 'lodash'; +import { InfoIcon } from 'lucide-react'; import React, { PropsWithChildren, useState, @@ -51,9 +53,7 @@ import { StringParam, useQueryParam } from 'use-query-params'; export type Endpoint = NonNullable< NonNullable< - NonNullable< - NonNullable['endpoint_list'] - >['items'] + NonNullable['endpoint_list']>['items'] >[0] >; export const isDestroyingStatus = ( @@ -68,7 +68,10 @@ export const isDestroyingStatus = ( type LifecycleStage = 'created&destroying' | 'destroyed'; -const EndpointListPage: React.FC = ({ children }) => { +interface EndpointListProps extends PropsWithChildren { + style?: React.CSSProperties; +} +const EndpointList: React.FC = ({ style, children }) => { const { t } = useTranslation(); const { token } = theme.useToken(); const { message, modal } = App.useApp(); @@ -286,12 +289,15 @@ const EndpointListPage: React.FC = ({ children }) => { }, { title: ( - + {t('modelService.RoutingsCount')} -
+ + + + {/*
({t('modelService.Active/Total')}) - + */}
), // dataIndex: "active_route_count", @@ -326,9 +332,9 @@ const EndpointListPage: React.FC = ({ children }) => { }, 7000); const { endpoint_list: modelServiceList } = - useLazyLoadQuery( + useLazyLoadQuery( graphql` - query EndpointListPageQuery( + query EndpointListQuery( $offset: Int! $limit: Int! $projectID: UUID @@ -419,18 +425,13 @@ const EndpointListPage: React.FC = ({ children }) => { }); return ( - + = ({ children }) => { > {baiClient.supports('endpoint-lifecycle-stage-filter') && ( <> - { startPageChangeTransition(() => { @@ -532,70 +533,66 @@ const EndpointListPage: React.FC = ({ children }) => { - , - }} - scroll={{ x: 'max-content' }} - rowKey={'endpoint_id'} - dataSource={filterNonNullItems(modelServiceList?.items)} - columns={_.filter( - columns, - (column) => !_.includes(hiddenColumnKeys, _.toString(column?.key)), - )} - sortDirections={['descend', 'ascend', 'descend']} - pagination={{ - pageSize: tablePaginationOption.pageSize, - current: tablePaginationOption.current, - pageSizeOptions: ['10', '20', '50'], - total: modelServiceList?.total_count || 0, - showSizeChanger: true, - style: { marginRight: token.marginXS }, - }} - onChange={({ pageSize, current }, filter, sorter) => { - startPageChangeTransition(() => { - if (_.isNumber(current) && _.isNumber(pageSize)) { - setTablePaginationOption({ - current, - pageSize, - }); - } - setOrder(transformSorterToOrderString(sorter)); - }); - }} - /> - -