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.
Files changed (88) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +768 -35
  3. package/build/Ago/index.d.ts +0 -1
  4. package/build/Alert/Wrapper.d.ts +0 -1
  5. package/build/Alert/Wrapper.js +12 -12
  6. package/build/Alert/index.js +2 -2
  7. package/build/AutoGrid/index.js +16 -21
  8. package/build/AutoGrid/style.d.ts +5 -5
  9. package/build/CarouselNotice/index.d.ts +0 -1
  10. package/build/CarouselNotice/index.js +6 -6
  11. package/build/CarouselNotice/style.d.ts +6 -1
  12. package/build/CarouselNotice/style.js +7 -7
  13. package/build/Clickable/index.d.ts +5 -5
  14. package/build/Clickable/index.js +11 -8
  15. package/build/Container/index.d.ts +2 -2
  16. package/build/Container/index.js +85 -60
  17. package/build/Countdowner/index.d.ts +3 -3
  18. package/build/Countdowner/index.js +18 -11
  19. package/build/Dialog/Wrapper.d.ts +0 -1
  20. package/build/Dialog/Wrapper.js +7 -10
  21. package/build/Dialog/style.d.ts +15 -10
  22. package/build/Dialog/style.js +88 -88
  23. package/build/Effect/useInterval.d.ts +1 -1
  24. package/build/Effect/useInterval.js +1 -1
  25. package/build/Effect/useUpdate.d.ts +1 -1
  26. package/build/Effect/useUpdate.js +1 -1
  27. package/build/Effect/useWindowResize.d.ts +4 -2
  28. package/build/Effect/useWindowResize.js +28 -11
  29. package/build/Flex/index.d.ts +0 -1
  30. package/build/Indicator/index.js +22 -23
  31. package/build/Indicator/style.d.ts +6 -1
  32. package/build/Indicator/style.js +7 -7
  33. package/build/Loading/Wrapper.js +2 -2
  34. package/build/Loading/style.d.ts +12 -2
  35. package/build/Loading/style.js +14 -14
  36. package/build/Overlay/index.d.ts +1 -1
  37. package/build/Overlay/index.js +41 -37
  38. package/build/SafeArea/index.d.ts +1 -2
  39. package/build/SafeArea/index.js +6 -6
  40. package/build/ScrollView/index.d.ts +1 -1
  41. package/build/ScrollView/index.js +111 -27
  42. package/build/Toast/Toast.js +4 -4
  43. package/build/Toast/style.d.ts +42 -12
  44. package/build/Toast/style.js +54 -54
  45. package/build/index.d.ts +3 -0
  46. package/build/index.js +3 -0
  47. package/build/utils/Countdown.d.ts +3 -1
  48. package/build/utils/Countdown.js +5 -2
  49. package/build/utils/createApp.d.ts +14 -13
  50. package/build/utils/createApp.js +58 -42
  51. package/build/utils/cssUtil.d.ts +1 -1
  52. package/build/utils/defaultScroll.d.ts +0 -1
  53. package/build/utils/defaultScroll.js +8 -5
  54. package/build/utils/is.d.ts +23 -4
  55. package/build/utils/is.js +97 -15
  56. package/build/utils/jsonp.js +2 -2
  57. package/build/utils/request.js +12 -2
  58. package/package.json +15 -12
  59. package/test/README.md +16 -0
  60. package/test/eslint.config.js +29 -0
  61. package/test/index.html +13 -0
  62. package/test/jsconfig.json +8 -0
  63. package/test/package.json +27 -0
  64. package/test/public/vite.svg +1 -0
  65. package/test/src/ago/index.jsx +30 -0
  66. package/test/src/alert/index.jsx +111 -0
  67. package/test/src/autogrid/index.css +0 -0
  68. package/test/src/autogrid/index.jsx +26 -0
  69. package/test/src/carouse-notice/index.jsx +59 -0
  70. package/test/src/clickable/index.css +21 -0
  71. package/test/src/clickable/index.jsx +39 -0
  72. package/test/src/countdown/index.jsx +95 -0
  73. package/test/src/dialog/index.jsx +104 -0
  74. package/test/src/dialog/index.module.css +5 -0
  75. package/test/src/image-picker/index.css +0 -0
  76. package/test/src/image-picker/index.jsx +88 -0
  77. package/test/src/index/index.jsx +46 -0
  78. package/test/src/index.css +49 -0
  79. package/test/src/index.jsx +31 -0
  80. package/test/src/indicator/index.jsx +25 -0
  81. package/test/src/loading/index.jsx +36 -0
  82. package/test/src/overlay/index.jsx +31 -0
  83. package/test/src/privacy/index.css +13 -0
  84. package/test/src/privacy/index.jsx +34 -0
  85. package/test/src/scrollview/index.css +10 -0
  86. package/test/src/scrollview/index.jsx +52 -0
  87. package/test/src/toast/index.jsx +86 -0
  88. package/test/vite.config.js +15 -0
