npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

do-comps

v0.2.62

Published

常用组件库,基于 MUI

Downloads

319

Readme

do-comps

发布:使用yarn build编译完成后,只需发布dist/main.*文件到npm。由于已在package.jsonfiles属性中配置,不需要手动删除dist中的其它文件

使用:在其它工程引用本库时,import的来源应为do-comps,不可以含路径,如do-comps/dist/main

正确的引用语句:

  import {DoAutocomplete, useSharedBackdrop, DoList, useSharedSnackbar} from "do-comps"

新添加组件的步骤

新建源码文件

src/components目录下,新建组件文件,格式为.tsx

编写组件

完成后,并导出组件props属性类型useShared*(可选)

   // 属性类型
   export type DoBackdropProps = {
      // 是否显示
      open: Boolean
   }
   
   // 在其它地方共享
   export const useSharedBackdrop = () => useBetween(useBackdrop)
   
   // 导出组件
   export default DoBackdrop

再次导出组件

main.tsx中再次统一导出。如果自动提示无效,可以手动输入组件路径导出

   import DoBackdrop, {DoBackdropProps, useSharedBackdrop} from "./components/DoBackdrop"
   
   export {DoBackdrop, useSharedBackdrop}
   export type {DoBackdropProps}

测试组件

  1. test文件夹中新建测试文件DoSnackbar_test.tsx,编写使用对应组件的代码
  2. index.tsx文件中添加第一步中测试组件的路由。如果该组件是全局组件,可以在Router上面一层全局引用
    <Route path="/DoSnackbar_Dialog" element={<DoSnackbarTest/>}/>
  3. App.tsx文件中添加路由的入口,注意to对应上步的path
    <Link to={"DoSnackbar_Dialog"}><Button size={"large"}>DoSnackbar_Dialog</Button></Link>
  4. 执行yarn start点击入口,测试功能

编译

  1. 执行yarn build

发布

  1. 根据文档开头的提示,删除不必要的文件、文件夹
  2. 修改package.json中的版本信息version
  3. 执行npm publish,推送到仓库

已编写组件

DoBackupPanel、DoBackupPanelChromium

组件:备份、恢复面板

  const handleClear = React.useCallback(() => {}, [])

  const handleRead = React.useCallback(async () => ({a: 123, b: "test."}), [])

  const handleRestore = React.useCallback(() => {}, [])

  return (
    <Stack>
       <DoBackupPanel title={"配置"} filename={"data.json"} onClear={handleClear}
                      onRead={handleRead} onRestore={handleRestore}/>

       {/* 需要在 chromium 扩展中引用才不报错 */}
       {/*<DoBackupPanelChromium/>*/}
    </Stack>
  )

DoSnackbar

组件:提示消息

全局引用<DoSnackbar/>,再在需要的地方修改显示showSb({open: true, message: "消息1。"})

  const {showSb} = useSharedSnackbar()

  return (
    <Stack>
       <DoSnackbar/>

       <Button onClick={() => showSb({open: true, message: "消息1。"})}>打开 Snakebar1</Button>
     
       <Button onClick={() => showSb({open: true, message: "消息2。"})}>打开 Snakebar</Button>
    </Stack>
)

DoDialog

组件:对话框

  const {showSb} = useSharedSnackbar()
  const {showDialog} = useSharedDialog()

  return (
    <Stack>
      <Button onClick={() => showDialog({
          open: true, title: "标题", message: "消息1。", content: <Button>测试按钮</Button>
      })}>
        打开 Dialog1
      </Button>

      <Button onClick={() => showDialog({
        open: true, title: "标题", message: "消息2。",
        modal: true,
        action: <Button onClick={() => showDialog({open: false})}>确定</Button>
      })}>
        打开 Dialog2
      </Button>

      <Button onClick={() => showDialog({
        open: true, title: "标题", message: "消息3。", fullWidth: true
      })}>
        打开 Dialog3
      </Button>
    </Stack>
)

DoDrawer

组件:抽屉组件

  const {showDoDrawer} = useSharedDrawer()

  return (
   <Stack>
      <DoDrawer/>

      <Button onClick={() => showDrawer({open: true, content: "测试抽屉"})}>
        打开 Drawer
      </Button>
    </Stack>
  )

DoBackdrop

组件:蒙版组件

  const {showDoBackdrop} = useSharedDrawer()

  return (
    <Stack>
       <DoBackdrop/>

       <Button onClick={() => showBackdrop({open: true, onClick: () => showBackdrop({open: false})})}>
          显示蒙版
       </Button>
    </Stack>
  )

DoList

组件:列表组件,滑动到上下两端时可加载更多数据

