clxx 2.1.8 → 3.0.1
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/AGENTS.md +2 -1
- package/README.md +147 -22
- package/build/Alert/Wrapper.js +4 -3
- package/build/Alert/style.js +11 -7
- package/build/AutoGrid/index.js +21 -15
- package/build/CarouselNotice/index.d.ts +19 -11
- package/build/CarouselNotice/index.js +80 -74
- package/build/CarouselNotice/style.js +14 -4
- package/build/CitySelect/index.js +30 -55
- package/build/CitySelect/style.js +22 -56
- package/build/Clickable/index.js +7 -0
- package/build/Container/index.d.ts +12 -4
- package/build/Container/index.js +94 -89
- package/build/Countdowner/index.js +4 -2
- package/build/DatePicker/Column.d.ts +9 -0
- package/build/DatePicker/Column.js +330 -0
- package/build/DatePicker/index.d.ts +32 -0
- package/build/DatePicker/index.js +230 -0
- package/build/DatePicker/style.d.ts +6 -0
- package/build/DatePicker/style.js +130 -0
- package/build/Dialog/Wrapper.d.ts +0 -1
- package/build/Dialog/Wrapper.js +22 -12
- package/build/Dialog/index.d.ts +7 -1
- package/build/Dialog/index.js +57 -32
- package/build/Dialog/style.js +6 -2
- package/build/Effect/useInterval.js +6 -3
- package/build/Fixed/index.js +13 -22
- package/build/Flex/FlexItem.d.ts +11 -0
- package/build/Flex/FlexItem.js +26 -0
- package/build/Flex/index.d.ts +2 -10
- package/build/Flex/index.js +12 -22
- package/build/Indicator/index.d.ts +9 -6
- package/build/Indicator/index.js +34 -37
- package/build/Indicator/style.d.ts +4 -3
- package/build/Indicator/style.js +8 -13
- package/build/Loading/Wrapper.js +2 -1
- package/build/Loading/style.js +9 -12
- package/build/Overlay/index.js +6 -1
- package/build/RegionPicker/data.d.ts +6 -0
- package/build/RegionPicker/data.js +14486 -0
- package/build/RegionPicker/index.d.ts +33 -0
- package/build/RegionPicker/index.js +205 -0
- package/build/RegionPicker/style.d.ts +4 -0
- package/build/RegionPicker/style.js +187 -0
- package/build/SafeArea/index.js +14 -17
- package/build/ScrollView/index.d.ts +23 -11
- package/build/ScrollView/index.js +132 -118
- package/build/ScrollView/style.d.ts +1 -1
- package/build/ScrollView/style.js +33 -22
- package/build/Toast/Toast.d.ts +0 -1
- package/build/Toast/Toast.js +6 -4
- package/build/Toast/style.d.ts +3 -7
- package/build/Toast/style.js +33 -41
- package/build/index.d.ts +3 -0
- package/build/index.js +7 -1
- package/build/utils/color.d.ts +5 -0
- package/build/utils/color.js +18 -0
- package/build/utils/createApp.d.ts +16 -2
- package/build/utils/createApp.js +142 -109
- package/build/utils/dom.js +4 -3
- package/build/utils/theme.d.ts +2 -0
- package/build/utils/theme.js +7 -0
- package/package.json +1 -1
- package/test/src/date-picker/index.jsx +119 -0
- package/test/src/index/index.jsx +2 -0
- package/test/src/index.jsx +1 -0
- package/test/src/loading/index.jsx +2 -2
- package/test/src/region-picker/index.jsx +120 -0
- package/test/src/scrollview/BasicSection.jsx +56 -0
- package/test/src/scrollview/CustomLoadingSection.jsx +53 -0
- package/test/src/scrollview/HeightModeSection.jsx +42 -0
- package/test/src/scrollview/ImperativeSection.jsx +56 -0
- package/test/src/scrollview/NotScrollableSection.jsx +32 -0
- package/test/src/scrollview/PerfSection.jsx +34 -0
- package/test/src/scrollview/index.css +92 -8
- package/test/src/scrollview/index.jsx +13 -45
package/AGENTS.md
CHANGED
package/README.md
CHANGED
|
@@ -22,12 +22,24 @@
|
|
|
22
22
|
- 📱 **响应式布局** — 基于 rem 的自适应方案,自动处理浏览器字体缩放
|
|
23
23
|
- 🎨 **CSS-in-JS** — 使用 Emotion 实现样式隔离和动态样式
|
|
24
24
|
- 🔧 **TypeScript** — 完整的类型定义,提供更好的开发体验
|
|
25
|
-
- 🎪 **函数式调用** — Toast、Dialog、Loading、Alert 等支持命令式调用
|
|
25
|
+
- 🎪 **函数式调用** — Toast、Dialog、Loading、Alert、Picker 等支持命令式调用
|
|
26
26
|
- 🚀 **现代化** — 基于 React 19,使用最新的 Hooks API
|
|
27
27
|
- 📦 **轻量级** — tree-shaking 友好,按需引入
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
+
## 🆕 What's New (v3.0)
|
|
32
|
+
|
|
33
|
+
- **新增 `DatePicker` / `showDatePicker`**:iOS 风格滚轮日期选择器,支持 day / hour / minute / second 多档精度,含 min/max 范围联动夹取。
|
|
34
|
+
- **新增 `RegionPicker` / `showRegionPicker`**:iOS 风格三级(省/市/区)地区选择器,内置中国行政区数据。
|
|
35
|
+
- **`Dialog` 健壮化**:关闭操作幂等(多次调用 close / 并发遮罩点击只触发一次动画);用户 `onBlankClick` 改为同步触发,事件对象不再失效;动画走 `transform/opacity` GPU 合成;`onAnimationEnd` 严格匹配自身关键帧防止子元素动画冒泡误触。
|
|
36
|
+
- **`CitySelect` 重构**:复用 `Dialog`(`pullLeft`)完成弹出/收起动画,删除组件内自管动画代码。
|
|
37
|
+
- **性能优化**:`Toast` / `Flex` / `FlexItem` / `Fixed` / `SafeArea` / `AutoGrid` 中纯 CSS 属性赋值改走原生 inline `style`,省去 emotion 哈希;模块级常量替代 render 内对象字面量。
|
|
38
|
+
|
|
39
|
+
> 公共 API 完全兼容,旧版 `showCitySelect` 等签名保持不变。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
31
43
|
## 📦 安装
|
|
32
44
|
|
|
33
45
|
```bash
|
|
@@ -434,20 +446,20 @@ import { CarouselNotice } from 'clxx';
|
|
|
434
446
|
|
|
435
447
|
#### CitySelect / showCitySelect — 城市选择器
|
|
436
448
|
|
|
437
|
-
移动端全屏城市选择器,支持字母侧边栏触摸导航、粘滞字母标题、列表与字母双向联动、搜索(含中文 IME
|
|
449
|
+
移动端全屏城市选择器,支持字母侧边栏触摸导航、粘滞字母标题、列表与字母双向联动、搜索(含中文 IME 保护)、外部定位回显等。弹出 / 收起动画由 `showDialog`(`pullLeft`)统一接管,与其它弹出组件行为一致。
|
|
438
450
|
|
|
439
451
|
```tsx
|
|
440
452
|
import { CitySelect, showCitySelect } from 'clxx';
|
|
441
453
|
|
|
442
|
-
//
|
|
454
|
+
// 组件方式(作为 showDialog 的内容渲染;弹出动画由 Dialog 提供)
|
|
443
455
|
<CitySelect
|
|
444
456
|
primary="#2f7dff"
|
|
445
457
|
getLocation={async () => '北京'}
|
|
446
458
|
onSelect={(city) => console.log(city)}
|
|
447
|
-
onClose={() => console.log('
|
|
459
|
+
onClose={() => console.log('请关闭')}
|
|
448
460
|
/>
|
|
449
461
|
|
|
450
|
-
//
|
|
462
|
+
// 函数式方式(推荐):内部用 showDialog 挂到 body,选中或退出时自动卸载
|
|
451
463
|
showCitySelect({
|
|
452
464
|
primary: '#2f7dff',
|
|
453
465
|
// 同步返回
|
|
@@ -465,7 +477,7 @@ showCitySelect({
|
|
|
465
477
|
| 属性 | 类型 | 默认值 | 说明 |
|
|
466
478
|
|------|------|--------|------|
|
|
467
479
|
| `onSelect` | `(city: SelectedCity) => void` | — | 选中城市时触发 |
|
|
468
|
-
| `onClose` | `() => void` | — |
|
|
480
|
+
| `onClose` | `() => void` | — | 请求关闭(由外部 `showCitySelect` 处理动画与卸载) |
|
|
469
481
|
| `onLetterChange` | `(letter: string) => void` | — | 侧边栏当前字母变化回调 |
|
|
470
482
|
| `getLocation` | `() => string \| null \| undefined \| Promise<string \| null \| undefined>` | — | 外部定位能力,可同步或异步,返回城市名或城市 code |
|
|
471
483
|
| `primary` | `string` | `'#2f7dff'` | 主题主色,形如 `#rrggbb`;active 态颜色自动派生 |
|
|
@@ -488,11 +500,134 @@ interface SelectedCity {
|
|
|
488
500
|
- **字母侧边栏触摸导航**:非 passive 触摸监听,滑动时实时切换字母并滚动列表;松开显示大字母提示。
|
|
489
501
|
- **双向联动**:手动滚动列表时基于缓存的 `offsetTop` 二分查找激活对应字母;并通过 `ResizeObserver` 重新测量。
|
|
490
502
|
- **搜索**:按拼音全拼、拼音首字母、中文名前缀匹配;中文输入法合成期间不触发搜索,避免中间态干扰。
|
|
491
|
-
-
|
|
503
|
+
- **弹出 / 收起动画**:由 `showDialog` 统一接管(`type: 'pullLeft'`,`showMask: false`,全宽覆盖);不再在组件内自管动画。
|
|
492
504
|
- **纯属性化主题**:无 `containerStyle`,只允许通过 `primary` 控制主题色。
|
|
493
505
|
|
|
494
506
|
---
|
|
495
507
|
|
|
508
|
+
#### DatePicker / showDatePicker — 日期 / 时间选择器
|
|
509
|
+
|
|
510
|
+
iOS 风格滚轮式日期选择器,支持年 / 月 / 日 / 时 / 分 / 秒 多档精度,自动夹取到 `[minDate, maxDate]` 范围内。弹出动画由 `showDialog`(`pullUp`)接管。
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
import { DatePicker, showDatePicker } from 'clxx';
|
|
514
|
+
|
|
515
|
+
// 函数式方式(推荐)
|
|
516
|
+
showDatePicker({
|
|
517
|
+
title: '选择日期',
|
|
518
|
+
value: '2026-01-01',
|
|
519
|
+
precision: 'minute', // 'day' | 'hour' | 'minute' | 'second'
|
|
520
|
+
minDate: '2024-01-01',
|
|
521
|
+
maxDate: '2030-12-31',
|
|
522
|
+
primary: '#2f7dff',
|
|
523
|
+
onConfirm: (d) => {
|
|
524
|
+
console.log(d.format('YYYY-MM-DD HH:mm')); // dayjs 实例
|
|
525
|
+
},
|
|
526
|
+
onCancel: () => {},
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// 组件方式:作为 Dialog 内容嵌入自定义弹出场景
|
|
530
|
+
<DatePicker
|
|
531
|
+
precision="day"
|
|
532
|
+
showUnit
|
|
533
|
+
units={{ year: '年', month: '月', day: '日' }}
|
|
534
|
+
onConfirm={(d) => {}}
|
|
535
|
+
onClose={() => {}}
|
|
536
|
+
/>
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**Props:**
|
|
540
|
+
|
|
541
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
542
|
+
|------|------|--------|------|
|
|
543
|
+
| `value` | `Date \| string \| number \| Dayjs` | `dayjs()` | 初始值;超出 min/max 自动夹取 |
|
|
544
|
+
| `precision` | `'day' \| 'hour' \| 'minute' \| 'second'` | `'day'` | 精度档位 |
|
|
545
|
+
| `title` | `ReactNode` | `'请选择'` | 头部标题 |
|
|
546
|
+
| `cancelText` | `ReactNode` | `'取消'` | 取消按钮文案 |
|
|
547
|
+
| `confirmText` | `ReactNode` | `'确定'` | 确认按钮文案 |
|
|
548
|
+
| `maskClosable` | `boolean` | `true` | 点击遮罩是否可关闭(仅 `showDatePicker` 路径生效) |
|
|
549
|
+
| `primary` | `string` | `'#2f7dff'` | 主题主色 |
|
|
550
|
+
| `rounded` | `boolean` | `true` | 顶部圆角与中间选中条圆角 |
|
|
551
|
+
| `showUnit` | `boolean` | `true` | 是否在数字后显示单位(年/月/日 等) |
|
|
552
|
+
| `units` | `DatePickerUnits` | `{ year: '年', ... }` | 单位文案,可部分覆盖 |
|
|
553
|
+
| `minDate` | `DateInput` | `'1900-01-01'` | 最小可选日期 |
|
|
554
|
+
| `maxDate` | `DateInput` | `'2100-12-31 23:59:59'` | 最大可选日期 |
|
|
555
|
+
| `onConfirm` | `(date: Dayjs) => void` | — | 确认回调,参数为 dayjs 对象 |
|
|
556
|
+
| `onCancel` | `() => void` | — | 取消回调 |
|
|
557
|
+
| `onClose` | `() => void` | — | 请求关闭(由外部处理动画与卸载) |
|
|
558
|
+
|
|
559
|
+
**特性:**
|
|
560
|
+
- **联动夹取**:上级列变化时下级自动重算范围(如年→月→日→时→分→秒),当前值越界自动夹到最近边界。
|
|
561
|
+
- **月份溢出兜底**:使用 ISO 字符串构造日期,避免 `dayjs().set()` 链式赋值时的月末日溢出问题。
|
|
562
|
+
- **滚轮列复用**:拆出 `Column` 子组件,items / value 变化时按需重渲染。
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
#### RegionPicker / showRegionPicker — 三级地区选择器
|
|
567
|
+
|
|
568
|
+
iOS 风格的省 / 市 / 区三级联动选择器,内置中国行政区划数据。弹出动画由 `showDialog`(`pullUp`)接管。
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
import { RegionPicker, showRegionPicker } from 'clxx';
|
|
572
|
+
|
|
573
|
+
// 函数式方式(推荐)
|
|
574
|
+
showRegionPicker({
|
|
575
|
+
title: '请选择地区',
|
|
576
|
+
value: ['110000', '110100', '110101'], // 省/市/区 value
|
|
577
|
+
primary: '#2f7dff',
|
|
578
|
+
onConfirm: ({ province, city, district }) => {
|
|
579
|
+
console.log(province.label, city.label, district.label);
|
|
580
|
+
console.log(province.value, city.value, district.value);
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// 组件方式
|
|
585
|
+
<RegionPicker
|
|
586
|
+
data={customRegionData} // 自定义数据源
|
|
587
|
+
labels={{ province: '省', city: '市', district: '区' }}
|
|
588
|
+
onConfirm={(selection) => {}}
|
|
589
|
+
onClose={() => {}}
|
|
590
|
+
/>
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Props:**
|
|
594
|
+
|
|
595
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
596
|
+
|------|------|--------|------|
|
|
597
|
+
| `value` | `[string?, string?, string?]` | — | 初始三级 value |
|
|
598
|
+
| `data` | `TreeRegionItem[]` | 内置中国行政区 | 数据源 |
|
|
599
|
+
| `title` | `ReactNode` | `'请选择地区'` | 头部标题 |
|
|
600
|
+
| `cancelText` | `ReactNode` | `'取消'` | 取消按钮文案 |
|
|
601
|
+
| `confirmText` | `ReactNode` | `'确定'` | 确认按钮文案 |
|
|
602
|
+
| `labels` | `RegionLabels` | `{ province: '省', city: '市', district: '区' }` | tabs 未选占位文案 |
|
|
603
|
+
| `maskClosable` | `boolean` | `true` | 点击遮罩是否可关闭(仅 `showRegionPicker` 路径生效) |
|
|
604
|
+
| `primary` | `string` | `'#2f7dff'` | 主题主色 |
|
|
605
|
+
| `rounded` | `boolean` | `true` | 顶部圆角 |
|
|
606
|
+
| `onConfirm` | `(selection: RegionSelection) => void` | — | 确认回调,仅在三级齐全时可点击 |
|
|
607
|
+
| `onCancel` | `() => void` | — | 取消回调 |
|
|
608
|
+
| `onClose` | `() => void` | — | 请求关闭(由外部处理动画与卸载) |
|
|
609
|
+
|
|
610
|
+
**RegionSelection:**
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
interface RegionNode {
|
|
614
|
+
value: string; // 行政区 code
|
|
615
|
+
label: string; // 行政区名称
|
|
616
|
+
}
|
|
617
|
+
interface RegionSelection {
|
|
618
|
+
province: RegionNode;
|
|
619
|
+
city: RegionNode;
|
|
620
|
+
district: RegionNode;
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
**特性:**
|
|
625
|
+
- **tabs 联动**:选中后自动跳到下一级;切换上级会清空所有下级。
|
|
626
|
+
- **滚动定位**:切 tab 时把当前选中项对齐到列表顶部(用 `getBoundingClientRect` 差分计算偏移,不依赖 offsetParent)。
|
|
627
|
+
- **三级未齐时**:确认按钮置灰禁用,避免半选确认。
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
496
631
|
### 🔔 反馈组件
|
|
497
632
|
|
|
498
633
|
#### showToast / showUniqToast — 轻提示
|
|
@@ -1097,20 +1232,6 @@ unmount(); // 卸载并移除 DOM
|
|
|
1097
1232
|
|
|
1098
1233
|
---
|
|
1099
1234
|
|
|
1100
|
-
#### setContextValue / getContextValue — 全局上下文
|
|
1101
|
-
|
|
1102
|
-
简单的全局键值存储,用于跨组件共享数据。
|
|
1103
|
-
|
|
1104
|
-
```typescript
|
|
1105
|
-
import { setContextValue, getContextValue } from 'clxx';
|
|
1106
|
-
|
|
1107
|
-
setContextValue({ token: 'xxx', userId: 123 });
|
|
1108
|
-
getContextValue('token'); // 'xxx'
|
|
1109
|
-
getContextValue(); // { token: 'xxx', userId: 123 }
|
|
1110
|
-
```
|
|
1111
|
-
|
|
1112
|
-
---
|
|
1113
|
-
|
|
1114
1235
|
## 📋 完整导出列表
|
|
1115
1236
|
|
|
1116
1237
|
### 组件
|
|
@@ -1122,11 +1243,14 @@ getContextValue(); // { token: 'xxx', userId: 123 }
|
|
|
1122
1243
|
| `Col` / `ColStart` ~ `ColEvenly` | 组件 | 垂直布局快捷组件 |
|
|
1123
1244
|
| `AutoGrid` | 组件 | 自动网格 |
|
|
1124
1245
|
| `SafeArea` | 组件 | 安全区域 |
|
|
1246
|
+
| `Fixed` | 组件 | 固定定位 |
|
|
1125
1247
|
| `Clickable` | 组件 | 点击态 |
|
|
1126
1248
|
| `Overlay` | 组件 | 覆盖层 |
|
|
1127
1249
|
| `ScrollView` | 组件 | 滚动视图 |
|
|
1128
1250
|
| `CarouselNotice` | 组件 | 轮播公告 |
|
|
1129
1251
|
| `CitySelect` | 组件 | 城市选择器 |
|
|
1252
|
+
| `DatePicker` | 组件 | 日期 / 时间选择器 |
|
|
1253
|
+
| `RegionPicker` | 组件 | 三级地区选择器 |
|
|
1130
1254
|
| `Indicator` | 组件 | 加载指示器 |
|
|
1131
1255
|
| `Countdowner` | 组件 | 倒计时 |
|
|
1132
1256
|
| `Ago` | 组件 | 相对时间 |
|
|
@@ -1139,6 +1263,8 @@ getContextValue(); // { token: 'xxx', userId: 123 }
|
|
|
1139
1263
|
| `showAlert` | 弹窗提示 |
|
|
1140
1264
|
| `showLoading` / `showLoadingAtLeast` | 加载指示 |
|
|
1141
1265
|
| `showCitySelect` | 城市选择器 |
|
|
1266
|
+
| `showDatePicker` | 日期 / 时间选择器 |
|
|
1267
|
+
| `showRegionPicker` | 三级地区选择器 |
|
|
1142
1268
|
|
|
1143
1269
|
### Hooks
|
|
1144
1270
|
| 导出 | 说明 |
|
|
@@ -1166,7 +1292,6 @@ getContextValue(); // { token: 'xxx', userId: 123 }
|
|
|
1166
1292
|
| `defaultScroll` | 滚动控制 |
|
|
1167
1293
|
| `uniqKey` | 唯一键 |
|
|
1168
1294
|
| `createPortalDOM` | Portal 容器 |
|
|
1169
|
-
| `setContextValue` / `getContextValue` | 全局上下文 |
|
|
1170
1295
|
|
|
1171
1296
|
---
|
|
1172
1297
|
|
package/build/Alert/Wrapper.js
CHANGED
|
@@ -5,16 +5,17 @@ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
|
5
5
|
const Clickable_1 = require("../Clickable");
|
|
6
6
|
const Row_1 = require("../Flex/Row");
|
|
7
7
|
const style_1 = require("./style");
|
|
8
|
+
// 按下态底色:模块级常量,Dialog 可能多次重提交(onConfirm 后重新 render)时避免重复分配
|
|
9
|
+
const activeBg = { backgroundColor: 'rgba(0,0,0,.04)' };
|
|
8
10
|
function AlertWrapper(props) {
|
|
9
|
-
const { title = '提示', description, confirm = '确定', confirmColor = '#007aff', cancel = '取消', cancelColor = '#
|
|
11
|
+
const { title = '提示', description, confirm = '确定', confirmColor = '#007aff', cancel = '取消', cancelColor = '#3c3c43', showCancel = false, onConfirm, onCancel,
|
|
10
12
|
// 可定制的样式
|
|
11
13
|
titleStyle, descStyle, btnStyle, cancelStyle, confirmStyle, } = props;
|
|
12
14
|
// 展示按钮组
|
|
13
15
|
const btnBoxCss = [
|
|
14
16
|
style_1.style.btnBox,
|
|
15
|
-
showCancel ? style_1.style.btnBoxWithCancel :
|
|
17
|
+
showCancel ? style_1.style.btnBoxWithCancel : null,
|
|
16
18
|
];
|
|
17
|
-
const activeBg = { backgroundColor: 'rgba(0,0,0,.04)' };
|
|
18
19
|
return ((0, jsx_runtime_1.jsxs)("div", { css: style_1.style.container, children: [(0, jsx_runtime_1.jsxs)("div", { css: style_1.style.content, children: [(0, jsx_runtime_1.jsx)("div", { css: [style_1.style.title, titleStyle], children: title }), description && (0, jsx_runtime_1.jsx)("div", { css: [style_1.style.desc, descStyle], children: description })] }), (0, jsx_runtime_1.jsxs)(Row_1.Row, { alignItems: "stretch", css: btnBoxCss, children: [showCancel && ((0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: [style_1.style.btn, btnStyle, cancelStyle, { color: cancelColor }], onClick: onCancel, activeStyle: activeBg, children: cancel })), (0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: [
|
|
19
20
|
style_1.style.btn,
|
|
20
21
|
style_1.style.btnConfirm,
|
package/build/Alert/style.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.style = void 0;
|
|
4
|
+
const theme_1 = require("../utils/theme");
|
|
4
5
|
// 1px 硬边框(高清屏 hairline)
|
|
5
6
|
const hairline = 1 / (typeof window !== "undefined" ? window.devicePixelRatio : 1);
|
|
6
|
-
|
|
7
|
+
// iOS 风色板
|
|
8
|
+
const textPrimary = "#000000";
|
|
9
|
+
const textSecondary = "#3c3c43";
|
|
10
|
+
const separator = "rgba(60,60,67,.29)"; // iOS opaqueSeparator
|
|
7
11
|
exports.style = {
|
|
8
12
|
container: {
|
|
9
13
|
position: "relative",
|
|
@@ -11,8 +15,8 @@ exports.style = {
|
|
|
11
15
|
backgroundColor: "#ffffff",
|
|
12
16
|
borderRadius: ".28rem",
|
|
13
17
|
width: (750 * 0.78) / 100 + "rem",
|
|
14
|
-
fontFamily: fontStack,
|
|
15
|
-
color:
|
|
18
|
+
fontFamily: theme_1.fontStack,
|
|
19
|
+
color: textPrimary,
|
|
16
20
|
WebkitFontSmoothing: "antialiased",
|
|
17
21
|
MozOsxFontSmoothing: "grayscale",
|
|
18
22
|
boxShadow: "0 .2rem .6rem rgba(0,0,0,.18)",
|
|
@@ -30,7 +34,7 @@ exports.style = {
|
|
|
30
34
|
left: 0,
|
|
31
35
|
right: 0,
|
|
32
36
|
height: "1px",
|
|
33
|
-
backgroundColor:
|
|
37
|
+
backgroundColor: separator,
|
|
34
38
|
transform: `scale(1, ${hairline})`,
|
|
35
39
|
transformOrigin: "0 100%",
|
|
36
40
|
},
|
|
@@ -38,7 +42,7 @@ exports.style = {
|
|
|
38
42
|
title: {
|
|
39
43
|
textAlign: "center",
|
|
40
44
|
lineHeight: 1.45,
|
|
41
|
-
color:
|
|
45
|
+
color: textPrimary,
|
|
42
46
|
fontSize: ".34rem",
|
|
43
47
|
fontWeight: 600,
|
|
44
48
|
letterSpacing: ".01rem",
|
|
@@ -46,7 +50,7 @@ exports.style = {
|
|
|
46
50
|
desc: {
|
|
47
51
|
textAlign: "center",
|
|
48
52
|
lineHeight: 1.55,
|
|
49
|
-
color:
|
|
53
|
+
color: textSecondary,
|
|
50
54
|
fontSize: ".28rem",
|
|
51
55
|
marginTop: ".18rem",
|
|
52
56
|
wordBreak: "break-word",
|
|
@@ -64,7 +68,7 @@ exports.style = {
|
|
|
64
68
|
left: "50%",
|
|
65
69
|
marginLeft: "-.5px",
|
|
66
70
|
width: "1px",
|
|
67
|
-
backgroundColor:
|
|
71
|
+
backgroundColor: separator,
|
|
68
72
|
transform: `scale(${hairline}, 1)`,
|
|
69
73
|
transformOrigin: "0 0",
|
|
70
74
|
},
|
package/build/AutoGrid/index.js
CHANGED
|
@@ -80,19 +80,25 @@ function AutoGrid(props) {
|
|
|
80
80
|
}, [children, cols]);
|
|
81
81
|
// 缓存 item 宽度计算
|
|
82
82
|
const itemWidth = (0, react_1.useMemo)(() => `calc((100% - ${cols - 1} * ${gap}) / ${cols})`, [cols, gap]);
|
|
83
|
-
return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, extra, { css: containerStyle, children: gridData.map((row, rowIndex) =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, extra, { css: containerStyle, children: gridData.map((row, rowIndex) => {
|
|
84
|
+
// 行级布局属性走 inline style:纯属性赋值,无伪类/嵌套,避免 emotion 序列化
|
|
85
|
+
const rowInlineStyle = {
|
|
86
|
+
gap,
|
|
87
|
+
marginBottom: rowIndex !== gridData.length - 1 ? gap : undefined,
|
|
88
|
+
};
|
|
89
|
+
return ((0, jsx_runtime_1.jsx)("div", { css: style_1.style.row, style: rowInlineStyle, children: row.map((item, colIndex) => {
|
|
90
|
+
const isPlaceholder = item === null;
|
|
91
|
+
// cell 宽度 / 占位可见性走 inline style
|
|
92
|
+
const cellInlineStyle = {
|
|
93
|
+
width: itemWidth,
|
|
94
|
+
visibility: isPlaceholder ? "hidden" : undefined,
|
|
95
|
+
};
|
|
96
|
+
const cellCss = [
|
|
97
|
+
style_1.style.itemBox,
|
|
98
|
+
isSquare ? style_1.style.itemBoxSquare : null,
|
|
99
|
+
itemStyle,
|
|
100
|
+
];
|
|
101
|
+
return isSquare ? ((0, jsx_runtime_1.jsx)("div", { css: cellCss, style: cellInlineStyle, children: (0, jsx_runtime_1.jsx)("div", { css: style_1.style.itemInner, children: item }) }, colIndex)) : ((0, jsx_runtime_1.jsx)("div", { css: cellCss, style: cellInlineStyle, children: item }, colIndex));
|
|
102
|
+
}) }, rowIndex));
|
|
103
|
+
}) })));
|
|
98
104
|
}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { Interpolation, Theme } from "@emotion/react";
|
|
3
|
+
import * as CSS from "csstype";
|
|
4
|
+
export interface CarouselNoticeProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
5
|
+
list: Array<ReactNode>;
|
|
6
|
+
width?: CSS.Property.Width;
|
|
7
|
+
height?: CSS.Property.Height;
|
|
8
|
+
justify?: "start" | "center" | "end";
|
|
9
|
+
duration?: number;
|
|
10
|
+
interval?: number;
|
|
10
11
|
containerStyle?: Interpolation<Theme>;
|
|
11
12
|
wrapperStyle?: Interpolation<Theme>;
|
|
12
13
|
itemStyle?: Interpolation<Theme>;
|
|
13
14
|
}
|
|
15
|
+
export type CarouselNoticeOption = CarouselNoticeProps;
|
|
14
16
|
/**
|
|
15
17
|
* 滚动循环轮播公告
|
|
16
|
-
*
|
|
18
|
+
*
|
|
19
|
+
* 实现要点:
|
|
20
|
+
* - 渲染当前条 + 下一条共两项,wrapper 高 200%
|
|
21
|
+
* - 用 keyframes 把 wrapper 从 0 推到 -50%,配合 fill-mode: forwards
|
|
22
|
+
* 保证动画结束后定格在 -50%,与 React 提交解耦,杜绝"回弹闪烁"
|
|
23
|
+
* - animationend 触发后同步切换 current(新 current 的第一项 = 旧 current 的第二项)
|
|
24
|
+
* 且 wrapper 因 key 变化重挂载,回到 0 即开始下一轮
|
|
17
25
|
*/
|
|
18
|
-
export declare function CarouselNotice(props:
|
|
26
|
+
export declare function CarouselNotice(props: CarouselNoticeProps): import("@emotion/react/jsx-runtime").JSX.Element | null;
|
|
@@ -13,87 +13,93 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.CarouselNotice = CarouselNotice;
|
|
15
15
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
16
|
-
const react_1 = require("
|
|
17
|
-
const react_2 = require("react");
|
|
16
|
+
const react_1 = require("react");
|
|
17
|
+
const react_2 = require("@emotion/react");
|
|
18
18
|
const useInterval_1 = require("../Effect/useInterval");
|
|
19
19
|
const style_1 = require("./style");
|
|
20
|
+
const justifyMap = {
|
|
21
|
+
start: style_1.style.justifyStart,
|
|
22
|
+
center: style_1.style.justifyCenter,
|
|
23
|
+
end: style_1.style.justifyEnd,
|
|
24
|
+
};
|
|
20
25
|
/**
|
|
21
26
|
* 滚动循环轮播公告
|
|
22
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* 实现要点:
|
|
29
|
+
* - 渲染当前条 + 下一条共两项,wrapper 高 200%
|
|
30
|
+
* - 用 keyframes 把 wrapper 从 0 推到 -50%,配合 fill-mode: forwards
|
|
31
|
+
* 保证动画结束后定格在 -50%,与 React 提交解耦,杜绝"回弹闪烁"
|
|
32
|
+
* - animationend 触发后同步切换 current(新 current 的第一项 = 旧 current 的第二项)
|
|
33
|
+
* 且 wrapper 因 key 变化重挂载,回到 0 即开始下一轮
|
|
23
34
|
*/
|
|
24
35
|
function CarouselNotice(props) {
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
var _a;
|
|
37
|
+
const { width, height, justify = "center", interval = 3000, duration = 400, list, containerStyle, wrapperStyle, itemStyle } = props, attrs = __rest(props, ["width", "height", "justify", "interval", "duration", "list", "containerStyle", "wrapperStyle", "itemStyle"]);
|
|
38
|
+
const safeList = Array.isArray(list) ? list : [];
|
|
39
|
+
const len = safeList.length;
|
|
40
|
+
const [current, setCurrent] = (0, react_1.useState)(0);
|
|
41
|
+
const [animating, setAnimating] = (0, react_1.useState)(false);
|
|
42
|
+
// list 变化:归零 + 取消动画态;用引用比较保险
|
|
43
|
+
const lastListRef = (0, react_1.useRef)(safeList);
|
|
44
|
+
if (lastListRef.current !== safeList) {
|
|
45
|
+
lastListRef.current = safeList;
|
|
46
|
+
}
|
|
47
|
+
(0, react_1.useEffect)(() => {
|
|
32
48
|
setCurrent(0);
|
|
33
|
-
|
|
34
|
-
}, [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}, list.length > 1 ? interval : null);
|
|
41
|
-
/**
|
|
42
|
-
* 当前显示的两条数据,只用两条数据来轮播
|
|
43
|
-
*/
|
|
44
|
-
const showContent = () => {
|
|
45
|
-
const justifyStyle = {};
|
|
46
|
-
if (justify === 'center') {
|
|
47
|
-
justifyStyle.justifyContent = 'center';
|
|
48
|
-
}
|
|
49
|
-
else if (justify === 'start') {
|
|
50
|
-
justifyStyle.justifyContent = 'flex-start';
|
|
51
|
-
}
|
|
52
|
-
else if (justify === 'end') {
|
|
53
|
-
justifyStyle.justifyContent = 'flex-end';
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
justifyStyle.justifyContent = 'center';
|
|
49
|
+
setAnimating(false);
|
|
50
|
+
}, [safeList]);
|
|
51
|
+
// current 越界保护(list 缩短时)
|
|
52
|
+
(0, react_1.useEffect)(() => {
|
|
53
|
+
if (len > 0 && current >= len) {
|
|
54
|
+
setCurrent(0);
|
|
55
|
+
setAnimating(false);
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
newIndex = 0;
|
|
57
|
+
}, [len, current]);
|
|
58
|
+
// 定时触发动画;只有列表 >1 条且未在动画中才启动 interval
|
|
59
|
+
(0, useInterval_1.useInterval)(() => {
|
|
60
|
+
// 若上一轮动画还没结束(极端时序),跳过本次
|
|
61
|
+
if (!animating)
|
|
62
|
+
setAnimating(true);
|
|
63
|
+
}, len > 1 ? interval : null);
|
|
64
|
+
// 容器尺寸样式(仅在 width/height 变化时重建)
|
|
65
|
+
const sizeStyle = (0, react_1.useMemo)(() => ({ width, height }), [width, height]);
|
|
66
|
+
// 对齐样式(静态映射,无新对象产生)
|
|
67
|
+
const justifyStyle = (_a = justifyMap[justify]) !== null && _a !== void 0 ? _a : style_1.style.justifyCenter;
|
|
68
|
+
// 动画 css(仅依赖 duration,缓存复用)
|
|
69
|
+
const animationCss = (0, react_1.useMemo)(() => (0, react_2.css)({
|
|
70
|
+
animationName: `${style_1.Bubble}`,
|
|
71
|
+
animationTimingFunction: "ease-in-out",
|
|
72
|
+
animationDuration: `${duration}ms`,
|
|
73
|
+
animationFillMode: "forwards",
|
|
74
|
+
}), [duration]);
|
|
75
|
+
// 动画结束:切到下一条 + 重置动画态
|
|
76
|
+
const handleAnimationEnd = (0, react_1.useCallback)((e) => {
|
|
77
|
+
// 防止子元素动画事件冒泡误触
|
|
78
|
+
if (e.currentTarget !== e.target)
|
|
79
|
+
return;
|
|
80
|
+
setCurrent((prev) => (prev + 1) % len);
|
|
81
|
+
setAnimating(false);
|
|
82
|
+
}, [len]);
|
|
83
|
+
// 当前显示的两条
|
|
84
|
+
const items = (0, react_1.useMemo)(() => {
|
|
85
|
+
if (len === 0)
|
|
86
|
+
return [];
|
|
87
|
+
if (len === 1) {
|
|
88
|
+
return [
|
|
89
|
+
(0, jsx_runtime_1.jsx)("div", { css: [style_1.style.item, justifyStyle, itemStyle], children: safeList[0] }, "only"),
|
|
90
|
+
];
|
|
93
91
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
const nextIndex = (current + 1) % len;
|
|
93
|
+
return [
|
|
94
|
+
(0, jsx_runtime_1.jsx)("div", { css: [style_1.style.item, justifyStyle, itemStyle], children: safeList[current] }, `a-${current}`),
|
|
95
|
+
(0, jsx_runtime_1.jsx)("div", { css: [style_1.style.item, justifyStyle, itemStyle], children: safeList[nextIndex] }, `b-${nextIndex}`),
|
|
96
|
+
];
|
|
97
|
+
}, [safeList, current, len, justifyStyle, itemStyle]);
|
|
98
|
+
if (len === 0)
|
|
99
|
+
return null;
|
|
100
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, attrs, { css: [style_1.style.box, sizeStyle, containerStyle], children: (0, jsx_runtime_1.jsx)("div", { onAnimationEnd: handleAnimationEnd, css: [
|
|
101
|
+
style_1.style.wrapper,
|
|
102
|
+
animating && len > 1 ? animationCss : null,
|
|
103
|
+
wrapperStyle,
|
|
104
|
+
], children: items }, animating ? `anim-${current}` : `idle-${current}`) })));
|
|
99
105
|
}
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.style = exports.Bubble = void 0;
|
|
4
4
|
const react_1 = require("@emotion/react");
|
|
5
|
+
// translate 走百分比;fill-mode: forwards 保证动画结束后定格在 -50%,
|
|
6
|
+
// 与 React 的 setState 提交顺序解耦,杜绝"动画结束帧"回弹到 0 造成的闪烁
|
|
5
7
|
exports.Bubble = (0, react_1.keyframes) `
|
|
6
8
|
from {
|
|
7
|
-
transform:
|
|
9
|
+
transform: translate3d(0, 0, 0);
|
|
8
10
|
}
|
|
9
11
|
to {
|
|
10
|
-
transform:
|
|
12
|
+
transform: translate3d(0, -50%, 0);
|
|
11
13
|
}
|
|
12
14
|
`;
|
|
13
15
|
exports.style = {
|
|
14
16
|
box: {
|
|
15
17
|
position: "relative",
|
|
16
18
|
overflow: "hidden",
|
|
17
|
-
|
|
18
|
-
height: '.8rem',
|
|
19
|
+
height: ".8rem",
|
|
19
20
|
},
|
|
20
21
|
wrapper: {
|
|
21
22
|
position: "absolute",
|
|
@@ -23,6 +24,10 @@ exports.style = {
|
|
|
23
24
|
top: 0,
|
|
24
25
|
width: "100%",
|
|
25
26
|
height: "200%",
|
|
27
|
+
// 提示分层,避免动画期间反复合成层切换
|
|
28
|
+
willChange: "transform",
|
|
29
|
+
// 默认静止态,由 animation 驱动
|
|
30
|
+
transform: "translate3d(0, 0, 0)",
|
|
26
31
|
},
|
|
27
32
|
item: {
|
|
28
33
|
width: "100%",
|
|
@@ -30,5 +35,10 @@ exports.style = {
|
|
|
30
35
|
display: "flex",
|
|
31
36
|
alignItems: "center",
|
|
32
37
|
fontSize: "initial",
|
|
38
|
+
// 避免子元素文本溢出影响 box 高度计算
|
|
39
|
+
overflow: "hidden",
|
|
33
40
|
},
|
|
41
|
+
justifyStart: { justifyContent: "flex-start" },
|
|
42
|
+
justifyCenter: { justifyContent: "center" },
|
|
43
|
+
justifyEnd: { justifyContent: "flex-end" },
|
|
34
44
|
};
|