package/README.md CHANGED
@@ -1,35 +1,768 @@
1
- # v2.0.8
2
-
3
- ## 功能函数
4
-
5
- - [`ago`](./src/Ago/index.tsx):显示多久以前
6
- - [`calendarTable`](./src/utils/calendarTable.ts):创建一个月历视图的原始数据表
7
- - [`Countdown`](./src/utils/Countdown.ts):倒计时器,最大可以到天
8
- - [`createApp`](./src/utils/createApp.tsx):创建一个带路由的 APP 对象,这是个非常实用的功能函数
9
- - [`defaultScroll`](./src/utils/defaultScroll.ts):禁用和启用默认滚动
10
- - [`createPortalDOM`](./src/utils/dom.tsx):任意位置创建一个 DOM 节点,用来挂载和卸载 React 组件
11
- - [`is`](./src/utils/is.ts):一些简单的环境判断
12
- - [`jsonp`](./src/utils/jsonp.ts):发送一个 jsonp 请求
13
- - [`GET,POST`](./src/utils/request.ts):ajax 请求的简单封装
14
- - [`tick`](./src/utils/tick.ts):嘀嗒器,每帧都会执行
15
- - [`uniqKey`](./src/utils/uniqKey.ts):生成一个全局唯一的 key
16
- - [`waitUntil`](./src/utils/wait.ts):执行某种检测,直接条件为真或者超时才返回
17
-
18
- ## 基础组件
19
-
20
- - [`<Ago>`](./src/Ago/index.tsx):显示多久以前
21
- - [`<AutoGrid>`](./src/AutoGrid/index.tsx):里面的对象会自动网格排列
22
- - [`<CarouselNotice>`](./src/CarouselNotice/index.tsx):滚动走马灯
23
- - [`<Clickable>`](./src/Clickable/index.tsx):一个可点击的对象
24
- - [`<Container>`](./src/Container/index.tsx):这是一个全局容器,主要设置移动端环境,自适应等逻辑
25
- - [`<Countdowner>`](./src/Countdowner/index.tsx):倒计时器
26
- - [`<Row>|<RowStart>|<RowEnd>|<RowBetween>|<RowCenter>|<RowAround>|<RowEvenly>`](./src/Flex/Row.tsx):水平弹性布局
27
- - [`<Col>|<ColStart>|<ColEnd>|<ColBetween>|<ColCenter>|<ColAround>|<ColEvenly>`](./src/Flex/Col.tsx):垂直弹性布局
28
- - [`<Indicator>`](./src/Indicator/index.tsx):转动的菊花,用来作为Loading组件的一部分
29
- - [`<Overlay>`](./src/Overlay/index.tsx):一个全屏覆盖框,会覆盖可操作区域
30
- - [`<SafeArea>`](./src/SafeArea/index.tsx):这个组件会自动剔除手机的非操作区域
31
- - [`<ScrollView>`](./src/ScrollView/index.tsx):滚动容器,自带原生滚动条
32
- - [`showDialog`](./src/Dialog/index.tsx):函数式调用,显示一个弹框组件
33
- - [`showAlert`](./src/Alert/index.tsx):函数式调用,模拟`window.alert`,但UI可定制
34
- - [`showToast|showUniqToast`](./src/Toast/index.tsx):函数式调用,显示一个Toast组件
35
- - [`showLoading|showLoadingAtLeast`](./src/Loading/index.tsx):函数式调用,显示一个Loading组件
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>