clxx 2.1.8 → 3.0.0
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/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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PRIMARY = exports.VISIBLE_ROWS = exports.ITEM_HEIGHT_REM = void 0;
|
|
4
|
+
exports.createStyle = createStyle;
|
|
5
|
+
const react_1 = require("@emotion/react");
|
|
6
|
+
const color_1 = require("../utils/color");
|
|
7
|
+
const theme_1 = require("../utils/theme");
|
|
8
|
+
// iOS 风格设计变量
|
|
9
|
+
const textPrimary = "#000000";
|
|
10
|
+
const textSecondary = "#3c3c43"; // iOS secondaryLabel
|
|
11
|
+
const textTertiary = "#8e8e93"; // iOS tertiaryLabel
|
|
12
|
+
const bgPage = "#ffffff";
|
|
13
|
+
const bgSubtle = "rgba(120,120,128,.12)"; // iOS quaternary fill
|
|
14
|
+
// 可见行数 = 5,单元高度 = .8rem
|
|
15
|
+
exports.ITEM_HEIGHT_REM = 0.8;
|
|
16
|
+
exports.VISIBLE_ROWS = 5;
|
|
17
|
+
function createStyle(primary, rounded = true) {
|
|
18
|
+
const primaryActive = (0, color_1.darken)(primary, 0.15);
|
|
19
|
+
const sheetRadius = rounded ? ".28rem" : "0";
|
|
20
|
+
const indicatorRadius = rounded ? ".12rem" : "0";
|
|
21
|
+
return {
|
|
22
|
+
// 内容容器:动画/全屏/居中由 Dialog 提供,这里只保留视觉与排版
|
|
23
|
+
sheet: (0, react_1.css)({
|
|
24
|
+
width: "100%",
|
|
25
|
+
backgroundColor: bgPage,
|
|
26
|
+
borderTopLeftRadius: sheetRadius,
|
|
27
|
+
borderTopRightRadius: sheetRadius,
|
|
28
|
+
overflow: "hidden",
|
|
29
|
+
userSelect: "none",
|
|
30
|
+
color: textPrimary,
|
|
31
|
+
fontFamily: theme_1.fontStack,
|
|
32
|
+
WebkitFontSmoothing: "antialiased",
|
|
33
|
+
MozOsxFontSmoothing: "grayscale",
|
|
34
|
+
}),
|
|
35
|
+
// iOS 风标题栏:底部 hairline 与下方 body 做轻量区块分隔
|
|
36
|
+
header: (0, react_1.css)({
|
|
37
|
+
height: ".92rem",
|
|
38
|
+
display: "flex",
|
|
39
|
+
alignItems: "center",
|
|
40
|
+
justifyContent: "space-between",
|
|
41
|
+
padding: "0 .16rem",
|
|
42
|
+
borderBottom: "1px solid rgba(60,60,67,.18)",
|
|
43
|
+
}),
|
|
44
|
+
title: (0, react_1.css)({
|
|
45
|
+
flex: 1,
|
|
46
|
+
textAlign: "center",
|
|
47
|
+
fontSize: ".32rem",
|
|
48
|
+
fontWeight: 600,
|
|
49
|
+
color: textPrimary,
|
|
50
|
+
letterSpacing: ".01rem",
|
|
51
|
+
}),
|
|
52
|
+
btn: (0, react_1.css)({
|
|
53
|
+
minWidth: "1.1rem",
|
|
54
|
+
padding: "0 .08rem",
|
|
55
|
+
fontSize: ".3rem",
|
|
56
|
+
fontWeight: 400,
|
|
57
|
+
lineHeight: ".92rem",
|
|
58
|
+
cursor: "pointer",
|
|
59
|
+
transition: "opacity .15s, color .15s",
|
|
60
|
+
}),
|
|
61
|
+
btnCancel: (0, react_1.css)({
|
|
62
|
+
textAlign: "left",
|
|
63
|
+
color: textSecondary,
|
|
64
|
+
"&:active": { opacity: 0.55 },
|
|
65
|
+
}),
|
|
66
|
+
btnConfirm: (0, react_1.css)({
|
|
67
|
+
textAlign: "right",
|
|
68
|
+
fontWeight: 600,
|
|
69
|
+
color: primary,
|
|
70
|
+
"&:active": { color: primaryActive, opacity: 0.65 },
|
|
71
|
+
}),
|
|
72
|
+
body: (0, react_1.css)({
|
|
73
|
+
position: "relative",
|
|
74
|
+
display: "flex",
|
|
75
|
+
height: `${exports.ITEM_HEIGHT_REM * exports.VISIBLE_ROWS}rem`,
|
|
76
|
+
padding: "0 .16rem .12rem",
|
|
77
|
+
}),
|
|
78
|
+
// iOS 风选中背景:柔和的 quaternary fill、充足圆角
|
|
79
|
+
indicator: (0, react_1.css)({
|
|
80
|
+
position: "absolute",
|
|
81
|
+
left: ".16rem",
|
|
82
|
+
right: ".16rem",
|
|
83
|
+
top: `${exports.ITEM_HEIGHT_REM * 2}rem`,
|
|
84
|
+
height: `${exports.ITEM_HEIGHT_REM}rem`,
|
|
85
|
+
pointerEvents: "none",
|
|
86
|
+
backgroundColor: bgSubtle,
|
|
87
|
+
borderRadius: indicatorRadius,
|
|
88
|
+
}),
|
|
89
|
+
column: (0, react_1.css)({
|
|
90
|
+
position: "relative",
|
|
91
|
+
flex: 1,
|
|
92
|
+
minWidth: 0,
|
|
93
|
+
height: "100%",
|
|
94
|
+
overflow: "hidden",
|
|
95
|
+
touchAction: "none",
|
|
96
|
+
// 自管理手势,禁用浏览器原生选中/拖拽
|
|
97
|
+
userSelect: "none",
|
|
98
|
+
WebkitUserSelect: "none",
|
|
99
|
+
}),
|
|
100
|
+
// 内层位移容器:transform translateY(-offset) 实现"滚动"
|
|
101
|
+
columnInner: (0, react_1.css)({
|
|
102
|
+
position: "absolute",
|
|
103
|
+
top: 0,
|
|
104
|
+
left: 0,
|
|
105
|
+
right: 0,
|
|
106
|
+
willChange: "transform",
|
|
107
|
+
}),
|
|
108
|
+
item: (0, react_1.css)({
|
|
109
|
+
height: `${exports.ITEM_HEIGHT_REM}rem`,
|
|
110
|
+
lineHeight: `${exports.ITEM_HEIGHT_REM}rem`,
|
|
111
|
+
fontSize: ".32rem",
|
|
112
|
+
fontWeight: 400,
|
|
113
|
+
fontFamily: theme_1.numberFontStack,
|
|
114
|
+
fontVariantNumeric: "tabular-nums",
|
|
115
|
+
letterSpacing: ".01rem",
|
|
116
|
+
textAlign: "center",
|
|
117
|
+
color: textTertiary,
|
|
118
|
+
transition: "color .18s ease",
|
|
119
|
+
}),
|
|
120
|
+
itemActive: (0, react_1.css)({
|
|
121
|
+
color: textPrimary,
|
|
122
|
+
fontWeight: 500,
|
|
123
|
+
}),
|
|
124
|
+
spacer: (0, react_1.css)({
|
|
125
|
+
height: `${exports.ITEM_HEIGHT_REM * 2}rem`,
|
|
126
|
+
pointerEvents: "none",
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
exports.DEFAULT_PRIMARY = "#2f7dff";
|
package/build/Dialog/Wrapper.js
CHANGED
|
@@ -2,32 +2,42 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Wrapper = Wrapper;
|
|
4
4
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
5
6
|
const Overlay_1 = require("../Overlay");
|
|
6
7
|
const style_1 = require("./style");
|
|
8
|
+
// 常量:Overlay 顶层容器 css
|
|
9
|
+
const overlayCss = { overflow: "hidden" };
|
|
10
|
+
// 仅这几种类型需要附加位置样式;center 由 Overlay 的 centerContent 居中
|
|
11
|
+
const POSITIONED_TYPES = new Set([
|
|
12
|
+
"pullUp",
|
|
13
|
+
"pullDown",
|
|
14
|
+
"pullLeft",
|
|
15
|
+
"pullRight",
|
|
16
|
+
]);
|
|
7
17
|
function Wrapper(props) {
|
|
8
18
|
const { type = "center", status = "show", children, onHide, showMask = true, maskColor, maskStyle, boxStyle, onBlankClick, } = props;
|
|
9
19
|
const { animation, keyframes } = (0, style_1.getAnimation)(type, status);
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
];
|
|
15
|
-
// 遮罩的样式
|
|
16
|
-
let maskCss = [
|
|
20
|
+
// 缓存:仅当 type 变化时重建
|
|
21
|
+
const positionStyle = (0, react_1.useMemo)(() => (POSITIONED_TYPES.has(type) ? style_1.style[type] : null), [type]);
|
|
22
|
+
// 缓存:仅当 status / 外部 maskStyle / maskColor 变化时重建
|
|
23
|
+
const maskCss = (0, react_1.useMemo)(() => [
|
|
17
24
|
style_1.style.mask,
|
|
18
25
|
status === "show" ? style_1.style.maskShow : style_1.style.maskHide,
|
|
19
26
|
maskStyle,
|
|
20
|
-
maskColor ? { backgroundColor: maskColor } :
|
|
21
|
-
];
|
|
22
|
-
//
|
|
27
|
+
maskColor ? { backgroundColor: maskColor } : null,
|
|
28
|
+
], [status, maskStyle, maskColor]);
|
|
29
|
+
// 空白处点击:仅在事件 target 与 currentTarget 一致时触发,避免冒泡误关闭
|
|
23
30
|
const blankClick = (event) => {
|
|
24
31
|
if (event.target === event.currentTarget) {
|
|
25
32
|
event.stopPropagation();
|
|
26
33
|
onBlankClick === null || onBlankClick === void 0 ? void 0 : onBlankClick(event);
|
|
27
34
|
}
|
|
28
35
|
};
|
|
29
|
-
return ((0, jsx_runtime_1.jsxs)(Overlay_1.Overlay, { css:
|
|
30
|
-
|
|
36
|
+
return ((0, jsx_runtime_1.jsxs)(Overlay_1.Overlay, { css: overlayCss, centerContent: type === "center", maskColor: "transparent", fullScreen: true, onClick: showMask ? undefined : blankClick, children: [showMask && (0, jsx_runtime_1.jsx)("div", { css: maskCss, onClick: blankClick }), (0, jsx_runtime_1.jsx)("div", { css: [style_1.style.boxCss, positionStyle, boxStyle, animation], onAnimationEnd: (event) => {
|
|
37
|
+
// 仅响应隐藏动画结束,且必须是 box 自身的动画(避免子元素同名动画事件冒泡误触发)
|
|
38
|
+
if (status === "hide" &&
|
|
39
|
+
event.target === event.currentTarget &&
|
|
40
|
+
event.animationName === keyframes.name) {
|
|
31
41
|
onHide === null || onHide === void 0 ? void 0 : onHide();
|
|
32
42
|
}
|
|
33
43
|
}, children: children })] }));
|
package/build/Dialog/index.d.ts
CHANGED
|
@@ -8,7 +8,13 @@ export interface ShowDialogOption extends WrapperProps {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* 显示一个对话框,出现和隐藏都带有动画效果
|
|
11
|
+
*
|
|
12
|
+
* 关键不变量:
|
|
13
|
+
* 1. 关闭操作幂等:多次调用返回的 close(),或 close() 与遮罩点击并发,都只触发一次隐藏动画与 unmount。
|
|
14
|
+
* 2. 所有等待同一次关闭的 Promise 都会 resolve(不会因后续调用覆盖 onHide 而悬挂)。
|
|
15
|
+
* 3. 用户的 onBlankClick 在事件循环同步阶段触发(避免 React 合成事件被回收),关闭动画并行进行。
|
|
16
|
+
*
|
|
11
17
|
* @param option
|
|
12
|
-
* @returns
|
|
18
|
+
* @returns 关闭函数;返回的 Promise 在隐藏动画结束、容器卸载完成后 resolve
|
|
13
19
|
*/
|
|
14
20
|
export declare function showDialog(option: React.ReactNode | ShowDialogOption): () => Promise<void>;
|
package/build/Dialog/index.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -21,8 +12,14 @@ const isPlainObject_1 = __importDefault(require("lodash/isPlainObject"));
|
|
|
21
12
|
const omit_1 = __importDefault(require("lodash/omit"));
|
|
22
13
|
/**
|
|
23
14
|
* 显示一个对话框,出现和隐藏都带有动画效果
|
|
15
|
+
*
|
|
16
|
+
* 关键不变量:
|
|
17
|
+
* 1. 关闭操作幂等:多次调用返回的 close(),或 close() 与遮罩点击并发,都只触发一次隐藏动画与 unmount。
|
|
18
|
+
* 2. 所有等待同一次关闭的 Promise 都会 resolve(不会因后续调用覆盖 onHide 而悬挂)。
|
|
19
|
+
* 3. 用户的 onBlankClick 在事件循环同步阶段触发(避免 React 合成事件被回收),关闭动画并行进行。
|
|
20
|
+
*
|
|
24
21
|
* @param option
|
|
25
|
-
* @returns
|
|
22
|
+
* @returns 关闭函数;返回的 Promise 在隐藏动画结束、容器卸载完成后 resolve
|
|
26
23
|
*/
|
|
27
24
|
function showDialog(option) {
|
|
28
25
|
const { mount, unmount } = (0, dom_1.createPortalDOM)();
|
|
@@ -37,34 +34,62 @@ function showDialog(option) {
|
|
|
37
34
|
// 提取需要单独处理的配置项
|
|
38
35
|
const blankClosable = !!config.blankClosable;
|
|
39
36
|
const children = config.content;
|
|
40
|
-
const
|
|
41
|
-
const
|
|
37
|
+
const userOnBlankClick = config.onBlankClick;
|
|
38
|
+
const userOnHide = config.onHide;
|
|
42
39
|
const props = (0, omit_1.default)(config, [
|
|
43
40
|
'blankClosable',
|
|
44
41
|
'content',
|
|
45
42
|
'onHide',
|
|
43
|
+
'onBlankClick',
|
|
46
44
|
]);
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
// 关闭状态机:'idle' -> 'closing' -> 'closed'
|
|
46
|
+
// 通过状态保证 closeDialog 幂等,避免重复 mount 已 unmount 的 root,避免 onHide 覆盖丢失 resolve
|
|
47
|
+
let closeState = 'idle';
|
|
48
|
+
const waiters = [];
|
|
49
|
+
const closeDialog = () => {
|
|
50
|
+
if (closeState === 'closed') {
|
|
51
|
+
return Promise.resolve();
|
|
52
|
+
}
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
waiters.push(resolve);
|
|
55
|
+
if (closeState === 'closing') {
|
|
56
|
+
// 已经在关闭流程中,仅排队等待
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
closeState = 'closing';
|
|
60
|
+
props.status = 'hide';
|
|
61
|
+
props.onHide = () => {
|
|
62
|
+
// 防御:onAnimationEnd 仅应触发一次,但若 React 重渲染导致再次冒泡也只处理一次
|
|
63
|
+
if (closeState === 'closed')
|
|
64
|
+
return;
|
|
65
|
+
closeState = 'closed';
|
|
66
|
+
unmount();
|
|
67
|
+
try {
|
|
68
|
+
userOnHide === null || userOnHide === void 0 ? void 0 : userOnHide();
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
// 唤醒全部等待者,即使 user onHide 抛错也不影响 Promise 链
|
|
72
|
+
const list = waiters.splice(0);
|
|
73
|
+
list.forEach((fn) => fn());
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
mount((0, jsx_runtime_1.jsx)(Wrapper_1.Wrapper, Object.assign({}, props, { children: children })));
|
|
61
77
|
});
|
|
78
|
+
};
|
|
79
|
+
// 空白处可点击关闭:同步触发用户回调,并行启动关闭动画
|
|
80
|
+
if (blankClosable) {
|
|
81
|
+
props.onBlankClick = (event) => {
|
|
82
|
+
// 先同步调用用户回调(保留事件对象的有效性)
|
|
83
|
+
userOnBlankClick === null || userOnBlankClick === void 0 ? void 0 : userOnBlankClick(event);
|
|
84
|
+
// 再触发关闭(不 await,避免事件对象失效)
|
|
85
|
+
closeDialog();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else if (userOnBlankClick) {
|
|
89
|
+
// 不自动关闭但用户仍订阅了 blank click
|
|
90
|
+
props.onBlankClick = userOnBlankClick;
|
|
62
91
|
}
|
|
63
92
|
// 挂载容器对象
|
|
64
|
-
|
|
65
|
-
return
|
|
66
|
-
// 关闭前确保容器已经被挂载
|
|
67
|
-
yield amountShow;
|
|
68
|
-
yield closeDialog();
|
|
69
|
-
});
|
|
93
|
+
mount((0, jsx_runtime_1.jsx)(Wrapper_1.Wrapper, Object.assign({}, props, { children: children })));
|
|
94
|
+
return closeDialog;
|
|
70
95
|
}
|
package/build/Dialog/style.js
CHANGED
|
@@ -127,7 +127,7 @@ function getAnimation(type, status) {
|
|
|
127
127
|
}
|
|
128
128
|
exports.style = {
|
|
129
129
|
maskShow: (0, react_1.css)({
|
|
130
|
-
animation: `${maskShow} 300ms ease`,
|
|
130
|
+
animation: `${maskShow} 300ms ease forwards`,
|
|
131
131
|
}),
|
|
132
132
|
maskHide: (0, react_1.css)({
|
|
133
133
|
animation: `${exports.maskHide} 300ms ease forwards`,
|
|
@@ -139,10 +139,14 @@ exports.style = {
|
|
|
139
139
|
bottom: 0,
|
|
140
140
|
width: "100%",
|
|
141
141
|
height: "100%",
|
|
142
|
-
backgroundColor: "rgba(0, 0, 0, 0.
|
|
142
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
143
|
+
// 提升为合成层,opacity 动画走 GPU,避免触发重绘
|
|
144
|
+
willChange: "opacity",
|
|
143
145
|
}),
|
|
144
146
|
boxCss: (0, react_1.css)({
|
|
145
147
|
zIndex: 2,
|
|
148
|
+
// transform 动画走 GPU 合成
|
|
149
|
+
willChange: "transform, opacity",
|
|
146
150
|
}),
|
|
147
151
|
pullUp: (0, react_1.css)({
|
|
148
152
|
position: "absolute",
|
|
@@ -6,9 +6,12 @@ function useInterval(callback, delay) {
|
|
|
6
6
|
const savedCallback = (0, react_1.useRef)(callback);
|
|
7
7
|
savedCallback.current = callback;
|
|
8
8
|
(0, react_1.useEffect)(() => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// 仅当 delay 为有限的非负数时才启动定时器;
|
|
10
|
+
// null/undefined/NaN 等都视为暂停
|
|
11
|
+
if (typeof delay !== "number" || !isFinite(delay) || delay < 0) {
|
|
12
|
+
return;
|
|
12
13
|
}
|
|
14
|
+
const interval = setInterval(() => savedCallback.current(), delay);
|
|
15
|
+
return () => clearInterval(interval);
|
|
13
16
|
}, [delay]);
|
|
14
17
|
}
|
package/build/Fixed/index.js
CHANGED
|
@@ -13,28 +13,19 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.Fixed = Fixed;
|
|
15
15
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
16
|
+
// 模块级常量:4 种 position 的样式静态、与 props 无关,只哈希一次
|
|
17
|
+
const baseStyle = {
|
|
18
|
+
position: "fixed",
|
|
19
|
+
// 与 Container 的 maxWidth 联动,PC 端 fixed 元素也水平居中限宽
|
|
20
|
+
maxWidth: "var(--clxx-max-width, 100%)",
|
|
21
|
+
};
|
|
22
|
+
const positionStyles = {
|
|
23
|
+
top: Object.assign(Object.assign({}, baseStyle), { top: 0, width: "100%", left: "50%", transform: "translateX(-50%)" }),
|
|
24
|
+
bottom: Object.assign(Object.assign({}, baseStyle), { bottom: 0, width: "100%", left: "50%", transform: "translateX(-50%)" }),
|
|
25
|
+
left: Object.assign(Object.assign({}, baseStyle), { top: 0, left: "50%", height: "100%", transform: "translateX(calc(var(--clxx-max-width, 100vw) / -2))" }),
|
|
26
|
+
right: Object.assign(Object.assign({}, baseStyle), { top: 0, left: "50%", height: "100%", transform: "translateX(calc(var(--clxx-max-width, 100vw) / 2 - 100%))" }),
|
|
27
|
+
};
|
|
16
28
|
function Fixed(props) {
|
|
17
29
|
const { children, position = "bottom" } = props, extra = __rest(props, ["children", "position"]);
|
|
18
|
-
|
|
19
|
-
position: "fixed",
|
|
20
|
-
};
|
|
21
|
-
if (position === "top") {
|
|
22
|
-
styles.top = 0;
|
|
23
|
-
styles.width = "100%";
|
|
24
|
-
styles.left = 0;
|
|
25
|
-
}
|
|
26
|
-
else if (position === "bottom") {
|
|
27
|
-
styles.bottom = 0;
|
|
28
|
-
styles.width = "100%";
|
|
29
|
-
styles.left = 0;
|
|
30
|
-
}
|
|
31
|
-
else if (position === "left") {
|
|
32
|
-
styles.left = 0;
|
|
33
|
-
styles.height = "100%";
|
|
34
|
-
}
|
|
35
|
-
else if (position === "right") {
|
|
36
|
-
styles.right = 0;
|
|
37
|
-
styles.height = "100%";
|
|
38
|
-
}
|
|
39
|
-
return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, props, { css: styles }, extra, { children: props.children })));
|
|
30
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({}, extra, { css: positionStyles[position], children: children })));
|
|
40
31
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as CSS from 'csstype';
|
|
2
|
+
export interface FlexItemProps extends React.HTMLProps<HTMLDivElement> {
|
|
3
|
+
children?: React.ReactNode;
|
|
4
|
+
alignSelf?: CSS.Property.AlignSelf;
|
|
5
|
+
order?: CSS.Property.Order;
|
|
6
|
+
flex?: CSS.Property.BoxFlex;
|
|
7
|
+
flexGrow?: CSS.Property.FlexGrow;
|
|
8
|
+
flexShrink?: CSS.Property.FlexShrink;
|
|
9
|
+
flexBasis?: CSS.Property.FlexBasis;
|
|
10
|
+
}
|
|
11
|
+
export declare function FlexItem(props: FlexItemProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.FlexItem = FlexItem;
|
|
15
|
+
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
16
|
+
function FlexItem(props) {
|
|
17
|
+
const { children, alignSelf, order, flex, flexGrow, flexShrink, flexBasis, style } = props, extra = __rest(props, ["children", "alignSelf", "order", "flex", "flexGrow", "flexShrink", "flexBasis", "style"]);
|
|
18
|
+
// 布局属性走原生 inline style:避免 emotion 在每次 render 哈希对象字面量
|
|
19
|
+
const inlineStyle = Object.assign({ alignSelf,
|
|
20
|
+
order,
|
|
21
|
+
flex,
|
|
22
|
+
flexGrow,
|
|
23
|
+
flexShrink,
|
|
24
|
+
flexBasis }, style);
|
|
25
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ style: inlineStyle }, extra, { children: children })));
|
|
26
|
+
}
|
package/build/Flex/index.d.ts
CHANGED
|
@@ -8,14 +8,6 @@ export interface FlexProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
8
8
|
flexWrap?: CSS.Property.FlexWrap;
|
|
9
9
|
flexDirection?: CSS.Property.FlexDirection;
|
|
10
10
|
}
|
|
11
|
-
export interface FlexItemProps extends React.HTMLProps<HTMLDivElement> {
|
|
12
|
-
children?: React.ReactNode;
|
|
13
|
-
alignSelf?: CSS.Property.AlignSelf;
|
|
14
|
-
order?: CSS.Property.Order;
|
|
15
|
-
flex?: CSS.Property.BoxFlex;
|
|
16
|
-
flexGrow?: CSS.Property.FlexGrow;
|
|
17
|
-
flexShrink?: CSS.Property.FlexShrink;
|
|
18
|
-
flexBasis?: CSS.Property.FlexBasis;
|
|
19
|
-
}
|
|
20
11
|
export declare function Flex(props: FlexProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
21
|
-
export
|
|
12
|
+
export type { FlexItemProps } from './FlexItem';
|
|
13
|
+
export { FlexItem } from './FlexItem';
|
package/build/Flex/index.js
CHANGED
|
@@ -11,29 +11,19 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.FlexItem = void 0;
|
|
14
15
|
exports.Flex = Flex;
|
|
15
|
-
exports.FlexItem = FlexItem;
|
|
16
16
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
17
17
|
function Flex(props) {
|
|
18
|
-
const { children, alignItems = 'center', alignContent, justifyContent, flexFlow, flexWrap, flexDirection } = props, extra = __rest(props, ["children", "alignItems", "alignContent", "justifyContent", "flexFlow", "flexWrap", "flexDirection"]);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} }, extra, { children: children })));
|
|
28
|
-
}
|
|
29
|
-
function FlexItem(props) {
|
|
30
|
-
const { children, alignSelf, order, flex, flexGrow, flexShrink, flexBasis } = props, extra = __rest(props, ["children", "alignSelf", "order", "flex", "flexGrow", "flexShrink", "flexBasis"]);
|
|
31
|
-
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: {
|
|
32
|
-
alignSelf,
|
|
33
|
-
order,
|
|
34
|
-
flex,
|
|
35
|
-
flexGrow,
|
|
36
|
-
flexShrink,
|
|
37
|
-
flexBasis,
|
|
38
|
-
} }, extra, { children: children })));
|
|
18
|
+
const { children, alignItems = 'center', alignContent, justifyContent, flexFlow, flexWrap, flexDirection, style } = props, extra = __rest(props, ["children", "alignItems", "alignContent", "justifyContent", "flexFlow", "flexWrap", "flexDirection", "style"]);
|
|
19
|
+
// 布局属性走原生 inline style:避免 emotion 在每次 render 哈希对象字面量
|
|
20
|
+
const inlineStyle = Object.assign({ display: 'flex', alignItems,
|
|
21
|
+
alignContent,
|
|
22
|
+
justifyContent,
|
|
23
|
+
flexFlow,
|
|
24
|
+
flexWrap,
|
|
25
|
+
flexDirection }, style);
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ style: inlineStyle }, extra, { children: children })));
|
|
39
27
|
}
|
|
28
|
+
var FlexItem_1 = require("./FlexItem");
|
|
29
|
+
Object.defineProperty(exports, "FlexItem", { enumerable: true, get: function () { return FlexItem_1.FlexItem; } });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Interpolation, Theme } from
|
|
2
|
-
import
|
|
3
|
-
import * as CSS from
|
|
4
|
-
export interface IndicatorProps extends
|
|
1
|
+
import { Interpolation, Theme } from "@emotion/react";
|
|
2
|
+
import { HTMLAttributes } from "react";
|
|
3
|
+
import * as CSS from "csstype";
|
|
4
|
+
export interface IndicatorProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
5
|
size?: CSS.Property.Width | number;
|
|
6
6
|
rounded?: boolean;
|
|
7
7
|
barWidth?: number;
|
|
@@ -12,7 +12,10 @@ export interface IndicatorProps extends React.DetailedHTMLProps<React.HTMLAttrib
|
|
|
12
12
|
containerStyle?: Interpolation<Theme>;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* iOS 风菊花转圈指示器(仿 UIActivityIndicatorView 节奏)。
|
|
16
|
+
* 性能要点:
|
|
17
|
+
* - 用 opacity 动画替代 fill 动画,触发 GPU 合成而非 SVG paint
|
|
18
|
+
* - keyframes 全局单例,颜色/时长变化不会污染样式表
|
|
19
|
+
* - animation-delay 走 inline style,emotion 不再为每条 bar 生成独立类名
|
|
17
20
|
*/
|
|
18
21
|
export declare function Indicator(props: IndicatorProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
package/build/Indicator/index.js
CHANGED
|
@@ -10,56 +10,53 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
}
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
14
|
exports.Indicator = Indicator;
|
|
18
15
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
19
16
|
const react_1 = require("@emotion/react");
|
|
20
|
-
const react_2 =
|
|
17
|
+
const react_2 = require("react");
|
|
21
18
|
const cssUtil_1 = require("../utils/cssUtil");
|
|
22
19
|
const style_1 = require("./style");
|
|
23
20
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* iOS 风菊花转圈指示器(仿 UIActivityIndicatorView 节奏)。
|
|
22
|
+
* 性能要点:
|
|
23
|
+
* - 用 opacity 动画替代 fill 动画,触发 GPU 合成而非 SVG paint
|
|
24
|
+
* - keyframes 全局单例,颜色/时长变化不会污染样式表
|
|
25
|
+
* - animation-delay 走 inline style,emotion 不再为每条 bar 生成独立类名
|
|
26
26
|
*/
|
|
27
27
|
function Indicator(props) {
|
|
28
|
-
const { size, rounded = true, barWidth =
|
|
28
|
+
const { size, rounded = true, barWidth = 8, barHeight = 26, barColor = "#ffffff", barCount = 12, duration = 1000, containerStyle } = props, attributes = __rest(props, ["size", "rounded", "barWidth", "barHeight", "barColor", "barCount", "duration", "containerStyle"]);
|
|
29
29
|
const radius = rounded ? barWidth / 2 : 0;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
animationDelay: `${-(duration * (barCount - i)) / barCount}ms`,
|
|
36
|
-
} }, i));
|
|
37
|
-
}
|
|
38
|
-
return bars;
|
|
39
|
-
}, [barCount, barWidth, barHeight, radius, duration]);
|
|
40
|
-
// 使用 useMemo 缓存样式,避免每次都重新计算
|
|
41
|
-
const style = react_2.default.useMemo(() => [
|
|
42
|
-
{
|
|
43
|
-
fontSize: 0,
|
|
44
|
-
},
|
|
45
|
-
typeof size !== 'undefined' ? {
|
|
46
|
-
width: (0, cssUtil_1.normalizeUnit)(size),
|
|
47
|
-
height: (0, cssUtil_1.normalizeUnit)(size),
|
|
48
|
-
} : {
|
|
49
|
-
width: '.6rem',
|
|
50
|
-
height: '.6rem',
|
|
51
|
-
}
|
|
30
|
+
const containerCss = (0, react_2.useMemo)(() => [
|
|
31
|
+
{ fontSize: 0, display: "inline-block", lineHeight: 0 },
|
|
32
|
+
size !== undefined
|
|
33
|
+
? { width: (0, cssUtil_1.normalizeUnit)(size), height: (0, cssUtil_1.normalizeUnit)(size) }
|
|
34
|
+
: { width: ".4rem", height: ".4rem" },
|
|
52
35
|
], [size]);
|
|
53
|
-
const
|
|
54
|
-
width:
|
|
55
|
-
height:
|
|
36
|
+
const svgCss = (0, react_2.useMemo)(() => (0, react_1.css)({
|
|
37
|
+
width: "100%",
|
|
38
|
+
height: "100%",
|
|
39
|
+
display: "block",
|
|
56
40
|
rect: {
|
|
57
|
-
fill:
|
|
58
|
-
animationName:
|
|
41
|
+
fill: barColor,
|
|
42
|
+
animationName: `${style_1.barFadeKeyframes}`,
|
|
59
43
|
animationDuration: `${duration}ms`,
|
|
60
|
-
animationTimingFunction:
|
|
61
|
-
animationIterationCount:
|
|
44
|
+
animationTimingFunction: "linear",
|
|
45
|
+
animationIterationCount: "infinite",
|
|
46
|
+
willChange: "opacity",
|
|
62
47
|
},
|
|
63
48
|
}), [barColor, duration]);
|
|
64
|
-
|
|
49
|
+
const bars = (0, react_2.useMemo)(() => {
|
|
50
|
+
const list = [];
|
|
51
|
+
const x = (100 - barWidth) / 2;
|
|
52
|
+
for (let i = 0; i < barCount; i++) {
|
|
53
|
+
list.push((0, jsx_runtime_1.jsx)("rect", { x: x, y: 0, rx: radius, ry: radius, width: barWidth, height: barHeight, transform: `rotate(${(360 / barCount) * i} 50 50)`,
|
|
54
|
+
// 负 delay:组件挂载即处于稳态动画中,不会先停后转
|
|
55
|
+
style: {
|
|
56
|
+
animationDelay: `${-(duration * (barCount - i)) / barCount}ms`,
|
|
57
|
+
} }, i));
|
|
58
|
+
}
|
|
59
|
+
return list;
|
|
60
|
+
}, [barCount, barWidth, barHeight, radius, duration]);
|
|
61
|
+
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: [containerCss, containerStyle] }, attributes, { children: (0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 100 100", css: svgCss, "aria-hidden": "true", children: bars }) })));
|
|
65
62
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* iOS 菊花单条 bar 的不透明度衰减动画
|
|
3
|
+
* 1 -> 0.18 线性衰减;keyframes 与颜色解耦,全局共享单例,
|
|
4
|
+
* 走 opacity 触发 GPU 合成而非 SVG paint,性能更高。
|
|
4
5
|
*/
|
|
5
|
-
export declare
|
|
6
|
+
export declare const barFadeKeyframes: {
|
|
6
7
|
name: string;
|
|
7
8
|
styles: string;
|
|
8
9
|
anim: 1;
|