clxx 2.1.4 → 2.1.6
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/README.md +766 -422
- package/build/Alert/style.js +41 -58
- package/build/AutoGrid/index.js +32 -43
- package/build/AutoGrid/style.d.ts +3 -4
- package/build/AutoGrid/style.js +7 -19
- package/build/CarouselNotice/style.js +6 -11
- package/build/Clickable/index.d.ts +1 -0
- package/build/Clickable/index.js +77 -41
- package/build/Container/index.js +46 -80
- package/build/Dialog/style.js +2 -2
- package/build/Fixed/index.d.ts +5 -0
- package/build/Fixed/index.js +37 -0
- package/build/Indicator/index.js +5 -5
- package/build/Loading/style.js +16 -17
- package/build/Overlay/index.js +4 -32
- package/build/ScrollView/index.js +65 -47
- package/build/ScrollView/style.js +12 -13
- package/build/Toast/index.js +5 -3
- package/build/Toast/style.js +11 -14
- package/build/context.d.ts +2 -3
- package/build/context.js +1 -4
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/utils/Countdown.js +11 -4
- package/build/utils/ago.js +39 -52
- package/build/utils/createApp.d.ts +1 -2
- package/build/utils/createApp.js +23 -12
- package/build/utils/cssUtil.d.ts +0 -9
- package/build/utils/cssUtil.js +0 -38
- package/build/utils/defaultScroll.d.ts +4 -4
- package/build/utils/defaultScroll.js +6 -15
- package/build/utils/dom.js +5 -8
- package/build/utils/jsonp.js +7 -4
- package/build/utils/request.js +23 -33
- package/build/utils/tick.js +7 -14
- package/build/utils/uniqKey.js +7 -7
- package/build/utils/wait.js +1 -1
- package/package.json +10 -10
- package/test/eslint.config.js +5 -2
- package/test/package.json +11 -11
- package/test/src/ago/index.jsx +3 -1
- package/test/src/dialog/index.module.css +1 -1
- package/test/src/index.jsx +3 -2
package/README.md
CHANGED
|
@@ -18,27 +18,23 @@
|
|
|
18
18
|
|
|
19
19
|
## ✨ 特性
|
|
20
20
|
|
|
21
|
-
- 🎯 **专为移动端设计**
|
|
22
|
-
- 📱 **响应式布局**
|
|
23
|
-
- 🎨 **CSS-in-JS**
|
|
24
|
-
- 🔧 **TypeScript**
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
- 📦 **轻量级** - 按需加载,tree-shaking 友好
|
|
21
|
+
- 🎯 **专为移动端设计** — 完美适配移动端交互和体验
|
|
22
|
+
- 📱 **响应式布局** — 基于 rem 的自适应方案,自动处理浏览器字体缩放
|
|
23
|
+
- 🎨 **CSS-in-JS** — 使用 Emotion 实现样式隔离和动态样式
|
|
24
|
+
- 🔧 **TypeScript** — 完整的类型定义,提供更好的开发体验
|
|
25
|
+
- 🎪 **函数式调用** — Toast、Dialog、Loading、Alert 等支持命令式调用
|
|
26
|
+
- 🚀 **现代化** — 基于 React 19,使用最新的 Hooks API
|
|
27
|
+
- 📦 **轻量级** — tree-shaking 友好,按需引入
|
|
29
28
|
|
|
30
29
|
---
|
|
31
30
|
|
|
32
31
|
## 📦 安装
|
|
33
32
|
|
|
34
33
|
```bash
|
|
35
|
-
# npm
|
|
36
34
|
npm install clxx
|
|
37
|
-
|
|
38
|
-
# yarn
|
|
35
|
+
# or
|
|
39
36
|
yarn add clxx
|
|
40
|
-
|
|
41
|
-
# pnpm
|
|
37
|
+
# or
|
|
42
38
|
pnpm add clxx
|
|
43
39
|
```
|
|
44
40
|
|
|
@@ -46,11 +42,18 @@ pnpm add clxx
|
|
|
46
42
|
|
|
47
43
|
```json
|
|
48
44
|
{
|
|
49
|
-
"react": "^19.2.
|
|
50
|
-
"react-dom": "^19.2.
|
|
45
|
+
"react": "^19.2.4",
|
|
46
|
+
"react-dom": "^19.2.4"
|
|
51
47
|
}
|
|
52
48
|
```
|
|
53
49
|
|
|
50
|
+
### Dependencies
|
|
51
|
+
|
|
52
|
+
- `@emotion/react` — CSS-in-JS 样式方案
|
|
53
|
+
- `dayjs` — 日期处理
|
|
54
|
+
- `history` — 路由历史管理
|
|
55
|
+
- `lodash` — 工具函数
|
|
56
|
+
|
|
54
57
|
---
|
|
55
58
|
|
|
56
59
|
## 🚀 快速开始
|
|
@@ -58,7 +61,6 @@ pnpm add clxx
|
|
|
58
61
|
### 基础使用
|
|
59
62
|
|
|
60
63
|
```tsx
|
|
61
|
-
import React from 'react';
|
|
62
64
|
import { Container, showToast } from 'clxx';
|
|
63
65
|
|
|
64
66
|
function App() {
|
|
@@ -70,8 +72,6 @@ function App() {
|
|
|
70
72
|
</Container>
|
|
71
73
|
);
|
|
72
74
|
}
|
|
73
|
-
|
|
74
|
-
export default App;
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
### 使用路由创建应用
|
|
@@ -82,12 +82,14 @@ import { createApp } from 'clxx';
|
|
|
82
82
|
createApp({
|
|
83
83
|
target: '#root',
|
|
84
84
|
designWidth: 750,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
mode: 'hash',
|
|
86
|
+
default: '/index',
|
|
87
|
+
loading: () => <div>加载中...</div>,
|
|
88
|
+
render: async (pathname) => {
|
|
89
|
+
const Page = await import(`./pages/${pathname}`);
|
|
89
90
|
return <Page.default />;
|
|
90
91
|
},
|
|
92
|
+
notFound: (pathname) => <div>页面不存在: {pathname}</div>,
|
|
91
93
|
});
|
|
92
94
|
```
|
|
93
95
|
|
|
@@ -95,674 +97,1016 @@ createApp({
|
|
|
95
97
|
|
|
96
98
|
## 📚 组件文档
|
|
97
99
|
|
|
98
|
-
###
|
|
100
|
+
### 🏗️ 布局组件
|
|
99
101
|
|
|
100
|
-
#### Container
|
|
102
|
+
#### Container — 自适应容器
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
全局根容器,提供移动端 rem 自适应布局、viewport 设置和初始化逻辑。
|
|
103
105
|
|
|
104
106
|
```tsx
|
|
105
107
|
import { Container } from 'clxx';
|
|
106
108
|
|
|
107
|
-
<Container
|
|
108
|
-
designWidth={750}
|
|
109
|
-
|
|
110
|
-
globalStyle={css`...`} // 全局样式
|
|
109
|
+
<Container
|
|
110
|
+
designWidth={750} // 设计稿宽度,默认 750
|
|
111
|
+
globalStyle={css`...`} // 全局自定义样式(Emotion Interpolation)
|
|
111
112
|
>
|
|
112
113
|
<YourApp />
|
|
113
114
|
</Container>
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
**Props:**
|
|
118
|
+
|
|
119
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
120
|
+
|------|------|--------|------|
|
|
121
|
+
| `designWidth` | `number` | `750` | 设计稿宽度 |
|
|
122
|
+
| `globalStyle` | `Interpolation<Theme>` | — | 全局附加样式 |
|
|
123
|
+
| `children` | `ReactNode` | — | 子元素 |
|
|
124
|
+
|
|
125
|
+
**核心能力:**
|
|
126
|
+
- 自动计算并设置 `html` 根字体大小,实现 rem 适配
|
|
127
|
+
- 检测浏览器字体缩放并自动修正,避免布局偏差
|
|
128
|
+
- 通过 `useLayoutEffect` 在绘制前完成修正,防止闪烁
|
|
129
|
+
- `useViewport` 自动设置 viewport meta
|
|
130
|
+
- 激活 iOS 上的 `:active` 伪类
|
|
131
|
+
- 全局设置 `box-sizing: border-box`
|
|
121
132
|
|
|
122
133
|
---
|
|
123
134
|
|
|
124
|
-
#### Flex 布局
|
|
135
|
+
#### Flex / FlexItem — Flex 布局
|
|
125
136
|
|
|
126
|
-
|
|
137
|
+
提供灵活的 Flex 容器和子元素组件,支持所有标准 CSS Flex 属性。
|
|
127
138
|
|
|
128
139
|
```tsx
|
|
129
|
-
import {
|
|
140
|
+
import { Flex, FlexItem } from 'clxx';
|
|
141
|
+
|
|
142
|
+
<Flex justifyContent="center" alignItems="center">
|
|
143
|
+
<FlexItem flex={1}>自适应宽度</FlexItem>
|
|
144
|
+
<FlexItem flexBasis="100px">固定宽度</FlexItem>
|
|
145
|
+
</Flex>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Flex Props:**
|
|
149
|
+
|
|
150
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
151
|
+
|------|------|--------|------|
|
|
152
|
+
| `alignItems` | `CSS.Property.AlignItems` | `'center'` | 交叉轴对齐 |
|
|
153
|
+
| `justifyContent` | `CSS.Property.JustifyContent` | — | 主轴对齐 |
|
|
154
|
+
| `flexDirection` | `CSS.Property.FlexDirection` | — | 主轴方向 |
|
|
155
|
+
| `flexWrap` | `CSS.Property.FlexWrap` | — | 换行方式 |
|
|
156
|
+
| `flexFlow` | `CSS.Property.FlexFlow` | — | direction + wrap 简写 |
|
|
157
|
+
| `alignContent` | `CSS.Property.AlignContent` | — | 多行内容对齐 |
|
|
158
|
+
|
|
159
|
+
**FlexItem Props:**
|
|
160
|
+
|
|
161
|
+
| 属性 | 类型 | 说明 |
|
|
162
|
+
|------|------|------|
|
|
163
|
+
| `flex` | `CSS.Property.BoxFlex` | flex 简写 |
|
|
164
|
+
| `flexGrow` | `CSS.Property.FlexGrow` | 放大比例 |
|
|
165
|
+
| `flexShrink` | `CSS.Property.FlexShrink` | 缩小比例 |
|
|
166
|
+
| `flexBasis` | `CSS.Property.FlexBasis` | 基准大小 |
|
|
167
|
+
| `alignSelf` | `CSS.Property.AlignSelf` | 自身对齐 |
|
|
168
|
+
| `order` | `CSS.Property.Order` | 排列顺序 |
|
|
169
|
+
|
|
170
|
+
---
|
|
130
171
|
|
|
131
|
-
|
|
132
|
-
<RowCenter>
|
|
133
|
-
<div>居中对齐</div>
|
|
134
|
-
</RowCenter>
|
|
172
|
+
#### Row / Col — 快捷 Flex 布局
|
|
135
173
|
|
|
174
|
+
基于 `Flex` 封装的水平/垂直快捷布局组件,预设 `flexDirection` 和 `justifyContent`。
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { RowCenter, RowBetween, ColCenter } from 'clxx';
|
|
178
|
+
|
|
179
|
+
<RowCenter>居中对齐</RowCenter>
|
|
136
180
|
<RowBetween>
|
|
137
|
-
<
|
|
181
|
+
<span>左侧</span>
|
|
182
|
+
<span>右侧</span>
|
|
138
183
|
</RowBetween>
|
|
139
|
-
|
|
140
|
-
// 垂直布局
|
|
141
|
-
<ColCenter>
|
|
142
|
-
<div>垂直居中</div>
|
|
143
|
-
</ColCenter>
|
|
184
|
+
<ColCenter>垂直居中</ColCenter>
|
|
144
185
|
```
|
|
145
186
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
**水平布局:**
|
|
188
|
+
| 组件 | justifyContent |
|
|
189
|
+
|------|---------------|
|
|
190
|
+
| `Row` / `RowStart` | `flex-start` |
|
|
191
|
+
| `RowCenter` | `center` |
|
|
192
|
+
| `RowEnd` | `flex-end` |
|
|
193
|
+
| `RowBetween` | `space-between` |
|
|
194
|
+
| `RowAround` | `space-around` |
|
|
195
|
+
| `RowEvenly` | `space-evenly` |
|
|
196
|
+
|
|
197
|
+
**垂直布局:**
|
|
198
|
+
| 组件 | justifyContent |
|
|
199
|
+
|------|---------------|
|
|
200
|
+
| `Col` / `ColStart` | `flex-start` |
|
|
201
|
+
| `ColCenter` | `center` |
|
|
202
|
+
| `ColEnd` | `flex-end` |
|
|
203
|
+
| `ColBetween` | `space-between` |
|
|
204
|
+
| `ColAround` | `space-around` |
|
|
205
|
+
| `ColEvenly` | `space-evenly` |
|
|
206
|
+
|
|
207
|
+
> 所有 Row/Col 组件均支持 `FlexProps` 的全部属性,可覆盖默认预设。
|
|
154
208
|
|
|
155
209
|
---
|
|
156
210
|
|
|
157
|
-
#### AutoGrid
|
|
211
|
+
#### AutoGrid — 自动网格
|
|
158
212
|
|
|
159
|
-
|
|
213
|
+
自动将子元素排列为网格布局,支持正方形模式。
|
|
160
214
|
|
|
161
215
|
```tsx
|
|
162
216
|
import { AutoGrid } from 'clxx';
|
|
163
217
|
|
|
164
|
-
<AutoGrid
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
>
|
|
169
|
-
<div>
|
|
170
|
-
<div>Item 2</div>
|
|
171
|
-
<div>Item 3</div>
|
|
218
|
+
<AutoGrid cols={3} gap="10px" isSquare>
|
|
219
|
+
<div>1</div>
|
|
220
|
+
<div>2</div>
|
|
221
|
+
<div>3</div>
|
|
222
|
+
<div>4</div>
|
|
223
|
+
<div>5</div>
|
|
172
224
|
</AutoGrid>
|
|
173
225
|
```
|
|
174
226
|
|
|
227
|
+
**Props:**
|
|
228
|
+
|
|
229
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
230
|
+
|------|------|--------|------|
|
|
231
|
+
| `cols` | `number` | `1` | 列数(最小 1) |
|
|
232
|
+
| `gap` | `CSS.Property.Width` | `0` | 表格间距 |
|
|
233
|
+
| `isSquare` | `boolean` | `false` | 是否强制正方形 |
|
|
234
|
+
| `containerStyle` | `Interpolation<Theme>` | — | 容器样式 |
|
|
235
|
+
| `itemStyle` | `Interpolation<Theme>` | — | 每个子元素容器样式 |
|
|
236
|
+
|
|
237
|
+
**特性:**
|
|
238
|
+
- 自动补齐最后一行空位(隐藏占位符),保证列宽一致
|
|
239
|
+
- 正方形模式通过 `padding-bottom: 100%` 实现
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
#### Fixed — 固定定位
|
|
244
|
+
|
|
245
|
+
简化 `position: fixed` 的使用,快速将元素固定到页面四边。
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
import { Fixed } from 'clxx';
|
|
249
|
+
|
|
250
|
+
<Fixed position="bottom">底部固定栏</Fixed>
|
|
251
|
+
<Fixed position="top">顶部固定栏</Fixed>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Props:**
|
|
255
|
+
|
|
256
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
257
|
+
|------|------|--------|------|
|
|
258
|
+
| `position` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 固定位置 |
|
|
259
|
+
|
|
175
260
|
---
|
|
176
261
|
|
|
177
|
-
#### SafeArea
|
|
262
|
+
#### SafeArea — 安全区域
|
|
178
263
|
|
|
179
|
-
|
|
264
|
+
处理 iPhone 刘海屏、底部手势条等安全区域。自动设置 `viewport-fit=cover`。
|
|
180
265
|
|
|
181
266
|
```tsx
|
|
182
267
|
import { SafeArea } from 'clxx';
|
|
183
268
|
|
|
184
|
-
<SafeArea type="top"
|
|
185
|
-
<
|
|
269
|
+
<SafeArea type="top" /> {/* 顶部安全区域 */}
|
|
270
|
+
<div>页面内容</div>
|
|
271
|
+
<SafeArea type="bottom" /> {/* 底部安全区域 */}
|
|
186
272
|
```
|
|
187
273
|
|
|
274
|
+
**Props:**
|
|
275
|
+
|
|
276
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
277
|
+
|------|------|--------|------|
|
|
278
|
+
| `type` | `'top' \| 'bottom'` | `'bottom'` | 安全区域位置 |
|
|
279
|
+
|
|
188
280
|
---
|
|
189
281
|
|
|
190
282
|
### 🎭 交互组件
|
|
191
283
|
|
|
192
|
-
#### Clickable
|
|
284
|
+
#### Clickable — 点击态
|
|
193
285
|
|
|
194
|
-
|
|
286
|
+
提供触摸/点击反馈效果的容器组件,兼容移动端触摸和 PC 鼠标事件。
|
|
195
287
|
|
|
196
288
|
```tsx
|
|
197
289
|
import { Clickable } from 'clxx';
|
|
198
290
|
|
|
199
|
-
<Clickable
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
>
|
|
204
|
-
|
|
291
|
+
<Clickable activeStyle={{ opacity: 0.5 }} onClick={() => console.log('clicked')}>
|
|
292
|
+
点我
|
|
293
|
+
</Clickable>
|
|
294
|
+
|
|
295
|
+
<Clickable activeClassName="pressed" moveThreshold={20}>
|
|
296
|
+
自定义激活类名
|
|
205
297
|
</Clickable>
|
|
206
298
|
```
|
|
207
299
|
|
|
300
|
+
**Props:**
|
|
301
|
+
|
|
302
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
303
|
+
|------|------|--------|------|
|
|
304
|
+
| `activeStyle` | `React.CSSProperties` | `{ opacity: 0.6 }` | 激活时的样式 |
|
|
305
|
+
| `activeClassName` | `string` | — | 激活时追加的类名 |
|
|
306
|
+
| `bubble` | `boolean` | `true` | 是否允许事件冒泡 |
|
|
307
|
+
| `disable` | `boolean` | `false` | 是否禁用点击态 |
|
|
308
|
+
| `moveThreshold` | `number` | `10` | touchmove 取消 active 的位移阈值(px) |
|
|
309
|
+
|
|
310
|
+
**特性:**
|
|
311
|
+
- 自动检测运行环境,触屏设备使用 touch 事件,PC 使用 mouse 事件
|
|
312
|
+
- touchmove 超出阈值自动取消激活态
|
|
313
|
+
- PC 端在 `document` 上监听 `mouseup`,处理鼠标移出元素后释放的场景
|
|
314
|
+
- 未设置 `activeClassName` 和 `activeStyle` 时,默认使用 `opacity: 0.6`
|
|
315
|
+
|
|
208
316
|
---
|
|
209
317
|
|
|
210
|
-
#### Overlay
|
|
318
|
+
#### Overlay — 覆盖层
|
|
211
319
|
|
|
212
|
-
|
|
320
|
+
通用遮罩层组件,可作为弹窗、对话框等的底层容器。
|
|
213
321
|
|
|
214
322
|
```tsx
|
|
215
323
|
import { Overlay } from 'clxx';
|
|
216
324
|
|
|
217
|
-
<Overlay
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<div>遮罩内容</div>
|
|
325
|
+
<Overlay fullScreen maskColor="rgba(0,0,0,.6)" centerContent>
|
|
326
|
+
<div>居中内容</div>
|
|
327
|
+
</Overlay>
|
|
328
|
+
|
|
329
|
+
<Overlay outside>
|
|
330
|
+
通过 Portal 挂载到 body
|
|
224
331
|
</Overlay>
|
|
225
332
|
```
|
|
226
333
|
|
|
334
|
+
**Props:**
|
|
335
|
+
|
|
336
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
337
|
+
|------|------|--------|------|
|
|
338
|
+
| `fullScreen` | `boolean` | `true` | 是否全屏覆盖 |
|
|
339
|
+
| `maskColor` | `string` | `'rgba(0,0,0,.6)'` | 遮罩背景色 |
|
|
340
|
+
| `centerContent` | `boolean` | `true` | 内容是否居中 |
|
|
341
|
+
| `outside` | `boolean` | `false` | 是否通过 Portal 挂载到 body |
|
|
342
|
+
|
|
227
343
|
---
|
|
228
344
|
|
|
229
|
-
#### ScrollView
|
|
345
|
+
#### ScrollView — 滚动视图
|
|
230
346
|
|
|
231
|
-
|
|
347
|
+
功能丰富的滚动容器,支持触顶/触底事件、滚动节流和自动 loading 显示。
|
|
232
348
|
|
|
233
349
|
```tsx
|
|
234
350
|
import { ScrollView } from 'clxx';
|
|
235
351
|
|
|
236
352
|
<ScrollView
|
|
237
353
|
height="100vh"
|
|
238
|
-
reachBottomThreshold={
|
|
239
|
-
onReachBottom={(
|
|
240
|
-
console.log('
|
|
354
|
+
reachBottomThreshold={100}
|
|
355
|
+
onReachBottom={(event) => {
|
|
356
|
+
console.log('触底加载', event.scrollTop);
|
|
241
357
|
}}
|
|
242
|
-
|
|
243
|
-
console.log('
|
|
358
|
+
onScroll={(event) => {
|
|
359
|
+
console.log('滚动方向:', event.direction);
|
|
244
360
|
}}
|
|
245
361
|
>
|
|
246
|
-
|
|
362
|
+
{/* 列表内容 */}
|
|
247
363
|
</ScrollView>
|
|
248
364
|
```
|
|
249
365
|
|
|
366
|
+
**Props:**
|
|
367
|
+
|
|
368
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
369
|
+
|------|------|--------|------|
|
|
370
|
+
| `height` | `CSS.Property.Height` | `'100%'` | 容器高度 |
|
|
371
|
+
| `reachTopThreshold` | `number` | `50` | 触顶事件阈值(px) |
|
|
372
|
+
| `onReachTop` | `(event: ScrollEvent) => void` | — | 触顶回调 |
|
|
373
|
+
| `reachBottomThreshold` | `number` | `50` | 触底事件阈值(px) |
|
|
374
|
+
| `onReachBottom` | `(event: ScrollEvent) => void` | — | 触底回调 |
|
|
375
|
+
| `onScroll` | `(event: ScrollEvent) => void` | — | 滚动事件回调 |
|
|
376
|
+
| `scrollThrottle` | `number` | `16` | 滚动节流时间(ms),约 60fps |
|
|
377
|
+
| `showLoading` | `boolean` | `true` | 是否显示底部 loading |
|
|
378
|
+
| `loadingContent` | `ReactNode` | — | 自定义 loading 内容 |
|
|
379
|
+
| `containerStyle` | `SerializedStyles` | — | 容器样式 |
|
|
380
|
+
| `wrapperStyle` | `SerializedStyles` | — | 内容包裹样式 |
|
|
381
|
+
|
|
382
|
+
**ScrollEvent:**
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
interface ScrollEvent {
|
|
386
|
+
containerHeight: number; // 容器可视高度
|
|
387
|
+
contentHeight: number; // 内容总高度
|
|
388
|
+
scrollTop: number; // 当前滚动位置
|
|
389
|
+
maxScroll: number; // 最大可滚动距离
|
|
390
|
+
direction: 'upward' | 'downward'; // 滚动方向
|
|
391
|
+
rawEvent?: React.UIEvent; // 原始事件
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**特性:**
|
|
396
|
+
- 使用 `ResizeObserver` 自动检测内容变化和滚动条状态
|
|
397
|
+
- 内置 leading + trailing 节流策略
|
|
398
|
+
- 触顶/触底事件防重复触发
|
|
399
|
+
- 有滚动条时自动显示底部 loading
|
|
400
|
+
|
|
250
401
|
---
|
|
251
402
|
|
|
252
|
-
|
|
403
|
+
#### CarouselNotice — 轮播公告
|
|
253
404
|
|
|
254
|
-
|
|
405
|
+
垂直滚动的循环轮播公告组件,常用于消息通知、跑马灯等场景。
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { CarouselNotice } from 'clxx';
|
|
409
|
+
|
|
410
|
+
<CarouselNotice
|
|
411
|
+
list={['公告一:xxx', '公告二:xxx', '公告三:xxx']}
|
|
412
|
+
width="100%"
|
|
413
|
+
height="40px"
|
|
414
|
+
interval={3000}
|
|
415
|
+
duration={200}
|
|
416
|
+
/>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Props:**
|
|
420
|
+
|
|
421
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
422
|
+
|------|------|--------|------|
|
|
423
|
+
| `list` | `ReactNode[]` | `[]` | 轮播内容列表 |
|
|
424
|
+
| `width` | `CSS.Property.Width` | — | 容器宽度 |
|
|
425
|
+
| `height` | `CSS.Property.Height` | — | 容器高度 |
|
|
426
|
+
| `justify` | `'start' \| 'center' \| 'end'` | `'center'` | 内容水平对齐方式 |
|
|
427
|
+
| `interval` | `number` | `3000` | 切换间隔时间(ms) |
|
|
428
|
+
| `duration` | `number` | `200` | 每次冒泡动画持续时间(ms) |
|
|
429
|
+
| `containerStyle` | `Interpolation<Theme>` | — | 容器样式 |
|
|
430
|
+
| `wrapperStyle` | `Interpolation<Theme>` | — | 内部容器样式 |
|
|
431
|
+
| `itemStyle` | `Interpolation<Theme>` | — | 条目样式 |
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
### 🔔 反馈组件
|
|
436
|
+
|
|
437
|
+
#### showToast / showUniqToast — 轻提示
|
|
438
|
+
|
|
439
|
+
函数式调用的全局轻提示,支持自定义位置、持续时间和内容。
|
|
255
440
|
|
|
256
441
|
```tsx
|
|
257
442
|
import { showToast, showUniqToast } from 'clxx';
|
|
258
443
|
|
|
259
|
-
//
|
|
444
|
+
// 简单用法:传入字符串或 ReactNode
|
|
260
445
|
showToast('操作成功');
|
|
446
|
+
showToast(<MyCustomContent />);
|
|
261
447
|
|
|
262
|
-
//
|
|
448
|
+
// 高级用法:传入配置对象
|
|
263
449
|
showToast({
|
|
264
|
-
content: '
|
|
265
|
-
position: '
|
|
266
|
-
duration:
|
|
267
|
-
|
|
450
|
+
content: '提示内容',
|
|
451
|
+
position: 'top', // 'top' | 'middle' | 'bottom'
|
|
452
|
+
duration: 3000, // 持续时间,默认 2000ms
|
|
453
|
+
radius: 16, // 圆角,默认 16
|
|
454
|
+
offsetTop: 50, // top 位置偏移
|
|
455
|
+
offsetBottom: 50, // bottom 位置偏移
|
|
456
|
+
containerStyle: css`...`,
|
|
457
|
+
contentStyle: css`...`,
|
|
268
458
|
});
|
|
269
459
|
|
|
270
|
-
//
|
|
271
|
-
showUniqToast('
|
|
460
|
+
// 唯一 Toast(新调用会自动关闭上一个)
|
|
461
|
+
showUniqToast('只显示最新的一条');
|
|
272
462
|
```
|
|
273
463
|
|
|
274
464
|
---
|
|
275
465
|
|
|
276
|
-
####
|
|
466
|
+
#### showDialog — 对话框
|
|
467
|
+
|
|
468
|
+
函数式调用的模态对话框,支持多种弹出动画,返回关闭函数。
|
|
277
469
|
|
|
278
470
|
```tsx
|
|
279
471
|
import { showDialog } from 'clxx';
|
|
280
472
|
|
|
473
|
+
// 简单用法
|
|
474
|
+
const close = showDialog(<div>对话框内容</div>);
|
|
475
|
+
// 调用 close() 关闭
|
|
476
|
+
|
|
477
|
+
// 高级用法
|
|
281
478
|
const close = showDialog({
|
|
282
|
-
content: <div
|
|
283
|
-
type: 'center', // center | pullUp | pullDown | pullLeft | pullRight
|
|
284
|
-
blankClosable: true, //
|
|
479
|
+
content: <div>自定义内容</div>,
|
|
480
|
+
type: 'center', // 'center' | 'pullUp' | 'pullDown' | 'pullLeft' | 'pullRight'
|
|
481
|
+
blankClosable: true, // 点击空白区域关闭
|
|
285
482
|
showMask: true, // 显示遮罩
|
|
483
|
+
maskColor: 'rgba(0,0,0,.6)', // 遮罩颜色
|
|
484
|
+
boxStyle: css`...`, // 对话框容器样式
|
|
485
|
+
maskStyle: css`...`, // 遮罩样式
|
|
486
|
+
onHide: () => {}, // 关闭动画结束后回调
|
|
487
|
+
onBlankClick: () => {}, // 空白处点击回调
|
|
286
488
|
});
|
|
287
489
|
|
|
288
|
-
//
|
|
289
|
-
close();
|
|
490
|
+
// 手动关闭(带关闭动画)
|
|
491
|
+
await close();
|
|
290
492
|
```
|
|
291
493
|
|
|
494
|
+
**动画类型:**
|
|
495
|
+
| type | 效果 |
|
|
496
|
+
|------|------|
|
|
497
|
+
| `center` | 居中缩放弹出 |
|
|
498
|
+
| `pullUp` | 从底部上滑 |
|
|
499
|
+
| `pullDown` | 从顶部下滑 |
|
|
500
|
+
| `pullLeft` | 从右侧左滑 |
|
|
501
|
+
| `pullRight` | 从左侧右滑 |
|
|
502
|
+
|
|
292
503
|
---
|
|
293
504
|
|
|
294
|
-
####
|
|
505
|
+
#### showAlert — 弹窗提示
|
|
506
|
+
|
|
507
|
+
基于 `showDialog` 封装的标准提示弹窗,支持确认/取消按钮。
|
|
295
508
|
|
|
296
509
|
```tsx
|
|
297
510
|
import { showAlert } from 'clxx';
|
|
298
511
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
512
|
+
// 简单用法
|
|
513
|
+
showAlert('确认要删除吗?');
|
|
514
|
+
|
|
515
|
+
// 高级用法
|
|
516
|
+
const close = showAlert({
|
|
517
|
+
title: '删除确认',
|
|
518
|
+
description: '删除后不可恢复',
|
|
519
|
+
confirm: '确定',
|
|
520
|
+
confirmColor: '#ff4d4d',
|
|
521
|
+
cancel: '取消',
|
|
522
|
+
cancelColor: '#999',
|
|
302
523
|
showCancel: true,
|
|
303
|
-
|
|
304
|
-
|
|
524
|
+
showMask: true,
|
|
525
|
+
onConfirm: () => {
|
|
526
|
+
console.log('点击了确认');
|
|
527
|
+
},
|
|
528
|
+
onCancel: () => {
|
|
529
|
+
console.log('点击了取消');
|
|
530
|
+
},
|
|
531
|
+
// 可定制样式
|
|
532
|
+
titleStyle: css`...`,
|
|
533
|
+
descStyle: css`...`,
|
|
534
|
+
btnStyle: css`...`,
|
|
535
|
+
confirmStyle: css`...`,
|
|
536
|
+
cancelStyle: css`...`,
|
|
305
537
|
});
|
|
306
538
|
```
|
|
307
539
|
|
|
540
|
+
**Props:**
|
|
541
|
+
|
|
542
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
543
|
+
|------|------|--------|------|
|
|
544
|
+
| `title` | `ReactNode` | `'提示'` | 标题 |
|
|
545
|
+
| `description` | `ReactNode` | — | 描述内容 |
|
|
546
|
+
| `confirm` | `ReactNode` | `'确定'` | 确认按钮文字 |
|
|
547
|
+
| `confirmColor` | `string` | `'#007afe'` | 确认按钮颜色 |
|
|
548
|
+
| `cancel` | `ReactNode` | `'取消'` | 取消按钮文字 |
|
|
549
|
+
| `cancelColor` | `string` | `'#666'` | 取消按钮颜色 |
|
|
550
|
+
| `showCancel` | `boolean` | `false` | 是否显示取消按钮 |
|
|
551
|
+
| `showMask` | `boolean` | `true` | 是否显示遮罩 |
|
|
552
|
+
| `onConfirm` | `() => void` | — | 确认回调 |
|
|
553
|
+
| `onCancel` | `() => void` | — | 取消回调 |
|
|
554
|
+
|
|
308
555
|
---
|
|
309
556
|
|
|
310
|
-
####
|
|
557
|
+
#### showLoading / showLoadingAtLeast — 加载指示器
|
|
558
|
+
|
|
559
|
+
函数式调用的全局 Loading,返回关闭函数。
|
|
311
560
|
|
|
312
561
|
```tsx
|
|
313
562
|
import { showLoading, showLoadingAtLeast } from 'clxx';
|
|
314
563
|
|
|
315
564
|
// 基础用法
|
|
316
565
|
const close = showLoading('加载中...');
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
566
|
+
// 异步操作完成后关闭
|
|
567
|
+
await fetchData();
|
|
568
|
+
await close();
|
|
569
|
+
|
|
570
|
+
// 保证最少显示时间(避免闪烁)
|
|
571
|
+
const close = showLoadingAtLeast(500, '处理中...');
|
|
572
|
+
await fetchData(); // 即使请求很快完成,也会至少显示 500ms
|
|
573
|
+
await close();
|
|
574
|
+
|
|
575
|
+
// 高级用法
|
|
576
|
+
const close = showLoading('加载中...', {
|
|
577
|
+
overlay: { maskColor: 'rgba(0,0,0,.3)' }, // 自定义遮罩
|
|
578
|
+
indicator: { barCount: 14, barColor: '#fff' }, // 自定义指示器
|
|
579
|
+
containerStyle: css`...`,
|
|
580
|
+
});
|
|
324
581
|
```
|
|
325
582
|
|
|
326
583
|
---
|
|
327
584
|
|
|
328
|
-
###
|
|
585
|
+
### 📊 展示组件
|
|
586
|
+
|
|
587
|
+
#### Indicator — 加载指示器
|
|
329
588
|
|
|
330
|
-
|
|
589
|
+
基于 SVG 实现的旋转加载指示器,高度可定制。
|
|
331
590
|
|
|
332
591
|
```tsx
|
|
333
592
|
import { Indicator } from 'clxx';
|
|
334
593
|
|
|
335
|
-
<Indicator
|
|
336
|
-
|
|
337
|
-
barCount={12}
|
|
338
|
-
barColor="#fff"
|
|
339
|
-
duration={600}
|
|
340
|
-
/>
|
|
594
|
+
<Indicator />
|
|
595
|
+
<Indicator size={80} barColor="#007afe" barCount={10} duration={800} />
|
|
341
596
|
```
|
|
342
597
|
|
|
598
|
+
**Props:**
|
|
599
|
+
|
|
600
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
601
|
+
|------|------|--------|------|
|
|
602
|
+
| `size` | `CSS.Property.Width \| number` | `'.6rem'` | 容器尺寸 |
|
|
603
|
+
| `barWidth` | `number` | `7` | bar 宽度(SVG 坐标) |
|
|
604
|
+
| `barHeight` | `number` | `28` | bar 高度(SVG 坐标) |
|
|
605
|
+
| `barColor` | `string` | `'#fff'` | bar 颜色 |
|
|
606
|
+
| `barCount` | `number` | `12` | bar 数量 |
|
|
607
|
+
| `rounded` | `boolean` | `true` | bar 是否圆角 |
|
|
608
|
+
| `duration` | `number` | `600` | 一圈旋转时间(ms) |
|
|
609
|
+
| `containerStyle` | `Interpolation<Theme>` | — | 容器样式 |
|
|
610
|
+
|
|
343
611
|
---
|
|
344
612
|
|
|
345
|
-
####
|
|
613
|
+
#### Countdowner — 倒计时
|
|
346
614
|
|
|
347
|
-
|
|
615
|
+
倒计时展示组件,支持自定义格式、样式和渲染方式。
|
|
348
616
|
|
|
349
617
|
```tsx
|
|
350
|
-
import {
|
|
618
|
+
import { Countdowner } from 'clxx';
|
|
351
619
|
|
|
352
|
-
|
|
353
|
-
|
|
620
|
+
// 基础用法(倒计时 60 秒)
|
|
621
|
+
<Countdowner remain={60} />
|
|
354
622
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
623
|
+
// 自定义格式和样式
|
|
624
|
+
<Countdowner
|
|
625
|
+
remain={86400}
|
|
626
|
+
format="dhis"
|
|
627
|
+
separator=":"
|
|
628
|
+
numberStyle={css`color: red; font-weight: bold;`}
|
|
629
|
+
separatorStyle={css`color: #999; margin: 0 4px;`}
|
|
630
|
+
onEnd={() => console.log('倒计时结束')}
|
|
631
|
+
/>
|
|
632
|
+
|
|
633
|
+
// 完全自定义渲染
|
|
634
|
+
<Countdowner
|
|
635
|
+
remain={3600}
|
|
636
|
+
format="his"
|
|
637
|
+
renderNumber={(value, key) => (
|
|
638
|
+
<span className="number-box">{value < 10 ? `0${value}` : value}</span>
|
|
639
|
+
)}
|
|
640
|
+
renderSeparator={(value, key) => <span>:</span>}
|
|
358
641
|
/>
|
|
359
642
|
```
|
|
360
643
|
|
|
644
|
+
**Props:**
|
|
645
|
+
|
|
646
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
647
|
+
|------|------|--------|------|
|
|
648
|
+
| `remain` | `number \| string` | `0` | 剩余时间(秒) |
|
|
649
|
+
| `format` | `string` | `'his'` | 显示格式:`d`天`h`时`i`分`s`秒 |
|
|
650
|
+
| `separator` | `ReactNode` | `':'` | 数字间分隔符 |
|
|
651
|
+
| `onUpdate` | `(value: CountdownValue) => void` | — | 每秒更新回调 |
|
|
652
|
+
| `onEnd` | `() => void` | — | 倒计时结束回调 |
|
|
653
|
+
| `numberStyle` | `Interpolation<Theme>` | — | 数字样式 |
|
|
654
|
+
| `separatorStyle` | `Interpolation<Theme>` | — | 分隔符样式 |
|
|
655
|
+
| `containerStyle` | `Interpolation<Theme>` | — | 容器样式 |
|
|
656
|
+
| `renderNumber` | `(value, key) => ReactNode` | — | 自定义数字渲染 |
|
|
657
|
+
| `renderSeparator` | `(value, key) => ReactNode` | — | 自定义分隔符渲染 |
|
|
658
|
+
|
|
361
659
|
---
|
|
362
660
|
|
|
363
|
-
####
|
|
661
|
+
#### Ago — 相对时间
|
|
662
|
+
|
|
663
|
+
将日期格式化为相对时间展示(如 "3分钟前"、"2天后")。
|
|
364
664
|
|
|
365
665
|
```tsx
|
|
366
|
-
import {
|
|
666
|
+
import { Ago } from 'clxx';
|
|
367
667
|
|
|
368
|
-
<
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
668
|
+
<Ago date="2025-01-01" /> // "x个月前"
|
|
669
|
+
<Ago date={new Date()} /> // "刚刚"
|
|
670
|
+
<Ago date="2028-01-01" /> // "x年后"
|
|
671
|
+
<Ago block /> // 渲染为 <div>
|
|
672
|
+
<Ago date={someDate} renderContent={(result) => (
|
|
673
|
+
<span>{result.num}{result.unit === 'h' ? '小时' : '...'}</span>
|
|
674
|
+
)} />
|
|
374
675
|
```
|
|
375
676
|
|
|
376
|
-
|
|
677
|
+
**Props:**
|
|
377
678
|
|
|
378
|
-
|
|
679
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
680
|
+
|------|------|--------|------|
|
|
681
|
+
| `date` | `dayjs.ConfigType` | `dayjs()` | 要格式化的日期 |
|
|
682
|
+
| `block` | `boolean` | `false` | 是否渲染为 `<div>`,默认 `<span>` |
|
|
683
|
+
| `renderContent` | `(result: AgoValue) => ReactNode` | — | 自定义渲染函数 |
|
|
379
684
|
|
|
380
|
-
|
|
381
|
-
import { CarouselNotice } from 'clxx';
|
|
685
|
+
**AgoValue:**
|
|
382
686
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
687
|
+
```typescript
|
|
688
|
+
interface AgoValue {
|
|
689
|
+
num: number; // 数字值
|
|
690
|
+
unit: 'y' | 'm' | 'd' | 'h' | 'i' | 's'; // 单位
|
|
691
|
+
format: string; // 格式化中文字符串,如 "3天前"
|
|
692
|
+
}
|
|
389
693
|
```
|
|
390
694
|
|
|
391
695
|
---
|
|
392
696
|
|
|
393
|
-
##
|
|
697
|
+
## 🪝 Hooks
|
|
394
698
|
|
|
395
|
-
|
|
699
|
+
#### useWindowResize
|
|
396
700
|
|
|
397
|
-
|
|
398
|
-
import { GET, POST, sendJSON, jsonp } from 'clxx';
|
|
701
|
+
监听窗口尺寸变化(包括屏幕旋转),回调通过 ref 保持最新引用。
|
|
399
702
|
|
|
400
|
-
|
|
401
|
-
|
|
703
|
+
```tsx
|
|
704
|
+
import { useWindowResize } from 'clxx';
|
|
402
705
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
username: 'admin',
|
|
406
|
-
password: '123456'
|
|
706
|
+
useWindowResize(() => {
|
|
707
|
+
console.log('窗口大小变化:', window.innerWidth);
|
|
407
708
|
});
|
|
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
709
|
```
|
|
415
710
|
|
|
416
|
-
|
|
417
|
-
```tsx
|
|
418
|
-
import { sendRequest, registerHostAlias } from 'clxx';
|
|
711
|
+
---
|
|
419
712
|
|
|
420
|
-
|
|
421
|
-
registerHostAlias({
|
|
422
|
-
api: 'https://api.example.com',
|
|
423
|
-
cdn: 'https://cdn.example.com'
|
|
424
|
-
});
|
|
713
|
+
#### useViewport
|
|
425
714
|
|
|
426
|
-
|
|
427
|
-
await GET('api@/users'); // 实际请求:https://api.example.com/users
|
|
715
|
+
自动设置或更新 `<meta name="viewport">` 标签。
|
|
428
716
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
717
|
+
```tsx
|
|
718
|
+
import { useViewport } from 'clxx';
|
|
719
|
+
|
|
720
|
+
useViewport(); // 使用默认值
|
|
721
|
+
useViewport({
|
|
722
|
+
width: 'device-width',
|
|
723
|
+
initialScale: 1,
|
|
724
|
+
userScalable: 'no',
|
|
725
|
+
viewportFit: 'cover',
|
|
437
726
|
});
|
|
438
727
|
```
|
|
439
728
|
|
|
440
729
|
---
|
|
441
730
|
|
|
442
|
-
|
|
731
|
+
#### useInterval
|
|
732
|
+
|
|
733
|
+
安全的 `setInterval` Hook,通过 ref 保持回调最新,`delay` 为 `null` 时暂停。
|
|
443
734
|
|
|
444
735
|
```tsx
|
|
445
|
-
import {
|
|
736
|
+
import { useInterval } from 'clxx';
|
|
737
|
+
|
|
738
|
+
useInterval(() => {
|
|
739
|
+
setCount(c => c + 1);
|
|
740
|
+
}, 1000);
|
|
446
741
|
|
|
447
|
-
//
|
|
448
|
-
|
|
449
|
-
|
|
742
|
+
// 传入 null 暂停
|
|
743
|
+
useInterval(callback, isRunning ? 1000 : null);
|
|
744
|
+
```
|
|
450
745
|
|
|
451
|
-
|
|
452
|
-
const table = calendarTable('2024-10', false, true);
|
|
453
|
-
// 返回 6x7 的日期数组
|
|
746
|
+
---
|
|
454
747
|
|
|
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();
|
|
748
|
+
#### useTick
|
|
463
749
|
|
|
464
|
-
|
|
465
|
-
await waitFor(1000);
|
|
750
|
+
基于 `requestAnimationFrame` 的逐帧执行 Hook,适用于动画或高频更新场景。
|
|
466
751
|
|
|
467
|
-
|
|
468
|
-
|
|
752
|
+
```tsx
|
|
753
|
+
import { useTick } from 'clxx';
|
|
754
|
+
|
|
755
|
+
useTick(() => {
|
|
756
|
+
// 每帧执行
|
|
757
|
+
updateAnimation();
|
|
758
|
+
});
|
|
469
759
|
```
|
|
470
760
|
|
|
471
761
|
---
|
|
472
762
|
|
|
473
|
-
|
|
763
|
+
#### useUpdate
|
|
764
|
+
|
|
765
|
+
返回一个强制组件重新渲染的函数。
|
|
474
766
|
|
|
475
767
|
```tsx
|
|
476
|
-
import {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
normalizeUnit,
|
|
483
|
-
adaptive
|
|
484
|
-
} from 'clxx';
|
|
485
|
-
|
|
486
|
-
// 逐帧执行
|
|
487
|
-
const stop = tick(() => {
|
|
488
|
-
console.log('每帧执行');
|
|
489
|
-
}, 16); // 可选的间隔时间
|
|
490
|
-
stop(); // 停止
|
|
768
|
+
import { useUpdate } from 'clxx';
|
|
769
|
+
|
|
770
|
+
const forceUpdate = useUpdate();
|
|
771
|
+
// 需要时调用
|
|
772
|
+
forceUpdate();
|
|
773
|
+
```
|
|
491
774
|
|
|
492
|
-
|
|
493
|
-
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
## 🛠️ 工具函数
|
|
494
778
|
|
|
495
|
-
|
|
496
|
-
defaultScroll.disable();
|
|
497
|
-
defaultScroll.enable();
|
|
779
|
+
#### createApp — 创建路由应用
|
|
498
780
|
|
|
499
|
-
|
|
500
|
-
const portal = createPortalDOM();
|
|
501
|
-
portal.mount(<YourComponent />);
|
|
502
|
-
portal.unmount();
|
|
781
|
+
创建一个带路由的单页应用,支持 browser / hash / memory 三种路由模式。
|
|
503
782
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
783
|
+
```tsx
|
|
784
|
+
import { createApp, history, getHistory } from 'clxx';
|
|
785
|
+
|
|
786
|
+
await createApp({
|
|
787
|
+
target: '#root', // 挂载目标元素
|
|
788
|
+
mode: 'hash', // 路由模式:'browser' | 'hash' | 'memory'
|
|
789
|
+
default: '/index', // 默认路由路径
|
|
790
|
+
designWidth: 750, // 设计稿宽度
|
|
791
|
+
globalStyle: css`...`, // 全局样式
|
|
792
|
+
loading: (pathname) => <Loading />, // 加载占位
|
|
793
|
+
render: async (pathname) => <PageComponent />, // 页面渲染
|
|
794
|
+
notFound: (pathname) => <NotFound />, // 404 页面
|
|
795
|
+
onBefore: async (pathname) => {}, // 页面加载前钩子
|
|
796
|
+
onAfter: async (pathname) => {}, // 页面加载后钩子
|
|
797
|
+
});
|
|
509
798
|
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
normalizeUnit(0.5, '%'); // "0.5%"
|
|
799
|
+
// 编程式导航
|
|
800
|
+
history.push('/about');
|
|
801
|
+
history.replace('/login');
|
|
514
802
|
```
|
|
515
803
|
|
|
516
804
|
---
|
|
517
805
|
|
|
518
|
-
|
|
806
|
+
#### sendRequest / GET / POST / sendJSON — 网络请求
|
|
519
807
|
|
|
520
|
-
|
|
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);
|
|
808
|
+
基于 Fetch API 封装的网络请求工具,支持多种数据格式和超时控制。
|
|
533
809
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
console.log('每帧执行');
|
|
537
|
-
});
|
|
810
|
+
```typescript
|
|
811
|
+
import { GET, POST, sendJSON, sendRequest, registerHostAlias } from 'clxx';
|
|
538
812
|
|
|
539
|
-
//
|
|
540
|
-
const
|
|
541
|
-
update(); // 触发组件重新渲染
|
|
813
|
+
// 简单 GET 请求
|
|
814
|
+
const result = await GET('/api/list', { page: 1 });
|
|
542
815
|
|
|
543
|
-
//
|
|
544
|
-
|
|
545
|
-
console.log('窗口大小改变');
|
|
546
|
-
}, 100); // 防抖 100ms
|
|
816
|
+
// POST 表单
|
|
817
|
+
const result = await POST('/api/submit', { name: 'test' });
|
|
547
818
|
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
819
|
+
// 发送 JSON
|
|
820
|
+
const result = await sendJSON('/api/create', { title: 'new' });
|
|
821
|
+
|
|
822
|
+
// 完整配置
|
|
823
|
+
const result = await sendRequest({
|
|
824
|
+
url: '/api/data',
|
|
825
|
+
method: 'POST',
|
|
826
|
+
sendType: 'json', // 'normal' | 'text' | 'form' | 'json' | 'blob' | 'params' | 'buffer'
|
|
827
|
+
data: { key: 'value' },
|
|
828
|
+
timeout: 10000, // 超时时间,默认 30s
|
|
829
|
+
disableUrlCache: true, // 禁用 URL 缓存
|
|
830
|
+
transmitPageParam: true, // 透传当前页面参数
|
|
553
831
|
});
|
|
832
|
+
|
|
833
|
+
// 注册 Host 别名
|
|
834
|
+
registerHostAlias({
|
|
835
|
+
api: 'https://api.example.com',
|
|
836
|
+
});
|
|
837
|
+
// 使用别名发送请求
|
|
838
|
+
GET('api@/user/info', { id: 1 });
|
|
554
839
|
```
|
|
555
840
|
|
|
841
|
+
**SendType 类型:**
|
|
842
|
+
|
|
843
|
+
| 类型 | 说明 | data 类型 |
|
|
844
|
+
|------|------|----------|
|
|
845
|
+
| `normal` | GET 请求,数据附加到 URL | `Record<string, any>` / `string` |
|
|
846
|
+
| `text` | 文本请求体 | `string` |
|
|
847
|
+
| `form` | FormData 请求体 | `Record<string, any>` / `FormData` |
|
|
848
|
+
| `json` | JSON 字符串请求体 | `Record<string, any>` |
|
|
849
|
+
| `blob` | Blob 原始二进制 | `Blob` |
|
|
850
|
+
| `params` | URLSearchParams 请求体 | `Record<string, any>` / `URLSearchParams` |
|
|
851
|
+
| `buffer` | ArrayBuffer 请求体 | `ArrayBuffer` / `TypedArray` / `DataView` |
|
|
852
|
+
|
|
556
853
|
---
|
|
557
854
|
|
|
558
|
-
|
|
855
|
+
#### jsonp — JSONP 请求
|
|
559
856
|
|
|
560
|
-
```
|
|
561
|
-
import {
|
|
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
|
-
});
|
|
857
|
+
```typescript
|
|
858
|
+
import { jsonp } from 'clxx';
|
|
571
859
|
|
|
572
|
-
|
|
573
|
-
const { num, unit } = splitValue('100px');
|
|
574
|
-
console.log(num, unit); // 100, "px"
|
|
860
|
+
const result = await jsonp('https://api.example.com/data', 'callback');
|
|
575
861
|
```
|
|
576
862
|
|
|
577
863
|
---
|
|
578
864
|
|
|
579
|
-
|
|
865
|
+
#### Countdown — 倒计时类
|
|
580
866
|
|
|
581
|
-
|
|
867
|
+
底层倒计时工具类,基于 `requestAnimationFrame` 实现精确计时。
|
|
582
868
|
|
|
583
|
-
```
|
|
584
|
-
import {
|
|
869
|
+
```typescript
|
|
870
|
+
import { Countdown } from 'clxx';
|
|
585
871
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
872
|
+
const countdown = new Countdown({
|
|
873
|
+
remain: 120, // 剩余秒数
|
|
874
|
+
format: 'his', // 格式:d天 h时 i分 s秒
|
|
875
|
+
onUpdate: (value) => {
|
|
876
|
+
console.log(value); // { h: 0, i: 2, s: 0 }
|
|
877
|
+
},
|
|
878
|
+
onEnd: () => {
|
|
879
|
+
console.log('倒计时结束');
|
|
880
|
+
},
|
|
590
881
|
});
|
|
882
|
+
|
|
883
|
+
countdown.start();
|
|
884
|
+
// countdown.stop(); // 暂停,可再次 start 恢复
|
|
591
885
|
```
|
|
592
886
|
|
|
593
887
|
---
|
|
594
888
|
|
|
595
|
-
|
|
889
|
+
#### tick — 帧循环
|
|
596
890
|
|
|
597
|
-
|
|
891
|
+
基于 `requestAnimationFrame` 的循环执行函数,支持可选间隔。
|
|
598
892
|
|
|
599
|
-
```
|
|
600
|
-
import
|
|
601
|
-
import ReactDOM from 'react-dom/client';
|
|
602
|
-
import { Container } from 'clxx';
|
|
603
|
-
import App from './App';
|
|
893
|
+
```typescript
|
|
894
|
+
import { tick } from 'clxx';
|
|
604
895
|
|
|
605
|
-
|
|
896
|
+
// 每帧执行
|
|
897
|
+
const stop = tick(() => {
|
|
898
|
+
// 每帧回调
|
|
899
|
+
});
|
|
606
900
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
);
|
|
901
|
+
// 指定间隔执行
|
|
902
|
+
const stop = tick(() => {
|
|
903
|
+
// 每 1000ms 执行一次
|
|
904
|
+
}, 1000);
|
|
905
|
+
|
|
906
|
+
// 停止
|
|
907
|
+
stop();
|
|
615
908
|
```
|
|
616
909
|
|
|
617
|
-
|
|
910
|
+
---
|
|
911
|
+
|
|
912
|
+
#### is — 环境检测
|
|
618
913
|
|
|
619
|
-
|
|
620
|
-
import { createApp, setContextValue } from 'clxx';
|
|
914
|
+
常用的运行环境判断工具,结果缓存,避免重复检测。
|
|
621
915
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
maxDocWidth: 576,
|
|
625
|
-
minDocWidth: 312,
|
|
626
|
-
});
|
|
916
|
+
```typescript
|
|
917
|
+
import { is } from 'clxx';
|
|
627
918
|
|
|
628
|
-
//
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
});
|
|
919
|
+
is('ios'); // iOS 平台(含 iPadOS 13+)
|
|
920
|
+
is('android'); // Android 平台
|
|
921
|
+
is('wechat'); // 微信环境
|
|
922
|
+
is('qq'); // QQ / QQ浏览器
|
|
923
|
+
is('alipay'); // 支付宝
|
|
924
|
+
is('weibo'); // 微博
|
|
925
|
+
is('douyin'); // 抖音
|
|
926
|
+
is('xiaohongshu'); // 小红书
|
|
927
|
+
is('toutiao'); // 今日头条
|
|
928
|
+
is('baidu'); // 百度 APP
|
|
929
|
+
is('touchable'); // 可触摸设备
|
|
659
930
|
```
|
|
660
931
|
|
|
661
|
-
|
|
932
|
+
---
|
|
662
933
|
|
|
663
|
-
|
|
664
|
-
// 1. 使用防抖优化 resize
|
|
665
|
-
<Container resizeDelay={150} />
|
|
934
|
+
#### ago — 相对时间格式化
|
|
666
935
|
|
|
667
|
-
|
|
668
|
-
showUniqToast('只显示最新的');
|
|
936
|
+
将任意日期格式化为中文相对时间描述,支持过去和未来。
|
|
669
937
|
|
|
670
|
-
|
|
671
|
-
|
|
938
|
+
```typescript
|
|
939
|
+
import { ago } from 'clxx';
|
|
672
940
|
|
|
673
|
-
//
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
onReachBottom={loadMore}
|
|
677
|
-
/>
|
|
941
|
+
ago('2025-01-01'); // { num: x, unit: 'm', format: 'x个月前' }
|
|
942
|
+
ago(new Date()); // { num: 0, unit: 's', format: '刚刚' }
|
|
943
|
+
ago('2028-06-01'); // { num: x, unit: 'y', format: 'x年后' }
|
|
678
944
|
```
|
|
679
945
|
|
|
680
946
|
---
|
|
681
947
|
|
|
682
|
-
|
|
948
|
+
#### calendarTable — 月历数据
|
|
683
949
|
|
|
684
|
-
|
|
950
|
+
生成一个月的日历表格数据(6×7 的二维 dayjs 数组),可用于自定义日历组件。
|
|
685
951
|
|
|
686
|
-
```
|
|
687
|
-
import
|
|
688
|
-
ContainerProps,
|
|
689
|
-
ToastProps,
|
|
690
|
-
DialogProps,
|
|
691
|
-
OverlayProps,
|
|
692
|
-
// ... 更多类型
|
|
693
|
-
} from 'clxx';
|
|
952
|
+
```typescript
|
|
953
|
+
import { calendarTable } from 'clxx';
|
|
694
954
|
|
|
695
|
-
|
|
696
|
-
|
|
955
|
+
const table = calendarTable('2026-03-01');
|
|
956
|
+
// table: Dayjs[][] — 6 行 7 列
|
|
697
957
|
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
content: message,
|
|
701
|
-
duration: 3000,
|
|
702
|
-
position: 'middle',
|
|
703
|
-
});
|
|
704
|
-
};
|
|
958
|
+
const table = calendarTable('2026-03', true); // 从周日开始
|
|
959
|
+
const table = calendarTable('2026-03', false, false); // 不保证 6 行
|
|
705
960
|
```
|
|
706
961
|
|
|
962
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
963
|
+
|------|------|--------|------|
|
|
964
|
+
| `usefulFormat` | `dayjs.ConfigType` | `dayjs()` | 日期值 |
|
|
965
|
+
| `startFromSunday` | `boolean` | `false` | 是否周日开始 |
|
|
966
|
+
| `sizeGuarantee` | `boolean` | `true` | 是否保证 6 行 |
|
|
967
|
+
|
|
707
968
|
---
|
|
708
969
|
|
|
709
|
-
|
|
970
|
+
#### waitFor / waitUntil — 等待工具
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
import { waitFor, waitUntil } from 'clxx';
|
|
974
|
+
|
|
975
|
+
// 等待指定时间
|
|
976
|
+
await waitFor(1000); // 等待 1 秒
|
|
710
977
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
978
|
+
// 等待条件为真
|
|
979
|
+
const success = await waitUntil(() => document.querySelector('.target') !== null, 5000);
|
|
980
|
+
// success: true(条件满足)或 false(超时)
|
|
981
|
+
```
|
|
714
982
|
|
|
715
983
|
---
|
|
716
984
|
|
|
717
|
-
|
|
985
|
+
#### normalizeUnit / splitValue — CSS 值处理
|
|
718
986
|
|
|
719
|
-
|
|
987
|
+
```typescript
|
|
988
|
+
import { normalizeUnit, splitValue } from 'clxx';
|
|
989
|
+
|
|
990
|
+
normalizeUnit(100); // '100px'
|
|
991
|
+
normalizeUnit('1.5rem'); // '1.5rem'
|
|
992
|
+
normalizeUnit('100'); // '100px'
|
|
993
|
+
|
|
994
|
+
splitValue(100); // { num: 100, unit: 'px' }
|
|
995
|
+
splitValue('1.5rem'); // { num: 1.5, unit: 'rem' }
|
|
996
|
+
```
|
|
720
997
|
|
|
721
998
|
---
|
|
722
999
|
|
|
723
|
-
|
|
1000
|
+
#### defaultScroll — 默认滚动控制
|
|
724
1001
|
|
|
725
|
-
|
|
1002
|
+
禁用/启用页面默认滚动行为(针对触摸事件)。
|
|
1003
|
+
|
|
1004
|
+
```typescript
|
|
1005
|
+
import { defaultScroll } from 'clxx';
|
|
1006
|
+
|
|
1007
|
+
defaultScroll.disable(); // 禁止页面滚动
|
|
1008
|
+
defaultScroll.enable(); // 恢复页面滚动
|
|
1009
|
+
```
|
|
726
1010
|
|
|
727
1011
|
---
|
|
728
1012
|
|
|
729
|
-
|
|
1013
|
+
#### uniqKey — 唯一键生成
|
|
1014
|
+
|
|
1015
|
+
生成全局唯一的字符串标识。
|
|
730
1016
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
1017
|
+
```typescript
|
|
1018
|
+
import { uniqKey } from 'clxx';
|
|
1019
|
+
|
|
1020
|
+
const key = uniqKey(); // 如 "m1abc1"
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
---
|
|
1024
|
+
|
|
1025
|
+
#### createPortalDOM — Portal 容器
|
|
1026
|
+
|
|
1027
|
+
创建挂载到 body 的 React Portal 容器,用于命令式渲染组件。
|
|
1028
|
+
|
|
1029
|
+
```typescript
|
|
1030
|
+
import { createPortalDOM } from 'clxx';
|
|
1031
|
+
|
|
1032
|
+
const { mount, unmount, element } = createPortalDOM();
|
|
1033
|
+
mount(<MyComponent />); // 渲染组件到 body
|
|
1034
|
+
unmount(); // 卸载并移除 DOM
|
|
1035
|
+
```
|
|
734
1036
|
|
|
735
1037
|
---
|
|
736
1038
|
|
|
737
|
-
|
|
1039
|
+
#### setContextValue / getContextValue — 全局上下文
|
|
738
1040
|
|
|
739
|
-
|
|
1041
|
+
简单的全局键值存储,用于跨组件共享数据。
|
|
740
1042
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
- Container 组件新增 `resizeDelay` 配置项
|
|
1043
|
+
```typescript
|
|
1044
|
+
import { setContextValue, getContextValue } from 'clxx';
|
|
744
1045
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
- 修复 SafeArea 类型定义
|
|
750
|
-
- 修复字体缩放可能导致的无限循环
|
|
1046
|
+
setContextValue({ token: 'xxx', userId: 123 });
|
|
1047
|
+
getContextValue('token'); // 'xxx'
|
|
1048
|
+
getContextValue(); // { token: 'xxx', userId: 123 }
|
|
1049
|
+
```
|
|
751
1050
|
|
|
752
|
-
|
|
753
|
-
- Overlay 组件添加 resize 防抖
|
|
754
|
-
- Container 组件优化初始化逻辑
|
|
755
|
-
- useWindowResize 使用现代 API 替代废弃的 orientationchange
|
|
756
|
-
- 优化网络请求超时逻辑,使用 AbortController
|
|
1051
|
+
---
|
|
757
1052
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1053
|
+
## 📋 完整导出列表
|
|
1054
|
+
|
|
1055
|
+
### 组件
|
|
1056
|
+
| 导出 | 类型 | 说明 |
|
|
1057
|
+
|------|------|------|
|
|
1058
|
+
| `Container` | 组件 | 自适应根容器 |
|
|
1059
|
+
| `Flex` / `FlexItem` | 组件 | Flex 布局 |
|
|
1060
|
+
| `Row` / `RowStart` ~ `RowEvenly` | 组件 | 水平布局快捷组件 |
|
|
1061
|
+
| `Col` / `ColStart` ~ `ColEvenly` | 组件 | 垂直布局快捷组件 |
|
|
1062
|
+
| `AutoGrid` | 组件 | 自动网格 |
|
|
1063
|
+
| `SafeArea` | 组件 | 安全区域 |
|
|
1064
|
+
| `Clickable` | 组件 | 点击态 |
|
|
1065
|
+
| `Overlay` | 组件 | 覆盖层 |
|
|
1066
|
+
| `ScrollView` | 组件 | 滚动视图 |
|
|
1067
|
+
| `CarouselNotice` | 组件 | 轮播公告 |
|
|
1068
|
+
| `Indicator` | 组件 | 加载指示器 |
|
|
1069
|
+
| `Countdowner` | 组件 | 倒计时 |
|
|
1070
|
+
| `Ago` | 组件 | 相对时间 |
|
|
1071
|
+
|
|
1072
|
+
### 函数式调用
|
|
1073
|
+
| 导出 | 说明 |
|
|
1074
|
+
|------|------|
|
|
1075
|
+
| `showToast` / `showUniqToast` | 轻提示 |
|
|
1076
|
+
| `showDialog` | 对话框 |
|
|
1077
|
+
| `showAlert` | 弹窗提示 |
|
|
1078
|
+
| `showLoading` / `showLoadingAtLeast` | 加载指示 |
|
|
1079
|
+
|
|
1080
|
+
### Hooks
|
|
1081
|
+
| 导出 | 说明 |
|
|
1082
|
+
|------|------|
|
|
1083
|
+
| `useWindowResize` | 窗口变化监听 |
|
|
1084
|
+
| `useViewport` | Viewport meta 管理 |
|
|
1085
|
+
| `useInterval` | 安全 setInterval |
|
|
1086
|
+
| `useTick` | 帧循环 |
|
|
1087
|
+
| `useUpdate` | 强制刷新 |
|
|
1088
|
+
|
|
1089
|
+
### 工具函数
|
|
1090
|
+
| 导出 | 说明 |
|
|
1091
|
+
|------|------|
|
|
1092
|
+
| `createApp` / `history` / `getHistory` | 路由应用 |
|
|
1093
|
+
| `GET` / `POST` / `sendJSON` / `sendRequest` / `sugarSend` | 网络请求 |
|
|
1094
|
+
| `buildUrlByOption` / `registerHostAlias` | URL 构建与别名 |
|
|
1095
|
+
| `jsonp` | JSONP 请求 |
|
|
1096
|
+
| `Countdown` | 倒计时类 |
|
|
1097
|
+
| `tick` | 帧循环函数 |
|
|
1098
|
+
| `is` | 环境检测 |
|
|
1099
|
+
| `ago` | 相对时间格式化 |
|
|
1100
|
+
| `calendarTable` | 月历数据 |
|
|
1101
|
+
| `waitFor` / `waitUntil` | 等待工具 |
|
|
1102
|
+
| `normalizeUnit` / `splitValue` | CSS 值处理 |
|
|
1103
|
+
| `defaultScroll` | 滚动控制 |
|
|
1104
|
+
| `uniqKey` | 唯一键 |
|
|
1105
|
+
| `createPortalDOM` | Portal 容器 |
|
|
1106
|
+
| `setContextValue` / `getContextValue` | 全局上下文 |
|
|
763
1107
|
|
|
764
1108
|
---
|
|
765
1109
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
1110
|
+
## 📄 License
|
|
1111
|
+
|
|
1112
|
+
[MIT](LICENSE) © [Joye](https://github.com/joye61)
|