diff --git a/src/pages/_components/collapse-panel/index.tsx b/src/pages/_components/collapse-panel/index.tsx deleted file mode 100644 index cb4f4705..00000000 --- a/src/pages/_components/collapse-panel/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { IconFont } from '@gpustack/core-ui'; -import { Collapse, CollapseProps } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -const CollapseInner = styled(Collapse)` - .ant-collapse-header { - display: flex; - align-items: center; - margin-bottom: 10px !important; - padding-inline: 5px !important; - padding-block: 8px !important; - border-radius: var(--border-radius-base) !important; - font-size: 14px !important; - font-weight: 600 !important; - &:hover { - background-color: var(--ant-color-fill-tertiary) !important; - } - } - - .ant-collapse-body { - padding-inline: 0 !important; - padding-block: 0 !important; - } - .ant-collapse-header-text { - display: flex; - align-items: center; - height: 24px; - } -`; - -const CollapsePanel: React.FC<{ - items: CollapseProps['items']; - activeKey: string | string[]; - accordion?: boolean; - defaultActiveKey?: string | string[]; - onChange?: (key: string | string[]) => void; - styles?: Record; -}> = ({ items, activeKey, accordion, defaultActiveKey, onChange, styles }) => { - return ( - ( - - )} - items={items} - > - ); -}; - -export default CollapsePanel; diff --git a/src/pages/_components/column-settings.tsx b/src/pages/_components/column-settings.tsx deleted file mode 100644 index 4984a9e0..00000000 --- a/src/pages/_components/column-settings.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { - readColumnSettings, - writeColumnSettings -} from '@/utils/localstore/index'; -import { SettingOutlined } from '@ant-design/icons'; -import { OverlayScroller } from '@gpustack/core-ui'; -import { useIntl } from '@umijs/max'; -import { Button, Checkbox, Col, Popover, Row, Tooltip } from 'antd'; -import React, { useEffect } from 'react'; -import styled from 'styled-components'; - -const Container = styled.div` - padding: 8px 12px; - padding-right: 4px; - .title { - font-weight: 500; - margin-bottom: 12px; - } - .btn-wrapper { - display: flex; - justify-content: space-between; - align-items: center; - padding-top: 12px; - } - .buttons { - display: flex; - gap: 8px; - justify-content: flex-end; - } -`; - -const Title = styled.div` - font-weight: 500; - margin-bottom: 8px; - margin-top: 4px; -`; - -const LabelWrapper = styled.span` - color: var(--ant-color-text-secondary); - > span { - display: flex; - align-items: center; - gap: 4px; - color: var(--ant-color-text-secondary); - .sub-title { - display: none; - } - } -`; - -const ColumnSettings: React.FC<{ - width?: number; - fixedColumns?: string[]; - tableName: string; - contentHeight: number; - columns: { - title: React.ReactNode; - dataIndex?: string; - children?: { title: React.ReactNode; dataIndex?: string }[]; - }[]; - selectedColumns?: string[]; - defaultSelectedColumns?: string[]; - grouped?: boolean; - onReset?: () => void; - onChange?: (selectedColumns: string[]) => void; -}> = (props) => { - const intl = useIntl(); - const { - tableName, - contentHeight, - width = 420, - columns, - selectedColumns, - defaultSelectedColumns, - grouped, - onReset, - onChange, - fixedColumns - } = props; - const [open, setOpen] = React.useState(false); - const [innerSelectedColumns, setInnerSelectedColumns] = React.useState< - string[] - >(defaultSelectedColumns || []); - - React.useEffect(() => { - if (open) { - setInnerSelectedColumns(selectedColumns ?? defaultSelectedColumns ?? []); - } - }, [open, selectedColumns, defaultSelectedColumns]); - - const handleToggle = () => { - setOpen(!open); - }; - - const handleDraftChange = (columns: string[]) => { - setInnerSelectedColumns(columns); - }; - - const handleSelectAll = () => { - if (grouped) { - const allCols: string[] = []; - columns.forEach((group) => { - group.children?.forEach((col) => { - if (col.dataIndex) { - allCols.push(col.dataIndex); - } - }); - }); - handleDraftChange(allCols); - } else { - const allCols = columns - .map((col) => col.dataIndex) - .filter((dataIndex): dataIndex is string => Boolean(dataIndex)); - handleDraftChange(allCols); - } - }; - - const handleConfirm = () => { - setOpen(false); - writeColumnSettings(tableName, innerSelectedColumns); - onChange?.(innerSelectedColumns); - }; - - const handleReset = () => { - const resetCols = defaultSelectedColumns ?? []; - writeColumnSettings(tableName, resetCols); - onChange?.(resetCols); - setInnerSelectedColumns(resetCols); - onReset?.(); - }; - - const handleOpenChange = (isOpen: boolean) => { - setOpen(isOpen); - }; - - useEffect(() => { - const initColumns = async () => { - const stored = await readColumnSettings(tableName); - if (stored && stored.length > 0) { - setInnerSelectedColumns(stored); - onChange?.(stored); - } else { - setInnerSelectedColumns(defaultSelectedColumns || []); - } - }; - initColumns(); - }, []); - - const contentRender = () => { - return ( - - {!grouped && ( -
- {intl.formatMessage({ id: 'benchmark.table.columnSettings' })} -
- )} - - - <> - {grouped ? ( - columns.map((row, index) => ( -
- {row.title} - - {row.children?.map((col) => ( - - - {col.title} - - - ))} - -
- )) - ) : ( - - {columns.map((col) => ( - - - {col.title} - - - ))} - - )} - -
-
- -
- -
- - -
-
-
- ); - }; - - return ( - - - - - - ); -}; - -export default ColumnSettings; diff --git a/src/pages/_components/column-wrapper/index.tsx b/src/pages/_components/column-wrapper/index.tsx deleted file mode 100644 index b9862451..00000000 --- a/src/pages/_components/column-wrapper/index.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useOverlayScroller } from '@gpustack/core-ui'; -import React, { useCallback } from 'react'; -import styled from 'styled-components'; -import { WrapperContext } from './use-wrapper-context'; - -const Wrapper = styled.div` - flex: 1; - display: flex; - flex-direction: column; - justify-content: space-between; - position: relative; - width: 100%; -`; - -const ContentWrapper = styled.div` - flex: 1; - position: relative; - overflow-y: auto; -`; - -const Footer = styled.div` - padding-block: 0; - background-color: var(--ant-color-bg-elevated); -`; - -interface ColumnWrapperProps { - children: React.ReactNode; - footer?: React.ReactNode; - maxHeight?: string | number; - paddingBottom?: number; - styles?: { - wrapper?: React.CSSProperties; - container?: React.CSSProperties; - }; -} - -const ColumnWrapper: React.FC = ({ - children, - footer, - maxHeight, - styles = {} -}) => { - const scroller = React.useRef(null); - const footerRef = React.useRef(null); - const contentRef = React.useRef(null); - const { - initialize, - instance, - scrollEventElement, - scrollToBottom, - scrollToTarget, - getScrollElementScrollableHeight - } = useOverlayScroller({ - options: { - scrollbars: { - autoHide: 'move' - } - } - }); - - React.useEffect(() => { - if (scroller.current) { - initialize(scroller.current); - } - }, []); - - const setContentPaddingBottom = useCallback((padding: number) => { - contentRef.current!.style.paddingBottom = `${padding}px`; - }, []); - - return ( - - - -
{children}
-
- {footer &&
{footer}
} -
-
- ); -}; - -export default ColumnWrapper; diff --git a/src/pages/_components/column-wrapper/use-wrapper-context.ts b/src/pages/_components/column-wrapper/use-wrapper-context.ts deleted file mode 100644 index 7b403beb..00000000 --- a/src/pages/_components/column-wrapper/use-wrapper-context.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createContext, useContext } from 'react'; - -interface WrapperContextProps { - osInstance?: any; - scroller?: any; - scrollEventElement?: any; - scrollToBottom?: () => void; - scrollToTop?: () => void; - getScrollElementScrollableHeight?: () => { - scrollHeight: number; - scrollTop: number; - }; - scrollToTarget?: (target: any, offset?: number) => void; - getScrollElement?: () => HTMLElement | null; - setSScrollContentPaddingBottom?: (padding: number) => void; -} - -export const WrapperContext = createContext( - {} as WrapperContextProps -); - -export const useWrapperContext = () => { - const context = useContext(WrapperContext); - if (!context) { - throw new Error('useWrapperContext must be used within a WrapperProvider'); - } - return context; -}; diff --git a/src/pages/_components/filter-form/index.tsx b/src/pages/_components/filter-form/index.tsx deleted file mode 100644 index ff9c108f..00000000 --- a/src/pages/_components/filter-form/index.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { CloseOutlined } from '@ant-design/icons'; -import { OverlayScroller } from '@gpustack/core-ui'; -import { useIntl } from '@umijs/max'; -import { Button, Form } from 'antd'; -import classNames from 'classnames'; -import React, { forwardRef, useImperativeHandle } from 'react'; -import filterFormCss from '../styles/filter-form.less'; - -const FilterForm: React.FC< - React.PropsWithChildren & { - ref?: any; - width?: number; - contentHeight?: number | string; - initialValues?: any; - hasFilters?: boolean; - open?: boolean; - onClear?: () => void; - onClose?: () => void; - onValuesChange?: (ChangeValues: any, allValues: any) => void; - styles?: { - container?: React.CSSProperties; - wrapper?: React.CSSProperties; - }; - } -> = forwardRef( - ( - { - children, - open, - width = 300, - contentHeight = 400, - initialValues = {}, - styles, - onClose, - onClear, - onValuesChange - }, - ref - ) => { - const intl = useIntl(); - const [form] = Form.useForm(); - - const handleOnReset = () => { - form.resetFields(Object.keys(initialValues)); - form.resetFields(); - onValuesChange?.({}, form.getFieldsValue()); - }; - - const handleOnClose = () => { - onClose?.(); - }; - - const handleOnValuesChange = (changedValues: any, allValues: any) => { - onValuesChange?.(changedValues, allValues); - }; - - const handleOnClear = () => { - handleOnReset(); - }; - - const filtersCount = Object.values(form.getFieldsValue()).filter( - (value) => value !== undefined && value !== null && value !== '' - ).length; - - const renderFooter = () => { - return ( -
- -
- -
-
- ); - }; - - useImperativeHandle(ref, () => ({ - form, - reset: handleOnReset, - getValues: () => form.getFieldsValue(), - setValues: (values: any) => form.setFieldsValue(values) - })); - - return ( -
-
-
- - {intl.formatMessage({ id: 'common.filter.label' })} - - -
- -
- {children} -
-
-
-
- ); - } -); - -export default FilterForm; diff --git a/src/pages/_components/form-drawer.tsx b/src/pages/_components/form-drawer.tsx deleted file mode 100644 index 168f5cad..00000000 --- a/src/pages/_components/form-drawer.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui'; -import type { DrawerProps } from 'antd'; -import { Tag } from 'antd'; -import React from 'react'; - -const ModalFooterStyle = { - padding: '16px 24px 8px', - display: 'flex', - justifyContent: 'flex-end' -}; - -type AddModalProps = { - title: React.ReactNode; - open: boolean; - onCancel?: () => void; - children?: React.ReactNode; - onSubmit?: () => void; - width?: number | string; - footer?: React.ReactNode; - subTitle?: React.ReactNode; - push?: DrawerProps['push']; -}; -const FormDrawer: React.FC = ({ - title, - open, - onCancel, - onSubmit, - children, - width = 600, - subTitle, - footer, - push -}) => { - return ( - - {title} - {subTitle && ( - - {subTitle} - - )} - - } - open={open} - onClose={onCancel} - destroyOnHidden={true} - closeIcon={false} - mask={{ - closable: false - }} - keyboard={false} - push={push} - styles={{ - wrapper: { width } - }} - footer={false} - > - - ) - } - > - {children} - - - ); -}; - -export default FormDrawer; diff --git a/src/pages/_components/form-overlay-view.module.less b/src/pages/_components/form-overlay-view.module.less deleted file mode 100644 index f3a228da..00000000 --- a/src/pages/_components/form-overlay-view.module.less +++ /dev/null @@ -1,81 +0,0 @@ -.mask { - position: fixed; - inset: 0; - z-index: 1004; - height: 100%; - background-color: var(--ant-color-bg-mask); - opacity: 0; - transition: opacity var(--ant-motion-duration-slow) - var(--ant-motion-ease-in-out); -} - -.maskOpen { - opacity: 1; -} - -.overlay { - position: fixed; - top: 0; - right: 0; - bottom: auto; - left: auto; - z-index: 1005; - display: flex; - flex-direction: column; - height: 100vh; - overflow: hidden; - background: var(--ant-color-bg-elevated); - border-radius: var(--ant-border-radius-lg) 0 0 var(--ant-border-radius-lg); - box-shadow: - -6px 0 16px 0 rgb(0 0 0 / 8%), - -3px 0 6px -4px rgb(0 0 0 / 12%), - -9px 0 28px 8px rgb(0 0 0 / 5%); - transform: translateX(100%); - transition: transform var(--ant-motion-duration-slow) - var(--ant-motion-ease-in-out); -} - -.overlayOpen { - transform: translateX(0); -} - -.header { - display: flex; - align-items: center; - gap: 12px; - min-height: 56px; - padding: var(--ant-padding) var(--ant-padding-lg); - border-bottom: 1px solid var(--ant-color-split); - color: var(--ant-color-text); - font-size: 14px; - font-weight: 600; - line-height: 24px; -} - -.title { - display: flex; - min-width: 0; - align-items: center; -} - -.subTitle { - margin-left: 8px; - border-color: var(--ant-color-border-secondary); - border-radius: 4px; - color: var(--ant-color-text-secondary); - font-size: 12px; - font-weight: 400; -} - -.content { - padding-block: 16px; - flex: 1; - height: calc(100vh - 57px); - overflow-y: auto; -} - -.footer { - display: flex; - justify-content: flex-end; - padding: 16px 24px 8px; -} diff --git a/src/pages/_components/form-overlay-view.tsx b/src/pages/_components/form-overlay-view.tsx deleted file mode 100644 index b15f5190..00000000 --- a/src/pages/_components/form-overlay-view.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { ColumnWrapper, IconFont } from '@gpustack/core-ui'; -import { Button, Tag } from 'antd'; -import classNames from 'classnames'; -import React from 'react'; -import { createPortal } from 'react-dom'; -import styles from './form-overlay-view.module.less'; - -type FormOverlayViewProps = { - title: React.ReactNode; - open: boolean; - onCancel?: () => void; - children?: React.ReactNode; - onSubmit?: () => void; - footer?: React.ReactNode; - subTitle?: React.ReactNode; - width?: number | string; - className?: string; - style?: React.CSSProperties; - maskClosable?: boolean; - getContainer?: () => HTMLElement | null | undefined; -}; - -const FormOverlayView: React.FC = ({ - title, - open, - onCancel, - onSubmit, - children, - subTitle, - footer, - width = 600, - className, - style, - maskClosable = false, - getContainer -}) => { - const [container, setContainer] = React.useState(null); - const [mounted, setMounted] = React.useState(false); - const [active, setActive] = React.useState(false); - - React.useEffect(() => { - if (open) { - setContainer(getContainer?.() ?? null); - setMounted(true); - return; - } - - setActive(false); - }, [getContainer, open]); - - React.useEffect(() => { - if (!mounted || !container) { - return; - } - - const id = requestAnimationFrame(() => setActive(true)); - return () => cancelAnimationFrame(id); - }, [mounted, container]); - - const handleTransitionEnd = (e: React.TransitionEvent) => { - if (e.target !== e.currentTarget || e.propertyName !== 'transform') { - return; - } - if (!open && !active) { - setMounted(false); - setContainer(null); - } - }; - - if (!mounted || !container) { - return null; - } - - return createPortal( - <> -
-
-
-
- - {children} - -
- , - container - ); -}; - -export default FormOverlayView; diff --git a/src/pages/_components/grafana-icon.tsx b/src/pages/_components/grafana-icon.tsx deleted file mode 100644 index 815bca95..00000000 --- a/src/pages/_components/grafana-icon.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; - -interface GrafanaIconProps { - style?: React.SVGProps; -} -const GrafanaIcon: React.FC = ({ style }) => { - return ( - - - - - - - - - - ); -}; - -export default GrafanaIcon; diff --git a/src/pages/_components/identicon.tsx b/src/pages/_components/identicon.tsx deleted file mode 100644 index 44f0b410..00000000 --- a/src/pages/_components/identicon.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { update } from 'jdenticon'; -import React, { useEffect, useRef } from 'react'; - -interface IdenticonProps { - value: string; - size: number; -} - -const Identicon: React.FC = ({ - value = 'test', - size = 28 -}: IdenticonProps) => { - const icon = useRef(null); - - useEffect(() => { - update(icon.current, value); - }, [value]); - - return ( - <> - - - ); -}; - -export default Identicon; diff --git a/src/pages/_components/password-validate/index.tsx b/src/pages/_components/password-validate/index.tsx deleted file mode 100644 index edbbcb64..00000000 --- a/src/pages/_components/password-validate/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { - digitReg, - lowercaseReg, - specialCharacterReg, - uppercaseReg -} from '@/config'; -import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons'; -import { useIntl } from '@umijs/max'; -import { Space } from 'antd'; - -const PasswordValidate: React.FC<{ value: string }> = ({ value = '' }) => { - const intl = useIntl(); - - const renderIcon = ({ valid, text }: { valid: boolean; text: string }) => { - return ( - <> - {valid ? ( - - ) : ( - - )} - - {text} - - - ); - }; - return ( - - - {renderIcon({ - valid: uppercaseReg.test(value), - text: intl.formatMessage({ id: 'users.password.uppcase' }) - })} - - - {renderIcon({ - valid: lowercaseReg.test(value), - text: intl.formatMessage({ id: 'users.password.lowercase' }) - })} - - - - {renderIcon({ - valid: digitReg.test(value), - text: intl.formatMessage({ id: 'users.password.number' }) - })} - - - {renderIcon({ - valid: value.length >= 6 && value.length <= 12, - text: intl.formatMessage({ id: 'users.password.length' }) - })} - - - {renderIcon({ - valid: specialCharacterReg.test(value), - text: intl.formatMessage({ id: 'users.password.special' }) - })} - - - ); -}; - -export default PasswordValidate; diff --git a/src/pages/_components/pill-button-group/index.tsx b/src/pages/_components/pill-button-group/index.tsx deleted file mode 100644 index 2b3f67ec..00000000 --- a/src/pages/_components/pill-button-group/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { AutoTooltip } from '@gpustack/core-ui'; -import React from 'react'; -import pillButtonCss from './styles.less'; -type Option = { - label: string; - value: string | number | null; - icon?: React.ReactNode; -}; - -type Props = { - value?: string | number; - onChange?: (value: string | number | undefined | null) => void; - options: Option[]; - disabled?: boolean; - variant?: 'filled' | 'outlined' | 'solid'; -}; - -const PillButtonGroup: React.FC = ({ - value, - onChange, - options, - disabled, - variant = 'outlined' -}) => { - return ( -
- {options.map((item) => { - const active = value === item.value; - - return ( - - { - if (item.value !== value) { - onChange?.(item.value); - } - if (item.value === value) { - onChange?.(undefined); - } - }} - > - {item.icon} - {item.label} - - - ); - })} -
- ); -}; - -export default PillButtonGroup; diff --git a/src/pages/_components/pill-button-group/styles.less b/src/pages/_components/pill-button-group/styles.less deleted file mode 100644 index d7bc8943..00000000 --- a/src/pages/_components/pill-button-group/styles.less +++ /dev/null @@ -1,31 +0,0 @@ -.pill-item { - height: 28px; - line-height: 28px; - display: flex; - align-items: center; - gap: 8px; - justify-content: center; - border-radius: var(--ant-border-radius); - background-color: var(--ant-color-fill-tertiary); - color: var(--ant-color-text-secondary); - padding: 0 8px; - cursor: pointer; - transition: all 0.3s; - - &.active { - background-color: var(--ant-color-fill); - color: var(--ant-color-text); - font-weight: 500; - } - - &:not(.active):hover { - background-color: var(--ant-color-fill-secondary); - } -} - -.wrapper { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 8px; - align-items: center; -} diff --git a/src/pages/_components/scroll-spy-tabs/index.tsx b/src/pages/_components/scroll-spy-tabs/index.tsx deleted file mode 100644 index 881e96cb..00000000 --- a/src/pages/_components/scroll-spy-tabs/index.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { SegmentLine } from '@gpustack/core-ui'; -import { useMemoizedFn } from 'ahooks'; -import _ from 'lodash'; -import React, { forwardRef, useImperativeHandle } from 'react'; -import styled from 'styled-components'; -import useFieldScroll from './use-field-scroll'; - -const SegmentedHeader = styled.div<{ $top?: number }>` - position: sticky; - top: ${(props) => props.$top || 0}px; - z-index: 10; - margin-bottom: 16px; - border-bottom: 1px solid var(--ant-color-split); - background-color: var(--ant-color-bg-elevated); -`; - -/** - * ScrollSpyTabs component - * defaultTarget: The default active target tab - * segmentedTop: { // Mostlty, It's always a constants. - * top: number; // The top offset for the sticky header - * offsetTop: number; // The offset top for the target - * } - * getScrollElementScrollableHeight: function to get the scrollable height of the scroll element - * segmentOptions.field: The target data-field={segmentOptions.field} to scroll to - * activeKey: The current active keys for collapsible sections - * setActiveKey: The function to set active keys for collapsible sections - */ -interface ScrollSpyTabsProps { - ref?: any; - children?: React.ReactNode; - defaultTarget?: string; - segmentedTop: { - top: number; - offsetTop: number; - }; - activeKey: string[]; - setActiveKey: (keys: string[]) => void; - getScrollElementScrollableHeight?: () => { - scrollHeight: number; - scrollTop: number; - }; - segmentOptions: { - label: string; - value: string; - icon?: React.ReactNode; - field: string; - }[]; -} - -const ScrollSpyTabs: React.FC = forwardRef( - ( - { - getScrollElementScrollableHeight, - segmentedTop, - segmentOptions, - defaultTarget, - activeKey, - setActiveKey, - children - }, - ref - ) => { - const [target, setTarget] = React.useState( - defaultTarget || segmentOptions[0]?.value || '' - ); - - const { scrollToSegment, holderHeight } = useFieldScroll({ - activeKey, - setActiveKey, - segmentOptions, - segmentedTop: segmentedTop, - getScrollElementScrollableHeight: getScrollElementScrollableHeight - }); - - const throttleScrollToSegment = useMemoizedFn( - _.throttle( - async (val: string) => { - setTarget(val); - scrollToSegment(val, { offsetTop: segmentedTop.offsetTop }); - }, - 500, - { trailing: true } - ) - ); - - const handleTargetChange = async (val: any) => { - throttleScrollToSegment(val); - }; - - useImperativeHandle(ref, () => ({ - handleTargetChange - })); - - return ( -
- {segmentOptions.length > 0 && ( - - - - )} - {children} -
-
- ); - } -); - -export default ScrollSpyTabs; diff --git a/src/pages/_components/scroll-spy-tabs/use-field-scroll.ts b/src/pages/_components/scroll-spy-tabs/use-field-scroll.ts deleted file mode 100644 index 3e820930..00000000 --- a/src/pages/_components/scroll-spy-tabs/use-field-scroll.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { useMemoizedFn } from 'ahooks'; -import { useCallback, useRef, useState } from 'react'; - -interface ScrollOptions { - wait?: number; - behavior?: 'smooth' | 'auto'; - block?: 'start' | 'end' | 'center'; - offsetTop?: number; -} - -export default function useScrollAfterExpand({ - activeKey, - setActiveKey, - segmentOptions, - defaultWait = 300, - segmentedTop = { top: 0, offsetTop: 96 }, - getScrollElementScrollableHeight -}: { - activeKey: string[]; - setActiveKey: (keys: string[]) => void; - segmentOptions: { value: string; field: string }[]; - getScrollElementScrollableHeight?: () => { - scrollHeight: number; - scrollTop: number; - }; - defaultWait?: number; - segmentedTop: { - top: number; // The top offset for the sticky header - offsetTop: number; // The offset top for the target - }; -}) { - const [holderHeight, setHolderHeight] = useState(0); - const boxHeightRef = useRef(0); - - const scrollToElement = useCallback( - ( - el: HTMLElement, - { behavior = 'smooth', offsetTop = 0 }: ScrollOptions = {} - ) => { - // find the nearest scrollable parent - const scrollParent = (() => { - let node: HTMLElement | null = el; - while (node) { - const { overflowY } = getComputedStyle(node); - if (overflowY === 'auto' || overflowY === 'scroll') return node; - node = node.parentElement; - } - return document.scrollingElement || document.documentElement; - })(); - - const parentRect = scrollParent.getBoundingClientRect(); - const elRect = el.getBoundingClientRect(); - const top = - elRect.top - parentRect.top + scrollParent.scrollTop - offsetTop; - - scrollParent.scrollTo({ top, behavior }); - }, - [] - ); - - /** - * due to the scrollheight changes after expanding the segment and including the holder height. - * - */ - const scrollToSegment = useMemoizedFn( - async (val: string, options?: ScrollOptions) => { - if (!activeKey.includes(val)) { - setActiveKey([...activeKey, val]); - await new Promise((r) => { - setTimeout(r, options?.wait ?? defaultWait); - }); - } - - const current = segmentOptions.find((item) => item.value === val); - if (!current?.field) return; - - await new Promise(requestAnimationFrame); - - const el: HTMLElement | null = document.querySelector( - `[data-field="${current.field}"]` - ) as HTMLElement | null; - - const targetRectTop = el?.getBoundingClientRect().top || 0; - - const scroller = getScrollElementScrollableHeight?.() || { - scrollHeight: 0, - scrollTop: 0 - }; - - // remaining scroll height - const remainingScrollHeight = scroller.scrollHeight - scroller.scrollTop; - - // total distance from the top of the scroller to the target element - const offsetDistance = - targetRectTop - segmentedTop.offsetTop - segmentedTop.top; - - let boxHeight = 0; - - // verify boxHeight is correct, if setting the boxHeight causes the element to be hidden, use the previous boxHeight - if (offsetDistance <= 0) { - boxHeight = boxHeightRef.current; - } else { - boxHeight = - offsetDistance - remainingScrollHeight + boxHeightRef.current; - } - - // update boxHeightRef - boxHeightRef.current = boxHeight; - - setHolderHeight(boxHeight); - await new Promise(requestAnimationFrame); - - if (el) scrollToElement(el, options); - } - ); - - return { scrollToSegment, holderHeight }; -} diff --git a/src/pages/_components/scroll-spy-tabs/use-finish-failed.ts b/src/pages/_components/scroll-spy-tabs/use-finish-failed.ts deleted file mode 100644 index 16364163..00000000 --- a/src/pages/_components/scroll-spy-tabs/use-finish-failed.ts +++ /dev/null @@ -1,44 +0,0 @@ -interface FinishFailedOptions { - requiredFields: { - [tab: string]: { - sort: number; - fields: string[]; - }; - }; - onTargetChange: (key: string) => void; - updateActiveKey: (key: string[]) => void; -} - -const useFinishFailed = (options: FinishFailedOptions) => { - const { requiredFields, onTargetChange, updateActiveKey } = options; - const handleOnFinishFailed = (errorInfo: any) => { - const { errorFields } = errorInfo; - - console.log('Finish failed:', errorInfo); - - if (errorFields && errorFields.length > 0) { - const collapseKeys: { sort: number; key: string }[] = []; - const names = errorFields.map((item: any) => item.name[0]); - Object.entries(requiredFields).forEach(([tab, { fields, sort }]) => { - const hasError = fields.some((field: string) => names.includes(field)); - if (hasError) { - collapseKeys.push({ sort, key: tab }); - } - }); - if (collapseKeys.length > 0) { - const keys = collapseKeys - .sort((a, b) => a.sort - b.sort) - .map((item) => item.key); - - updateActiveKey(keys); - onTargetChange(collapseKeys[0].key); - } - } - }; - - return { - handleOnFinishFailed - }; -}; - -export default useFinishFailed; diff --git a/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts b/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts deleted file mode 100644 index 51e5f831..00000000 --- a/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useState } from 'react'; - -const useScrollActiveChange = (options: { - initalActiveKeys: string[]; - initialCollapseKeys?: string[]; -}) => { - const [activeKey, setActiveKey] = useState( - options.initalActiveKeys - ); - const [collapseKeys, setCollapseKeys] = useState( - options.initialCollapseKeys || options.initalActiveKeys - ); - - const handleActiveChange = (key: string[]) => { - setActiveKey(key); - setCollapseKeys(key); - }; - - const handleOnCollapseChange = (keys: string | string[]) => { - const keysArray = Array.isArray(keys) ? keys : [keys]; - setActiveKey(keysArray); - setCollapseKeys(keysArray); - }; - - const updateActiveKey = (keys: string[]) => { - setActiveKey((prev: string[]) => [...new Set([...prev, ...keys])]); - setCollapseKeys((prev) => [...new Set([...prev, ...keys])]); - }; - - return { - activeKey, - collapseKeys, - setCollapseKeys, - handleActiveChange, - handleOnCollapseChange, - updateActiveKey - }; -}; - -export default useScrollActiveChange; diff --git a/src/pages/_components/small-close-button.tsx b/src/pages/_components/small-close-button.tsx deleted file mode 100644 index d074112f..00000000 --- a/src/pages/_components/small-close-button.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { CloseCircleFilled } from '@ant-design/icons'; -import { Button } from 'antd'; -import styled from 'styled-components'; - -const StyledButton = styled(Button)` - background-color: transparent !important; - padding: 0; - .anticon { - color: var(--ant-color-text-quaternary); - font-size: 12px !important; - transition: color 0.3s ease; - } - &:hover { - .anticon { - color: var(--ant-color-text-tertiary); - } - } -`; - -interface SmallCloseButtonProps { - onClick?: () => void; -} - -const SmallCloseButton: React.FC = ({ onClick }) => { - return ( - } - shape="circle" - type="text" - onClick={onClick} - size="small" - > - ); -}; - -export default SmallCloseButton; diff --git a/src/pages/_components/styles/filter-form.less b/src/pages/_components/styles/filter-form.less deleted file mode 100644 index 9bbdb3e6..00000000 --- a/src/pages/_components/styles/filter-form.less +++ /dev/null @@ -1,41 +0,0 @@ -.wrapper { - flex-shrink: 0; - overflow: hidden; - width: 0; - transition: all var(--ant-motion-duration-slow) var(--ant-motion-ease-in-out); - border-color: var(--ant-color-split); - - &.show { - width: 232px; - border-right: 1px solid var(--ant-color-split); - } -} - -.container { - padding: 0 16px; - - .title { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; - margin-top: 8px; - padding-inline: 8px; - font-weight: 500; - color: var(--ant-color-text-tertiary); - } - - .btn-wrapper { - display: flex; - justify-content: space-between; - align-items: center; - padding-top: 24px; - padding-right: 8px; - } - - .buttons { - display: flex; - gap: 8px; - justify-content: flex-end; - } -} diff --git a/src/pages/_components/transfer.tsx b/src/pages/_components/transfer.tsx deleted file mode 100644 index 9a663c2c..00000000 --- a/src/pages/_components/transfer.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { MoreOutlined } from '@ant-design/icons'; -import { useIntl } from '@umijs/max'; -import { Transfer, TransferProps } from 'antd'; -import styled from 'styled-components'; - -type TransferKey = string | number | bigint; - -const TransferWrap = styled.div` - .ant-transfer-section { - width: 100%; - height: 300px; - .anticon-more { - display: none; - } - .ant-input-outlined { - height: 32px; - padding-block: 4px; - border-radius: 4px; - } - } - .ant-transfer-actions { - margin: 0 16px; - gap: 12px; - .ant-btn-icon-only { - width: 32px; - height: 32px; - border-radius: 50%; - } - } - .ant-transfer-list-content { - &::-webkit-scrollbar { - width: var(--scrollbar-size); - } - - &::-webkit-scrollbar-thumb { - background-color: transparent; - border-radius: 4px; - } - - &::-webkit-scrollbar-track { - background-color: transparent; - } - - &:hover { - &::-webkit-scrollbar-thumb { - background-color: var(--color-scrollbar-thumb); - border-radius: 4px; - } - } - .ant-transfer-list-content-item { - &:hover { - background-color: var(--ant-control-item-bg-hover); - } - &.ant-transfer-list-content-item-checked { - background-color: unset; - &:hover { - background-color: var(--ant-control-item-bg-hover); - } - } - } - } - .ant-pagination { - justify-content: center; - } -`; - -interface TransferInnerProps extends TransferProps { - total?: number; - perPage?: number; - onPageChange?: (page: number, perPage?: number) => void; - dataSource?: Array<{ key: TransferKey; title: string }>; - targetKeys?: TransferKey[]; -} - -const TransferInner: React.FC = (props) => { - const intl = useIntl(); - - const renderAllLabels = (info: { - selectedCount: number; - totalCount: number; - }) => { - if (info.selectedCount) { - return ( - - {intl.formatMessage( - { id: 'common.select.count' }, - { count: info.selectedCount } - )} - - ); - } - return null; - }; - return ( - - - } - > - - ); -}; - -export default TransferInner; diff --git a/src/pages/backends/components/add-modal.tsx b/src/pages/backends/components/add-modal.tsx index 30141e26..06e6f193 100644 --- a/src/pages/backends/components/add-modal.tsx +++ b/src/pages/backends/components/add-modal.tsx @@ -3,6 +3,7 @@ import { PageActionType } from '@/config/types'; import useSubmitLock from '@/hooks/use-submit-lock'; import { AlertBlockInfo, + ColumnWrapper, GSDrawer, IconFont, ModalFooter, @@ -13,7 +14,6 @@ import { Tabs } from 'antd'; import _ from 'lodash'; import React, { useEffect, useId, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; -import ColumnWrapper from '../../_components/column-wrapper'; import { BackendSourceValueMap, builtInBackendFields, diff --git a/src/pages/gpu-service/instances/forms/public-key-overlay.tsx b/src/pages/gpu-service/instances/forms/public-key-overlay.tsx index 18f01bb6..eb6101d6 100644 --- a/src/pages/gpu-service/instances/forms/public-key-overlay.tsx +++ b/src/pages/gpu-service/instances/forms/public-key-overlay.tsx @@ -1,6 +1,5 @@ import { PageAction } from '@/config'; -import FormOverlayView from '@/pages/_components/form-overlay-view'; -import { ModalFooter } from '@gpustack/core-ui'; +import { ModalFooter, SubDrawer } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useRef, useState } from 'react'; import { FormData as PublicKeyFormData } from '../../public-keys/config/types'; @@ -45,7 +44,7 @@ const PublicKeyOverlay: React.FC = ({ }; return ( - = ({ open={open} onFinish={handleFinish} /> - + ); }; diff --git a/src/pages/gpu-service/instances/forms/storage-overlay.tsx b/src/pages/gpu-service/instances/forms/storage-overlay.tsx index c889612f..524aa2f1 100644 --- a/src/pages/gpu-service/instances/forms/storage-overlay.tsx +++ b/src/pages/gpu-service/instances/forms/storage-overlay.tsx @@ -1,8 +1,7 @@ import { PageAction } from '@/config'; -import FormOverlayView from '@/pages/_components/form-overlay-view'; import { FormContext } from '@/pages/gpu-service/storage/config/form-context'; import useQueryStorageClass from '@/pages/gpu-service/storage/services/use-query-storage-class'; -import { ModalFooter } from '@gpustack/core-ui'; +import { ModalFooter, SubDrawer } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useEffect, useRef, useState } from 'react'; import { FormData as StorageFormData } from '../../storage/config/types'; @@ -52,7 +51,7 @@ const StorageOverlay: React.FC = ({ }; return ( - = ({ onFinish={handleFinish} /> - + ); }; diff --git a/src/pages/gpu-service/public-keys/components/add-public-key-modal.tsx b/src/pages/gpu-service/public-keys/components/add-public-key-modal.tsx index c64c0590..037cb398 100644 --- a/src/pages/gpu-service/public-keys/components/add-public-key-modal.tsx +++ b/src/pages/gpu-service/public-keys/components/add-public-key-modal.tsx @@ -1,8 +1,7 @@ import { PageActionType } from '@/config/types'; import useSubmitLock from '@/hooks/use-submit-lock'; -import { ModalFooter } from '@gpustack/core-ui'; +import { FormDrawer, ModalFooter } from '@gpustack/core-ui'; import { useRef } from 'react'; -import FormDrawer from '../../../_components/form-drawer'; import { FormData, ListItem } from '../config/types'; import GPUServicePublicKeyForm from '../forms'; diff --git a/src/pages/gpu-service/storage-types/components/add-storage-type-modal.tsx b/src/pages/gpu-service/storage-types/components/add-storage-type-modal.tsx index ee9bbad5..502e3e4b 100644 --- a/src/pages/gpu-service/storage-types/components/add-storage-type-modal.tsx +++ b/src/pages/gpu-service/storage-types/components/add-storage-type-modal.tsx @@ -1,8 +1,7 @@ import { PageActionType } from '@/config/types'; import useSubmitLock from '@/hooks/use-submit-lock'; -import { ModalFooter } from '@gpustack/core-ui'; +import { FormDrawer, ModalFooter } from '@gpustack/core-ui'; import { useRef } from 'react'; -import FormDrawer from '../../../_components/form-drawer'; import { FormData, ListItem } from '../config/types'; import GPUServiceStorageTypeForm from '../forms'; diff --git a/src/pages/gpu-service/storage/components/add-modal.tsx b/src/pages/gpu-service/storage/components/add-modal.tsx index 17cca11d..67f5a5b6 100644 --- a/src/pages/gpu-service/storage/components/add-modal.tsx +++ b/src/pages/gpu-service/storage/components/add-modal.tsx @@ -1,8 +1,7 @@ import { PageActionType } from '@/config/types'; import useSubmitLock from '@/hooks/use-submit-lock'; -import { ModalFooter } from '@gpustack/core-ui'; +import { FormDrawer, ModalFooter } from '@gpustack/core-ui'; import { useRef } from 'react'; -import FormDrawer from '../../../_components/form-drawer'; import { FormContext } from '../config/form-context'; import { FormData, ListItem } from '../config/types'; import GPUServiceStorageForm from '../forms'; diff --git a/src/pages/gpu-service/templates/components/add-modal.tsx b/src/pages/gpu-service/templates/components/add-modal.tsx index 0499c15e..ad852bc9 100644 --- a/src/pages/gpu-service/templates/components/add-modal.tsx +++ b/src/pages/gpu-service/templates/components/add-modal.tsx @@ -1,8 +1,7 @@ import { PageActionType } from '@/config/types'; import useSubmitLock from '@/hooks/use-submit-lock'; -import { ModalFooter } from '@gpustack/core-ui'; +import { FormDrawer, ModalFooter } from '@gpustack/core-ui'; import { useRef } from 'react'; -import FormDrawer from '../../../_components/form-drawer'; import { FormData, ListItem } from '../config/types'; import GPUServiceTemplateForm from '../forms'; diff --git a/src/pages/llmodels/components/deployment/deploy-modal.tsx b/src/pages/llmodels/components/deployment/deploy-modal.tsx index d72c20fe..bd3b27f9 100644 --- a/src/pages/llmodels/components/deployment/deploy-modal.tsx +++ b/src/pages/llmodels/components/deployment/deploy-modal.tsx @@ -2,14 +2,13 @@ import { getRequestId } from '@/atoms/models'; import { PageActionType } from '@/config/types'; import useDeferredRequest from '@/hooks/use-deferred-request'; import { ClusterStatusValueMap } from '@/pages/cluster-management/config'; -import { GSDrawer, ModalFooter } from '@gpustack/core-ui'; +import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button } from 'antd'; import _ from 'lodash'; import { FC, useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; -import ColumnWrapper from '../../../_components/column-wrapper'; import { defaultFormValues, DeployFormKeyMap, diff --git a/src/pages/llmodels/components/download/index.tsx b/src/pages/llmodels/components/download/index.tsx index 34c04a21..6d061231 100644 --- a/src/pages/llmodels/components/download/index.tsx +++ b/src/pages/llmodels/components/download/index.tsx @@ -1,10 +1,9 @@ import { ProviderValueMap } from '@/pages/cluster-management/config'; -import { GSDrawer, ModalFooter } from '@gpustack/core-ui'; +import { ColumnWrapper, GSDrawer, ModalFooter } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { debounce } from 'lodash'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; -import ColumnWrapper from '../../../_components/column-wrapper'; import { modelSourceMap } from '../../config'; import { FormData } from '../../config/types'; import HFModelFile from '../model-source/hf-model-file';