diff --git a/.dumirc.ts b/.dumirc.ts index 4e57597d..e370ca83 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -2,84 +2,67 @@ import { defineConfig } from 'dumi'; import { homepage, name } from './package.json'; -const isProd = process.env.NODE_ENV === 'production'; +const isProduction = process.env.NODE_ENV === 'production'; const isWin = process.platform === 'win32'; const themeConfig = { - title: 'Lobe UI', - name: 'UI', - description: 'Lobe UI is an open-source UI component library for building chatbot web apps', - footer: 'Made with 🤯 by LobeHub', - socialLinks: { - github: homepage, - }, - apiHeader: { - // 组件库包名,可以从 package.json 中引入名称 - pkg: name, - // 匹配路由,默认为 /api 或 /components - match: ['/components'], - // github 会匹配 themeConfig.github 字段 - sourceUrl: `{github}/tree/master/src/{atomId}/index.tsx`, - docUrl: `{github}/tree/master/src/{atomId}/index.md`, - }, actions: [ { - text: 'Github', icon: 'Github', link: homepage, openExternal: true, + text: 'Github', }, { - text: 'Get Started', link: '/components/action-icon', + text: 'Get Started', type: 'primary', }, ], + apiHeader: { + docUrl: `{github}/tree/master/src/{atomId}/index.md`, + + // 匹配路由,默认为 /api 或 /components + match: ['/components'], + + // 组件库包名,可以从 package.json 中引入名称 + pkg: name, + // github 会匹配 themeConfig.github 字段 + sourceUrl: `{github}/tree/master/src/{atomId}/index.tsx`, + }, + description: 'Lobe UI is an open-source UI component library for building chatbot web apps', + features: [ { - icon: 'Palette', - title: 'Themeable', description: 'Provides a simple way to customize default themes, you can change the colors, fonts, breakpoints and everything you need.', + icon: 'Palette', + title: 'Themeable', }, { - icon: 'Zap', - title: 'Fast', description: 'voids unnecessary styles props at runtime, making it more performant than other UI libraries.', + icon: 'Zap', + title: 'Fast', }, { - icon: 'MoonStar', - title: 'Light & Dark UI', description: 'Automatic dark mode recognition, NextUI automatically changes the theme when detects HTML theme prop changes.', + icon: 'MoonStar', + title: 'Light & Dark UI', }, ], + footer: 'Made with 🤯 by LobeHub', + name: 'UI', + socialLinks: { + github: homepage, + }, + title: 'Lobe UI', }; export default defineConfig({ - themeConfig, - locales: [{ id: 'en-US', name: 'English' }], - title: 'Lobe UI', - favicons: ['https://raw.githubusercontent.com/lobehub/favicon/main/dist/favicon.ico'], - npmClient: 'pnpm', + apiParser: isProduction ? {} : false, base: '/', - publicPath: '/', - ssr: isProd ? {} : false, - apiParser: !isProd ? false : {}, - resolve: !isProd - ? undefined - : { - entryFile: './src/index.ts', - }, - define: { - 'process.env': process.env, - }, - mfsu: isWin - ? undefined - : { - exclude: ['@dqbd/tiktoken'], - }, chainWebpack(config: any) { config.set('experiments', { ...config.get('experiments'), @@ -99,15 +82,36 @@ export default defineConfig({ .type('webassembly/async') .end(); }, + define: { + 'process.env': process.env, + }, extraBabelPlugins: [ [ 'babel-plugin-styled-components', { - minify: true, - transpileTemplateLiterals: true, displayName: process.env.NODE_ENV === 'development', + minify: true, pure: true, + transpileTemplateLiterals: true, }, ], ], + favicons: ['https://raw.githubusercontent.com/lobehub/favicon/main/dist/favicon.ico'], + locales: [{ id: 'en-US', name: 'English' }], + mfsu: isWin + ? undefined + : { + exclude: ['@dqbd/tiktoken'], + }, + npmClient: 'pnpm', + publicPath: '/', + resolve: isProduction + ? { + entryFile: './src/index.ts', + } + : undefined, + + ssr: isProduction ? {} : false, + themeConfig, + title: 'Lobe UI', }); diff --git a/packages/dumi-theme-lobehub/src/components/Burger/index.tsx b/packages/dumi-theme-lobehub/src/components/Burger/index.tsx deleted file mode 100644 index fce373e2..00000000 --- a/packages/dumi-theme-lobehub/src/components/Burger/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Drawer, Menu } from 'antd'; -import { Link } from 'dumi'; -import isEqual from 'fast-deep-equal'; -import { uniq } from 'lodash-es'; -import { memo, useState } from 'react'; -import { Center } from 'react-layout-kit'; - -import { activePathSel, useSiteStore } from '@/store'; - -import { useStyles } from './style'; - -const Burger = memo(() => { - const [opened, setOpened] = useState(false); - const { styles, cx } = useStyles(); - - const nav = useSiteStore((s) => s.navData, isEqual); - const sidebar = useSiteStore((s) => s.sidebar, isEqual); - const activePath = useSiteStore(activePathSel); - const pathname = useSiteStore((s) => s.location.pathname); - - return ( -
{ - setOpened(!opened); - }} - > -
- - - ((item) => ({ - children: - (item.activePath || item.link) === activePath && - sidebar?.map((group) => { - return ( - !group.link && { - children: group.children.map((item) => ({ - key: `s-${item.link}`, - label: ( - { - setOpened(false); - }} - to={item.link} - > - {item.title} - - ), - })), - label: group.title, - type: 'group', - } - ); - }), - key: item.activePath! || item.link, - label: {item.title}, - }))} - mode={'inline'} - openKeys={[activePath]} - selectedKeys={uniq([activePath, `s-${pathname}`])} - /> -
- -
- ); -}); - -export default Burger; diff --git a/packages/dumi-theme-lobehub/src/components/Burger/style.ts b/packages/dumi-theme-lobehub/src/components/Burger/style.ts deleted file mode 100644 index 61de5a21..00000000 --- a/packages/dumi-theme-lobehub/src/components/Burger/style.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { createStyles } from 'antd-style'; -import { rgba } from 'polished'; - -export const useStyles = createStyles(({ token, prefixCls, cx, css, stylish }) => { - const offset = 6; - - return { - active: css` - &::before, - &::after { - background: ${token.colorText}; - } - - & { - background: transparent; - } - - &::before { - top: 0; - transform: rotate(-135deg); - } - - &::after { - top: 0; - transform: rotate(135deg); - } - `, - container: css` - cursor: pointer; - width: ${token.controlHeight}px; - height: ${token.controlHeight}px; - border-radius: ${token.borderRadius}px; - `, - drawer: css` - &.${prefixCls}-drawer-content { - background: transparent; - } - - .${prefixCls}-drawer-body { - display: flex; - flex-direction: column; - } - `, - drawerRoot: css` - top: ${token.headerHeight + 1}px; - - :focus-visible { - outline: none; - } - - .${prefixCls}-drawer { - &-mask { - ${stylish.blur}; - background-color: ${rgba(token.colorBgLayout, 0.5)}; - } - - &-content-wrapper { - box-shadow: none; - } - } - `, - - fillRect: css` - flex: 1; - width: 100%; - border-top: 1px solid ${token.colorBorderSecondary}; - `, - icon: cx( - 'site-burger-icon', - css` - position: relative; - - &::before, - &::after { - content: ''; - position: absolute; - left: 0; - } - - &::before { - top: ${offset}px; - } - - &::after { - top: -${offset}px; - } - - &, - &::before, - &::after { - display: inline-block; - - width: 16px; - height: 2px; - - background: ${token.colorTextSecondary}; - - transition: all 150ms ease; - } - `, - ), - - menu: css` - background: transparent; - border-inline-end: transparent !important; - - .${prefixCls}-menu-item-only-child:first-child { - border-top: none; - } - - > .${prefixCls}-menu-item-only-child,.${prefixCls}-menu-submenu-title { - width: 100%; - margin: 0 !important; - border-top: 1px solid ${token.colorBorderSecondary}; - border-radius: 0; - - &:active { - color: ${token.colorText}; - background-color: ${token.colorFill}; - } - } - - .ant-menu-item-group-title { - overflow: hidden; - - padding: 0; - - font-size: 12px; - font-weight: 500; - text-overflow: ellipsis; - text-transform: uppercase; - white-space: nowrap; - } - - .ant-menu-item-selected { - width: 100%; - margin: 0; - - color: ${token.colorText}; - - background: ${token.colorFillSecondary}; - border-radius: 0; - } - - .ant-menu-item, - a::before { - width: 100% !important; - margin: 0 !important; - border-radius: 0 !important; - } - `, - }; -}); diff --git a/packages/dumi-theme-lobehub/src/index.ts b/packages/dumi-theme-lobehub/src/index.ts index 39c010e6..a7a31828 100644 --- a/packages/dumi-theme-lobehub/src/index.ts +++ b/packages/dumi-theme-lobehub/src/index.ts @@ -1,6 +1,4 @@ export { ApiHeader, type ApiTitleProps } from './components/ApiHeader'; export { defineThemeConfig } from './config'; -// 导出所有需要消费的 store export { siteSelectors, type SiteStore, useSiteStore } from './store'; -// 导出所有需要消费的类型 export * from './types'; diff --git a/packages/dumi-theme-lobehub/src/layouts/DocLayout/GlobalStyle.ts b/packages/dumi-theme-lobehub/src/layouts/DocLayout/GlobalStyle.ts index bff6dd6c..1efa8504 100644 --- a/packages/dumi-theme-lobehub/src/layouts/DocLayout/GlobalStyle.ts +++ b/packages/dumi-theme-lobehub/src/layouts/DocLayout/GlobalStyle.ts @@ -4,13 +4,11 @@ const GlobalStyle = createGlobalStyle` #nprogress { .bar { - z-index: 9999; background: ${({ theme }) => theme.colorText}; } - - + .peg { - box-shadow: none; + display: none !important; } .spinner { diff --git a/packages/dumi-theme-lobehub/src/layouts/DocLayout/index.tsx b/packages/dumi-theme-lobehub/src/layouts/DocLayout/index.tsx index 0eeb7e76..6cc9b699 100644 --- a/packages/dumi-theme-lobehub/src/layouts/DocLayout/index.tsx +++ b/packages/dumi-theme-lobehub/src/layouts/DocLayout/index.tsx @@ -28,19 +28,29 @@ const DocumentLayout = memo(() => { const theme = useTheme(); const { mobile, laptop } = useResponsive(); - const { fm, noToc, siteTitle, isHomePage, loading, isChanlogPage } = useSiteStore( - (s) => ({ + const { fm, noToc, siteTitle, page, loading } = useSiteStore((s) => { + const isChanlogPage = s.location.pathname === '/changelog'; + const isHomePage = isHeroPageSel(s); + let page; + + if (isHomePage) { + page = 'home'; + } else if (isChanlogPage) { + page = 'changelog'; + } else { + page = 'docs'; + } + + return { fm: s.routeMeta.frontmatter, - isChanlogPage: s.location.pathname === '/changelog', - isHomePage: isHeroPageSel(s), loading: s.siteData.loading, noToc: tocAnchorItemSel(s).length === 0, + page: page, siteTitle: siteTitleSel(s), - }), - shallow, - ); + }; + }, shallow); - const hideSidebar = isHomePage || isChanlogPage || mobile || fm.sidebar === false; + const hideSidebar = page !== 'docs' || mobile || fm.sidebar === false; const shouldHideToc = fm.toc === false || noToc; const hideToc = mobile ? shouldHideToc : !laptop || shouldHideToc; @@ -53,7 +63,7 @@ const DocumentLayout = memo(() => { {fm.description && } {fm.keywords && } {fm.keywords && } - {!fm.title || isHomePage ? ( + {!fm.title || page === 'home' ? ( {siteTitle} ) : ( @@ -62,7 +72,7 @@ const DocumentLayout = memo(() => { )} </Helmet> ), - [intl, fm, siteTitle, isHomePage], + [intl, fm, siteTitle, page === 'home'], ); // handle hash change or visit page hash after async chunk loaded @@ -83,28 +93,20 @@ const DocumentLayout = memo(() => { document.body.scrollTo(0, 0); }, [window.location.pathname]); - let Page; - - if (isHomePage) { - Page = Home; - } else if (isChanlogPage) { - Page = Changelog; - } else { - Page = Docs; - } - return ( <Layout asideWidth={theme.sidebarWidth} footer={<Footer />} header={<Header />} - headerHeight={mobile && !isHomePage ? theme.headerHeight + 36 : theme.headerHeight} + headerHeight={mobile && page !== 'home' ? theme.headerHeight + 36 : theme.headerHeight} helmet={<HelmetBlock />} sidebar={hideSidebar ? undefined : <Sidebar />} toc={hideToc ? undefined : <Toc />} tocWidth={hideToc ? 0 : theme.tocWidth} > - <Page /> + {page === 'home' && <Home />} + {page === 'changelog' && <Changelog />} + {page === 'docs' && <Docs />} </Layout> ); }); diff --git a/packages/dumi-theme-lobehub/src/slots/Header/Burger.tsx b/packages/dumi-theme-lobehub/src/slots/Header/Burger.tsx new file mode 100644 index 00000000..21844927 --- /dev/null +++ b/packages/dumi-theme-lobehub/src/slots/Header/Burger.tsx @@ -0,0 +1,54 @@ +import { Burger as Menu } from '@lobehub/ui'; +import { Link } from 'dumi'; +import isEqual from 'fast-deep-equal'; +import { uniq } from 'lodash-es'; +import { memo, useState } from 'react'; + +import { activePathSel, useSiteStore } from '@/store'; + +const Burger = memo(() => { + const [opened, setOpened] = useState(false); + + const nav = useSiteStore((s) => s.navData, isEqual); + const sidebar = useSiteStore((s) => s.sidebar, isEqual); + const activePath = useSiteStore(activePathSel); + const pathname = useSiteStore((s) => s.location.pathname); + + return ( + <Menu + items={nav.map<any>((item) => ({ + children: + (item.activePath || item.link) === activePath && + sidebar?.map((group) => { + return ( + !group.link && { + children: group.children.map((item) => ({ + key: `s-${item.link}`, + label: ( + <Link + onClick={() => { + setOpened(false); + }} + to={item.link} + > + {item.title} + </Link> + ), + })), + label: group.title, + type: 'group', + } + ); + }), + key: item.activePath! || item.link, + label: <Link to={String(item.link)}>{item.title}</Link>, + }))} + openKeys={[activePath]} + opened={opened} + selectedKeys={uniq([activePath, `s-${pathname}`])} + setOpened={setOpened} + /> + ); +}); + +export default Burger; diff --git a/packages/dumi-theme-lobehub/src/slots/Header/index.tsx b/packages/dumi-theme-lobehub/src/slots/Header/index.tsx index 674dbf73..abc31e3d 100644 --- a/packages/dumi-theme-lobehub/src/slots/Header/index.tsx +++ b/packages/dumi-theme-lobehub/src/slots/Header/index.tsx @@ -2,12 +2,12 @@ import { Header as Head } from '@lobehub/ui'; import { useResponsive } from 'antd-style'; import { memo } from 'react'; -import Burger from '@/components/Burger'; import Logo from '@/slots/Logo'; import Navbar from '@/slots/Navbar'; import SearchBar from '@/slots/SearchBar'; import { useSiteStore } from '@/store/useSiteStore'; +import Burger from './Burger'; import GithubButton from './GithubButton'; import LangSwitch from './LangSwitch'; import ThemeSwitch from './ThemeSwitch'; diff --git a/src/ActionIcon/style.ts b/src/ActionIcon/style.ts index dfb914b7..f5d6ecfd 100644 --- a/src/ActionIcon/style.ts +++ b/src/ActionIcon/style.ts @@ -19,7 +19,7 @@ export const useStyles = createStyles( background: ${active ? token.colorFillTertiary : 'transparent'}; - transition: color 600ms ${token.motionEaseOut}, transform 400ms ${token.motionEaseOut}, + transition: color 600ms ${token.motionEaseOut}, scale 400ms ${token.motionEaseOut}, background-color 100ms ${token.motionEaseOut}; &:hover { @@ -28,7 +28,7 @@ export const useStyles = createStyles( } &:active { - transform: scale(0.8); + scale: 0.8; color: ${token.colorText}; background-color: ${token.colorFill}; } diff --git a/src/Burger/index.tsx b/src/Burger/index.tsx new file mode 100644 index 00000000..91001aeb --- /dev/null +++ b/src/Burger/index.tsx @@ -0,0 +1,66 @@ +import { Drawer, Menu, MenuProps } from 'antd'; +import { MenuIcon, X } from 'lucide-react'; +import { memo } from 'react'; +import { Center } from 'react-layout-kit'; + +import ActionIcon from '@/ActionIcon'; +import { DivProps } from '@/types'; + +import { useStyles } from './style'; + +export interface BurgerProps extends DivProps { + headerHeight?: number; + items: MenuProps['items']; + openKeys?: MenuProps['openKeys']; + opened: boolean; + selectedKeys?: MenuProps['selectedKeys']; + setOpened: (state: boolean) => void; +} + +const Burger = memo<BurgerProps>( + ({ + items, + openKeys, + selectedKeys, + opened, + setOpened, + className, + headerHeight = 64, + ...props + }) => { + const { cx, styles } = useStyles(headerHeight); + + return ( + <Center + className={cx(styles.container, className)} + onClick={() => { + setOpened(!opened); + }} + {...props} + > + <ActionIcon icon={opened ? X : MenuIcon} size="site" /> + <Drawer + bodyStyle={{ padding: 0 }} + className={styles.drawer} + closeIcon={undefined} + headerStyle={{ display: 'none' }} + open={opened} + placement={'left'} + rootClassName={styles.drawerRoot} + width={'100vw'} + > + <Menu + className={styles.menu} + items={items} + mode={'inline'} + openKeys={openKeys} + selectedKeys={selectedKeys} + /> + <div className={styles.fillRect} /> + </Drawer> + </Center> + ); + }, +); + +export default Burger; diff --git a/src/Burger/style.ts b/src/Burger/style.ts new file mode 100644 index 00000000..84bbb2eb --- /dev/null +++ b/src/Burger/style.ts @@ -0,0 +1,120 @@ +import { createStyles } from 'antd-style'; +import { rgba } from 'polished'; + +export const useStyles = createStyles(({ token, css, stylish }, headerHeight: number) => { + return { + container: css` + cursor: pointer; + width: ${token.controlHeight}px; + height: ${token.controlHeight}px; + border-radius: ${token.borderRadius}px; + `, + drawer: css` + &.ant-drawer-content { + background: transparent; + } + + .ant-drawer-body { + display: flex; + flex-direction: column; + } + `, + drawerRoot: css` + top: ${headerHeight + 1}px; + + :focus-visible { + outline: none; + } + + .ant-drawer { + &-mask { + ${stylish.blur}; + background-color: ${rgba(token.colorBgLayout, 0.5)}; + } + + &-content-wrapper { + box-shadow: none; + } + } + `, + + fillRect: css` + flex: 1; + width: 100%; + border-top: none; + `, + + menu: css` + background: transparent; + border-inline-end: transparent !important; + + > .ant-menu-item-only-child, + .ant-menu-submenu-title { + width: 100%; + margin: 0 !important; + border-top: none; + border-radius: 0; + + &:active { + color: ${token.colorText}; + background-color: ${token.colorFill}; + } + } + + .ant-menu-item-only-child:first-child { + border-top: none; + } + + .ant-menu-submenu-title[aria-expanded='true'] { + a { + font-weight: 600; + color: ${token.colorText} !important; + } + } + + .ant-menu-item-group-title { + padding-top: 4px; + padding-bottom: 4px; + + font-size: 12px; + font-weight: 500; + text-overflow: ellipsis; + text-transform: uppercase; + white-space: nowrap; + + background: ${token.colorFillSecondary}; + } + + .ant-menu-item { + width: calc(100% - 16px) !important; + margin: 8px !important; + border-radius: ${token.borderRadius}px; + + &:hover, + &:active { + color: ${token.colorText} !important; + background: ${token.colorFillSecondary} !important; + } + } + + .ant-menu-item-active { + width: calc(100% - 16px) !important; + margin: 8px !important; + background: ${token.colorFillSecondary}; + border-radius: ${token.borderRadius}px; + } + + .ant-menu-item-selected { + font-weight: 600; + color: ${token.colorBgLayout}; + background: ${token.colorText}; + + &:hover, + &:active { + color: ${token.colorBgLayout} !important; + background: ${token.colorText} !important; + } + } + `, + }; +}); diff --git a/src/ColorScales/style.ts b/src/ColorScales/style.ts index 19fdcfcd..3f71671d 100644 --- a/src/ColorScales/style.ts +++ b/src/ColorScales/style.ts @@ -6,7 +6,7 @@ export const alphaBg = { 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAFpJREFUWAntljEKADAIA23p6v//qQ+wfUEcCu1yriEgp0FHRJSJcnehmmWm1Dv/lO4HIg1AAAKjTqm03ea88zMCCEDgO4HV5bS757f+7wRoAAIQ4B9gByAAgQ3pfiDmXmAeEwAAAABJRU5ErkJggg==) 0% 0% / 26px', }; -export const useStyles = createStyles(({ css }) => ({ +export const useStyles = createStyles(({ css, token }) => ({ scaleBox: css` cursor: pointer; @@ -18,8 +18,10 @@ export const useStyles = createStyles(({ css }) => ({ background-position: 0 0, 0 8px, 8px -8px, -8px 0; background-size: 16px 16px; + transition: scale 400ms ${token.motionEaseOut}; + &:active { - transform: scale(0.95); + scale: 0.8; } `, scaleItem: css` diff --git a/src/Hero/index.tsx b/src/Hero/index.tsx index 9057d12b..9781fd4c 100644 --- a/src/Hero/index.tsx +++ b/src/Hero/index.tsx @@ -1,5 +1,5 @@ import { GradientButton, Icon } from '@lobehub/ui'; -import { Button, ConfigProvider, Space } from 'antd'; +import { Button, ConfigProvider } from 'antd'; import { useResponsive } from 'antd-style'; import * as LucideIcon from 'lucide-react'; import { memo, useCallback } from 'react'; @@ -28,7 +28,7 @@ const Hero = memo<HeroProps>(({ title, description, actions }) => { const ButtonGroups = useCallback( () => Boolean(actions?.length) && ( - <Space className={styles.actions} direction={mobile ? 'vertical' : 'horizontal'} size={24}> + <div className={styles.actions}> {actions!.map(({ text, link, openExternal, icon, type }, index) => { // @ts-ignore const ButtonIcon = icon && LucideIcon[icon]; @@ -42,6 +42,7 @@ const Hero = memo<HeroProps>(({ title, description, actions }) => { > {type === 'primary' ? ( <GradientButton + block={mobile} icon={ButtonIcon && <Icon icon={ButtonIcon} />} key={index} size="large" @@ -50,6 +51,7 @@ const Hero = memo<HeroProps>(({ title, description, actions }) => { </GradientButton> ) : ( <Button + block={mobile} icon={ButtonIcon && <Icon icon={ButtonIcon} />} key={index} size="large" @@ -61,7 +63,7 @@ const Hero = memo<HeroProps>(({ title, description, actions }) => { </a> ); })} - </Space> + </div> ), [actions], ); diff --git a/src/Hero/style.ts b/src/Hero/style.ts index 55915d92..5bd42e6a 100644 --- a/src/Hero/style.ts +++ b/src/Hero/style.ts @@ -3,6 +3,7 @@ import { createStyles } from 'antd-style'; export const useStyles = createStyles(({ cx, css, responsive, token, stylish }) => ({ actions: css` display: flex; + gap: 24px; justify-content: center; margin-top: 24px; @@ -11,9 +12,16 @@ export const useStyles = createStyles(({ cx, css, responsive, token, stylish }) font-weight: 500; } - ${responsive({ - mobile: { marginTop: 24 }, - })} + ${responsive.mobile} { + flex-direction: column; + gap: 16px; + width: 100%; + margin-top: 24px; + + button { + width: 100%; + } + } `, canvas: cx( diff --git a/src/Icon/style.ts b/src/Icon/style.ts index 53d6f503..0697cd39 100644 --- a/src/Icon/style.ts +++ b/src/Icon/style.ts @@ -3,10 +3,10 @@ import { createStyles, keyframes } from 'antd-style'; export const useStyles = createStyles(({ css }) => { const spin = keyframes` 0% { - transform: rotate(0deg); + rotate: 0deg; } 100% { - transform: rotate(360deg); + rotate: 360deg; } `; return { diff --git a/src/Layout/index.tsx b/src/Layout/index.tsx index 8afc7671..875af5d9 100644 --- a/src/Layout/index.tsx +++ b/src/Layout/index.tsx @@ -9,27 +9,30 @@ import { useStyles } from './style'; export interface LayoutHeaderProps extends DivProps { headerHeight?: number; } -export const LayoutHeader = memo<LayoutHeaderProps>(({ headerHeight, children, ...props }) => { - const { styles } = useStyles(headerHeight); - return ( - <header - className={styles.header} - style={{ - height: headerHeight, - }} - {...props} - > - <div className={styles.glass} /> - {children} - </header> - ); -}); +export const LayoutHeader = memo<LayoutHeaderProps>( + ({ headerHeight, children, className, style, ...props }) => { + const { cx, styles } = useStyles(headerHeight); + return ( + <header + className={cx(styles.header, className)} + style={{ + height: headerHeight, + ...style, + }} + {...props} + > + <div className={styles.glass} /> + {children} + </header> + ); + }, +); export type LayoutMainProps = DivProps; -export const LayoutMain = memo<LayoutMainProps>(({ children, ...props }) => { - const { styles } = useStyles(); +export const LayoutMain = memo<LayoutMainProps>(({ children, className, ...props }) => { + const { cx, styles } = useStyles(); return ( - <main className={styles.main} {...props}> + <main className={cx(styles.main, className)} {...props}> {children} </main> ); @@ -38,23 +41,29 @@ export const LayoutMain = memo<LayoutMainProps>(({ children, ...props }) => { export interface LayoutSidebarProps extends DivProps { headerHeight?: number; } -export const LayoutSidebar = memo<LayoutSidebarProps>(({ headerHeight, children, ...props }) => { - const { styles } = useStyles(headerHeight); - return ( - <aside className={styles.aside} style={{ top: headerHeight }} {...props}> - {children} - </aside> - ); -}); +export const LayoutSidebar = memo<LayoutSidebarProps>( + ({ headerHeight, children, className, style, ...props }) => { + const { cx, styles } = useStyles(headerHeight); + return ( + <aside + className={cx(styles.aside, className)} + style={{ top: headerHeight, ...style }} + {...props} + > + {children} + </aside> + ); + }, +); export interface LayoutSidebarInnerProps extends DivProps { headerHeight?: number; } export const LayoutSidebarInner = memo<LayoutSidebarInnerProps>( - ({ headerHeight, children, ...props }) => { - const { styles } = useStyles(headerHeight); + ({ headerHeight, children, className, ...props }) => { + const { cx, styles } = useStyles(headerHeight); return ( - <div className={styles.asideInner} {...props}> + <div className={cx(styles.asideInner, className)} {...props}> {children} </div> ); @@ -64,20 +73,26 @@ export const LayoutSidebarInner = memo<LayoutSidebarInnerProps>( export interface LayoutTocProps extends DivProps { tocWidth?: number; } -export const LayoutToc = memo<LayoutTocProps>(({ tocWidth, children, ...props }) => { - const { styles } = useStyles(); - return ( - <nav className={styles.toc} style={{ width: tocWidth }} {...props}> - {children} - </nav> - ); -}); +export const LayoutToc = memo<LayoutTocProps>( + ({ tocWidth, style, className, children, ...props }) => { + const { cx, styles } = useStyles(); + return ( + <nav + className={cx(styles.toc, className)} + style={tocWidth ? { width: tocWidth, ...style } : style} + {...props} + > + {children} + </nav> + ); + }, +); export type LayoutFooterProps = DivProps; -export const LayoutFooter = memo<LayoutFooterProps>(({ children, ...props }) => { - const { styles } = useStyles(); +export const LayoutFooter = memo<LayoutFooterProps>(({ children, className, ...props }) => { + const { cx, styles } = useStyles(); return ( - <footer className={styles.footer} {...props}> + <footer className={cx(styles.footer, className)} {...props}> {children} </footer> ); @@ -111,7 +126,7 @@ const Layout = memo<LayoutProps>( {header && ( <LayoutHeader headerHeight={headerHeight}> {header} - {mobile && toc && <LayoutToc tocWidth={tocWidth}>{toc}</LayoutToc>} + {mobile && toc && <LayoutToc>{toc}</LayoutToc>} </LayoutHeader> )} <LayoutMain> diff --git a/src/Swatches/demos/index.tsx b/src/Swatches/demos/index.tsx index fdcfac4a..a30ca368 100644 --- a/src/Swatches/demos/index.tsx +++ b/src/Swatches/demos/index.tsx @@ -1,38 +1,28 @@ -import { - blue, - cyan, - geekblue, - gold, - green, - lime, - magenta, - orange, - purple, - red, - volcano, - yellow, -} from '@ant-design/colors'; import { Swatches } from '@lobehub/ui'; - -const STEP = 4; +import { useTheme } from 'antd-style'; +import { useState } from 'react'; export default () => { + const [activeColor, setActiveColor] = useState<string>(); + const theme = useTheme(); return ( <Swatches + activeColor={activeColor} colors={[ - red[STEP], - orange[STEP], - gold[STEP], - yellow[STEP], - lime[STEP], - green[STEP], - cyan[STEP], - blue[STEP], - geekblue[STEP], - purple[STEP], - magenta[STEP], - volcano[STEP], + theme.red, + theme.orange, + theme.gold, + theme.yellow, + theme.lime, + theme.green, + theme.cyan, + theme.blue, + theme.geekblue, + theme.purple, + theme.magenta, + theme.volcano, ]} + onSelect={setActiveColor} /> ); }; diff --git a/src/Swatches/index.tsx b/src/Swatches/index.tsx index 3ce59dd7..5286ccc5 100644 --- a/src/Swatches/index.tsx +++ b/src/Swatches/index.tsx @@ -2,6 +2,8 @@ import { useTheme } from 'antd-style'; import { memo } from 'react'; import { Flexbox } from 'react-layout-kit'; +import { useStyles } from './style'; + export interface SwatchesProps { /** * @description The currently active color @@ -17,42 +19,40 @@ export interface SwatchesProps { * @default undefined */ onSelect?: (c?: string | undefined) => void; + /** + * @description The size of swatch + * @default 24 + */ + size?: number; } -const Swatches = memo<SwatchesProps>(({ colors, activeColor, onSelect }) => { +const Swatches = memo<SwatchesProps>(({ colors, activeColor, onSelect, size = 24 }) => { const theme = useTheme(); + const { cx, styles } = useStyles(size); return ( <Flexbox gap={8} horizontal> <Flexbox + className={cx(styles.container, !activeColor && styles.active)} onClick={() => { onSelect?.(); }} style={{ background: theme.colorBgContainer, - borderRadius: '50%', - boxShadow: `inset 0 0 0px 2px ${activeColor ? 'rgba(0,0,0,0.1)' : theme.colorPrimary}`, - cursor: 'pointer', - height: 24, - width: 24, }} /> {colors.map((c) => { - const borderColor = c === activeColor ? theme.colorPrimary : 'rgba(0,0,0,0.1)'; + const isActive = c === activeColor; return ( <Flexbox + className={cx(styles.container, isActive && styles.active)} key={c} onClick={() => { onSelect?.(c); }} style={{ background: c, - borderRadius: '50%', - boxShadow: `inset 0 0 0px 2px ${borderColor}`, - cursor: 'pointer', - height: 24, - width: 24, }} /> ); diff --git a/src/Swatches/style.ts b/src/Swatches/style.ts new file mode 100644 index 00000000..c33f4ce7 --- /dev/null +++ b/src/Swatches/style.ts @@ -0,0 +1,25 @@ +import { createStyles } from 'antd-style'; + +export const useStyles = createStyles(({ css, token }, size: number) => { + return { + active: css` + box-shadow: inset 0 0 0 2px ${token.colorPrimary}; + `, + container: css` + cursor: pointer; + + width: ${size}px; + height: ${size}px; + + background: ${token.colorBgContainer}; + border-radius: 50%; + box-shadow: inset 0 0 0 1px ${token.colorFill}; + + transition: scale 400ms ${token.motionEaseOut}; + + &:active { + scale: 0.8; + } + `, + }; +}); diff --git a/src/ThemeProvider/GlobalStyle.ts b/src/ThemeProvider/GlobalStyle.ts index 65f1ed04..89e2486a 100644 --- a/src/ThemeProvider/GlobalStyle.ts +++ b/src/ThemeProvider/GlobalStyle.ts @@ -58,11 +58,13 @@ const GlobalStyle = createGlobalStyle` background: ${({ theme }) => theme.yellow9}; } + * { + box-sizing: border-box; + vertical-align: baseline; + } + @media only screen and (min-width: 574px) { * { - box-sizing: border-box; - vertical-align: baseline; - ::-webkit-scrollbar { cursor: pointer; width: 4px; diff --git a/src/Toc/style.ts b/src/Toc/style.ts index 60d745b7..e9139bef 100644 --- a/src/Toc/style.ts +++ b/src/Toc/style.ts @@ -62,10 +62,6 @@ export const useStyles = createStyles( .ant-collapse-header { z-index: 10; padding: 8px 16px !important; - - &[aria-expanded='true'] { - box-shadow: ${token.boxShadowSecondary}; - } } `, ), diff --git a/src/index.ts b/src/index.ts index 8f56143b..8c22274b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export { default as ActionIcon, type ActionIconProps, type ActionIconSize } from './ActionIcon'; export { default as ActionIconGroup, type ActionIconGroupProps } from './ActionIconGroup'; export { default as Avatar, type AvatarProps } from './Avatar'; +export { default as Burger, type BurgerProps } from './Burger'; export type { ChatMessage, MessageRoleType } from './Chat'; export { default as ChatInputArea, type ChatInputAreaProps } from './ChatInputArea'; export { default as ChatItem, type ChatItemProps } from './ChatItem'; @@ -69,5 +70,4 @@ export { default as ThemeSwitch, type ThemeSwitchProps } from './ThemeSwitch'; export { default as Toc, type TocProps } from './Toc'; export { default as TokenTag, type TokenTagProps } from './TokenTag'; export { default as Tooltip, type TooltipProps } from './Tooltip'; -export { type LobeCustomStylish } from './types/customStylish'; -export { type LobeCustomToken } from './types/customToken'; +export type * from './types'; diff --git a/src/types/customStylish.d.ts b/src/types/customStylish.ts similarity index 100% rename from src/types/customStylish.d.ts rename to src/types/customStylish.ts diff --git a/src/types/customToken.d.ts b/src/types/customToken.ts similarity index 100% rename from src/types/customToken.d.ts rename to src/types/customToken.ts diff --git a/src/types/index.ts b/src/types/index.ts index 2f0863ac..e072d651 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,11 @@ import { type HTMLAttributes } from 'react'; +export * from './chatMessage'; +export * from './customStylish'; +export * from './customToken'; +export * from './llm'; +export * from './meta'; + export type DivProps = HTMLAttributes<HTMLDivElement>; export type SvgProps = HTMLAttributes<SVGSVGElement>;