clxx 2.1.8 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/README.md +147 -22
- package/build/Alert/Wrapper.js +4 -3
- package/build/Alert/style.js +11 -7
- package/build/AutoGrid/index.js +21 -15
- package/build/CarouselNotice/index.d.ts +19 -11
- package/build/CarouselNotice/index.js +80 -74
- package/build/CarouselNotice/style.js +14 -4
- package/build/CitySelect/index.js +30 -55
- package/build/CitySelect/style.js +22 -56
- package/build/Clickable/index.js +7 -0
- package/build/Container/index.d.ts +12 -4
- package/build/Container/index.js +94 -89
- package/build/Countdowner/index.js +4 -2
- package/build/DatePicker/Column.d.ts +9 -0
- package/build/DatePicker/Column.js +330 -0
- package/build/DatePicker/index.d.ts +32 -0
- package/build/DatePicker/index.js +230 -0
- package/build/DatePicker/style.d.ts +6 -0
- package/build/DatePicker/style.js +130 -0
- package/build/Dialog/Wrapper.d.ts +0 -1
- package/build/Dialog/Wrapper.js +22 -12
- package/build/Dialog/index.d.ts +7 -1
- package/build/Dialog/index.js +57 -32
- package/build/Dialog/style.js +6 -2
- package/build/Effect/useInterval.js +6 -3
- package/build/Fixed/index.js +13 -22
- package/build/Flex/FlexItem.d.ts +11 -0
- package/build/Flex/FlexItem.js +26 -0
- package/build/Flex/index.d.ts +2 -10
- package/build/Flex/index.js +12 -22
- package/build/Indicator/index.d.ts +9 -6
- package/build/Indicator/index.js +34 -37
- package/build/Indicator/style.d.ts +4 -3
- package/build/Indicator/style.js +8 -13
- package/build/Loading/Wrapper.js +2 -1
- package/build/Loading/style.js +9 -12
- package/build/Overlay/index.js +6 -1
- package/build/RegionPicker/data.d.ts +6 -0
- package/build/RegionPicker/data.js +14486 -0
- package/build/RegionPicker/index.d.ts +33 -0
- package/build/RegionPicker/index.js +205 -0
- package/build/RegionPicker/style.d.ts +4 -0
- package/build/RegionPicker/style.js +187 -0
- package/build/SafeArea/index.js +14 -17
- package/build/ScrollView/index.d.ts +23 -11
- package/build/ScrollView/index.js +132 -118
- package/build/ScrollView/style.d.ts +1 -1
- package/build/ScrollView/style.js +33 -22
- package/build/Toast/Toast.d.ts +0 -1
- package/build/Toast/Toast.js +6 -4
- package/build/Toast/style.d.ts +3 -7
- package/build/Toast/style.js +33 -41
- package/build/index.d.ts +3 -0
- package/build/index.js +7 -1
- package/build/utils/color.d.ts +5 -0
- package/build/utils/color.js +18 -0
- package/build/utils/createApp.d.ts +16 -2
- package/build/utils/createApp.js +142 -109
- package/build/utils/dom.js +4 -3
- package/build/utils/theme.d.ts +2 -0
- package/build/utils/theme.js +7 -0
- package/package.json +1 -1
- package/test/src/date-picker/index.jsx +119 -0
- package/test/src/index/index.jsx +2 -0
- package/test/src/index.jsx +1 -0
- package/test/src/loading/index.jsx +2 -2
- package/test/src/region-picker/index.jsx +120 -0
- package/test/src/scrollview/BasicSection.jsx +56 -0
- package/test/src/scrollview/CustomLoadingSection.jsx +53 -0
- package/test/src/scrollview/HeightModeSection.jsx +42 -0
- package/test/src/scrollview/ImperativeSection.jsx +56 -0
- package/test/src/scrollview/NotScrollableSection.jsx +32 -0
- package/test/src/scrollview/PerfSection.jsx +34 -0
- package/test/src/scrollview/index.css +92 -8
- package/test/src/scrollview/index.jsx +13 -45
package/build/utils/createApp.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
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
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -8,140 +41,140 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
42
|
});
|
|
10
43
|
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
45
|
exports.history = void 0;
|
|
16
46
|
exports.getHistory = getHistory;
|
|
17
47
|
exports.createApp = createApp;
|
|
18
48
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
19
|
-
const react_1 = require("react");
|
|
49
|
+
const react_1 = __importStar(require("react"));
|
|
20
50
|
const client_1 = require("react-dom/client");
|
|
21
51
|
const history_1 = require("history");
|
|
22
52
|
const Container_1 = require("../Container");
|
|
23
|
-
const pick_1 = __importDefault(require("lodash/pick"));
|
|
24
53
|
// 存储历史记录对象
|
|
25
54
|
exports.history = null;
|
|
26
55
|
// 获取历史记录对象
|
|
27
56
|
function getHistory(mode = "browser") {
|
|
28
57
|
if (exports.history === null) {
|
|
29
|
-
const
|
|
58
|
+
const factories = {
|
|
30
59
|
browser: history_1.createBrowserHistory,
|
|
31
60
|
hash: history_1.createHashHistory,
|
|
32
61
|
memory: history_1.createMemoryHistory,
|
|
33
62
|
};
|
|
34
|
-
exports.history =
|
|
63
|
+
exports.history = factories[mode]();
|
|
35
64
|
}
|
|
36
65
|
return exports.history;
|
|
37
66
|
}
|
|
67
|
+
const VALID_MODES = new Set(["browser", "hash", "memory"]);
|
|
68
|
+
// 模块级常量,移除路径首尾斜杠;g 标志确保首尾各自替换一次
|
|
69
|
+
const PATH_TRIM_RE = /^\/*|\/*$/g;
|
|
38
70
|
/**
|
|
39
|
-
* 创建带路由的
|
|
71
|
+
* 创建带路由的 App,全局单例,通常只调用一次。
|
|
40
72
|
* @param option CreateAppOption
|
|
41
73
|
*/
|
|
42
74
|
function createApp(option) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
var _a;
|
|
76
|
+
// 不修改入参,用局部变量保存规范化后的值
|
|
77
|
+
const mode = option.mode && VALID_MODES.has(option.mode) ? option.mode : "browser";
|
|
78
|
+
const defaultPath = ((_a = option.default) !== null && _a !== void 0 ? _a : "/index").replace(PATH_TRIM_RE, "");
|
|
79
|
+
const scrollToTop = option.scrollToTop !== false;
|
|
80
|
+
// 确保 history 在组件渲染前已就绪
|
|
81
|
+
exports.history = getHistory(mode);
|
|
82
|
+
// 提取 ContainerProps(含 maxWidth,原 pick 遗漏此项)
|
|
83
|
+
const { designWidth, maxWidth, globalStyle } = option;
|
|
84
|
+
const containerProps = { designWidth, maxWidth, globalStyle };
|
|
85
|
+
const { onBefore, onAfter, loading, render, notFound, onError } = option;
|
|
86
|
+
// 规范化路径:移除首尾斜杠,空路径回退到 defaultPath
|
|
87
|
+
const normalizePath = (path) => {
|
|
88
|
+
const normalized = path.replace(PATH_TRIM_RE, "");
|
|
89
|
+
return normalized || defaultPath;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* 全局 App 组件,仅在 createApp 内实例化一次
|
|
93
|
+
*/
|
|
94
|
+
const App = () => {
|
|
95
|
+
const [page, setPage] = (0, react_1.useState)(null);
|
|
96
|
+
// 导航版本号:每次发起新导航自增;过时的异步回调检测到不匹配后静默丢弃,
|
|
97
|
+
// 防止慢请求在更新的导航完成后覆盖页面(竞态条件)
|
|
98
|
+
const navIdRef = (0, react_1.useRef)(0);
|
|
99
|
+
const loadPage = (0, react_1.useCallback)((pathname) => __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
const navId = ++navIdRef.current;
|
|
101
|
+
const path = normalizePath(pathname);
|
|
102
|
+
// 1. 展示加载占位
|
|
103
|
+
if (typeof loading === "function") {
|
|
104
|
+
const loadingNode = yield loading(path);
|
|
105
|
+
if (navId !== navIdRef.current)
|
|
106
|
+
return;
|
|
107
|
+
setPage(loadingNode);
|
|
108
|
+
}
|
|
109
|
+
// 2. 前置钩子
|
|
110
|
+
yield (onBefore === null || onBefore === void 0 ? void 0 : onBefore(path));
|
|
111
|
+
if (navId !== navIdRef.current)
|
|
112
|
+
return;
|
|
113
|
+
// 3. 渲染页面
|
|
114
|
+
if (typeof render === "function") {
|
|
115
|
+
let content;
|
|
116
|
+
let loadError;
|
|
117
|
+
let hasError = false;
|
|
118
|
+
try {
|
|
119
|
+
content = yield render(path);
|
|
81
120
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
// 默认 404 页面
|
|
95
|
-
setPage((0, jsx_runtime_1.jsxs)("div", { children: ["Not Found: ", normalizedPath] }));
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
setPage(pageContent);
|
|
100
|
-
}
|
|
101
|
-
catch (_a) {
|
|
102
|
-
// 动态 import 失败等场景
|
|
103
|
-
if (typeof notFound === "function") {
|
|
104
|
-
setPage(yield notFound(normalizedPath));
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
setPage((0, jsx_runtime_1.jsxs)("div", { children: ["Not Found: ", normalizedPath] }));
|
|
108
|
-
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
loadError = err;
|
|
123
|
+
hasError = true;
|
|
124
|
+
}
|
|
125
|
+
if (navId !== navIdRef.current)
|
|
126
|
+
return;
|
|
127
|
+
if (hasError) {
|
|
128
|
+
// render 抛出异常:优先 onError,降级 notFound,再降级内置提示
|
|
129
|
+
const errNode = typeof onError === "function" ? (yield onError(path, loadError)) : typeof notFound === "function" ? (yield notFound(path)) : ((0, jsx_runtime_1.jsxs)("div", { children: ["Error: ", path] }));
|
|
130
|
+
if (navId !== navIdRef.current)
|
|
109
131
|
return;
|
|
110
|
-
|
|
132
|
+
setPage(errNode);
|
|
133
|
+
return;
|
|
111
134
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
135
|
+
if (content == null) {
|
|
136
|
+
// render 返回 null/undefined:404
|
|
137
|
+
const notFoundNode = typeof notFound === "function" ? (yield notFound(path)) : ((0, jsx_runtime_1.jsxs)("div", { children: ["Not Found: ", path] }));
|
|
138
|
+
if (navId !== navIdRef.current)
|
|
139
|
+
return;
|
|
140
|
+
setPage(notFoundNode);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// 成功:切换前滚回顶部,再替换页面内容
|
|
144
|
+
if (scrollToTop)
|
|
145
|
+
window.scrollTo(0, 0);
|
|
146
|
+
setPage(content);
|
|
147
|
+
}
|
|
148
|
+
if (navId !== navIdRef.current)
|
|
149
|
+
return;
|
|
150
|
+
// 4. 后置钩子(仅成功渲染后调用)
|
|
151
|
+
yield (onAfter === null || onAfter === void 0 ? void 0 : onAfter(path));
|
|
152
|
+
}),
|
|
153
|
+
// 所有依赖均来自 createApp 闭包,生命周期内不变
|
|
154
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
155
|
+
[]);
|
|
156
|
+
(0, react_1.useEffect)(() => {
|
|
157
|
+
// 初始渲染当前路径
|
|
158
|
+
loadPage(exports.history.location.pathname);
|
|
159
|
+
// 监听后续路由变化,返回 unlisten 作为 cleanup
|
|
160
|
+
return exports.history.listen(({ location }) => loadPage(location.pathname));
|
|
161
|
+
}, [loadPage]);
|
|
162
|
+
return (0, jsx_runtime_1.jsx)(Container_1.Container, Object.assign({}, containerProps, { children: page }));
|
|
163
|
+
};
|
|
164
|
+
// 解析挂载目标
|
|
165
|
+
let mount;
|
|
166
|
+
if (typeof option.target === "string") {
|
|
167
|
+
mount = document.querySelector(option.target);
|
|
168
|
+
}
|
|
169
|
+
else if (option.target instanceof HTMLElement) {
|
|
170
|
+
mount = option.target;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
mount = null;
|
|
174
|
+
}
|
|
175
|
+
if (!mount) {
|
|
176
|
+
throw new Error(`createApp: mount target not found — "${typeof option.target === "string" ? option.target : "[invalid element]"}"`);
|
|
177
|
+
}
|
|
178
|
+
const root = (0, client_1.createRoot)(mount);
|
|
179
|
+
root.render(option.strict ? ((0, jsx_runtime_1.jsx)(react_1.default.StrictMode, { children: (0, jsx_runtime_1.jsx)(App, {}) })) : ((0, jsx_runtime_1.jsx)(App, {})));
|
|
147
180
|
}
|
package/build/utils/dom.js
CHANGED
|
@@ -23,12 +23,13 @@ function createPortalDOM(point) {
|
|
|
23
23
|
root.render(component);
|
|
24
24
|
},
|
|
25
25
|
unmount() {
|
|
26
|
-
//
|
|
27
|
-
//
|
|
26
|
+
// 先卸载 React 根,再从 DOM 移除容器
|
|
27
|
+
// React 18+ 推荐先 unmount 让 React 完成清理(包括 effect 的 cleanup),
|
|
28
|
+
// 然后再移除真实 DOM 节点;倒过来在严格模式下可能产生警告
|
|
29
|
+
root.unmount();
|
|
28
30
|
if (container.parentNode) {
|
|
29
31
|
container.parentNode.removeChild(container);
|
|
30
32
|
}
|
|
31
|
-
root.unmount();
|
|
32
33
|
},
|
|
33
34
|
};
|
|
34
35
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const fontStack = "-apple-system, BlinkMacSystemFont, \"Helvetica Neue\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", sans-serif";
|
|
2
|
+
export declare const numberFontStack = "\"SF Pro Text\", -apple-system, BlinkMacSystemFont, \"Helvetica Neue\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", sans-serif";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.numberFontStack = exports.fontStack = void 0;
|
|
4
|
+
// 设计通用字体栈(与 iOS / 桌面/ 中文常见系统字体兼容)
|
|
5
|
+
exports.fontStack = '-apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif';
|
|
6
|
+
// 数字场景优先使用 SF Pro Text(iOS),其它退化到通用字体栈
|
|
7
|
+
exports.numberFontStack = `"SF Pro Text", ${exports.fontStack}`;
|
package/package.json
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { showDatePicker } from "@";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
export default function DatePickerDemo() {
|
|
5
|
+
const [result, setResult] = useState("");
|
|
6
|
+
|
|
7
|
+
const open = (precision) => {
|
|
8
|
+
showDatePicker({
|
|
9
|
+
precision,
|
|
10
|
+
title: `选择日期(${precision})`,
|
|
11
|
+
onConfirm: (d) => {
|
|
12
|
+
const fmt =
|
|
13
|
+
precision === "day"
|
|
14
|
+
? "YYYY-MM-DD"
|
|
15
|
+
: precision === "hour"
|
|
16
|
+
? "YYYY-MM-DD HH时"
|
|
17
|
+
: precision === "minute"
|
|
18
|
+
? "YYYY-MM-DD HH:mm"
|
|
19
|
+
: "YYYY-MM-DD HH:mm:ss";
|
|
20
|
+
setResult(d.format(fmt));
|
|
21
|
+
},
|
|
22
|
+
onCancel: () => {
|
|
23
|
+
console.log("取消选择");
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div style={{ padding: "0.3rem" }}>
|
|
30
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.2rem" }}>
|
|
31
|
+
<button onClick={() => open("day")}>选择年/月/日</button>
|
|
32
|
+
<button onClick={() => open("hour")}>选择年/月/日 + 时</button>
|
|
33
|
+
<button onClick={() => open("minute")}>选择年/月/日 + 时分</button>
|
|
34
|
+
<button onClick={() => open("second")}>选择年/月/日 + 时分秒</button>
|
|
35
|
+
<button
|
|
36
|
+
onClick={() => {
|
|
37
|
+
showDatePicker({
|
|
38
|
+
precision: "minute",
|
|
39
|
+
value: "2024-06-15 10:30",
|
|
40
|
+
primary: "#16a34a",
|
|
41
|
+
minDate: "2024-01-01",
|
|
42
|
+
maxDate: "2026-12-31 23:59",
|
|
43
|
+
title: "自定义范围 + 绿色主题",
|
|
44
|
+
onConfirm: (d) => {
|
|
45
|
+
setResult(d.format("YYYY-MM-DD HH:mm"));
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
自定义初值 / 范围 / 主题
|
|
51
|
+
</button>
|
|
52
|
+
<button
|
|
53
|
+
onClick={() => {
|
|
54
|
+
showDatePicker({
|
|
55
|
+
precision: "minute",
|
|
56
|
+
showUnit: false,
|
|
57
|
+
title: "不显示单位",
|
|
58
|
+
onConfirm: (d) => setResult(d.format("YYYY-MM-DD HH:mm")),
|
|
59
|
+
});
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
不显示单位
|
|
63
|
+
</button>
|
|
64
|
+
<button
|
|
65
|
+
onClick={() => {
|
|
66
|
+
showDatePicker({
|
|
67
|
+
precision: "second",
|
|
68
|
+
units: {
|
|
69
|
+
year: "Y",
|
|
70
|
+
month: "M",
|
|
71
|
+
day: "D",
|
|
72
|
+
hour: "h",
|
|
73
|
+
minute: "m",
|
|
74
|
+
second: "s",
|
|
75
|
+
},
|
|
76
|
+
title: "英文单位",
|
|
77
|
+
onConfirm: (d) => setResult(d.format("YYYY-MM-DD HH:mm:ss")),
|
|
78
|
+
});
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
自定义单位(英文)
|
|
82
|
+
</button>
|
|
83
|
+
<button
|
|
84
|
+
onClick={() => {
|
|
85
|
+
showDatePicker({
|
|
86
|
+
precision: "day",
|
|
87
|
+
rounded: false,
|
|
88
|
+
title: "无圆角(rounded=false)",
|
|
89
|
+
onConfirm: (d) => setResult(d.format("YYYY-MM-DD")),
|
|
90
|
+
});
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
无圆角
|
|
94
|
+
</button>
|
|
95
|
+
<button
|
|
96
|
+
onClick={() => {
|
|
97
|
+
showDatePicker({
|
|
98
|
+
precision: "day",
|
|
99
|
+
title: (
|
|
100
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: "0.08rem" }}>
|
|
101
|
+
<span style={{ color: "#ef4444" }}>★</span>
|
|
102
|
+
<span>ReactNode 标题</span>
|
|
103
|
+
</span>
|
|
104
|
+
),
|
|
105
|
+
cancelText: <span style={{ color: "#ef4444" }}>✕ 关</span>,
|
|
106
|
+
confirmText: <strong>OK ✓</strong>,
|
|
107
|
+
onConfirm: (d) => setResult(d.format("YYYY-MM-DD")),
|
|
108
|
+
});
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
ReactNode 标题/按钮文案
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
<div style={{ marginTop: "0.4rem", fontSize: "0.3rem" }}>
|
|
115
|
+
选择结果:<span style={{ color: "#2f7dff" }}>{result || "(未选择)"}</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
package/test/src/index/index.jsx
CHANGED
|
@@ -17,6 +17,8 @@ const pageConfig = [
|
|
|
17
17
|
// { path: 'privacy', title: 'Privacy去标识化', enable: true },
|
|
18
18
|
{ path: "autogrid", title: "AutoGrid生成自动对齐的表格", enable: true },
|
|
19
19
|
{ path: "city-select", title: "CitySelect城市选择器", enable: true },
|
|
20
|
+
{ path: "date-picker", title: "DatePicker日期时间选择器", enable: true },
|
|
21
|
+
{ path: "region-picker", title: "RegionPicker省市区选择器", enable: true },
|
|
20
22
|
];
|
|
21
23
|
|
|
22
24
|
export default function Index() {
|
package/test/src/index.jsx
CHANGED
|
@@ -8,7 +8,7 @@ export default function Index () {
|
|
|
8
8
|
<button
|
|
9
9
|
onClick={async () => {
|
|
10
10
|
const close = await showLoading();
|
|
11
|
-
window.setTimeout(close,
|
|
11
|
+
window.setTimeout(close, 500000);
|
|
12
12
|
}}
|
|
13
13
|
>
|
|
14
14
|
显示Loading
|
|
@@ -17,7 +17,7 @@ export default function Index () {
|
|
|
17
17
|
<button
|
|
18
18
|
onClick={async () => {
|
|
19
19
|
const close = await showLoading("数据加载中...");
|
|
20
|
-
window.setTimeout(close,
|
|
20
|
+
window.setTimeout(close, 500000);
|
|
21
21
|
}}
|
|
22
22
|
>
|
|
23
23
|
显示Loading
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { showRegionPicker } from "@";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
export default function RegionPickerDemo() {
|
|
5
|
+
const [result, setResult] = useState("");
|
|
6
|
+
const [initial, setInitial] = useState(null);
|
|
7
|
+
|
|
8
|
+
const openDefault = () => {
|
|
9
|
+
showRegionPicker({
|
|
10
|
+
value: initial || undefined,
|
|
11
|
+
onConfirm: (sel) => {
|
|
12
|
+
const text = `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`;
|
|
13
|
+
setResult(text);
|
|
14
|
+
setInitial([sel.province.value, sel.city.value, sel.district.value]);
|
|
15
|
+
},
|
|
16
|
+
onCancel: () => {
|
|
17
|
+
console.log("取消选择");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const openWithInitial = () => {
|
|
23
|
+
showRegionPicker({
|
|
24
|
+
// 广东 / 广州 / 天河区
|
|
25
|
+
value: ["440000", "440100", "440106"],
|
|
26
|
+
title: "带初始值",
|
|
27
|
+
onConfirm: (sel) => {
|
|
28
|
+
setResult(
|
|
29
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
30
|
+
);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const openCustomTheme = () => {
|
|
36
|
+
showRegionPicker({
|
|
37
|
+
primary: "#e53935",
|
|
38
|
+
title: "京东红主题",
|
|
39
|
+
onConfirm: (sel) => {
|
|
40
|
+
setResult(
|
|
41
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const openNoRounded = () => {
|
|
48
|
+
showRegionPicker({
|
|
49
|
+
rounded: false,
|
|
50
|
+
title: "无圆角",
|
|
51
|
+
onConfirm: (sel) => {
|
|
52
|
+
setResult(
|
|
53
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const openCustomLabels = () => {
|
|
60
|
+
showRegionPicker({
|
|
61
|
+
labels: { province: "选省份", city: "选城市", district: "选区县" },
|
|
62
|
+
cancelText: "放弃",
|
|
63
|
+
confirmText: "选好了",
|
|
64
|
+
title: "自定义文案",
|
|
65
|
+
onConfirm: (sel) => {
|
|
66
|
+
setResult(
|
|
67
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const openNoMaskClose = () => {
|
|
74
|
+
showRegionPicker({
|
|
75
|
+
maskClosable: false,
|
|
76
|
+
title: "点击遮罩不关闭",
|
|
77
|
+
onConfirm: (sel) => {
|
|
78
|
+
setResult(
|
|
79
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const openReactNodeTitle = () => {
|
|
86
|
+
showRegionPicker({
|
|
87
|
+
title: (
|
|
88
|
+
<span style={{ display: "inline-flex", alignItems: "center", gap: "0.08rem" }}>
|
|
89
|
+
<span style={{ color: "#f59e0b" }}>★</span>
|
|
90
|
+
<span>ReactNode 标题</span>
|
|
91
|
+
</span>
|
|
92
|
+
),
|
|
93
|
+
cancelText: <span style={{ color: "#ef4444" }}>✕ 关</span>,
|
|
94
|
+
confirmText: <strong>OK ✓</strong>,
|
|
95
|
+
onConfirm: (sel) => {
|
|
96
|
+
setResult(
|
|
97
|
+
`${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<div style={{ padding: "0.3rem" }}>
|
|
105
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.2rem" }}>
|
|
106
|
+
<button onClick={openDefault}>默认(保留上次选择)</button>
|
|
107
|
+
<button onClick={openWithInitial}>带初始值(广东/广州/天河)</button>
|
|
108
|
+
<button onClick={openCustomTheme}>自定义主色(京东红)</button>
|
|
109
|
+
<button onClick={openNoRounded}>无圆角</button>
|
|
110
|
+
<button onClick={openCustomLabels}>自定义 tab 占位 / 按钮文案</button>
|
|
111
|
+
<button onClick={openNoMaskClose}>点击遮罩不关闭</button>
|
|
112
|
+
<button onClick={openReactNodeTitle}>ReactNode 标题/按钮</button>
|
|
113
|
+
</div>
|
|
114
|
+
<div style={{ marginTop: "0.4rem", fontSize: "0.3rem" }}>
|
|
115
|
+
选择结果:
|
|
116
|
+
<span style={{ color: "#2f7dff" }}>{result || "(未选择)"}</span>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
import { ScrollView } from "@";
|
|
3
|
+
|
|
4
|
+
export default function BasicSection() {
|
|
5
|
+
const [list, setList] = useState(() =>
|
|
6
|
+
Array.from({ length: 8 }, (_, i) => i + 1),
|
|
7
|
+
);
|
|
8
|
+
const [hasMore, setHasMore] = useState(true);
|
|
9
|
+
const loadingRef = useRef(false);
|
|
10
|
+
const [stat, setStat] = useState({ scrollTop: 0, dir: "-" });
|
|
11
|
+
|
|
12
|
+
const loadMore = () => {
|
|
13
|
+
if (loadingRef.current || !hasMore) return;
|
|
14
|
+
loadingRef.current = true;
|
|
15
|
+
window.setTimeout(() => {
|
|
16
|
+
setList((prev) => {
|
|
17
|
+
const next = [...prev];
|
|
18
|
+
const start = prev.length;
|
|
19
|
+
for (let i = 1; i <= 8; i++) next.push(start + i);
|
|
20
|
+
if (next.length >= 40) setHasMore(false);
|
|
21
|
+
return next;
|
|
22
|
+
});
|
|
23
|
+
loadingRef.current = false;
|
|
24
|
+
}, 700);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="section">
|
|
29
|
+
<div className="section-title">基础:触底加载更多 + 触顶事件</div>
|
|
30
|
+
<div className="stat">
|
|
31
|
+
scrollTop: {stat.scrollTop.toFixed(0)}px / 方向: {stat.dir} / 共{" "}
|
|
32
|
+
{list.length} 项
|
|
33
|
+
{!hasMore && <span className="badge">已全部加载</span>}
|
|
34
|
+
</div>
|
|
35
|
+
<div className="section-body" style={{ height: "4.4rem" }}>
|
|
36
|
+
<ScrollView
|
|
37
|
+
showLoading={hasMore}
|
|
38
|
+
onScroll={(e) =>
|
|
39
|
+
setStat({ scrollTop: e.scrollTop, dir: e.direction })
|
|
40
|
+
}
|
|
41
|
+
onReachTop={() => console.log("[basic] reach top")}
|
|
42
|
+
onReachBottom={() => {
|
|
43
|
+
console.log("[basic] reach bottom => loadMore");
|
|
44
|
+
loadMore();
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{list.map((n) => (
|
|
48
|
+
<div className="item" key={n}>
|
|
49
|
+
第 {n} 项 — 内容占位行
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
</ScrollView>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|