clxx 3.0.1 → 3.0.2

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.
@@ -8,7 +8,7 @@ export interface RegionNode {
8
8
  export interface RegionSelection {
9
9
  province: RegionNode;
10
10
  city: RegionNode;
11
- district: RegionNode;
11
+ district: RegionNode | null;
12
12
  }
13
13
  export interface RegionLabels {
14
14
  province?: string;
@@ -25,9 +25,10 @@ export interface RegionPickerProps {
25
25
  maskClosable?: boolean;
26
26
  primary?: string;
27
27
  rounded?: boolean;
28
+ taiwanHKMacau?: boolean;
28
29
  onClose?: () => void;
29
30
  onCancel?: () => void;
30
31
  onConfirm?: (selection: RegionSelection) => void;
31
32
  }
32
33
  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;
34
+ export declare function showRegionPicker(options?: Pick<RegionPickerProps, "value" | "data" | "title" | "cancelText" | "confirmText" | "labels" | "maskClosable" | "primary" | "rounded" | "taiwanHKMacau" | "onCancel" | "onConfirm">): void;
@@ -19,6 +19,12 @@ const Dialog_1 = require("../Dialog");
19
19
  const Clickable_1 = require("../Clickable");
20
20
  const style_1 = require("./style");
21
21
  const data_1 = require("./data");
22
+ // 港澳台对应的顶层省级 value(台湾、香港、澳门)
23
+ const HK_MO_TW_VALUES = new Set([
24
+ "710000",
25
+ "810000",
26
+ "820000",
27
+ ]);
22
28
  const DEFAULT_LABELS = {
23
29
  province: "省",
24
30
  city: "市",
@@ -26,8 +32,12 @@ const DEFAULT_LABELS = {
26
32
  };
27
33
  const TAB_ORDER = ["province", "city", "district"];
28
34
  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;
35
+ const { value, data = data_1.treeRegionData, title = "请选择地区", cancelText = "取消", confirmText = "确定", labels, primary = style_1.DEFAULT_PRIMARY, rounded = true, taiwanHKMacau = false, onClose, onCancel, onConfirm, } = props;
30
36
  const style = (0, react_1.useMemo)(() => (0, style_1.createStyle)(primary, rounded), [primary, rounded]);
37
+ // 过滤后的顶层数据:仅在 data / taiwanHKMacau 变化时重建
38
+ const effectiveData = (0, react_1.useMemo)(() => taiwanHKMacau
39
+ ? data
40
+ : data.filter((x) => !HK_MO_TW_VALUES.has(x.value)), [data, taiwanHKMacau]);
31
41
  const lab = (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, DEFAULT_LABELS), labels)), [labels]);
32
42
  // === 选中状态 ===
33
43
  // 当前每级选中节点;未选为 null
