clxx 2.1.3 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +768 -35
- package/build/Ago/index.d.ts +0 -1
- package/build/Alert/Wrapper.d.ts +0 -1
- package/build/Alert/Wrapper.js +12 -12
- package/build/Alert/index.js +2 -2
- package/build/AutoGrid/index.js +16 -21
- package/build/AutoGrid/style.d.ts +5 -5
- package/build/CarouselNotice/index.d.ts +0 -1
- package/build/CarouselNotice/index.js +6 -6
- package/build/CarouselNotice/style.d.ts +6 -1
- package/build/CarouselNotice/style.js +7 -7
- package/build/Clickable/index.d.ts +5 -5
- package/build/Clickable/index.js +11 -8
- package/build/Container/index.d.ts +2 -2
- package/build/Container/index.js +85 -60
- package/build/Countdowner/index.d.ts +3 -3
- package/build/Countdowner/index.js +18 -11
- package/build/Dialog/Wrapper.d.ts +0 -1
- package/build/Dialog/Wrapper.js +7 -10
- package/build/Dialog/style.d.ts +15 -10
- package/build/Dialog/style.js +88 -88
- package/build/Effect/useInterval.d.ts +1 -1
- package/build/Effect/useInterval.js +1 -1
- package/build/Effect/useUpdate.d.ts +1 -1
- package/build/Effect/useUpdate.js +1 -1
- package/build/Effect/useWindowResize.d.ts +4 -2
- package/build/Effect/useWindowResize.js +28 -11
- package/build/Flex/index.d.ts +0 -1
- package/build/Indicator/index.js +22 -23
- package/build/Indicator/style.d.ts +6 -1
- package/build/Indicator/style.js +7 -7
- package/build/Loading/Wrapper.js +2 -2
- package/build/Loading/style.d.ts +12 -2
- package/build/Loading/style.js +14 -14
- package/build/Overlay/index.d.ts +1 -1
- package/build/Overlay/index.js +41 -37
- package/build/SafeArea/index.d.ts +1 -2
- package/build/SafeArea/index.js +6 -6
- package/build/ScrollView/index.d.ts +1 -1
- package/build/ScrollView/index.js +111 -27
- package/build/Toast/Toast.js +4 -4
- package/build/Toast/style.d.ts +42 -12
- package/build/Toast/style.js +54 -54
- package/build/index.d.ts +3 -0
- package/build/index.js +3 -0
- package/build/utils/Countdown.d.ts +3 -1
- package/build/utils/Countdown.js +5 -2
- package/build/utils/createApp.d.ts +14 -13
- package/build/utils/createApp.js +58 -42
- package/build/utils/cssUtil.d.ts +1 -1
- package/build/utils/defaultScroll.d.ts +0 -1
- package/build/utils/defaultScroll.js +8 -5
- package/build/utils/is.d.ts +23 -4
- package/build/utils/is.js +97 -15
- package/build/utils/jsonp.js +2 -2
- package/build/utils/request.js +12 -2
- package/package.json +15 -12
- package/test/README.md +16 -0
- package/test/eslint.config.js +29 -0
- package/test/index.html +13 -0
- package/test/jsconfig.json +8 -0
- package/test/package.json +27 -0
- package/test/public/vite.svg +1 -0
- package/test/src/ago/index.jsx +30 -0
- package/test/src/alert/index.jsx +111 -0
- package/test/src/autogrid/index.css +0 -0
- package/test/src/autogrid/index.jsx +26 -0
- package/test/src/carouse-notice/index.jsx +59 -0
- package/test/src/clickable/index.css +21 -0
- package/test/src/clickable/index.jsx +39 -0
- package/test/src/countdown/index.jsx +95 -0
- package/test/src/dialog/index.jsx +104 -0
- package/test/src/dialog/index.module.css +5 -0
- package/test/src/image-picker/index.css +0 -0
- package/test/src/image-picker/index.jsx +88 -0
- package/test/src/index/index.jsx +46 -0
- package/test/src/index.css +49 -0
- package/test/src/index.jsx +31 -0
- package/test/src/indicator/index.jsx +25 -0
- package/test/src/loading/index.jsx +36 -0
- package/test/src/overlay/index.jsx +31 -0
- package/test/src/privacy/index.css +13 -0
- package/test/src/privacy/index.jsx +34 -0
- package/test/src/scrollview/index.css +10 -0
- package/test/src/scrollview/index.jsx +52 -0
- package/test/src/toast/index.jsx +86 -0
- package/test/vite.config.js +15 -0
package/README.md
CHANGED
|
@@ -1,35 +1,768 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
# CLXX
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>轻量级 React 移动端组件库</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
基于 React 19 + TypeScript + Emotion 构建的现代化移动端 UI 组件库
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/clxx"><img src="https://img.shields.io/npm/v/clxx.svg" alt="npm version"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/clxx"><img src="https://img.shields.io/npm/dm/clxx.svg" alt="npm downloads"></a>
|
|
14
|
+
<a href="https://github.com/joye61/clxx/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/clxx.svg" alt="license"></a>
|
|
15
|
+
</p>
|
|
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
|
+
<Container designWidth={750}>
|
|
67
|
+
<button onClick={() => showToast('Hello CLXX!')}>
|
|
68
|
+
点击显示 Toast
|
|
69
|
+
</button>
|
|
70
|
+
</Container>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default App;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 使用路由创建应用
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { createApp } from 'clxx';
|
|
81
|
+
|
|
82
|
+
createApp({
|
|
83
|
+
target: '#root',
|
|
84
|
+
designWidth: 750,
|
|
85
|
+
routeMethod: 'hash',
|
|
86
|
+
renderPage: async (pathname) => {
|
|
87
|
+
// 动态加载页面组件
|
|
88
|
+
const Page = await import(`./pages${pathname}`);
|
|
89
|
+
return <Page.default />;
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 📚 组件文档
|
|
97
|
+
|
|
98
|
+
### 🎨 布局组件
|
|
99
|
+
|
|
100
|
+
#### Container - 容器组件
|
|
101
|
+
|
|
102
|
+
全局根容器,提供移动端自适应、rem 布局和初始化逻辑。
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { Container } from 'clxx';
|
|
106
|
+
|
|
107
|
+
<Container
|
|
108
|
+
designWidth={750} // 设计稿宽度,默认 750
|
|
109
|
+
resizeDelay={100} // resize 防抖延迟,默认 100ms
|
|
110
|
+
globalStyle={css`...`} // 全局样式
|
|
111
|
+
>
|
|
112
|
+
<YourApp />
|
|
113
|
+
</Container>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**特性**:
|
|
117
|
+
- ✅ 自动 rem 适配
|
|
118
|
+
- ✅ 处理浏览器字体缩放
|
|
119
|
+
- ✅ 防止初始化闪烁
|
|
120
|
+
- ✅ 支持自定义全局样式
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
#### Flex 布局
|
|
125
|
+
|
|
126
|
+
提供快捷的 Flex 布局组件。
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { Row, RowCenter, RowBetween, Col, ColCenter } from 'clxx';
|
|
130
|
+
|
|
131
|
+
// 水平布局
|
|
132
|
+
<RowCenter>
|
|
133
|
+
<div>居中对齐</div>
|
|
134
|
+
</RowCenter>
|
|
135
|
+
|
|
136
|
+
<RowBetween>
|
|
137
|
+
<div>两端对齐</div>
|
|
138
|
+
</RowBetween>
|
|
139
|
+
|
|
140
|
+
// 垂直布局
|
|
141
|
+
<ColCenter>
|
|
142
|
+
<div>垂直居中</div>
|
|
143
|
+
</ColCenter>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**可用组件**:
|
|
147
|
+
- `Row` / `RowStart` - 水平布局(左对齐)
|
|
148
|
+
- `RowCenter` - 水平居中
|
|
149
|
+
- `RowEnd` - 水平右对齐
|
|
150
|
+
- `RowBetween` - 两端对齐
|
|
151
|
+
- `RowAround` - 周围分布
|
|
152
|
+
- `RowEvenly` - 均匀分布
|
|
153
|
+
- `Col` / `ColStart` / `ColCenter` / `ColEnd` / `ColBetween` / `ColAround` / `ColEvenly` - 对应的垂直布局
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
#### AutoGrid - 自动网格
|
|
158
|
+
|
|
159
|
+
自动计算并排列网格布局。
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { AutoGrid } from 'clxx';
|
|
163
|
+
|
|
164
|
+
<AutoGrid
|
|
165
|
+
cols={3} // 列数
|
|
166
|
+
gap="10px" // 间距
|
|
167
|
+
isSquare={true} // 是否正方形
|
|
168
|
+
>
|
|
169
|
+
<div>Item 1</div>
|
|
170
|
+
<div>Item 2</div>
|
|
171
|
+
<div>Item 3</div>
|
|
172
|
+
</AutoGrid>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
#### SafeArea - 安全区域
|
|
178
|
+
|
|
179
|
+
处理手机刘海屏、底部横条等安全区域。
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import { SafeArea } from 'clxx';
|
|
183
|
+
|
|
184
|
+
<SafeArea type="top">顶部安全区域</SafeArea>
|
|
185
|
+
<SafeArea type="bottom">底部安全区域</SafeArea>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### 🎭 交互组件
|
|
191
|
+
|
|
192
|
+
#### Clickable - 点击态
|
|
193
|
+
|
|
194
|
+
提供点击反馈效果的容器组件。
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { Clickable } from 'clxx';
|
|
198
|
+
|
|
199
|
+
<Clickable
|
|
200
|
+
activeStyle={{ opacity: 0.6 }} // 激活样式
|
|
201
|
+
bubble={false} // 是否冒泡
|
|
202
|
+
onClick={() => console.log('clicked')}
|
|
203
|
+
>
|
|
204
|
+
点击我
|
|
205
|
+
</Clickable>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
#### Overlay - 遮罩层
|
|
211
|
+
|
|
212
|
+
全屏或局部遮罩层组件。
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { Overlay } from 'clxx';
|
|
216
|
+
|
|
217
|
+
<Overlay
|
|
218
|
+
fullScreen={true} // 是否全屏
|
|
219
|
+
centerContent={true} // 内容是否居中
|
|
220
|
+
maskColor="rgba(0,0,0,0.6)" // 遮罩颜色
|
|
221
|
+
outside={true} // 是否渲染到 body
|
|
222
|
+
>
|
|
223
|
+
<div>遮罩内容</div>
|
|
224
|
+
</Overlay>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
#### ScrollView - 滚动容器
|
|
230
|
+
|
|
231
|
+
带触底、触顶检测的滚动容器。
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { ScrollView } from 'clxx';
|
|
235
|
+
|
|
236
|
+
<ScrollView
|
|
237
|
+
height="100vh"
|
|
238
|
+
reachBottomThreshold={50}
|
|
239
|
+
onReachBottom={(e) => {
|
|
240
|
+
console.log('到底了', e);
|
|
241
|
+
}}
|
|
242
|
+
onReachTop={(e) => {
|
|
243
|
+
console.log('到顶了', e);
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
<div>滚动内容</div>
|
|
247
|
+
</ScrollView>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### 💬 反馈组件
|
|
253
|
+
|
|
254
|
+
#### Toast - 轻提示
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
import { showToast, showUniqToast } from 'clxx';
|
|
258
|
+
|
|
259
|
+
// 基础用法
|
|
260
|
+
showToast('操作成功');
|
|
261
|
+
|
|
262
|
+
// 完整配置
|
|
263
|
+
showToast({
|
|
264
|
+
content: '操作成功',
|
|
265
|
+
position: 'middle', // top | middle | bottom
|
|
266
|
+
duration: 2000,
|
|
267
|
+
offsetTop: 50,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// 全局唯一 Toast(新的会替换旧的)
|
|
271
|
+
showUniqToast('只显示一个');
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
#### Dialog - 对话框
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
import { showDialog } from 'clxx';
|
|
280
|
+
|
|
281
|
+
const close = showDialog({
|
|
282
|
+
content: <div>对话框内容</div>,
|
|
283
|
+
type: 'center', // center | pullUp | pullDown | pullLeft | pullRight
|
|
284
|
+
blankClosable: true, // 点击空白处关闭
|
|
285
|
+
showMask: true, // 显示遮罩
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// 手动关闭
|
|
289
|
+
close();
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
#### Alert - 警告框
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import { showAlert } from 'clxx';
|
|
298
|
+
|
|
299
|
+
showAlert({
|
|
300
|
+
title: '提示',
|
|
301
|
+
description: '确定要删除吗?',
|
|
302
|
+
showCancel: true,
|
|
303
|
+
onConfirm: () => console.log('确认'),
|
|
304
|
+
onCancel: () => console.log('取消'),
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
#### Loading - 加载中
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
import { showLoading, showLoadingAtLeast } from 'clxx';
|
|
314
|
+
|
|
315
|
+
// 基础用法
|
|
316
|
+
const close = showLoading('加载中...');
|
|
317
|
+
// 关闭
|
|
318
|
+
close();
|
|
319
|
+
|
|
320
|
+
// 至少显示指定时间(避免闪烁)
|
|
321
|
+
const close = showLoadingAtLeast(300, '加载中...');
|
|
322
|
+
// 即使立即调用 close(),也会至少显示 300ms
|
|
323
|
+
close();
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
### 🎪 展示组件
|
|
329
|
+
|
|
330
|
+
#### Indicator - 加载指示器
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
import { Indicator } from 'clxx';
|
|
334
|
+
|
|
335
|
+
<Indicator
|
|
336
|
+
size={60}
|
|
337
|
+
barCount={12}
|
|
338
|
+
barColor="#fff"
|
|
339
|
+
duration={600}
|
|
340
|
+
/>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
#### Ago - 相对时间
|
|
346
|
+
|
|
347
|
+
显示相对时间(多久以前)。
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { Ago } from 'clxx';
|
|
351
|
+
|
|
352
|
+
<Ago date={new Date()} />
|
|
353
|
+
// 输出:刚刚
|
|
354
|
+
|
|
355
|
+
<Ago
|
|
356
|
+
date="2024-01-01"
|
|
357
|
+
renderContent={(value) => `${value.num}${value.unit}前`}
|
|
358
|
+
/>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
#### Countdowner - 倒计时
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
import { Countdowner } from 'clxx';
|
|
367
|
+
|
|
368
|
+
<Countdowner
|
|
369
|
+
remain={3600} // 剩余秒数
|
|
370
|
+
format="his" // 格式:d(天) h(时) i(分) s(秒)
|
|
371
|
+
seperator=":" // 分隔符
|
|
372
|
+
onEnd={() => console.log('倒计时结束')}
|
|
373
|
+
/>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
#### CarouselNotice - 滚动公告
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
import { CarouselNotice } from 'clxx';
|
|
382
|
+
|
|
383
|
+
<CarouselNotice
|
|
384
|
+
list={['公告1', '公告2', '公告3']}
|
|
385
|
+
height="40px"
|
|
386
|
+
interval={3000}
|
|
387
|
+
duration={200}
|
|
388
|
+
/>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## 🛠️ 工具函数
|
|
394
|
+
|
|
395
|
+
### 网络请求
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
import { GET, POST, sendJSON, jsonp } from 'clxx';
|
|
399
|
+
|
|
400
|
+
// GET 请求
|
|
401
|
+
const data = await GET('/api/users', { page: 1 });
|
|
402
|
+
|
|
403
|
+
// POST 请求(FormData)
|
|
404
|
+
const result = await POST('/api/login', {
|
|
405
|
+
username: 'admin',
|
|
406
|
+
password: '123456'
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// 发送 JSON
|
|
410
|
+
const result = await sendJSON('/api/data', { data: {...} });
|
|
411
|
+
|
|
412
|
+
// JSONP 请求
|
|
413
|
+
const data = await jsonp('https://api.example.com/data');
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**高级配置**:
|
|
417
|
+
```tsx
|
|
418
|
+
import { sendRequest, registerHostAlias } from 'clxx';
|
|
419
|
+
|
|
420
|
+
// 注册 Host 别名
|
|
421
|
+
registerHostAlias({
|
|
422
|
+
api: 'https://api.example.com',
|
|
423
|
+
cdn: 'https://cdn.example.com'
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// 使用别名
|
|
427
|
+
await GET('api@/users'); // 实际请求:https://api.example.com/users
|
|
428
|
+
|
|
429
|
+
// 完整配置
|
|
430
|
+
const result = await sendRequest({
|
|
431
|
+
url: '/api/data',
|
|
432
|
+
method: 'POST',
|
|
433
|
+
sendType: 'json',
|
|
434
|
+
data: { key: 'value' },
|
|
435
|
+
timeout: 5000,
|
|
436
|
+
disableUrlCache: true,
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
### 时间处理
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
import { ago, calendarTable, Countdown, waitFor, waitUntil } from 'clxx';
|
|
446
|
+
|
|
447
|
+
// 相对时间
|
|
448
|
+
const result = ago('2024-01-01');
|
|
449
|
+
console.log(result.format); // "3个月前"
|
|
450
|
+
|
|
451
|
+
// 日历表格
|
|
452
|
+
const table = calendarTable('2024-10', false, true);
|
|
453
|
+
// 返回 6x7 的日期数组
|
|
454
|
+
|
|
455
|
+
// 倒计时器
|
|
456
|
+
const countdown = new Countdown({
|
|
457
|
+
remain: 3600,
|
|
458
|
+
format: 'his',
|
|
459
|
+
onUpdate: (value) => console.log(value),
|
|
460
|
+
onEnd: () => console.log('结束'),
|
|
461
|
+
});
|
|
462
|
+
countdown.start();
|
|
463
|
+
|
|
464
|
+
// 等待指定时间
|
|
465
|
+
await waitFor(1000);
|
|
466
|
+
|
|
467
|
+
// 等待条件满足
|
|
468
|
+
await waitUntil(() => document.querySelector('.loaded'), 5000);
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
### 工具类
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
import {
|
|
477
|
+
tick,
|
|
478
|
+
uniqKey,
|
|
479
|
+
defaultScroll,
|
|
480
|
+
createPortalDOM,
|
|
481
|
+
is,
|
|
482
|
+
normalizeUnit,
|
|
483
|
+
adaptive
|
|
484
|
+
} from 'clxx';
|
|
485
|
+
|
|
486
|
+
// 逐帧执行
|
|
487
|
+
const stop = tick(() => {
|
|
488
|
+
console.log('每帧执行');
|
|
489
|
+
}, 16); // 可选的间隔时间
|
|
490
|
+
stop(); // 停止
|
|
491
|
+
|
|
492
|
+
// 生成唯一 Key
|
|
493
|
+
const key = uniqKey(); // "1730123456789_1"
|
|
494
|
+
|
|
495
|
+
// 禁用/启用滚动
|
|
496
|
+
defaultScroll.disable();
|
|
497
|
+
defaultScroll.enable();
|
|
498
|
+
|
|
499
|
+
// 创建 Portal DOM
|
|
500
|
+
const portal = createPortalDOM();
|
|
501
|
+
portal.mount(<YourComponent />);
|
|
502
|
+
portal.unmount();
|
|
503
|
+
|
|
504
|
+
// 环境判断
|
|
505
|
+
if (is('ios')) { /* iOS 设备 */ }
|
|
506
|
+
if (is('android')) { /* Android 设备 */ }
|
|
507
|
+
if (is('mobile')) { /* 移动设备 */ }
|
|
508
|
+
if (is('touchable')) { /* 支持触摸 */ }
|
|
509
|
+
|
|
510
|
+
// 单位标准化
|
|
511
|
+
normalizeUnit(100); // "100px"
|
|
512
|
+
normalizeUnit('50rem'); // "50rem"
|
|
513
|
+
normalizeUnit(0.5, '%'); // "0.5%"
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 🎯 自定义 Hooks
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
import {
|
|
522
|
+
useInterval,
|
|
523
|
+
useTick,
|
|
524
|
+
useUpdate,
|
|
525
|
+
useWindowResize,
|
|
526
|
+
useViewport
|
|
527
|
+
} from 'clxx';
|
|
528
|
+
|
|
529
|
+
// 定时器 Hook
|
|
530
|
+
useInterval(() => {
|
|
531
|
+
console.log('每秒执行');
|
|
532
|
+
}, 1000);
|
|
533
|
+
|
|
534
|
+
// 逐帧执行 Hook
|
|
535
|
+
useTick(() => {
|
|
536
|
+
console.log('每帧执行');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// 强制更新
|
|
540
|
+
const update = useUpdate();
|
|
541
|
+
update(); // 触发组件重新渲染
|
|
542
|
+
|
|
543
|
+
// 窗口大小变化
|
|
544
|
+
useWindowResize(() => {
|
|
545
|
+
console.log('窗口大小改变');
|
|
546
|
+
}, 100); // 防抖 100ms
|
|
547
|
+
|
|
548
|
+
// Viewport 设置
|
|
549
|
+
useViewport({
|
|
550
|
+
width: 'device-width',
|
|
551
|
+
initialScale: 1,
|
|
552
|
+
userScalable: 'no',
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## 🎨 样式工具
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
import { css } from '@emotion/react';
|
|
562
|
+
import { adaptive, normalizeUnit, splitValue } from 'clxx';
|
|
563
|
+
|
|
564
|
+
// 自适应样式(基于 750 设计稿)
|
|
565
|
+
const styles = adaptive({
|
|
566
|
+
width: 375, // 自动转换为 vw + 媒体查询
|
|
567
|
+
height: 200,
|
|
568
|
+
padding: 20,
|
|
569
|
+
fontSize: 28,
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// 拆分数值和单位
|
|
573
|
+
const { num, unit } = splitValue('100px');
|
|
574
|
+
console.log(num, unit); // 100, "px"
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## ⚙️ 配置
|
|
580
|
+
|
|
581
|
+
### 全局配置
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
import { setContextValue } from 'clxx';
|
|
585
|
+
|
|
586
|
+
// 设置文档宽度范围
|
|
587
|
+
setContextValue({
|
|
588
|
+
maxDocWidth: 576, // 最大宽度
|
|
589
|
+
minDocWidth: 312, // 最小宽度
|
|
590
|
+
});
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## 📱 最佳实践
|
|
596
|
+
|
|
597
|
+
### 1. 项目入口配置
|
|
598
|
+
|
|
599
|
+
```tsx
|
|
600
|
+
import React from 'react';
|
|
601
|
+
import ReactDOM from 'react-dom/client';
|
|
602
|
+
import { Container } from 'clxx';
|
|
603
|
+
import App from './App';
|
|
604
|
+
|
|
605
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
606
|
+
|
|
607
|
+
root.render(
|
|
608
|
+
<Container
|
|
609
|
+
designWidth={750}
|
|
610
|
+
resizeDelay={100}
|
|
611
|
+
>
|
|
612
|
+
<App />
|
|
613
|
+
</Container>
|
|
614
|
+
);
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### 2. 使用路由
|
|
618
|
+
|
|
619
|
+
```tsx
|
|
620
|
+
import { createApp, setContextValue } from 'clxx';
|
|
621
|
+
|
|
622
|
+
// 配置全局上下文
|
|
623
|
+
setContextValue({
|
|
624
|
+
maxDocWidth: 576,
|
|
625
|
+
minDocWidth: 312,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// 创建应用
|
|
629
|
+
createApp({
|
|
630
|
+
target: '#root',
|
|
631
|
+
designWidth: 750,
|
|
632
|
+
routeMethod: 'hash',
|
|
633
|
+
|
|
634
|
+
// 加载前
|
|
635
|
+
onBeforeRenderPage: async (pathname) => {
|
|
636
|
+
console.log('准备加载:', pathname);
|
|
637
|
+
},
|
|
638
|
+
|
|
639
|
+
// 加载中
|
|
640
|
+
onLoadingPage: () => <div>Loading...</div>,
|
|
641
|
+
|
|
642
|
+
// 渲染页面
|
|
643
|
+
renderPage: async (pathname) => {
|
|
644
|
+
const modules = {
|
|
645
|
+
'/': () => import('./pages/Home'),
|
|
646
|
+
'/about': () => import('./pages/About'),
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const loadModule = modules[pathname] || modules['/'];
|
|
650
|
+
const module = await loadModule();
|
|
651
|
+
return <module.default />;
|
|
652
|
+
},
|
|
653
|
+
|
|
654
|
+
// 加载后
|
|
655
|
+
onAfterRenderPage: (pathname) => {
|
|
656
|
+
document.title = pathname;
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### 3. 性能优化建议
|
|
662
|
+
|
|
663
|
+
```tsx
|
|
664
|
+
// 1. 使用防抖优化 resize
|
|
665
|
+
<Container resizeDelay={150} />
|
|
666
|
+
|
|
667
|
+
// 2. 使用 showUniqToast 避免多个 Toast 堆叠
|
|
668
|
+
showUniqToast('只显示最新的');
|
|
669
|
+
|
|
670
|
+
// 3. Loading 至少显示一定时间避免闪烁
|
|
671
|
+
const close = showLoadingAtLeast(300, '加载中...');
|
|
672
|
+
|
|
673
|
+
// 4. ScrollView 使用合适的阈值
|
|
674
|
+
<ScrollView
|
|
675
|
+
reachBottomThreshold={100}
|
|
676
|
+
onReachBottom={loadMore}
|
|
677
|
+
/>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## 🔧 TypeScript 支持
|
|
683
|
+
|
|
684
|
+
所有组件和函数都提供完整的 TypeScript 类型定义:
|
|
685
|
+
|
|
686
|
+
```tsx
|
|
687
|
+
import type {
|
|
688
|
+
ContainerProps,
|
|
689
|
+
ToastProps,
|
|
690
|
+
DialogProps,
|
|
691
|
+
OverlayProps,
|
|
692
|
+
// ... 更多类型
|
|
693
|
+
} from 'clxx';
|
|
694
|
+
|
|
695
|
+
// 自定义扩展
|
|
696
|
+
import { showToast } from 'clxx';
|
|
697
|
+
|
|
698
|
+
const myShowToast = (message: string) => {
|
|
699
|
+
showToast({
|
|
700
|
+
content: message,
|
|
701
|
+
duration: 3000,
|
|
702
|
+
position: 'middle',
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## 📋 浏览器支持
|
|
710
|
+
|
|
711
|
+
- iOS Safari 10+
|
|
712
|
+
- Android Chrome 5.0+
|
|
713
|
+
- 现代浏览器(Chrome, Firefox, Safari, Edge)
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## 🤝 贡献
|
|
718
|
+
|
|
719
|
+
欢迎提交 Issue 和 Pull Request!
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## 📄 License
|
|
724
|
+
|
|
725
|
+
[MIT](./LICENSE)
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## 🔗 相关链接
|
|
730
|
+
|
|
731
|
+
- [GitHub](https://github.com/joye61/clxx)
|
|
732
|
+
- [NPM](https://www.npmjs.com/package/clxx)
|
|
733
|
+
- [Issues](https://github.com/joye61/clxx/issues)
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## 📝 更新日志
|
|
738
|
+
|
|
739
|
+
### v2.1.4 (2025-10-28)
|
|
740
|
+
|
|
741
|
+
#### 🎉 新增
|
|
742
|
+
- 新增 `useUpdate`、`useWindowResize`、`useViewport` Hooks 导出
|
|
743
|
+
- Container 组件新增 `resizeDelay` 配置项
|
|
744
|
+
|
|
745
|
+
#### 🐛 Bug 修复
|
|
746
|
+
- 修复 Overlay 组件 resize 性能问题
|
|
747
|
+
- 修复 Toast 组件定时器清理错误
|
|
748
|
+
- 修复 Countdowner 依赖数组缺失
|
|
749
|
+
- 修复 SafeArea 类型定义
|
|
750
|
+
- 修复字体缩放可能导致的无限循环
|
|
751
|
+
|
|
752
|
+
#### ⚡ 性能优化
|
|
753
|
+
- Overlay 组件添加 resize 防抖
|
|
754
|
+
- Container 组件优化初始化逻辑
|
|
755
|
+
- useWindowResize 使用现代 API 替代废弃的 orientationchange
|
|
756
|
+
- 优化网络请求超时逻辑,使用 AbortController
|
|
757
|
+
|
|
758
|
+
#### 🔨 代码改进
|
|
759
|
+
- 移除不必要的 lodash/round 依赖
|
|
760
|
+
- 统一 Hooks 导出风格
|
|
761
|
+
- 改进类型定义准确性
|
|
762
|
+
- 优化事件监听器使用 passive 选项
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
<p align="center">
|
|
767
|
+
Made with ❤️ by <a href="https://github.com/joye61">Joye</a>
|
|
768
|
+
</p>
|