// 每次产生的数据
  const step = 20

  const [list, setList] = useState([...Array(step).keys()])

  const genContent = React.useMemo(() => list.map(item => <ListItem key={item + Math.random()}>{item}</ListItem>), [list])

  const handleLoadNext = React.useCallback(() => {
    setList(prev => [...prev, ...Array(step).keys()])
  }, [])

  const handleLoadPrev = React.useCallback(() => {
    setList(prev => [...Array(step).keys(), ...prev])
  }, [])

  return (
    <Stack direction={"row"} gap={2} height={"100%"}>
       <Stack>
          <div>{count}</div>
          <Button onClick={() => setCount(prev => ++prev)}>只更新计数时,不重新渲染 DoAutocomplete</Button>
       </Stack>

       <DoList width={"450px"} height={"100%"} border={"#AAA solid 1px"} 
               content={genContent} 
               onLoadNext={handleLoadNext} 
               onLoadPrev={handleLoadPrev}
       />
    </Stack>
  )

DoListAdd

组件:含添加新项的列表

// 组件外
  const source = [
   {
      id: 1,
      avatar: "https://huyaimg.msstatic.com/avatar/1050/ff/927a25d35eab6729f4cdb59f13ce37_180_135.jpg",
      primary: "hello",
      secondary: "world",
      isMarked: true
   },
   {
      id: 2,
      avatar: "https://huyaimg.msstatic.com/avatar/1050/ff/927a25d35eab6729f4cdb59f13ce37_180_135.jpg",
      primary: "zhang",
      secondary: "san"
   },
   {
      id: 3,
      avatar: "https://huyaimg.msstatic.com/avatar/1050/ff/927a25d35eab6729f4cdb59f13ce37_180_135.jpg",
      primary: "zhang",
      secondary: "san"
   },
   {
      id: 4,
      avatar: "",
      primary: "zhang",
      secondary: "san",
      isNewAdded: true,
      extra: <Stack direction={"row"} alignItems={"center"}>
         <span>测试</span>
         <Switch title={"开关1"}/>
         <Switch title={"开关2"}/>
      </Stack>
   }
]

  // 组件中
  const [list, setList] = useState<Array<DoLItemProps>>(source)

  const genSx = useMemo((): SxProps => ({width: 500}), [])
  const genSlot = useMemo((): JSX.Element => (<Button>测试按钮</Button>), [])

  const getInput = useMemo((): DoOptionsInputProps => (
        {
           size: "small",
           enterNode: "确定",
           onEnter(value: string, sList: Array<string>) {
              setList(prev => {
                 let n: DoLItemProps = {id: sList[0] + sList[1] + value, primary: value, avatar: ""}
                 return [...prev, n]
              })
           },
           optionsList: [{
              label: "平台",
              options: [{"title": "哔哩", "value": "bili", "tip": "主播的UID,不是房间号"}, {
                 "title": "斗鱼",
                 "value": "douyu",
                 "tip": "斗鱼房间号"
              }]
           }, {
              label: "地区",
              options: [{"title": "国区", "value": "cn", "tip": "国区"}, {"title": "俄罗斯", "value": "ru", "tip": "俄罗斯"}]
           }],
           placeholder: "输入框"
        }
), [])

  return (
    <Stack direction={"row"} gap={2} height={"100%"}>
       <Stack>
          <div>{count}</div>
          <Button onClick={() => setCount(prev => ++prev)}>只更新计数时,不重新渲染</Button>
       </Stack>
       
       <DoListAdd sx={genSx} title={"列表"} slot={genSlot} list={list} inputProps={getInput}/>
    </Stack>
  )

DoOptionsInput

组件:带选择框(可多个)的输入面板

  // 组件外
  const list = [
   {
      label: "平台",
      options: [
         {"title": "哔哩", "value": "bili", "tip": "主播的UID,不是房间号"},
         {"title": "斗鱼", "value": "douyu", "tip": "斗鱼房间号"}
      ]
   },
   {
      label: "地区",
      options: [
         {"title": "国区", "value": "cn", "tip": "国区"},
         {"title": "美区", "value": "us", "tip": "美区"}
      ]
   }
]

  // 组件中
  const handleEnter = React.useCallback((value: string, sList: string[]) => alert(value), [])

  return (
    <Stack>
       <Button onClick={() => setCount(prev => ++prev)}>只更新计数时,不重新渲染</Button>
       
       <DoOptionsInput size={"small"} optionsList={list} enterNode={"确定"} 
                       placeholder={"提示输入"} onEnter={handleEnter}/>
    </Stack>
  )

DoTextFieldBtn

组件:带确认按钮的文本输入框

  const handleEnter = React.useCallback((v: string) => console.log(v), [])
  const genSvgIcon = React.useMemo(() => <Visibility/>, [])

  return (
    <Stack padding={1} gap={2}>
       <DoTextFieldBtn label={"输入框1"} enterNode={"确定"} onEnter={handleEnter} clearAfterEnter/>
       <DoTextFieldBtn label={"输入框3"} size={"small"} enterNode={genSvgIcon} onEnter={handleEnter}/>
    </Stack>
  )

DoPasswdField

组件:密码输入框,可明文显示输入的密码

  const [pass, setPass] = React.useState("")

  return (
    <DoPasswdField label={"密码"} value={pass} setValue={setPass}/>
  )

DoPanel

组件:默认固定高度为窗口高度,分 3 层的垂直面板,中间内容区可滚动

可通过指定属性isRow,转为水平布局的面板

  const v = React.useMemo(() => 
        Array.from(Array(100).keys()).map(item => <li key={item}>{item}</li>), [])

  const genDivider = React.useMemo(() => <Divider/>, [])

  // 如果直接作为 children 组件放在 DoPanel 中,
  // 每次当前组件更新时,传递到 DoPanel 的 children 组件的引用都不不一样
  // 可能导致不必要的更新
  // 所以使用`useMemo`生成
  const genChild = React.useMemo(() => (<>
     <DoPanelHeader divider={genDivider}>
        <DoTextTitle>标题</DoTextTitle>
        <Button>按钮</Button>
     </DoPanelHeader>
     <DoPanelContent component={"ul"}>{v}</DoPanelContent>
     <DoPanelFooter divider={genDivider}>底部</DoPanelFooter>
  </>), [v, genDivider])

  return (
    <DoPanel divider={genDivider} width={"500px"}>
       {genChild}
    </DoPanel>
  )

DoText

组件:可设置行数的文本组件

  const handleTitleClick = React.useCallback(() => console.log("点击了标题"), [])

  return (
    <Stack gap={2}>
       <DoTextTitle onClick={handleTitleClick}>测试标题</DoTextTitle>

       <DoText lines={2} color={"green"}>是的,它是一个比率:1.5 表示元素字体大小的 1.5 倍。所以它的含义与
          相同,但有一个重要例外:在继承中,当使用纯数字时,继承的是数字,而不是计算值。 因此,如果您有一个字体大小为 24pt
       </DoText>

       <DoText>
          but the problem is that this code makes everything in one-line. I want to go with 3 lines of text then... I
          in the Google about it and I find out we have a WebKit for it
       </DoText>
    </Stack>
  )

DoFileUpload

组件:文件上传组件

基础用法:

  const handleUpoad = React.useCallback((name: string) => {
    console.log("开始上传文件:", name)
  }, [])

  return (
    <Stack>
       <Button onClick={() => {
         (document.querySelector("#abc") as HTMLElement).click()
       }}>上传文件</Button>

       <DoFileUpload id={"abc"} apiURL={""} onUpload={handleUpoad}/>
    </Stack>
  )

高级用法

当需要传递请求头headers,而值需要通过await异步获取时,可以使用setState,在点击了Button时更新setHeaders即可

DoAutocomplete

组件:自动完成输入框组件

  const [options, setOptions] = useState(["选项1", "选项2", "选项3"])

  const handleEnter = React.useCallback((option: string) => alert(`点击了选项 ${option}`), [])

  const handleDel = React.useCallback((option: string) => setOptions(prev => {
    let n = [...prev]
     n.splice(n.findIndex(item => item === option), 1)
     return n
  }), [])

  return (
    <DoAutocomplete label={"输入姓名"} options={options} onEnter={handleEnter} onDelOption={handleDel}/>
  )

delRevoke、delRevokeArray

函数:删除、撤销

  const [nums, setNums] = useState([1, 2, 3])
  const [nums2, setNums2] = useState([19, 21, 32])

  const {showSb} = useSharedSnackbar()

  const list1 = nums.map((item, index) => <li key={item}>
     <Button onClick={() => delRevoke<Array<number>>(item, nums, () => {
        // 删除
        setNums(prev => {
          let newArray = [...prev]
           newArray.splice(index, 1)
           return newArray
         })
     }, (data) => {
        // 撤销
        setNums(data)
    }, showSb)}>{item}
    </Button>
  </li>)

  const list2 = nums2.map((item) => <li key={item}>
     <Button onClick={() => delRevokeArray<number>(item, showSb, item, nums2,
                   (d1, d2) => d1 === d2,
                   newDataList => setNums2(newDataList)
    )}>{item}
    </Button>
  </li>)

  return (
    <Stack spacing={3}>
       <ul>{list1}</ul>
       
      <ul>{list2}</ul>
    </Stack>
  )