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
|
@@ -4,7 +4,7 @@ exports.CitySelect = CitySelect;
|
|
|
4
4
|
exports.showCitySelect = showCitySelect;
|
|
5
5
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
6
6
|
const react_1 = require("react");
|
|
7
|
-
const
|
|
7
|
+
const Dialog_1 = require("../Dialog");
|
|
8
8
|
const data_1 = require("./data");
|
|
9
9
|
const search_1 = require("./search");
|
|
10
10
|
const style_1 = require("./style");
|
|
@@ -33,7 +33,7 @@ function findCity(key) {
|
|
|
33
33
|
}
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
|
-
//
|
|
36
|
+
// 弹出与滑入/滑出动画由 showDialog (pullLeft) 接管
|
|
37
37
|
function CitySelect(props) {
|
|
38
38
|
const { onClose, onSelect, onLetterChange, getLocation, primary = style_1.DEFAULT_PRIMARY, } = props;
|
|
39
39
|
const style = (0, react_1.useMemo)(() => (0, style_1.createStyle)(primary), [primary]);
|
|
@@ -79,8 +79,6 @@ function CitySelect(props) {
|
|
|
79
79
|
const [touching, setTouching] = (0, react_1.useState)(false);
|
|
80
80
|
const [keyword, setKeyword] = (0, react_1.useState)("");
|
|
81
81
|
const [composing, setComposing] = (0, react_1.useState)(false);
|
|
82
|
-
// 动画状态:entering 刚挂载还未滑入;active 已滑入到位;exiting 正在滑出
|
|
83
|
-
const [phase, setPhase] = (0, react_1.useState)("entering");
|
|
84
82
|
// IME 合成期间不触发搜索,避免中文输入时的拼音中间态干扰结果
|
|
85
83
|
const searchResult = (0, react_1.useMemo)(() => {
|
|
86
84
|
if (composing)
|
|
@@ -93,37 +91,7 @@ function CitySelect(props) {
|
|
|
93
91
|
lettersRef.current = letters;
|
|
94
92
|
const onLetterChangeRef = (0, react_1.useRef)(onLetterChange);
|
|
95
93
|
onLetterChangeRef.current = onLetterChange;
|
|
96
|
-
//
|
|
97
|
-
(0, react_1.useEffect)(() => {
|
|
98
|
-
const id = requestAnimationFrame(() => {
|
|
99
|
-
// 再套一层,确保初始 transform 已被浏览器应用
|
|
100
|
-
requestAnimationFrame(() => setPhase("active"));
|
|
101
|
-
});
|
|
102
|
-
return () => cancelAnimationFrame(id);
|
|
103
|
-
}, []);
|
|
104
|
-
// 触发退出动画,真正的 onClose 在 transitionend 中触发
|
|
105
|
-
const triggerClose = () => {
|
|
106
|
-
if (phase === "exiting")
|
|
107
|
-
return;
|
|
108
|
-
// 进入动画未完成前退出:进入态和退出态 transform 相同,
|
|
109
|
-
// 切到 exiting 不会产生 transform 变化,transitionend 不会触发;
|
|
110
|
-
// 此时面板尚未滑入,直接调用 onClose 即可
|
|
111
|
-
if (phase === "entering") {
|
|
112
|
-
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
setPhase("exiting");
|
|
116
|
-
};
|
|
117
|
-
// 监听 inner 的 transform 过渡结束,退出阶段调用 onClose
|
|
118
|
-
const handleTransitionEnd = (e) => {
|
|
119
|
-
if (e.target !== e.currentTarget)
|
|
120
|
-
return;
|
|
121
|
-
if (e.propertyName !== "transform")
|
|
122
|
-
return;
|
|
123
|
-
if (phase === "exiting")
|
|
124
|
-
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
125
|
-
};
|
|
126
|
-
// 选中城市:回调后触发退出动画
|
|
94
|
+
// 选中城市:回调后请求关闭(动画与卸载交由 Dialog 处理)
|
|
127
95
|
const handleSelect = (city) => {
|
|
128
96
|
var _a;
|
|
129
97
|
const province = data_1.provinceData[city.pcode];
|
|
@@ -135,7 +103,7 @@ function CitySelect(props) {
|
|
|
135
103
|
code: city.pcode,
|
|
136
104
|
},
|
|
137
105
|
});
|
|
138
|
-
|
|
106
|
+
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
139
107
|
};
|
|
140
108
|
const measureSections = () => {
|
|
141
109
|
const listEl = listRef.current;
|
|
@@ -311,25 +279,32 @@ function CitySelect(props) {
|
|
|
311
279
|
cancelAnimationFrame(rafId);
|
|
312
280
|
};
|
|
313
281
|
}, [isSearching]);
|
|
314
|
-
return ((0, jsx_runtime_1.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// 每个字母对应一个区块,包含该字母开头的城市列表
|
|
325
|
-
(0, jsx_runtime_1.jsxs)("div", { ref: (el) => {
|
|
326
|
-
sectionRefs.current[k] = el;
|
|
327
|
-
}, children: [(0, jsx_runtime_1.jsx)("div", { css: style.title, children: k.toUpperCase() }), data_1.cityData[k].map((item) => ((0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: style.item, onClick: () => handleSelect(item), children: item.name }, item.code)))] }, k));
|
|
328
|
-
}) }))] }), !isSearching && ((0, jsx_runtime_1.jsx)("div", { ref: sidebarRef, css: style.sidebar, children: letters.map((k) => ((0, jsx_runtime_1.jsx)("div", { css: [style.letter, activeLetter === k && style.letterActive], children: k.toUpperCase() }, k))) })), touching && activeLetter && !isSearching && ((0, jsx_runtime_1.jsx)("div", { css: style.bigLetter, children: activeLetter.toUpperCase() }))] }) }));
|
|
282
|
+
return ((0, jsx_runtime_1.jsxs)("div", { css: style.inner, children: [(0, jsx_runtime_1.jsxs)(Col_1.ColStart, { css: style.original, alignItems: "stretch", children: [(0, jsx_runtime_1.jsx)("div", { css: style.top, children: (0, jsx_runtime_1.jsxs)(Row_1.RowStart, { children: [(0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 1024 1024", css: style.icon, children: (0, jsx_runtime_1.jsx)("path", { d: "M896 870.4l-128-128c55.467-68.267 89.6-149.333 89.6-238.933 0-98.134-38.4-192-110.933-264.534-149.334-149.333-384-149.333-533.334-4.266-145.066 145.066-145.066 384 0 529.066 72.534 72.534 166.4 110.934 264.534 110.934 89.6 0 174.933-29.867 238.933-89.6l128 128c4.267 4.266 12.8 8.533 21.333 8.533s17.067-4.267 21.334-8.533c17.066-8.534 17.066-29.867 8.533-42.667zM260.267 721.067c-119.467-123.734-119.467-320 0-439.467 59.733-59.733 140.8-89.6 217.6-89.6 81.066 0 157.866 29.867 217.6 89.6 59.733 59.733 89.6 136.533 89.6 217.6 0 81.067-34.134 162.133-89.6 217.6-55.467 59.733-132.267 93.867-217.6 93.867-81.067 0-157.867-34.134-217.6-89.6z" }) }), (0, jsx_runtime_1.jsx)("input", { css: style.input, placeholder: "\u8BF7\u8F93\u5165\u5B57\u6BCD\u6216\u6C49\u5B57\u641C\u7D22\u57CE\u5E02", value: keyword, onChange: (e) => setKeyword(e.target.value), onCompositionStart: () => setComposing(true), onCompositionEnd: (e) => {
|
|
283
|
+
setComposing(false);
|
|
284
|
+
setKeyword(e.currentTarget.value);
|
|
285
|
+
} }), (0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: style.exit, onClick: () => onClose === null || onClose === void 0 ? void 0 : onClose(), children: "\u9000\u51FA" })] }) }), !isSearching && locatedCity && ((0, jsx_runtime_1.jsxs)(Clickable_1.Clickable, { css: style.locate, onClick: () => handleSelect(locatedCity), children: [(0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 1024 1024", css: style.locateIcon, children: [(0, jsx_runtime_1.jsx)("path", { d: "M512 128C352 128 224 256 224 416c0 115.2 70.4 204.8 160 320 32 44.8 70.4 89.6 102.4 147.2C492.8 889.6 499.2 896 512 896l0 0c12.8 0 19.2-6.4 25.6-12.8 32-51.2 70.4-96 102.4-140.8 89.6-121.6 160-211.2 160-326.4C800 256 672 128 512 128zM588.8 704c-25.6 32-51.2 64-76.8 102.4-25.6-38.4-57.6-76.8-76.8-108.8-83.2-108.8-147.2-192-147.2-281.6C288 294.4 390.4 192 512 192s224 102.4 224 224C736 505.6 672 595.2 588.8 704z" }), (0, jsx_runtime_1.jsx)("path", { d: "M512 416m-96 0a1.5 1.5 0 1 0 192 0 1.5 1.5 0 1 0-192 0Z" })] }), (0, jsx_runtime_1.jsx)("span", { css: style.locateLabel, children: "\u5F53\u524D\u5B9A\u4F4D" }), (0, jsx_runtime_1.jsx)("span", { css: style.locateName, children: locatedCity.name })] })), isSearching ? (searchResult.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { css: style.searchList, children: searchResult.map((item) => ((0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: style.item, onClick: () => handleSelect(item), children: item.name }, item.code))) })) : ((0, jsx_runtime_1.jsx)("div", { css: style.empty, children: "\u6CA1\u6709\u5339\u914D\u7684\u57CE\u5E02" }))) : ((0, jsx_runtime_1.jsx)("div", { ref: listRef, css: style.list, children: letters.map((k) => {
|
|
286
|
+
return (
|
|
287
|
+
// 每个字母对应一个区块,包含该字母开头的城市列表
|
|
288
|
+
(0, jsx_runtime_1.jsxs)("div", { ref: (el) => {
|
|
289
|
+
sectionRefs.current[k] = el;
|
|
290
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { css: style.title, children: k.toUpperCase() }), data_1.cityData[k].map((item) => ((0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: style.item, onClick: () => handleSelect(item), children: item.name }, item.code)))] }, k));
|
|
291
|
+
}) }))] }), !isSearching && ((0, jsx_runtime_1.jsx)("div", { ref: sidebarRef, css: style.sidebar, children: letters.map((k) => ((0, jsx_runtime_1.jsx)("div", { css: [style.letter, activeLetter === k && style.letterActive], children: k.toUpperCase() }, k))) })), touching && activeLetter && !isSearching && ((0, jsx_runtime_1.jsx)("div", { css: style.bigLetter, children: activeLetter.toUpperCase() }))] }));
|
|
329
292
|
}
|
|
330
293
|
function showCitySelect(options = {}) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
294
|
+
let closing = false;
|
|
295
|
+
let close;
|
|
296
|
+
const requestClose = () => {
|
|
297
|
+
if (closing)
|
|
298
|
+
return;
|
|
299
|
+
closing = true;
|
|
300
|
+
close === null || close === void 0 ? void 0 : close();
|
|
301
|
+
};
|
|
302
|
+
close = (0, Dialog_1.showDialog)({
|
|
303
|
+
type: "pullLeft",
|
|
304
|
+
// 全屏不透明面板,无需遮罩
|
|
305
|
+
showMask: false,
|
|
306
|
+
// 给 Dialog 的 pullLeft 容器拉满宽度(默认仅 right:0/height:100%)
|
|
307
|
+
boxStyle: { left: 0, width: "100%" },
|
|
308
|
+
content: (0, jsx_runtime_1.jsx)(CitySelect, Object.assign({}, options, { onClose: requestClose })),
|
|
309
|
+
});
|
|
335
310
|
}
|
|
@@ -3,63 +3,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DEFAULT_PRIMARY = void 0;
|
|
4
4
|
exports.createStyle = createStyle;
|
|
5
5
|
const react_1 = require("@emotion/react");
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
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 / placeholder
|
|
10
12
|
const bgPage = "#ffffff";
|
|
11
|
-
const bgSubtle = "
|
|
12
|
-
const
|
|
13
|
-
const fontStack = '-apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif';
|
|
14
|
-
// 将 #rrggbb 颜色按比例变暗,用于派生 primaryActive
|
|
15
|
-
function darken(hex, amount) {
|
|
16
|
-
const m = hex.replace("#", "");
|
|
17
|
-
if (m.length !== 6)
|
|
18
|
-
return hex;
|
|
19
|
-
const r = parseInt(m.slice(0, 2), 16);
|
|
20
|
-
const g = parseInt(m.slice(2, 4), 16);
|
|
21
|
-
const b = parseInt(m.slice(4, 6), 16);
|
|
22
|
-
const f = (v) => Math.max(0, Math.min(255, Math.round(v * (1 - amount))));
|
|
23
|
-
const toHex = (v) => f(v).toString(16).padStart(2, "0");
|
|
24
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
25
|
-
}
|
|
13
|
+
const bgSubtle = "rgba(120,120,128,.12)"; // iOS quaternary fill
|
|
14
|
+
const bgGrouped = "#f2f2f7"; // iOS systemGroupedBackground
|
|
26
15
|
// 根据 primary 色值生成一整套样式;primaryActive 由 primary 派生
|
|
27
16
|
function createStyle(primary) {
|
|
28
|
-
const primaryActive = darken(primary, 0.15);
|
|
17
|
+
const primaryActive = (0, color_1.darken)(primary, 0.15);
|
|
29
18
|
return {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
// 内容容器:动画/全屏由 Dialog (pullLeft) 提供,这里只保留视觉与排版。
|
|
20
|
+
// 内部 sidebar / bigLetter 使用 absolute 时,以此为定位上下文。
|
|
21
|
+
inner: (0, react_1.css)({
|
|
22
|
+
position: "relative",
|
|
34
23
|
width: "100%",
|
|
35
24
|
height: "100%",
|
|
36
|
-
|
|
37
|
-
overflow: "hidden",
|
|
25
|
+
backgroundColor: bgPage,
|
|
38
26
|
userSelect: "none",
|
|
39
27
|
color: textPrimary,
|
|
40
|
-
fontFamily: fontStack,
|
|
28
|
+
fontFamily: theme_1.fontStack,
|
|
41
29
|
WebkitFontSmoothing: "antialiased",
|
|
42
30
|
MozOsxFontSmoothing: "grayscale",
|
|
43
31
|
}),
|
|
44
|
-
inner: (0, react_1.css)({
|
|
45
|
-
position: "absolute",
|
|
46
|
-
top: 0,
|
|
47
|
-
left: 0,
|
|
48
|
-
width: "100%",
|
|
49
|
-
height: "100%",
|
|
50
|
-
backgroundColor: bgPage,
|
|
51
|
-
transition: "transform .3s cubic-bezier(.22,.61,.36,1)",
|
|
52
|
-
willChange: "transform",
|
|
53
|
-
}),
|
|
54
|
-
innerEnter: (0, react_1.css)({
|
|
55
|
-
transform: "translateX(100%)",
|
|
56
|
-
}),
|
|
57
|
-
innerActive: (0, react_1.css)({
|
|
58
|
-
transform: "translateX(0)",
|
|
59
|
-
}),
|
|
60
|
-
innerExit: (0, react_1.css)({
|
|
61
|
-
transform: "translateX(100%)",
|
|
62
|
-
}),
|
|
63
32
|
sidebar: (0, react_1.css)({
|
|
64
33
|
position: "absolute",
|
|
65
34
|
top: "50%",
|
|
@@ -97,7 +66,7 @@ function createStyle(primary) {
|
|
|
97
66
|
top: "50%",
|
|
98
67
|
left: "50%",
|
|
99
68
|
transform: "translate(-50%, -50%)",
|
|
100
|
-
backgroundColor: "rgba(
|
|
69
|
+
backgroundColor: "rgba(0,0,0,0.55)",
|
|
101
70
|
color: "#fff",
|
|
102
71
|
fontSize: ".6rem",
|
|
103
72
|
width: "1.4rem",
|
|
@@ -112,8 +81,7 @@ function createStyle(primary) {
|
|
|
112
81
|
backgroundColor: bgPage,
|
|
113
82
|
}),
|
|
114
83
|
top: (0, react_1.css)({
|
|
115
|
-
padding: ".
|
|
116
|
-
borderBottom: `1px solid ${border}`,
|
|
84
|
+
padding: ".3rem",
|
|
117
85
|
"& > div": {
|
|
118
86
|
height: ".72rem",
|
|
119
87
|
backgroundColor: bgSubtle,
|
|
@@ -169,8 +137,7 @@ function createStyle(primary) {
|
|
|
169
137
|
locate: (0, react_1.css)({
|
|
170
138
|
display: "flex",
|
|
171
139
|
alignItems: "center",
|
|
172
|
-
padding: ".
|
|
173
|
-
borderBottom: `1px solid ${border}`,
|
|
140
|
+
padding: "0 .3rem .3rem",
|
|
174
141
|
backgroundColor: bgPage,
|
|
175
142
|
transition: "background-color .12s",
|
|
176
143
|
"&:active": {
|
|
@@ -195,13 +162,13 @@ function createStyle(primary) {
|
|
|
195
162
|
color: primary,
|
|
196
163
|
}),
|
|
197
164
|
title: (0, react_1.css)({
|
|
198
|
-
padding: ".
|
|
165
|
+
padding: ".12rem .3rem .06rem",
|
|
199
166
|
fontSize: ".22rem",
|
|
200
|
-
fontWeight:
|
|
201
|
-
color:
|
|
167
|
+
fontWeight: 500,
|
|
168
|
+
color: textTertiary,
|
|
202
169
|
letterSpacing: ".02rem",
|
|
203
170
|
textTransform: "uppercase",
|
|
204
|
-
backgroundColor:
|
|
171
|
+
backgroundColor: bgGrouped,
|
|
205
172
|
position: "sticky",
|
|
206
173
|
top: 0,
|
|
207
174
|
zIndex: 1,
|
|
@@ -210,7 +177,6 @@ function createStyle(primary) {
|
|
|
210
177
|
padding: ".24rem .3rem",
|
|
211
178
|
fontSize: ".3rem",
|
|
212
179
|
color: textPrimary,
|
|
213
|
-
borderBottom: `1px solid ${border}`,
|
|
214
180
|
backgroundColor: bgPage,
|
|
215
181
|
transition: "background-color .12s",
|
|
216
182
|
"&:active": {
|
package/build/Clickable/index.js
CHANGED
|
@@ -135,6 +135,13 @@ function Clickable(props) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
}, [disable, touchable, onMouseUp]);
|
|
138
|
+
// 切换到 disable 时,立即清掉激活态,避免视觉上停留在按下样式
|
|
139
|
+
(0, react_1.useEffect)(() => {
|
|
140
|
+
if (disable) {
|
|
141
|
+
touchRef.current = false;
|
|
142
|
+
deactivate();
|
|
143
|
+
}
|
|
144
|
+
}, [disable, deactivate]);
|
|
138
145
|
// 根据激活状态计算最终的 className 和 style
|
|
139
146
|
const finalClassName = isActive && typeof activeClassName === 'string'
|
|
140
147
|
? (typeof className === 'string' ? `${className} ${activeClassName}` : activeClassName)
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { Interpolation, Theme } from "@emotion/react";
|
|
2
|
-
import
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
3
|
export interface ContainerProps {
|
|
4
4
|
globalStyle?: Interpolation<Theme>;
|
|
5
|
-
children?:
|
|
5
|
+
children?: ReactNode;
|
|
6
6
|
designWidth?: number;
|
|
7
|
+
maxWidth?: number;
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* 自适应容器:所有使用本库的工程都需在根节点放置该组件,
|
|
11
|
+
* 否则各组件中的 rem 单位将无法自动跟随设备宽度缩放。
|
|
12
|
+
*
|
|
13
|
+
* 实现要点:
|
|
14
|
+
* - <Global> 通过 useInsertionEffect 早于 useLayoutEffect 注入样式,
|
|
15
|
+
* 所以首次 layout 阶段所有 rem 已使用正确 fontSize,无需阻塞 children
|
|
16
|
+
* - 浏览器字体缩放(用户系统调大字号)首挂载同步检测一次,scaleFactor 修正
|
|
17
|
+
* - resize 走 rAF 节流,桌面拖拽 / 模拟器切设备不会反复 setState
|
|
18
|
+
* - SSR 安全:所有 window 访问加 isBrowser 守卫
|
|
11
19
|
*/
|
|
12
20
|
export declare function Container(props: ContainerProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
package/build/Container/index.js
CHANGED
|
@@ -1,114 +1,119 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.Container = Container;
|
|
37
4
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
38
5
|
const react_1 = require("@emotion/react");
|
|
39
|
-
const react_2 =
|
|
6
|
+
const react_2 = require("react");
|
|
40
7
|
const useWindowResize_1 = require("../Effect/useWindowResize");
|
|
41
8
|
const useViewport_1 = require("../Effect/useViewport");
|
|
9
|
+
const isBrowser = typeof window !== "undefined";
|
|
10
|
+
// SSR 时 useLayoutEffect 会 warn,统一降级
|
|
11
|
+
const useIsomorphicLayoutEffect = isBrowser ? react_2.useLayoutEffect : react_2.useEffect;
|
|
12
|
+
// 取当前视口宽度,可选 maxWidth 兑底(0 / Infinity 表示不限制)
|
|
13
|
+
function getViewportWidth(maxWidth) {
|
|
14
|
+
if (!isBrowser)
|
|
15
|
+
return 0;
|
|
16
|
+
const w = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
17
|
+
if (!maxWidth || !isFinite(maxWidth))
|
|
18
|
+
return w;
|
|
19
|
+
return Math.min(w, maxWidth);
|
|
20
|
+
}
|
|
42
21
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
22
|
+
* 自适应容器:所有使用本库的工程都需在根节点放置该组件,
|
|
23
|
+
* 否则各组件中的 rem 单位将无法自动跟随设备宽度缩放。
|
|
24
|
+
*
|
|
25
|
+
* 实现要点:
|
|
26
|
+
* - <Global> 通过 useInsertionEffect 早于 useLayoutEffect 注入样式,
|
|
27
|
+
* 所以首次 layout 阶段所有 rem 已使用正确 fontSize,无需阻塞 children
|
|
28
|
+
* - 浏览器字体缩放(用户系统调大字号)首挂载同步检测一次,scaleFactor 修正
|
|
29
|
+
* - resize 走 rAF 节流,桌面拖拽 / 模拟器切设备不会反复 setState
|
|
30
|
+
* - SSR 安全:所有 window 访问加 isBrowser 守卫
|
|
45
31
|
*/
|
|
46
32
|
function Container(props) {
|
|
47
|
-
const { designWidth = 750, globalStyle, children } = props;
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
}, [designWidth]);
|
|
52
|
-
// 理论基准字体大小(跟随窗口尺寸变化)
|
|
53
|
-
const [rawFontSize, setRawFontSize] = (0, react_2.useState)(() => calculateFontSize(window.innerWidth));
|
|
54
|
-
// 浏览器字体缩放因子(>1 表示用户放大了系统字体,<1 表示缩小)
|
|
55
|
-
// 独立存储,使得 resize 后缩放修正依然生效
|
|
33
|
+
const { designWidth = 750, maxWidth = 750, globalStyle, children } = props;
|
|
34
|
+
// 当前视口宽度
|
|
35
|
+
const [viewportWidth, setViewportWidth] = (0, react_2.useState)(() => getViewportWidth(maxWidth));
|
|
36
|
+
// 浏览器字体缩放因子(>1 用户放大系统字体),首挂载探测一次
|
|
56
37
|
const [scaleFactor, setScaleFactor] = (0, react_2.useState)(1);
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// 因此 useLayoutEffect 内 getComputedStyle 可正确读取已注入的字体大小
|
|
66
|
-
// 检测和修正均在浏览器绘制前同步完成,避免闪烁
|
|
67
|
-
(0, react_2.useLayoutEffect)(() => {
|
|
68
|
-
// 缩放因子在页面生命周期内不变,只需检测一次
|
|
69
|
-
if (isInitialized)
|
|
38
|
+
// 理论 fontSize(未修正)
|
|
39
|
+
const rawFontSize = (0, react_2.useMemo)(() => (viewportWidth * 100) / designWidth, [viewportWidth, designWidth]);
|
|
40
|
+
// 最终 fontSize:缩放修正 + 1 位小数,避免浮点抖动
|
|
41
|
+
const fontSize = (0, react_2.useMemo)(() => Math.round((scaleFactor === 1 ? rawFontSize : rawFontSize / scaleFactor) * 10) / 10, [rawFontSize, scaleFactor]);
|
|
42
|
+
// 浏览器字体缩放检测:仅首挂载执行一次,避免 setState 触发循环
|
|
43
|
+
const detectedRef = (0, react_2.useRef)(false);
|
|
44
|
+
useIsomorphicLayoutEffect(() => {
|
|
45
|
+
if (detectedRef.current || !isBrowser)
|
|
70
46
|
return;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// 容差 1px
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
setScaleFactor(computedSize / rawFontSize);
|
|
47
|
+
detectedRef.current = true;
|
|
48
|
+
const computed = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
|
|
49
|
+
// 容差 1px 屏蔽浮点误差
|
|
50
|
+
if (computed > 0 && Math.abs(computed - rawFontSize) > 1) {
|
|
51
|
+
setScaleFactor(computed / rawFontSize);
|
|
77
52
|
}
|
|
78
|
-
|
|
79
|
-
}, [
|
|
80
|
-
//
|
|
81
|
-
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
}, []);
|
|
55
|
+
// maxWidth 变更时重算 viewportWidth
|
|
56
|
+
(0, react_2.useEffect)(() => {
|
|
57
|
+
setViewportWidth((prev) => {
|
|
58
|
+
const next = getViewportWidth(maxWidth);
|
|
59
|
+
return prev === next ? prev : next;
|
|
60
|
+
});
|
|
61
|
+
}, [maxWidth]);
|
|
62
|
+
// resize:rAF 节流;桌面拖动窗口、DevTools 切换设备模拟时也只每帧一次
|
|
63
|
+
const rafRef = (0, react_2.useRef)(null);
|
|
82
64
|
(0, useWindowResize_1.useWindowResize)(() => {
|
|
83
|
-
|
|
65
|
+
if (!isBrowser || rafRef.current !== null)
|
|
66
|
+
return;
|
|
67
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
68
|
+
rafRef.current = null;
|
|
69
|
+
const next = getViewportWidth(maxWidth);
|
|
70
|
+
setViewportWidth((prev) => (prev === next ? prev : next));
|
|
71
|
+
});
|
|
84
72
|
});
|
|
85
|
-
//
|
|
73
|
+
// 组件卸载清理 pending rAF
|
|
74
|
+
(0, react_2.useEffect)(() => {
|
|
75
|
+
return () => {
|
|
76
|
+
if (rafRef.current !== null) {
|
|
77
|
+
cancelAnimationFrame(rafRef.current);
|
|
78
|
+
rafRef.current = null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
// viewport meta
|
|
86
83
|
(0, useViewport_1.useViewport)();
|
|
87
84
|
// 激活 iOS 上的 :active 伪类
|
|
88
85
|
(0, react_2.useEffect)(() => {
|
|
86
|
+
if (!isBrowser)
|
|
87
|
+
return;
|
|
89
88
|
const noop = () => { };
|
|
90
89
|
document.body.addEventListener("touchstart", noop, { passive: true });
|
|
91
90
|
return () => {
|
|
92
91
|
document.body.removeEventListener("touchstart", noop);
|
|
93
92
|
};
|
|
94
93
|
}, []);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
94
|
+
// 全局样式:fontSize 写入 html,rem 自动跟随
|
|
95
|
+
// body 以 maxWidth 居中,保证 PC 端上页面不超过设计宽度,两侧留白
|
|
96
|
+
// CSS 变量 --clxx-max-width 供 Overlay/Fixed 等使用 fixed 定位的组件读取
|
|
97
|
+
// 从而把弹窗 / 遮罩限制在视口内(fixed 默认参考浏览器窗口,无法继承 body 宽度)
|
|
98
|
+
const hasMaxWidth = !!maxWidth && isFinite(maxWidth);
|
|
99
|
+
const globalStyles = (0, react_2.useMemo)(() => [
|
|
100
|
+
{
|
|
101
|
+
":root": {
|
|
102
|
+
"--clxx-max-width": hasMaxWidth ? `${maxWidth}px` : "100%",
|
|
103
|
+
},
|
|
104
|
+
"*": {
|
|
105
|
+
boxSizing: "border-box",
|
|
106
|
+
},
|
|
107
|
+
html: {
|
|
108
|
+
WebkitTapHighlightColor: "transparent",
|
|
109
|
+
WebkitOverflowScrolling: "touch",
|
|
110
|
+
WebkitTextSizeAdjust: "100%",
|
|
111
|
+
fontSize: `${fontSize}px`,
|
|
112
|
+
touchAction: "manipulation",
|
|
113
|
+
},
|
|
114
|
+
body: Object.assign({ fontSize: "16px", margin: "0 auto" }, (hasMaxWidth ? { maxWidth: `${maxWidth}px` } : null)),
|
|
115
|
+
},
|
|
116
|
+
globalStyle,
|
|
117
|
+
], [fontSize, maxWidth, hasMaxWidth, globalStyle]);
|
|
118
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_1.Global, { styles: globalStyles }), children] }));
|
|
114
119
|
}
|
|
@@ -50,12 +50,14 @@ const react_1 = __importStar(require("react"));
|
|
|
50
50
|
const Countdown_1 = require("../utils/Countdown");
|
|
51
51
|
const Row_1 = require("../Flex/Row");
|
|
52
52
|
function Countdowner(props) {
|
|
53
|
-
|
|
53
|
+
const { remain = 0, separator = ':', format = 'his', onUpdate, onEnd, separatorStyle, containerStyle, numberStyle, renderNumber, renderSeparator } = props, extra = __rest(props, ["remain", "separator", "format", "onUpdate", "onEnd", "separatorStyle", "containerStyle", "numberStyle", "renderNumber", "renderSeparator"]);
|
|
54
54
|
const [value, setValue] = (0, react_1.useState)(null);
|
|
55
55
|
// 使用 ref 保存最新的回调,避免频繁重建倒计时实例
|
|
56
56
|
const callbacksRef = react_1.default.useRef({ onUpdate, onEnd });
|
|
57
57
|
callbacksRef.current = { onUpdate, onEnd };
|
|
58
|
-
|
|
58
|
+
// content 每秒因 value 变化必然重建;这里没有 emotion 序列化负担:
|
|
59
|
+
// numberStyle / separatorStyle 仅做引用透传,emotion 对相同引用有 cache
|
|
60
|
+
const content = [];
|
|
59
61
|
if (value && typeof value === 'object') {
|
|
60
62
|
for (let i = 0; i < format.length; i++) {
|
|
61
63
|
// 渲染数字进组件
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DatePickerStyle } from "./style";
|
|
2
|
+
export interface ColumnProps {
|
|
3
|
+
items: number[];
|
|
4
|
+
value: number;
|
|
5
|
+
onChange: (v: number) => void;
|
|
6
|
+
format?: (n: number) => string;
|
|
7
|
+
style: DatePickerStyle;
|
|
8
|
+
}
|
|
9
|
+
export declare function Column(props: ColumnProps): import("@emotion/react/jsx-runtime").JSX.Element;
|