├── test ├── src │ ├── autogrid │ │ ├── index.css │ │ └── index.jsx │ ├── image-picker │ │ ├── index.css │ │ └── index.jsx │ ├── dialog │ │ ├── index.module.css │ │ └── index.jsx │ ├── privacy │ │ ├── index.css │ │ └── index.jsx │ ├── scrollview │ │ ├── index.css │ │ └── index.jsx │ ├── clickable │ │ ├── index.css │ │ └── index.jsx │ ├── index.jsx │ ├── indicator │ │ └── index.jsx │ ├── overlay │ │ └── index.jsx │ ├── index.css │ ├── loading │ │ └── index.jsx │ ├── ago │ │ └── index.jsx │ ├── carouse-notice │ │ └── index.jsx │ ├── index │ │ └── index.jsx │ ├── toast │ │ └── index.jsx │ ├── countdown │ │ └── index.jsx │ └── alert │ │ └── index.jsx ├── jsconfig.json ├── .gitignore ├── index.html ├── vite.config.js ├── package.json ├── eslint.config.js ├── README.md └── public │ └── vite.svg ├── .vscode └── settings.json ├── .npmignore ├── src ├── Indicator │ ├── style.ts │ └── index.tsx ├── Effect │ ├── useUpdate.ts │ ├── useTick.ts │ ├── useInterval.ts │ ├── useWindowResize.ts │ └── useViewport.ts ├── utils │ ├── uniqKey.ts │ ├── defaultScroll.ts │ ├── dom.tsx │ ├── jsonp.ts │ ├── tick.ts │ ├── wait.ts │ ├── calendarTable.ts │ ├── ago.ts │ ├── cssUtil.ts │ ├── is.ts │ ├── Countdown.ts │ ├── createApp.tsx │ └── request.ts ├── context.ts ├── ScrollView │ ├── style.ts │ └── index.tsx ├── CarouselNotice │ ├── style.ts │ └── index.tsx ├── AutoGrid │ ├── style.ts │ └── index.tsx ├── Ago │ └── index.tsx ├── SafeArea │ └── index.tsx ├── Loading │ ├── style.ts │ ├── index.tsx │ └── Wrapper.tsx ├── Alert │ ├── index.tsx │ ├── style.ts │ └── Wrapper.tsx ├── Toast │ ├── index.tsx │ ├── Toast.tsx │ └── style.ts ├── Flex │ ├── index.tsx │ ├── Row.tsx │ └── Col.tsx ├── Dialog │ ├── index.tsx │ ├── Wrapper.tsx │ └── style.ts ├── index.ts ├── Countdowner │ └── index.tsx ├── Overlay │ └── index.tsx ├── Clickable │ └── index.tsx └── Container │ └── index.tsx ├── LICENSE ├── .gitignore ├── package.json ├── tsconfig.json └── README.md /test/src/autogrid/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/src/image-picker/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib", 3 | "editor.tabSize": 2 4 | } -------------------------------------------------------------------------------- /test/src/dialog/index.module.css: -------------------------------------------------------------------------------- 1 | .contentBox { 2 | width: 7.5rem * 0.8; 3 | height: 4rem; 4 | background: goldenrod; 5 | } 6 | -------------------------------------------------------------------------------- /test/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["../build/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /scripts/ 2 | /examples/ 3 | /.vscode/ 4 | node_modules/ 5 | tsconfig.json 6 | package-lock.json 7 | /.npmignore 8 | /.gitignore 9 | /src/ 10 | .DS_Store -------------------------------------------------------------------------------- /test/src/privacy/index.css: -------------------------------------------------------------------------------- 1 | .Privacy { 2 | .item { 3 | border-bottom: 1px solid #f0f0f0; 4 | strong { 5 | color: blue; 6 | } 7 | i { 8 | font-size: .12rem; 9 | color: #af2d07; 10 | font-style: normal; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/src/scrollview/index.css: -------------------------------------------------------------------------------- 1 | .ScrollView { 2 | height: 400px; 3 | background-color: #f4f5f6; 4 | padding: 0 0.1rem; 5 | border: 1px solid #000; 6 | .content { 7 | height: 500px; 8 | background-image: linear-gradient(to top, #fff, green); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Indicator/style.ts: -------------------------------------------------------------------------------- 1 | import { keyframes } from '@emotion/react'; 2 | 3 | /** 4 | * 获取转圈每一条bar的过渡动画 5 | * @param color 6 | */ 7 | export function getBarChangeKeyFrames(color: string) { 8 | return keyframes` 9 | from { 10 | fill: ${color}; 11 | } 12 | to { 13 | fill: transparent; 14 | } 15 | `; 16 | } 17 | -------------------------------------------------------------------------------- /src/Effect/useUpdate.ts: -------------------------------------------------------------------------------- 1 | import { useReducer } from "react"; 2 | 3 | const updateReducer = (num: number): number => (num + 1) % 1_000_000; 4 | 5 | /** 6 | * 返回一个函数,调用该函数,组件会刷新一次 7 | * @returns 8 | */ 9 | export function useUpdate(): () => void { 10 | const [, update] = useReducer(updateReducer, 0); 11 | 12 | return update; 13 | } 14 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/utils/uniqKey.ts: -------------------------------------------------------------------------------- 1 | let keyIndex = 0; 2 | let last = Date.now(); 3 | 4 | /** 5 | * 生成一个全局唯一的key 6 | * @returns 7 | */ 8 | export function uniqKey() { 9 | keyIndex += 1; 10 | let now = Date.now(); 11 | if (now !== last && keyIndex > 1e9) { 12 | keyIndex = 0; 13 | } 14 | const key = now.toString(36) + keyIndex.toString(36); 15 | last = now; 16 | return key; 17 | } 18 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |简单测试
9 |最后一个
23 |常规圆角条
8 |直角条
10 |绿色条
12 |转圈速度变慢的绿色条
14 |20条
16 |条宽度变大
18 |条宽度和高度一致
20 |指示器尺寸变小
22 |11 | 12 |
13 |14 | 17 |
18 | 19 | {show1 && ( 20 |普通加载(5秒后自动关闭)
8 | 16 |普通加载带提示文字(5秒后自动关闭)
17 | 25 |至少展示2000毫秒
26 | 34 |2019-11-2
8 |2020-06-02
11 |2018/06/02
14 |2018/07/06 12:04:36
17 |Date.now()
20 |new Date("2012-07-16 12:30:06")
23 |(时间戳:毫秒)1583314718595
26 |
10 | // 13456567657(手机号)
11 | //
12 | // 包含模式:从第3位到第7位
13 | //
15 | //
22 | // 420281199509164579(身份证号)
23 | //
24 | // 排除模式:排除开头5位和结尾3位
25 | //
27 | //
轮播条目只有一条(不滚动):
8 |轮播条目为纯文本:
17 |轮播条目为任意React组件:
23 |29 | 第二条红色文字要小一些 30 |
, 31 |第3条滚动文字加粗
, 32 |{ 34 | alert('点击了第4条轮播条目'); 35 | }} 36 | style={{ textDecoration: 'underline' }} 37 | > 38 | 第4条可点击 39 |
, 40 | ]} 41 | style={{ background: '#f0f0f0' }} 42 | /> 43 | 44 |轮播条目左对齐:
45 |轮播条目右对齐:
52 |toast内容为纯文本
8 | 15 | 16 |toast内容为可定制组件
17 | 28 | 29 |toast内容在顶部显示
30 | 40 | 41 |toast内容在底部显示
42 | 52 | 53 |内容非常长的Toast
54 | 63 | 64 |全局唯一的Toast
65 | 72 |全局唯一的顶部Toast,永不消失
73 | 84 |格式:his,总时长:1000秒
51 |{show1}
52 |格式:s,总时长:10秒
53 |54 | {show2Closed ? ( 55 | 已触发结束事件 56 | ) : ( 57 | show2 58 | )} 59 |
60 |格式:dhis,总时长:259200秒
61 |{show3}
62 |下面是来自Countdowner组件的
64 |网格风格的图片选择器(多选)
9 | //网格风格的自定义选取按钮的图片选择器(多选)
34 | //普通选择器(多选,结果需要自己处理)
46 | //自定义选取按钮普通选择器(单选,结果需要自己处理)
64 | //限制最大宽度(单选,结果需要自己处理)
74 | //{content}
66 | ); 67 | } 68 | 69 | // toast消失动画结束触发 70 | const animationEnd = (event: React.AnimationEvent9 | 33 |
34 |
35 |
55 |
71 |
87 |
8 |
27 |
40 |
59 |
82 |
88 | 大标题
89 |
90 | ),
91 | showCancel: true,
92 | confirm: (
93 |
数据加载中...
231 |4 | 轻量级 React 移动端组件库 5 |
6 | 7 |8 | 基于 React 19 + TypeScript + Emotion 构建的现代化移动端 UI 组件库 9 |
10 | 11 | 16 | 17 | --- 18 | 19 | ## ✨ 特性 20 | 21 | - 🎯 **专为移动端设计** - 完美适配移动端交互和体验 22 | - 📱 **响应式布局** - 基于 rem 的自适应方案,支持多种屏幕尺寸 23 | - 🎨 **CSS-in-JS** - 使用 Emotion 实现样式隔离和动态样式 24 | - 🔧 **TypeScript** - 完整的类型定义,提供更好的开发体验 25 | - ⚡ **高性能** - 优化的渲染逻辑和事件处理 26 | - 🎪 **函数式调用** - Toast、Dialog、Loading 等支持命令式调用 27 | - 🚀 **现代化** - 支持 React 19,使用最新的 Hooks API 28 | - 📦 **轻量级** - 按需加载,tree-shaking 友好 29 | 30 | --- 31 | 32 | ## 📦 安装 33 | 34 | ```bash 35 | # npm 36 | npm install clxx 37 | 38 | # yarn 39 | yarn add clxx 40 | 41 | # pnpm 42 | pnpm add clxx 43 | ``` 44 | 45 | ### Peer Dependencies 46 | 47 | ```json 48 | { 49 | "react": "^19.2.0", 50 | "react-dom": "^19.2.0" 51 | } 52 | ``` 53 | 54 | --- 55 | 56 | ## 🚀 快速开始 57 | 58 | ### 基础使用 59 | 60 | ```tsx 61 | import React from 'react'; 62 | import { Container, showToast } from 'clxx'; 63 | 64 | function App() { 65 | return ( 66 |767 | Made with ❤️ by Joye 768 |
769 | --------------------------------------------------------------------------------