@@ -36,23 +46,26 @@ function RegionPicker(props) {
36
46
  const [districtNode, setDistrictNode] = (0, react_1.useState)(null);
37
47
  // 当前激活 tab
38
48
  const [activeTab, setActiveTab] = (0, react_1.useState)("province");
39
- // 根据 value 初始化(只在首次 & data 变化时)
49
+ // 初始化 / data 恢复选中里允许 city 无 children(部分地区没有区级)
40
50
  (0, react_1.useEffect)(() => {
41
51
  var _a, _b, _c, _d, _e;
42
52
  if (!value)
43
53
  return;
44
54
  const [pv, cv, dv] = value;
45
- const p = (_a = data.find((x) => x.value === pv)) !== null && _a !== void 0 ? _a : null;
55
+ const p = (_a = effectiveData.find((x) => x.value === pv)) !== null && _a !== void 0 ? _a : null;
46
56
  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
57
  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
58
  setProvinceNode(p);
49
59
  setCityNode(c);
50
60
  setDistrictNode(d);
51
- // 定位 activeTab 到「最深的未选层」,全部选好时停在 district
61
+ // 定位 activeTab 到「最深的未选层」;
62
+ // 如果选中的市无 children,则停在 city
52
63
  if (!p)
53
64
  setActiveTab("province");
54
65
  else if (!c)
55
66
  setActiveTab("city");
67
+ else if (!c.children || c.children.length === 0)
68
+ setActiveTab("city");
56
69
  else
57
70
  setActiveTab("district");
58
71
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -61,11 +74,11 @@ function RegionPicker(props) {
61
74
  const currentList = (0, react_1.useMemo)(() => {
62
75
  var _a, _b;
63
76
  if (activeTab === "province")
64
- return data;
77
+ return effectiveData;
65
78
  if (activeTab === "city")
66
79
  return (_a = provinceNode === null || provinceNode === void 0 ? void 0 : provinceNode.children) !== null && _a !== void 0 ? _a : [];
67
80
  return (_b = cityNode === null || cityNode === void 0 ? void 0 : cityNode.children) !== null && _b !== void 0 ? _b : [];
68
- }, [activeTab, data, provinceNode, cityNode]);
81
+ }, [activeTab, effectiveData, provinceNode, cityNode]);
69
82
  const selectedValueOfTab = (tab) => {
70
83
  var _a, _b, _c;
71
84
  if (tab === "province")
@@ -74,13 +87,13 @@ function RegionPicker(props) {
74
87
  return (_b = cityNode === null || cityNode === void 0 ? void 0 : cityNode.value) !== null && _b !== void 0 ? _b : null;
75
88
  return (_c = districtNode === null || districtNode === void 0 ? void 0 : districtNode.value) !== null && _c !== void 0 ? _c : null;
76
89
  };
77
- // tab 是否可点击:只有已有对应级数据时才可点(即上级已选)
90
+ // tab 是否可点击:上级已选;district 额外要求 city 有子级
78
91
  const tabEnabled = (tab) => {
79
92
  if (tab === "province")
80
93
  return true;
81
94
  if (tab === "city")
82
95
  return !!provinceNode;
83
- return !!cityNode;
96
+ return !!(cityNode && cityNode.children && cityNode.children.length > 0);
84
97
  };
85
98
  // 点击列表项
86
99
  const handlePick = (item) => {
@@ -146,14 +159,20 @@ function RegionPicker(props) {
146
159
  onCancel === null || onCancel === void 0 ? void 0 : onCancel();
147
160
  onClose === null || onClose === void 0 ? void 0 : onClose();
148
161
  };
149
- const canConfirm = !!(provinceNode && cityNode && districtNode);
162
+ const canConfirm = !!(provinceNode &&
163
+ cityNode &&
164
+ (!cityNode.children || cityNode.children.length === 0 || districtNode));
150
165
  const handleConfirm = () => {
151
166
  if (!canConfirm)
152
167
  return;
168
+ if (!provinceNode || !cityNode)
169
+ return;
153
170
  onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm({
154
171
  province: { value: provinceNode.value, label: provinceNode.label },
155
172
  city: { value: cityNode.value, label: cityNode.label },
156
- district: { value: districtNode.value, label: districtNode.label },
173
+ district: districtNode
174
+ ? { value: districtNode.value, label: districtNode.label }
175
+ : null,
157
176
  });
158
177
  onClose === null || onClose === void 0 ? void 0 : onClose();
159
178
  };
@@ -5,16 +5,17 @@ exports.createStyle = createStyle;
5
5
  const react_1 = require("@emotion/react");
6
6
  const color_1 = require("../utils/color");
7
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
8
+ // CitySelect 一致的设计变量(带线条风格)
9
+ const textPrimary = "#1f2328";
10
+ const textSecondary = "#6b7280";
11
+ const textTertiary = "#9ca3af";
12
12
  const bgPage = "#ffffff";
13
- const bgSubtle = "rgba(0,0,0,.04)"; // 按下态浅灰
13
+ const bgSubtle = "#f5f6f8";
14
+ const border = "#e5e7eb";
14
15
  exports.DEFAULT_PRIMARY = "#2f7dff";
15
16
  function createStyle(primary, rounded = true) {
16
17
  const primaryActive = (0, color_1.darken)(primary, 0.15);
17
- const sheetRadius = rounded ? ".28rem" : "0";
18
+ const sheetRadius = rounded ? ".24rem" : "0";
18
19
  return {
19
20
  // 内容容器:动画/遮罩/全屏由 Dialog 提供,这里只保留视觉与排版
20
21
  sheet: (0, react_1.css)({
@@ -32,7 +33,7 @@ function createStyle(primary, rounded = true) {
32
33
  WebkitFontSmoothing: "antialiased",
33
34
  MozOsxFontSmoothing: "grayscale",
34
35
  }),
35
- // iOS 风标题栏:无底边框,标题加粗居中,左右按钮都用主色
36
+ // 标题栏:底部 hairline 与列表区分;按钮中等字重
36
37
  header: (0, react_1.css)({
37
38
  flexShrink: 0,
38
39
  height: ".92rem",
@@ -40,11 +41,12 @@ function createStyle(primary, rounded = true) {
40
41
  alignItems: "center",
41
42
  justifyContent: "space-between",
42
43
  padding: "0 .16rem",
44
+ borderBottom: `1px solid ${border}`,
43
45
  }),
44
46
  title: (0, react_1.css)({
45
47
  flex: 1,
46
48
  textAlign: "center",
47
- fontSize: ".32rem",
49
+ fontSize: ".3rem",
48
50
  fontWeight: 600,
49
51
  color: textPrimary,
50
52
  letterSpacing: ".01rem",
@@ -52,7 +54,7 @@ function createStyle(primary, rounded = true) {
52
54
  btn: (0, react_1.css)({
53
55
  minWidth: "1.1rem",
54
56
  padding: "0 .08rem",
55
- fontSize: ".3rem",
57
+ fontSize: ".28rem",
56
58
  fontWeight: 400,
57
59
  lineHeight: ".92rem",
58
60
  cursor: "pointer",
@@ -65,7 +67,7 @@ function createStyle(primary, rounded = true) {
65
67
  }),
66
68
  btnConfirm: (0, react_1.css)({
67
69
  textAlign: "right",
68
- fontWeight: 600,
70
+ fontWeight: 500,
69
71
  color: primary,
70
72
  "&:active": { color: primaryActive, opacity: 0.65 },
71
73
  }),
@@ -73,17 +75,18 @@ function createStyle(primary, rounded = true) {
73
75
  color: textTertiary,
74
76
  cursor: "not-allowed",
75
77
  pointerEvents: "none",
76
- fontWeight: 600,
78
+ fontWeight: 500,
77
79
  "&:active": { opacity: 1, color: textTertiary },
78
80
  }),
79
- // tabs 行:轻量化,激活态用细下划线提示;底部加 hairline 分隔下方列表
81
+ // tabs 行:浅灰底色(与 CitySelect 字母分组标题色一致),激活态细下划线
80
82
  tabs: (0, react_1.css)({
81
83
  flexShrink: 0,
82
84
  display: "flex",
83
85
  alignItems: "stretch",
84
86
  height: ".8rem",
85
87
  padding: "0 .16rem",
86
- borderBottom: "1px solid rgba(60,60,67,.18)",
88
+ backgroundColor: bgSubtle,
89
+ borderBottom: `1px solid ${border}`,
87
90
  }),
88
91
  tab: (0, react_1.css)({
89
92
  flex: 1,
@@ -92,7 +95,7 @@ function createStyle(primary, rounded = true) {
92
95
  alignItems: "center",
93
96
  justifyContent: "center",
94
97
  padding: "0 .08rem",
95
- fontSize: ".28rem",
98
+ fontSize: ".26rem",
96
99
  color: textSecondary,
97
100
  position: "relative",
98
101
  cursor: "pointer",
@@ -107,7 +110,7 @@ function createStyle(primary, rounded = true) {
107
110
  }),
108
111
  tabActive: (0, react_1.css)({
109
112
  color: primary,
110
- fontWeight: 500,
113
+ fontWeight: 600,
111
114
  "&::after": {
112
115
  content: '""',
113
116
  position: "absolute",
@@ -120,7 +123,7 @@ function createStyle(primary, rounded = true) {
120
123
  borderRadius: ".02rem",
121
124
  },
122
125
  }),
123
- // 选项列表(固定高度,避免跳变;6 行 × .88rem = 5.28rem,整体更紧凑)
126
+ // 选项列表:固定高度避免跳变
124
127
  list: (0, react_1.css)({
125
128
  flexShrink: 0,
126
129
  height: "5.28rem",
@@ -129,18 +132,19 @@ function createStyle(primary, rounded = true) {
129
132
  WebkitOverflowScrolling: "touch",
130
133
  overscrollBehavior: "contain",
131
134
  backgroundColor: bgPage,
132
- paddingBottom: ".12rem",
133
135
  }),
134
- // iOS 风列表项:无分隔线,仅按下态浅灰底
136
+ // 列表项:底部 hairline 分隔(CitySelect 风),按下浅灰底
135
137
  listItem: (0, react_1.css)({
136
138
  position: "relative",
137
139
  height: ".88rem",
138
140
  display: "flex",
139
141
  alignItems: "center",
140
- padding: "0 .32rem",
142
+ padding: "0 .3rem",
141
143
  fontSize: ".3rem",
142
144
  color: textPrimary,
143
- transition: "background-color .15s",
145
+ backgroundColor: bgPage,
146
+ borderBottom: `1px solid ${border}`,
147
+ transition: "background-color .12s",
144
148
  "&:active": {
145
149
  backgroundColor: bgSubtle,
146
150
  },
@@ -157,7 +161,7 @@ function createStyle(primary, rounded = true) {
157
161
  color: primary,
158
162
  fontWeight: 500,
159
163
  }),
160
- // iOS 风对勾(SF Symbol checkmark 风格)
164
+ // 对勾
161
165
  checkIcon: (0, react_1.css)({
162
166
  width: ".32rem",
163
167
  height: ".32rem",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clxx",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Basic JS library for mobile devices",
5
5
  "main": "./build/index.js",
6
6
  "module": "./build/index.js",
@@ -1,21 +1,34 @@
1
1
  import { showCitySelect } from "@";
2
- import { useEffect } from "react";
3
2
 
4
3
  export default function Index() {
5
4
  return (
6
- <button
7
- onClick={() => {
8
- showCitySelect({
9
- onSelect: (city) => {
10
- console.log("选择了城市:", city);
11
- },
12
- getLocation() {
13
- return "北京";
14
- },
15
- });
16
- }}
17
- >
18
- 选择城市
19
- </button>
5
+ <div style={{ display: "flex", flexDirection: "column", gap: "0.2rem", padding: "0.3rem" }}>
6
+ <button
7
+ onClick={() => {
8
+ showCitySelect({
9
+ onSelect: (city) => {
10
+ console.log("选择了城市:", city);
11
+ },
12
+ getLocation() {
13
+ return "北京";
14
+ },
15
+ });
16
+ }}
17
+ >
18
+ 选择城市(默认:不含港澳台)
19
+ </button>
20
+ <button
21
+ onClick={() => {
22
+ showCitySelect({
23
+ taiwanHKMacau: true,
24
+ onSelect: (city) => {
25
+ console.log("选择了城市:", city);
26
+ },
27
+ });
28
+ }}
29
+ >
30
+ 选择城市(含港澳台)
31
+ </button>
32
+ </div>
20
33
  );
21
34
  }
@@ -1,6 +1,12 @@
1
1
  import { showRegionPicker } from "@";
2
2
  import { useState } from "react";
3
3
 
4
+ // 部分城市无区级,district 可能为 null
5
+ const formatSelection = (sel) =>
6
+ sel.district
7
+ ? `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`
8
+ : `${sel.province.label} / ${sel.city.label}`;
9
+
4
10
  export default function RegionPickerDemo() {
5
11
  const [result, setResult] = useState("");
6
12
  const [initial, setInitial] = useState(null);
@@ -9,9 +15,12 @@ export default function RegionPickerDemo() {
9
15
  showRegionPicker({
10
16
  value: initial || undefined,
11
17
  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]);
18
+ setResult(formatSelection(sel));
19
+ setInitial([
20
+ sel.province.value,
21
+ sel.city.value,
22
+ sel.district?.value,
23
+ ]);
15
24
  },
16
25
  onCancel: () => {
17
26
  console.log("取消选择");
@@ -25,9 +34,7 @@ export default function RegionPickerDemo() {
25
34
  value: ["440000", "440100", "440106"],
26
35
  title: "带初始值",
27
36
  onConfirm: (sel) => {
28
- setResult(
29
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
30
- );
37
+ setResult(formatSelection(sel));
31
38
  },
32
39
  });
33
40
  };
@@ -37,9 +44,7 @@ export default function RegionPickerDemo() {
37
44
  primary: "#e53935",
38
45
  title: "京东红主题",
39
46
  onConfirm: (sel) => {
40
- setResult(
41
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
42
- );
47
+ setResult(formatSelection(sel));
43
48
  },
44
49
  });
45
50
  };
@@ -49,9 +54,7 @@ export default function RegionPickerDemo() {
49
54
  rounded: false,
50
55
  title: "无圆角",
51
56
  onConfirm: (sel) => {
52
- setResult(
53
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
54
- );
57
+ setResult(formatSelection(sel));
55
58
  },
56
59
  });
57
60
  };
@@ -63,9 +66,7 @@ export default function RegionPickerDemo() {
63
66
  confirmText: "选好了",
64
67
  title: "自定义文案",
65
68
  onConfirm: (sel) => {
66
- setResult(
67
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
68
- );
69
+ setResult(formatSelection(sel));
69
70
  },
70
71
  });
71
72
  };
@@ -75,9 +76,7 @@ export default function RegionPickerDemo() {
75
76
  maskClosable: false,
76
77
  title: "点击遮罩不关闭",
77
78
  onConfirm: (sel) => {
78
- setResult(
79
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
80
- );
79
+ setResult(formatSelection(sel));
81
80
  },
82
81
  });
83
82
  };
@@ -93,9 +92,17 @@ export default function RegionPickerDemo() {
93
92
  cancelText: <span style={{ color: "#ef4444" }}>✕ 关</span>,
94
93
  confirmText: <strong>OK ✓</strong>,
95
94
  onConfirm: (sel) => {
96
- setResult(
97
- `${sel.province.label} / ${sel.city.label} / ${sel.district.label}`,
98
- );
95
+ setResult(formatSelection(sel));
96
+ },
97
+ });
98
+ };
99
+
100
+ const openWithHKMOTW = () => {
101
+ showRegionPicker({
102
+ taiwanHKMacau: true,
103
+ title: "含港澳台",
104
+ onConfirm: (sel) => {
105
+ setResult(formatSelection(sel));
99
106
  },
100
107
  });
101
108
  };
@@ -110,6 +117,7 @@ export default function RegionPickerDemo() {
110
117
  <button onClick={openCustomLabels}>自定义 tab 占位 / 按钮文案</button>
111
118
  <button onClick={openNoMaskClose}>点击遮罩不关闭</button>
112
119
  <button onClick={openReactNodeTitle}>ReactNode 标题/按钮</button>
120
+ <button onClick={openWithHKMOTW}>含港澳台</button>
113
121
  </div>
114
122
  <div style={{ marginTop: "0.4rem", fontSize: "0.3rem" }}>
115
123
  选择结果: