diff --git a/.dumi/hooks/use.ts b/.dumi/hooks/use.ts index d4d572981b..2502dcd1fa 100644 --- a/.dumi/hooks/use.ts +++ b/.dumi/hooks/use.ts @@ -1,11 +1,11 @@ -export default function use(promise: PromiseLike): T { +function use(promise: PromiseLike): T { const internal: PromiseLike & { status?: 'pending' | 'fulfilled' | 'rejected'; value?: T; reason?: any; } = promise; if (internal.status === 'fulfilled') { - return internal.value; + return internal.value as T; } if (internal.status === 'rejected') { throw internal.reason; @@ -26,3 +26,5 @@ export default function use(promise: PromiseLike): T { throw internal; } } + +export default use; diff --git a/.dumi/hooks/useLocale.ts b/.dumi/hooks/useLocale.ts index 9bc0dc4d48..8ad2969531 100644 --- a/.dumi/hooks/useLocale.ts +++ b/.dumi/hooks/useLocale.ts @@ -5,10 +5,12 @@ export interface LocaleMap { en: Record; } -export default function useLocale( +function useLocale( localeMap?: LocaleMap, ): [Record, 'cn' | 'en'] { const { id } = useDumiLocale(); - const localeType = id === 'zh-CN' ? 'cn' : 'en'; - return [localeMap?.[localeType], localeType]; + const localeType = id === 'zh-CN' ? ('cn' as const) : ('en' as const); + return [localeMap?.[localeType]!, localeType]; } + +export default useLocale; diff --git a/.dumi/hooks/useMenu.tsx b/.dumi/hooks/useMenu.tsx index 37483b149a..89bc9e1640 100644 --- a/.dumi/hooks/useMenu.tsx +++ b/.dumi/hooks/useMenu.tsx @@ -1,7 +1,8 @@ -import { useFullSidebarData, useSidebarData } from 'dumi'; import React, { useMemo } from 'react'; import type { MenuProps } from 'antd'; import { Tag, version } from 'antd'; +import { useFullSidebarData, useSidebarData } from 'dumi'; + import Link from '../theme/common/Link'; import useLocation from './useLocation'; @@ -136,7 +137,7 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] => const list = group.children || []; // 如果有 date 字段,我们就对其进行排序 if (list.every((info) => info?.frontmatter?.date)) { - list.sort((a, b) => (a.frontmatter.date > b.frontmatter.date ? -1 : 1)); + list.sort((a, b) => (a.frontmatter?.date > b.frontmatter?.date ? -1 : 1)); } result.push( diff --git a/.dumi/hooks/useThemeAnimation.ts b/.dumi/hooks/useThemeAnimation.ts index 0ca2049bd5..ebe5c4a7c5 100644 --- a/.dumi/hooks/useThemeAnimation.ts +++ b/.dumi/hooks/useThemeAnimation.ts @@ -32,11 +32,7 @@ const useThemeAnimation = () => { token: { colorBgElevated }, } = theme.useToken(); - const animateRef = useRef<{ - colorBgElevated: string; - }>({ - colorBgElevated, - }); + const animateRef = useRef<{ colorBgElevated: string }>({ colorBgElevated }); const startAnimationTheme = (clipPath: string[], isDark: boolean) => { updateCSS( @@ -64,9 +60,14 @@ const useThemeAnimation = () => { }); }; - const toggleAnimationTheme = (event: MouseEvent, isDark: boolean) => { + const toggleAnimationTheme = ( + event: React.MouseEvent, + isDark: boolean, + ) => { // @ts-ignore - if (!(event && typeof document.startViewTransition === 'function')) return; + if (!(event && typeof document.startViewTransition === 'function')) { + return; + } const x = event.clientX; const y = event.clientY; const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); diff --git a/.dumi/pages/index/components/Group.tsx b/.dumi/pages/index/components/Group.tsx index 20affa555c..d7fbba1c91 100644 --- a/.dumi/pages/index/components/Group.tsx +++ b/.dumi/pages/index/components/Group.tsx @@ -83,8 +83,8 @@ const Group: React.FC = (props) => { {description} diff --git a/.dumi/pages/index/components/Theme/BackgroundImage.tsx b/.dumi/pages/index/components/Theme/BackgroundImage.tsx index 32bc19c0b3..077f34dc28 100644 --- a/.dumi/pages/index/components/Theme/BackgroundImage.tsx +++ b/.dumi/pages/index/components/Theme/BackgroundImage.tsx @@ -1,7 +1,8 @@ -import { createStyles, css } from 'antd-style'; import React, { useMemo, useState } from 'react'; -import { CSSMotionList } from 'rc-motion'; +import { createStyles, css } from 'antd-style'; import classNames from 'classnames'; +import { CSSMotionList } from 'rc-motion'; + import { COLOR_IMAGES, getClosetColor } from './colorUtil'; export interface BackgroundImageProps { @@ -36,7 +37,7 @@ const BackgroundImage: React.FC = ({ colorPrimary, isLight const [keyList, setKeyList] = useState([]); React.useLayoutEffect(() => { - setKeyList([activeColor]); + setKeyList([activeColor as string]); }, [activeColor]); return ( @@ -56,7 +57,7 @@ const BackgroundImage: React.FC = ({ colorPrimary, isLight const entity = COLOR_IMAGES.find((ent) => ent.color === color); if (!entity || !entity.url) { - return null; + return null as unknown as React.ReactElement; } const { opacity } = style || {}; diff --git a/.dumi/pages/index/components/Theme/ColorPicker.tsx b/.dumi/pages/index/components/Theme/ColorPicker.tsx index 3642d13573..fa080e7661 100644 --- a/.dumi/pages/index/components/Theme/ColorPicker.tsx +++ b/.dumi/pages/index/components/Theme/ColorPicker.tsx @@ -1,39 +1,47 @@ -import { createStyles } from 'antd-style'; -import type { FC } from 'react'; import React, { useEffect, useState } from 'react'; -import classNames from 'classnames'; import { ColorPicker, Input, Space } from 'antd'; -import type { Color, ColorPickerProps } from 'antd/es/color-picker'; +import { createStyles } from 'antd-style'; +import type { Color } from 'antd/es/color-picker'; import { generateColor } from 'antd/es/color-picker/util'; +import classNames from 'classnames'; + import { PRESET_COLORS } from './colorUtil'; const useStyle = createStyles(({ token, css }) => ({ color: css` - width: ${token.controlHeightLG / 2}px; - height: ${token.controlHeightLG / 2}px; - border-radius: 100%; - cursor: pointer; - transition: all ${token.motionDurationFast}; - display: inline-block; + width: ${token.controlHeightLG / 2}px; + height: ${token.controlHeightLG / 2}px; + border-radius: 100%; + cursor: pointer; + transition: all ${token.motionDurationFast}; + display: inline-block; - & > input[type='radio'] { - width: 0; - height: 0; - opacity: 0; - } + & > input[type='radio'] { + width: 0; + height: 0; + opacity: 0; + } - &:focus-within { - // need ? - } - `, + &:focus-within { + // need ? + } + `, colorActive: css` - box-shadow: 0 0 0 1px ${token.colorBgContainer}, - 0 0 0 ${token.controlOutlineWidth * 2 + 1}px ${token.colorPrimary}; - `, + box-shadow: + 0 0 0 1px ${token.colorBgContainer}, + 0 0 0 ${token.controlOutlineWidth * 2 + 1}px ${token.colorPrimary}; + `, })); -const DebouncedColorPicker: FC = ({ value: color, onChange, children }) => { +export interface ColorPickerProps { + children?: React.ReactNode; + value?: string | Color; + onChange?: (value?: Color | string) => void; +} + +const DebouncedColorPicker: React.FC = (props) => { + const { value: color, children, onChange } = props; const [value, setValue] = useState(color); useEffect(() => { @@ -51,40 +59,24 @@ const DebouncedColorPicker: FC = ({ value: color, onChange, ch {children} ); }; -export interface RadiusPickerProps { - value?: string | Color; - onChange?: (value: string) => void; -} - -export default function ThemeColorPicker({ value, onChange }: RadiusPickerProps) { +const ThemeColorPicker: React.FC = ({ value, onChange }) => { const { styles } = useStyle(); const matchColors = React.useMemo(() => { - const valueStr = generateColor(value).toRgbString(); + const valueStr = generateColor(value || '').toRgbString(); let existActive = false; - const colors = PRESET_COLORS.map((color) => { const colorStr = generateColor(color).toRgbString(); const active = colorStr === valueStr; existActive = existActive || active; - - return { - color, - active, - picker: false, - }; + return { color, active, picker: false }; }); return [ @@ -100,10 +92,8 @@ export default function ThemeColorPicker({ value, onChange }: RadiusPickerProps) return ( { - onChange?.(event.target.value); - }} + value={typeof value === 'string' ? value : value?.toHexString()} + onChange={(event) => onChange?.(event.target.value)} style={{ width: 120 }} /> @@ -114,9 +104,7 @@ export default function ThemeColorPicker({ value, onChange }: RadiusPickerProps) ); -} +}; + +export default ThemeColorPicker; diff --git a/.dumi/pages/index/components/Theme/colorUtil.ts b/.dumi/pages/index/components/Theme/colorUtil.ts index 14c8254c3a..de913e83f0 100644 --- a/.dumi/pages/index/components/Theme/colorUtil.ts +++ b/.dumi/pages/index/components/Theme/colorUtil.ts @@ -1,3 +1,4 @@ +import type { Color } from 'antd/es/color-picker'; import { generateColor } from 'antd/es/color-picker/util'; export const DEFAULT_COLOR = '#1677FF'; @@ -50,7 +51,8 @@ export const COLOR_IMAGES = [ export const PRESET_COLORS = COLOR_IMAGES.map(({ color }) => color); const DISTANCE = 33; -export function getClosetColor(colorPrimary?: string | null) { + +export function getClosetColor(colorPrimary?: Color | string | null) { if (!colorPrimary) { return null; } diff --git a/.dumi/pages/index/components/Theme/index.tsx b/.dumi/pages/index/components/Theme/index.tsx index 2b33b22742..589a3f8a8c 100644 --- a/.dumi/pages/index/components/Theme/index.tsx +++ b/.dumi/pages/index/components/Theme/index.tsx @@ -197,7 +197,7 @@ const useStyle = createStyles(({ token, cx }) => { }); // ========================== Menu Config ========================== -const subMenuItems: MenuProps['items'] = [ +const subMenuItems = [ { key: `Design Values`, label: `Design Values`, @@ -287,11 +287,12 @@ const ThemesInfo: Record> = { }, }; +const normalize = (value: number) => value / 255; + function rgbToColorMatrix(color: string) { const rgb = new TinyColor(color).toRgb(); const { r, g, b } = rgb; - const normalize = (value) => value / 255; const invertValue = normalize(r) * 100; const sepiaValue = 100; const saturateValue = Math.max(normalize(r), normalize(g), normalize(b)) * 10000; @@ -360,10 +361,7 @@ export default function Theme() { const isRootDark = useDark(); React.useEffect(() => { - onThemeChange(null, { - ...themeData, - themeType: isRootDark ? 'dark' : 'default', - }); + onThemeChange({}, { ...themeData, themeType: isRootDark ? 'dark' : 'default' }); }, [isRootDark]); // ================================ Tokens ================================ diff --git a/.dumi/remarkAntd.ts b/.dumi/remarkAntd.ts index 61fdb859c5..84dc32df3e 100644 --- a/.dumi/remarkAntd.ts +++ b/.dumi/remarkAntd.ts @@ -1,4 +1,5 @@ -import { unistUtilVisit, type UnifiedTransformer } from 'dumi'; +import { unistUtilVisit } from 'dumi'; +import type { UnifiedTransformer } from 'dumi'; function remarkMeta(): UnifiedTransformer { return (tree, vFile) => { diff --git a/.dumi/theme/SiteThemeProvider.tsx b/.dumi/theme/SiteThemeProvider.tsx index 62bfb7dd09..819324eb19 100644 --- a/.dumi/theme/SiteThemeProvider.tsx +++ b/.dumi/theme/SiteThemeProvider.tsx @@ -1,10 +1,11 @@ +import React, { useContext } from 'react'; +import { theme as antdTheme, ConfigProvider } from 'antd'; +import type { ThemeConfig } from 'antd'; import type { ThemeProviderProps } from 'antd-style'; import { ThemeProvider } from 'antd-style'; -import type { FC } from 'react'; -import React, { useContext } from 'react'; -import { ConfigProvider, theme as antdTheme } from 'antd'; interface NewToken { + bannerHeight: number; headerHeight: number; menuItemBorder: number; mobileMaxWidth: number; @@ -24,15 +25,13 @@ declare module 'antd-style' { export interface CustomToken extends NewToken {} } -const SiteThemeProvider: FC = ({ children, theme, ...rest }) => { +const SiteThemeProvider: React.FC> = ({ children, theme, ...rest }) => { const { getPrefixCls, iconPrefixCls } = useContext(ConfigProvider.ConfigContext); const rootPrefixCls = getPrefixCls(); const { token } = antdTheme.useToken(); React.useEffect(() => { - ConfigProvider.config({ - theme, - }); + ConfigProvider.config({ theme: theme as ThemeConfig }); }, [theme]); return ( diff --git a/.dumi/theme/common/Link.tsx b/.dumi/theme/common/Link.tsx index 1cbe0e3933..14783fa0df 100644 --- a/.dumi/theme/common/Link.tsx +++ b/.dumi/theme/common/Link.tsx @@ -3,12 +3,12 @@ import React, { forwardRef, useLayoutEffect, useMemo, useTransition } from 'reac import { useLocation, useNavigate } from 'dumi'; import nprogress from 'nprogress'; -export type LinkProps = { +export interface LinkProps { to?: string | { pathname?: string; search?: string; hash?: string }; children?: React.ReactNode; style?: React.CSSProperties; className?: string; -}; +} const Link = forwardRef((props, ref) => { const { to, children, ...rest } = props; @@ -24,12 +24,14 @@ const Link = forwardRef((props, ref) => { }, [to]); const handleClick = (e: MouseEvent) => { - if (!href.startsWith('http')) { + if (!href?.startsWith('http')) { // Should support open in new tab if (!e.metaKey && !e.ctrlKey && !e.shiftKey) { e.preventDefault(); startTransition(() => { - navigate(href); + if (href) { + navigate(href); + } }); } } diff --git a/.dumi/theme/common/ThemeSwitch/index.tsx b/.dumi/theme/common/ThemeSwitch/index.tsx index 52ef5178b6..05caa5d536 100644 --- a/.dumi/theme/common/ThemeSwitch/index.tsx +++ b/.dumi/theme/common/ThemeSwitch/index.tsx @@ -12,10 +12,10 @@ import ThemeIcon from './ThemeIcon'; export type ThemeName = 'light' | 'dark' | 'compact' | 'motion-off' | 'happy-work'; -export type ThemeSwitchProps = { +export interface ThemeSwitchProps { value?: ThemeName[]; onChange: (value: ThemeName[]) => void; -}; +} const ThemeSwitch: React.FC = (props) => { const { value = ['light'], onChange } = props; @@ -72,23 +72,6 @@ const ThemeSwitch: React.FC = (props) => { }} tooltip={} /> - {/* Too many float button. Hide motion one */} - {/* } - type={!isMotionOff ? 'primary' : 'default'} - onClick={() => { - if (isMotionOff) { - onChange(value.filter((theme) => theme !== 'motion-off')); - } else { - onChange([...value, 'motion-off']); - } - }} - tooltip={ - - } - /> */} } diff --git a/.dumi/theme/utils.ts b/.dumi/theme/utils.ts index 2dc8e4b0e0..3d6d24c00c 100644 --- a/.dumi/theme/utils.ts +++ b/.dumi/theme/utils.ts @@ -1,5 +1,6 @@ import flatten from 'lodash/flatten'; import flattenDeep from 'lodash/flattenDeep'; + import themeConfig from './themeConfig'; interface Meta { diff --git a/package.json b/package.json index 48479775c2..660bed4e8a 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "@types/jquery": "^3.5.14", "@types/lodash": "^4.14.139", "@types/node": "^20.0.0", + "@types/nprogress": "^0.2.0", "@types/prismjs": "^1.26.0", "@types/progress": "^2.0.5", "@types/qs": "^6.9.7", diff --git a/tests/shared/imageTest.tsx b/tests/shared/imageTest.tsx index 26c1fd782c..f1510d7a8f 100644 --- a/tests/shared/imageTest.tsx +++ b/tests/shared/imageTest.tsx @@ -7,6 +7,7 @@ import { globSync } from 'glob'; import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; import MockDate from 'mockdate'; import ReactDOMServer from 'react-dom/server'; + import { App, ConfigProvider, theme } from '../../components'; const toMatchImageSnapshot = configureToMatchImageSnapshot({ @@ -77,7 +78,6 @@ export default function imageTest(component: React.ReactElement, options: ImageT const image = await page.screenshot({ fullPage: !options.onlyViewport, - optimizeForSpeed: true, }); expect(image).toMatchImageSnapshot();