clxx 2.1.7 → 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.
Files changed (75) hide show
  1. package/AGENTS.md +2 -1
  2. package/README.md +147 -22
  3. package/build/Alert/Wrapper.js +12 -14
  4. package/build/Alert/style.js +44 -25
  5. package/build/AutoGrid/index.js +21 -15
  6. package/build/CarouselNotice/index.d.ts +19 -11
  7. package/build/CarouselNotice/index.js +80 -74
  8. package/build/CarouselNotice/style.js +14 -4
  9. package/build/CitySelect/index.js +81 -71
  10. package/build/CitySelect/style.js +22 -56
  11. package/build/Clickable/index.js +7 -0
  12. package/build/Container/index.d.ts +12 -4
  13. package/build/Container/index.js +94 -89
  14. package/build/Countdowner/index.js +4 -2
  15. package/build/DatePicker/Column.d.ts +9 -0
  16. package/build/DatePicker/Column.js +330 -0
  17. package/build/DatePicker/index.d.ts +32 -0
  18. package/build/DatePicker/index.js +230 -0
  19. package/build/DatePicker/style.d.ts +6 -0
  20. package/build/DatePicker/style.js +130 -0
  21. package/build/Dialog/Wrapper.d.ts +0 -1
  22. package/build/Dialog/Wrapper.js +22 -12
  23. package/build/Dialog/index.d.ts +7 -1
  24. package/build/Dialog/index.js +57 -32
  25. package/build/Dialog/style.js +6 -2
  26. package/build/Effect/useInterval.js +6 -3
  27. package/build/Fixed/index.js +13 -22
  28. package/build/Flex/FlexItem.d.ts +11 -0
  29. package/build/Flex/FlexItem.js +26 -0
  30. package/build/Flex/index.d.ts +2 -10
  31. package/build/Flex/index.js +12 -22
  32. package/build/Indicator/index.d.ts +9 -6
  33. package/build/Indicator/index.js +34 -37
  34. package/build/Indicator/style.d.ts +4 -3
  35. package/build/Indicator/style.js +8 -13
  36. package/build/Loading/Wrapper.js +2 -1
  37. package/build/Loading/style.js +9 -12
  38. package/build/Overlay/index.js +6 -1
  39. package/build/RegionPicker/data.d.ts +6 -0
  40. package/build/RegionPicker/data.js +14486 -0
  41. package/build/RegionPicker/index.d.ts +33 -0
  42. package/build/RegionPicker/index.js +205 -0
  43. package/build/RegionPicker/style.d.ts +4 -0
  44. package/build/RegionPicker/style.js +187 -0
  45. package/build/SafeArea/index.js +14 -17
  46. package/build/ScrollView/index.d.ts +23 -11
  47. package/build/ScrollView/index.js +132 -118
  48. package/build/ScrollView/style.d.ts +1 -1
  49. package/build/ScrollView/style.js +33 -22
  50. package/build/Toast/Toast.d.ts +0 -1
  51. package/build/Toast/Toast.js +6 -4
  52. package/build/Toast/style.d.ts +3 -10
  53. package/build/Toast/style.js +41 -45
  54. package/build/index.d.ts +3 -0
  55. package/build/index.js +7 -1
  56. package/build/utils/color.d.ts +5 -0
  57. package/build/utils/color.js +18 -0
  58. package/build/utils/dom.js +4 -3
  59. package/build/utils/theme.d.ts +2 -0
  60. package/build/utils/theme.js +7 -0
  61. package/package.json +1 -1
  62. package/test/src/date-picker/index.jsx +119 -0
  63. package/test/src/index/index.jsx +2 -0
  64. package/test/src/index.jsx +1 -0
  65. package/test/src/loading/index.jsx +2 -2
  66. package/test/src/region-picker/index.jsx +120 -0
  67. package/test/src/scrollview/BasicSection.jsx +56 -0
  68. package/test/src/scrollview/CustomLoadingSection.jsx +53 -0
  69. package/test/src/scrollview/HeightModeSection.jsx +42 -0
  70. package/test/src/scrollview/ImperativeSection.jsx +56 -0
  71. package/test/src/scrollview/NotScrollableSection.jsx +32 -0
  72. package/test/src/scrollview/PerfSection.jsx +34 -0
  73. package/test/src/scrollview/index.css +92 -8
  74. package/test/src/scrollview/index.jsx +13 -45
  75. package/test/src/toast/index.jsx +1 -0
@@ -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,4 @@
1
+ import { Interpolation, Theme } from "@emotion/react";
2
+ export declare const DEFAULT_PRIMARY = "#2f7dff";
3
+ export type RegionPickerStyle = Record<string, Interpolation<Theme>>;
4
+ export declare function createStyle(primary: string, rounded?: boolean): RegionPickerStyle;
@@ -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
+ }
@@ -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
- let boxCss = {};
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
- /** @jsx jsx */
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?: React.UIEvent;
10
+ rawEvent?: Event;
11
11
  }
12
- export interface ScrollViewProps extends Omit<React.HTMLProps<HTMLDivElement>, "onScroll"> {
13
- children?: React.ReactNode;
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?: React.ReactNode;
33
+ loadingContent?: ReactNode;
21
34
  onScroll?: (event: ScrollEvent) => void;
22
- scrollThrottle?: number;
23
- containerStyle?: SerializedStyles;
24
- wrapperStyle?: SerializedStyles;
25
- loadingStyle?: SerializedStyles;
35
+ containerStyle?: Interpolation<Theme>;
36
+ wrapperStyle?: Interpolation<Theme>;
37
+ loadingStyle?: Interpolation<Theme>;
26
38
  }
27
- export declare function ScrollView(props: ScrollViewProps): import("@emotion/react/jsx-runtime").JSX.Element;
39
+ export declare const ScrollView: import("react").ForwardRefExoticComponent<ScrollViewProps & import("react").RefAttributes<ScrollViewHandle>>;