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,33 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { TreeRegionItem } from "./data";
|
|
3
|
+
export type { TreeRegionItem } from "./data";
|
|
4
|
+
export interface RegionNode {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RegionSelection {
|
|
9
|
+
province: RegionNode;
|
|
10
|
+
city: RegionNode;
|
|
11
|
+
district: RegionNode;
|
|
12
|
+
}
|
|
13
|
+
export interface RegionLabels {
|
|
14
|
+
province?: string;
|
|
15
|
+
city?: string;
|
|
16
|
+
district?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RegionPickerProps {
|
|
19
|
+
value?: [string?, string?, string?];
|
|
20
|
+
data?: TreeRegionItem[];
|
|
21
|
+
title?: ReactNode;
|
|
22
|
+
cancelText?: ReactNode;
|
|
23
|
+
confirmText?: ReactNode;
|
|
24
|
+
labels?: RegionLabels;
|
|
25
|
+
maskClosable?: boolean;
|
|
26
|
+
primary?: string;
|
|
27
|
+
rounded?: boolean;
|
|
28
|
+
onClose?: () => void;
|
|
29
|
+
onCancel?: () => void;
|
|
30
|
+
onConfirm?: (selection: RegionSelection) => void;
|
|
31
|
+
}
|
|
32
|
+
export declare function RegionPicker(props: RegionPickerProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare function showRegionPicker(options?: Pick<RegionPickerProps, "value" | "data" | "title" | "cancelText" | "confirmText" | "labels" | "maskClosable" | "primary" | "rounded" | "onCancel" | "onConfirm">): void;
|
|
@@ -0,0 +1,205 @@
|
|
|
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.RegionPicker = RegionPicker;
|
|
15
|
+
exports.showRegionPicker = showRegionPicker;
|
|
16
|
+
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const Dialog_1 = require("../Dialog");
|
|
19
|
+
const Clickable_1 = require("../Clickable");
|
|
20
|
+
const style_1 = require("./style");
|
|
21
|
+
const data_1 = require("./data");
|
|
22
|
+
const DEFAULT_LABELS = {
|
|
23
|
+
province: "省",
|
|
24
|
+
city: "市",
|
|
25
|
+
district: "区",
|
|
26
|
+
};
|
|
27
|
+
const TAB_ORDER = ["province", "city", "district"];
|
|
28
|
+
function RegionPicker(props) {
|
|
29
|
+
const { value, data = data_1.treeRegionData, title = "请选择地区", cancelText = "取消", confirmText = "确定", labels, primary = style_1.DEFAULT_PRIMARY, rounded = true, onClose, onCancel, onConfirm, } = props;
|
|
30
|
+
const style = (0, react_1.useMemo)(() => (0, style_1.createStyle)(primary, rounded), [primary, rounded]);
|
|
31
|
+
const lab = (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, DEFAULT_LABELS), labels)), [labels]);
|
|
32
|
+
// === 选中状态 ===
|
|
33
|
+
// 当前每级选中节点;未选为 null
|
|
34
|
+
const [provinceNode, setProvinceNode] = (0, react_1.useState)(null);
|
|
35
|
+
const [cityNode, setCityNode] = (0, react_1.useState)(null);
|
|
36
|
+
const [districtNode, setDistrictNode] = (0, react_1.useState)(null);
|
|
37
|
+
// 当前激活 tab
|
|
38
|
+
const [activeTab, setActiveTab] = (0, react_1.useState)("province");
|
|
39
|
+
// 根据 value 初始化(只在首次 & data 变化时)
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
var _a, _b, _c, _d, _e;
|
|
42
|
+
if (!value)
|
|
43
|
+
return;
|
|
44
|
+
const [pv, cv, dv] = value;
|
|
45
|
+
const p = (_a = data.find((x) => x.value === pv)) !== null && _a !== void 0 ? _a : null;
|
|
46
|
+
const c = (_c = (_b = p === null || p === void 0 ? void 0 : p.children) === null || _b === void 0 ? void 0 : _b.find((x) => x.value === cv)) !== null && _c !== void 0 ? _c : null;
|
|
47
|
+
const d = (_e = (_d = c === null || c === void 0 ? void 0 : c.children) === null || _d === void 0 ? void 0 : _d.find((x) => x.value === dv)) !== null && _e !== void 0 ? _e : null;
|
|
48
|
+
setProvinceNode(p);
|
|
49
|
+
setCityNode(c);
|
|
50
|
+
setDistrictNode(d);
|
|
51
|
+
// 定位 activeTab 到「最深的未选层」,全部选好时停在 district
|
|
52
|
+
if (!p)
|
|
53
|
+
setActiveTab("province");
|
|
54
|
+
else if (!c)
|
|
55
|
+
setActiveTab("city");
|
|
56
|
+
else
|
|
57
|
+
setActiveTab("district");
|
|
58
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59
|
+
}, []);
|
|
60
|
+
// 当前 tab 对应的待选列表
|
|
61
|
+
const currentList = (0, react_1.useMemo)(() => {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
if (activeTab === "province")
|
|
64
|
+
return data;
|
|
65
|
+
if (activeTab === "city")
|
|
66
|
+
return (_a = provinceNode === null || provinceNode === void 0 ? void 0 : provinceNode.children) !== null && _a !== void 0 ? _a : [];
|
|
67
|
+
return (_b = cityNode === null || cityNode === void 0 ? void 0 : cityNode.children) !== null && _b !== void 0 ? _b : [];
|
|
68
|
+
}, [activeTab, data, provinceNode, cityNode]);
|
|
69
|
+
const selectedValueOfTab = (tab) => {
|
|
70
|
+
var _a, _b, _c;
|
|
71
|
+
if (tab === "province")
|
|
72
|
+
return (_a = provinceNode === null || provinceNode === void 0 ? void 0 : provinceNode.value) !== null && _a !== void 0 ? _a : null;
|
|
73
|
+
if (tab === "city")
|
|
74
|
+
return (_b = cityNode === null || cityNode === void 0 ? void 0 : cityNode.value) !== null && _b !== void 0 ? _b : null;
|
|
75
|
+
return (_c = districtNode === null || districtNode === void 0 ? void 0 : districtNode.value) !== null && _c !== void 0 ? _c : null;
|
|
76
|
+
};
|
|
77
|
+
// tab 是否可点击:只有已有对应级数据时才可点(即上级已选)
|
|
78
|
+
const tabEnabled = (tab) => {
|
|
79
|
+
if (tab === "province")
|
|
80
|
+
return true;
|
|
81
|
+
if (tab === "city")
|
|
82
|
+
return !!provinceNode;
|
|
83
|
+
return !!cityNode;
|
|
84
|
+
};
|
|
85
|
+
// 点击列表项
|
|
86
|
+
const handlePick = (item) => {
|
|
87
|
+
if (activeTab === "province") {
|
|
88
|
+
// 换省 => 清空下级
|
|
89
|
+
if ((provinceNode === null || provinceNode === void 0 ? void 0 : provinceNode.value) !== item.value) {
|
|
90
|
+
setProvinceNode(item);
|
|
91
|
+
setCityNode(null);
|
|
92
|
+
setDistrictNode(null);
|
|
93
|
+
}
|
|
94
|
+
// 自动进入下一级;若无子级则停留
|
|
95
|
+
if (item.children && item.children.length > 0) {
|
|
96
|
+
setActiveTab("city");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (activeTab === "city") {
|
|
100
|
+
if ((cityNode === null || cityNode === void 0 ? void 0 : cityNode.value) !== item.value) {
|
|
101
|
+
setCityNode(item);
|
|
102
|
+
setDistrictNode(null);
|
|
103
|
+
}
|
|
104
|
+
if (item.children && item.children.length > 0) {
|
|
105
|
+
setActiveTab("district");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
setDistrictNode(item);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const handleTabClick = (tab) => {
|
|
113
|
+
if (!tabEnabled(tab))
|
|
114
|
+
return;
|
|
115
|
+
setActiveTab(tab);
|
|
116
|
+
};
|
|
117
|
+
// 切 tab / 列表变化时,把当前选中项的顶部对齐到列表顶部(即 tabs 下方的分隔线)
|
|
118
|
+
// 列表不可滚动时浏览器自然忽略 scrollTop 赋值,无需额外判断
|
|
119
|
+
const listRef = (0, react_1.useRef)(null);
|
|
120
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
121
|
+
const el = listRef.current;
|
|
122
|
+
if (!el)
|
|
123
|
+
return;
|
|
124
|
+
const sel = selectedValueOfTab(activeTab);
|
|
125
|
+
if (!sel) {
|
|
126
|
+
el.scrollTop = 0;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const target = el.querySelector(`[data-value="${CSS.escape(sel)}"]`);
|
|
130
|
+
if (!target) {
|
|
131
|
+
el.scrollTop = 0;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// 用 getBoundingClientRect 差分计算偏移,避免 offsetTop 受
|
|
135
|
+
// offsetParent 定位上下文影响(list 容器未必是 positioned 元素)
|
|
136
|
+
const offsetWithinList = target.getBoundingClientRect().top -
|
|
137
|
+
el.getBoundingClientRect().top +
|
|
138
|
+
el.scrollTop;
|
|
139
|
+
const maxScroll = Math.max(0, el.scrollHeight - el.clientHeight);
|
|
140
|
+
el.scrollTop = Math.max(0, Math.min(offsetWithinList, maxScroll));
|
|
141
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
142
|
+
}, [activeTab, currentList]);
|
|
143
|
+
// ---------------- 关闭逻辑 ----------------
|
|
144
|
+
// 动画与卸载交由 Dialog 处理
|
|
145
|
+
const handleCancel = () => {
|
|
146
|
+
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
147
|
+
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
148
|
+
};
|
|
149
|
+
const canConfirm = !!(provinceNode && cityNode && districtNode);
|
|
150
|
+
const handleConfirm = () => {
|
|
151
|
+
if (!canConfirm)
|
|
152
|
+
return;
|
|
153
|
+
onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm({
|
|
154
|
+
province: { value: provinceNode.value, label: provinceNode.label },
|
|
155
|
+
city: { value: cityNode.value, label: cityNode.label },
|
|
156
|
+
district: { value: districtNode.value, label: districtNode.label },
|
|
157
|
+
});
|
|
158
|
+
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
159
|
+
};
|
|
160
|
+
const tabLabelText = (tab) => {
|
|
161
|
+
if (tab === "province")
|
|
162
|
+
return provinceNode ? provinceNode.label : lab.province;
|
|
163
|
+
if (tab === "city")
|
|
164
|
+
return cityNode ? cityNode.label : lab.city;
|
|
165
|
+
return districtNode ? districtNode.label : lab.district;
|
|
166
|
+
};
|
|
167
|
+
const isTabPlaceholder = (tab) => {
|
|
168
|
+
if (tab === "province")
|
|
169
|
+
return !provinceNode;
|
|
170
|
+
if (tab === "city")
|
|
171
|
+
return !cityNode;
|
|
172
|
+
return !districtNode;
|
|
173
|
+
};
|
|
174
|
+
return ((0, jsx_runtime_1.jsxs)("div", { css: style.sheet, children: [(0, jsx_runtime_1.jsxs)("div", { css: style.header, children: [(0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: [style.btn, style.btnCancel], onClick: handleCancel, children: cancelText }), (0, jsx_runtime_1.jsx)("div", { css: style.title, children: title }), (0, jsx_runtime_1.jsx)(Clickable_1.Clickable, { css: [
|
|
175
|
+
style.btn,
|
|
176
|
+
style.btnConfirm,
|
|
177
|
+
!canConfirm && style.btnConfirmDisabled,
|
|
178
|
+
], onClick: handleConfirm, children: confirmText })] }), (0, jsx_runtime_1.jsx)("div", { css: style.tabs, children: TAB_ORDER.map((tab) => ((0, jsx_runtime_1.jsx)("div", { css: [
|
|
179
|
+
style.tab,
|
|
180
|
+
isTabPlaceholder(tab) && style.tabPlaceholder,
|
|
181
|
+
activeTab === tab && style.tabActive,
|
|
182
|
+
], onClick: () => handleTabClick(tab), children: tabLabelText(tab) }, tab))) }), (0, jsx_runtime_1.jsx)("div", { css: style.list, ref: listRef, children: currentList.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { css: style.empty, children: "\u6682\u65E0\u6570\u636E" })) : (currentList.map((item) => {
|
|
183
|
+
const selected = selectedValueOfTab(activeTab) === item.value;
|
|
184
|
+
return ((0, jsx_runtime_1.jsxs)(Clickable_1.Clickable, { "data-value": item.value, css: [style.listItem, selected && style.listItemSelected], onClick: () => handlePick(item), children: [(0, jsx_runtime_1.jsx)("div", { css: style.listItemLabel, children: item.label }), selected && (0, jsx_runtime_1.jsx)("div", { css: style.checkIcon })] }, item.value));
|
|
185
|
+
})) })] }));
|
|
186
|
+
}
|
|
187
|
+
function showRegionPicker(options = {}) {
|
|
188
|
+
const { maskClosable = true, onCancel } = options, rest = __rest(options, ["maskClosable", "onCancel"]);
|
|
189
|
+
let closing = false;
|
|
190
|
+
let close;
|
|
191
|
+
const requestClose = () => {
|
|
192
|
+
if (closing)
|
|
193
|
+
return;
|
|
194
|
+
closing = true;
|
|
195
|
+
close === null || close === void 0 ? void 0 : close();
|
|
196
|
+
};
|
|
197
|
+
close = (0, Dialog_1.showDialog)({
|
|
198
|
+
type: "pullUp",
|
|
199
|
+
blankClosable: maskClosable,
|
|
200
|
+
onBlankClick: () => {
|
|
201
|
+
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
202
|
+
},
|
|
203
|
+
content: ((0, jsx_runtime_1.jsx)(RegionPicker, Object.assign({}, rest, { onCancel: onCancel, onClose: requestClose }))),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PRIMARY = 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 / placeholder
|
|
12
|
+
const bgPage = "#ffffff";
|
|
13
|
+
const bgSubtle = "rgba(0,0,0,.04)"; // 按下态浅灰
|
|
14
|
+
exports.DEFAULT_PRIMARY = "#2f7dff";
|
|
15
|
+
function createStyle(primary, rounded = true) {
|
|
16
|
+
const primaryActive = (0, color_1.darken)(primary, 0.15);
|
|
17
|
+
const sheetRadius = rounded ? ".28rem" : "0";
|
|
18
|
+
return {
|
|
19
|
+
// 内容容器:动画/遮罩/全屏由 Dialog 提供,这里只保留视觉与排版
|
|
20
|
+
sheet: (0, react_1.css)({
|
|
21
|
+
width: "100%",
|
|
22
|
+
backgroundColor: bgPage,
|
|
23
|
+
borderTopLeftRadius: sheetRadius,
|
|
24
|
+
borderTopRightRadius: sheetRadius,
|
|
25
|
+
display: "flex",
|
|
26
|
+
flexDirection: "column",
|
|
27
|
+
maxHeight: "80vh",
|
|
28
|
+
overflow: "hidden",
|
|
29
|
+
userSelect: "none",
|
|
30
|
+
color: textPrimary,
|
|
31
|
+
fontFamily: theme_1.fontStack,
|
|
32
|
+
WebkitFontSmoothing: "antialiased",
|
|
33
|
+
MozOsxFontSmoothing: "grayscale",
|
|
34
|
+
}),
|
|
35
|
+
// iOS 风标题栏:无底边框,标题加粗居中,左右按钮都用主色
|
|
36
|
+
header: (0, react_1.css)({
|
|
37
|
+
flexShrink: 0,
|
|
38
|
+
height: ".92rem",
|
|
39
|
+
display: "flex",
|
|
40
|
+
alignItems: "center",
|
|
41
|
+
justifyContent: "space-between",
|
|
42
|
+
padding: "0 .16rem",
|
|
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
|
+
btnConfirmDisabled: (0, react_1.css)({
|
|
73
|
+
color: textTertiary,
|
|
74
|
+
cursor: "not-allowed",
|
|
75
|
+
pointerEvents: "none",
|
|
76
|
+
fontWeight: 600,
|
|
77
|
+
"&:active": { opacity: 1, color: textTertiary },
|
|
78
|
+
}),
|
|
79
|
+
// tabs 行:轻量化,激活态用细下划线提示;底部加 hairline 分隔下方列表
|
|
80
|
+
tabs: (0, react_1.css)({
|
|
81
|
+
flexShrink: 0,
|
|
82
|
+
display: "flex",
|
|
83
|
+
alignItems: "stretch",
|
|
84
|
+
height: ".8rem",
|
|
85
|
+
padding: "0 .16rem",
|
|
86
|
+
borderBottom: "1px solid rgba(60,60,67,.18)",
|
|
87
|
+
}),
|
|
88
|
+
tab: (0, react_1.css)({
|
|
89
|
+
flex: 1,
|
|
90
|
+
minWidth: 0,
|
|
91
|
+
display: "flex",
|
|
92
|
+
alignItems: "center",
|
|
93
|
+
justifyContent: "center",
|
|
94
|
+
padding: "0 .08rem",
|
|
95
|
+
fontSize: ".28rem",
|
|
96
|
+
color: textSecondary,
|
|
97
|
+
position: "relative",
|
|
98
|
+
cursor: "pointer",
|
|
99
|
+
transition: "color .2s ease",
|
|
100
|
+
whiteSpace: "nowrap",
|
|
101
|
+
overflow: "hidden",
|
|
102
|
+
textOverflow: "ellipsis",
|
|
103
|
+
letterSpacing: ".01rem",
|
|
104
|
+
}),
|
|
105
|
+
tabPlaceholder: (0, react_1.css)({
|
|
106
|
+
color: textTertiary,
|
|
107
|
+
}),
|
|
108
|
+
tabActive: (0, react_1.css)({
|
|
109
|
+
color: primary,
|
|
110
|
+
fontWeight: 500,
|
|
111
|
+
"&::after": {
|
|
112
|
+
content: '""',
|
|
113
|
+
position: "absolute",
|
|
114
|
+
left: "50%",
|
|
115
|
+
bottom: ".06rem",
|
|
116
|
+
transform: "translateX(-50%)",
|
|
117
|
+
width: ".32rem",
|
|
118
|
+
height: ".04rem",
|
|
119
|
+
backgroundColor: primary,
|
|
120
|
+
borderRadius: ".02rem",
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
// 选项列表(固定高度,避免跳变;6 行 × .88rem = 5.28rem,整体更紧凑)
|
|
124
|
+
list: (0, react_1.css)({
|
|
125
|
+
flexShrink: 0,
|
|
126
|
+
height: "5.28rem",
|
|
127
|
+
overflowY: "auto",
|
|
128
|
+
overflowX: "hidden",
|
|
129
|
+
WebkitOverflowScrolling: "touch",
|
|
130
|
+
overscrollBehavior: "contain",
|
|
131
|
+
backgroundColor: bgPage,
|
|
132
|
+
paddingBottom: ".12rem",
|
|
133
|
+
}),
|
|
134
|
+
// iOS 风列表项:无分隔线,仅按下态浅灰底
|
|
135
|
+
listItem: (0, react_1.css)({
|
|
136
|
+
position: "relative",
|
|
137
|
+
height: ".88rem",
|
|
138
|
+
display: "flex",
|
|
139
|
+
alignItems: "center",
|
|
140
|
+
padding: "0 .32rem",
|
|
141
|
+
fontSize: ".3rem",
|
|
142
|
+
color: textPrimary,
|
|
143
|
+
transition: "background-color .15s",
|
|
144
|
+
"&:active": {
|
|
145
|
+
backgroundColor: bgSubtle,
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
listItemLabel: (0, react_1.css)({
|
|
149
|
+
flex: 1,
|
|
150
|
+
minWidth: 0,
|
|
151
|
+
whiteSpace: "nowrap",
|
|
152
|
+
overflow: "hidden",
|
|
153
|
+
textOverflow: "ellipsis",
|
|
154
|
+
letterSpacing: ".01rem",
|
|
155
|
+
}),
|
|
156
|
+
listItemSelected: (0, react_1.css)({
|
|
157
|
+
color: primary,
|
|
158
|
+
fontWeight: 500,
|
|
159
|
+
}),
|
|
160
|
+
// iOS 风对勾(SF Symbol checkmark 风格)
|
|
161
|
+
checkIcon: (0, react_1.css)({
|
|
162
|
+
width: ".32rem",
|
|
163
|
+
height: ".32rem",
|
|
164
|
+
flexShrink: 0,
|
|
165
|
+
marginLeft: ".16rem",
|
|
166
|
+
position: "relative",
|
|
167
|
+
"&::after": {
|
|
168
|
+
content: '""',
|
|
169
|
+
position: "absolute",
|
|
170
|
+
left: ".06rem",
|
|
171
|
+
top: ".04rem",
|
|
172
|
+
width: ".1rem",
|
|
173
|
+
height: ".2rem",
|
|
174
|
+
border: `solid ${primary}`,
|
|
175
|
+
borderWidth: "0 .03rem .03rem 0",
|
|
176
|
+
transform: "rotate(45deg)",
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
// 空数据占位
|
|
180
|
+
empty: (0, react_1.css)({
|
|
181
|
+
padding: ".6rem 0",
|
|
182
|
+
textAlign: "center",
|
|
183
|
+
fontSize: ".26rem",
|
|
184
|
+
color: textTertiary,
|
|
185
|
+
}),
|
|
186
|
+
};
|
|
187
|
+
}
|
package/build/SafeArea/index.js
CHANGED
|
@@ -14,25 +14,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
exports.SafeArea = SafeArea;
|
|
15
15
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
16
16
|
const useViewport_1 = require("../Effect/useViewport");
|
|
17
|
+
// 模块级常量:避免每次 render 重新分配对象与哈希
|
|
18
|
+
const topStyle = {
|
|
19
|
+
height: [
|
|
20
|
+
`constant(safe-area-inset-top, 0)`,
|
|
21
|
+
`env(safe-area-inset-top, 0)`,
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
const bottomStyle = {
|
|
25
|
+
height: [
|
|
26
|
+
`constant(safe-area-inset-bottom, 0)`,
|
|
27
|
+
`env(safe-area-inset-bottom, 0)`,
|
|
28
|
+
],
|
|
29
|
+
};
|
|
17
30
|
function SafeArea(props) {
|
|
18
31
|
const { children, type = "bottom" } = props, extra = __rest(props, ["children", "type"]);
|
|
19
32
|
(0, useViewport_1.useViewport)({ viewportFit: "cover" });
|
|
20
|
-
|
|
21
|
-
if (type === "top") {
|
|
22
|
-
boxCss = {
|
|
23
|
-
height: [
|
|
24
|
-
`constant(safe-area-inset-top, 0)`,
|
|
25
|
-
`env(safe-area-inset-top, 0)`,
|
|
26
|
-
],
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
else if (type === "bottom") {
|
|
30
|
-
boxCss = {
|
|
31
|
-
height: [
|
|
32
|
-
`constant(safe-area-inset-bottom, 0)`,
|
|
33
|
-
`env(safe-area-inset-bottom, 0)`,
|
|
34
|
-
],
|
|
35
|
-
};
|
|
36
|
-
}
|
|
33
|
+
const boxCss = type === "top" ? topStyle : bottomStyle;
|
|
37
34
|
return ((0, jsx_runtime_1.jsx)("div", Object.assign({ css: boxCss }, extra, { children: children })));
|
|
38
35
|
}
|
|
@@ -1,27 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
import { SerializedStyles } from "@emotion/react";
|
|
1
|
+
import { Interpolation, Theme } from "@emotion/react";
|
|
3
2
|
import * as CSS from "csstype";
|
|
3
|
+
import { HTMLAttributes, ReactNode } from "react";
|
|
4
4
|
export interface ScrollEvent {
|
|
5
5
|
containerHeight: number;
|
|
6
6
|
contentHeight: number;
|
|
7
7
|
scrollTop: number;
|
|
8
8
|
maxScroll: number;
|
|
9
9
|
direction: "upward" | "downward";
|
|
10
|
-
rawEvent?:
|
|
10
|
+
rawEvent?: Event;
|
|
11
11
|
}
|
|
12
|
-
export interface
|
|
13
|
-
|
|
12
|
+
export interface ScrollViewHandle {
|
|
13
|
+
getElement: () => HTMLDivElement | null;
|
|
14
|
+
scrollTo: (options: {
|
|
15
|
+
top: number;
|
|
16
|
+
behavior?: ScrollBehavior;
|
|
17
|
+
}) => void;
|
|
18
|
+
scrollToTop: (behavior?: ScrollBehavior) => void;
|
|
19
|
+
scrollToBottom: (behavior?: ScrollBehavior) => void;
|
|
20
|
+
scrollToElement: (target: HTMLElement | string, options?: {
|
|
21
|
+
offset?: number;
|
|
22
|
+
behavior?: ScrollBehavior;
|
|
23
|
+
}) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface ScrollViewProps extends Omit<HTMLAttributes<HTMLDivElement>, "onScroll"> {
|
|
26
|
+
children?: ReactNode;
|
|
14
27
|
height?: CSS.Property.Height;
|
|
15
28
|
reachTopThreshold?: number;
|
|
16
29
|
onReachTop?: (event: ScrollEvent) => void;
|
|
17
30
|
reachBottomThreshold?: number;
|
|
18
31
|
onReachBottom?: (event: ScrollEvent) => void;
|
|
19
32
|
showLoading?: boolean;
|
|
20
|
-
loadingContent?:
|
|
33
|
+
loadingContent?: ReactNode;
|
|
21
34
|
onScroll?: (event: ScrollEvent) => void;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
loadingStyle?: SerializedStyles;
|
|
35
|
+
containerStyle?: Interpolation<Theme>;
|
|
36
|
+
wrapperStyle?: Interpolation<Theme>;
|
|
37
|
+
loadingStyle?: Interpolation<Theme>;
|
|
26
38
|
}
|
|
27
|
-
export declare
|
|
39
|
+
export declare const ScrollView: import("react").ForwardRefExoticComponent<ScrollViewProps & import("react").RefAttributes<ScrollViewHandle>>;
|