@tuya-miniapp/smart-ui 2.13.3 → 2.13.4-beta-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.
@@ -255,6 +255,37 @@ Page({
255
255
  });
256
256
  ```
257
257
 
258
+ ### 控制某一列是否循环滚动 `v2.13.4`
259
+
260
+ 默认情况下,`year`(年)与 `12HourClock`(上午/下午)列不循环滚动,其余列(月、日、时、分)循环滚动。通过 `loop-map` 可以按列覆盖该默认行为,未配置的列保持默认。
261
+
262
+ ```html
263
+ <smart-datetime-picker
264
+ type="date"
265
+ value="{{ currentDate }}"
266
+ loop-map="{{ loopMap }}"
267
+ bind:input="onInput"
268
+ />
269
+ ```
270
+
271
+ ```js
272
+ Page({
273
+ data: {
274
+ currentDate: new Date().getTime(),
275
+ loopMap: {
276
+ day: false, // 日不循环
277
+ year: true, // 年循环
278
+ },
279
+ },
280
+
281
+ onInput(event) {
282
+ this.setData({
283
+ currentDate: event.detail,
284
+ });
285
+ },
286
+ });
287
+ ```
288
+
258
289
  ### 样式风格 `v2.3.7`
259
290
 
260
291
  `active-style` 可以修改选中项的样式;`column-styles` 可以设置每一列的样式;`font-styles` 可以设置每一列文字的样式。
@@ -326,6 +357,7 @@ Page({
326
357
  | columnStyles `v2.3.7` | 任意列的样式 | _Record\<string, string>_ | - |
327
358
  | font-styles `v2.3.7` | 任意列的字体样式 | _Record\<string, string>_ | - |
328
359
  | active-style `v2.3.7` | 选中项的样式 | _string_ | - |
360
+ | loop-map `v2.13.4` | 控制每一列是否循环滚动(`type` 可选值为 `year`, `month`, `day`, `hour`, `minute`, `12HourClock`),未配置的列保持默认行为:`year` 与 `12HourClock` 不循环,其余列循环 | _Record\<type, boolean>_ | - |
329
361
 
330
362
  ### Events
331
363
 
@@ -115,6 +115,10 @@ SmartComponent({
115
115
  }, fontStyles: {
116
116
  type: Object,
117
117
  value: undefined,
118
+ }, loopMap: {
119
+ type: Object,
120
+ value: undefined,
121
+ observer: 'updateValue',
118
122
  } }),
119
123
  data: {
120
124
  part: 'am',
@@ -187,6 +191,16 @@ SmartComponent({
187
191
  }
188
192
  return formatter(type, value);
189
193
  },
194
+ // 某一列是否循环滚动,loopMap 未配置该列时使用默认值(年份、12 小时制上下午不循环)
195
+ getColumnLoop(type) {
196
+ var _a;
197
+ const { loopMap } = this.data;
198
+ const loop = (_a = loopMap === null || loopMap === void 0 ? void 0 : loopMap[type]) !== null && _a !== void 0 ? _a : (type === '12HourClock' ? loopMap === null || loopMap === void 0 ? void 0 : loopMap.part : undefined);
199
+ if (typeof loop === 'boolean') {
200
+ return loop;
201
+ }
202
+ return !['12HourClock', 'year'].includes(type);
203
+ },
190
204
  updateColumns(values) {
191
205
  const { locale, columnStyles, fontStyles } = this.data;
192
206
  const results = this.getOriginColumns().map((column, index) => {
@@ -200,7 +214,7 @@ SmartComponent({
200
214
  style: columnStyles === null || columnStyles === void 0 ? void 0 : columnStyles[column.type],
201
215
  fontStyle: fontStyles === null || fontStyles === void 0 ? void 0 : fontStyles[column.type],
202
216
  activeIndex: activeIndex === -1 ? 0 : activeIndex,
203
- loop: !['12HourClock', 'year'].includes(column.type),
217
+ loop: this.getColumnLoop(column.type),
204
218
  };
205
219
  });
206
220
  return this.setData({ columns: results });
@@ -1,7 +1,6 @@
1
1
  import { Success, Alarm, Error } from './icons';
2
2
  import { SmartComponent } from '../common/component';
3
- import { contextRef } from './toast';
4
- import { getCurrentPage } from '../common/utils';
3
+ import { registerToastContext, unregisterToastContext } from './toast';
5
4
  import appLog from '../common/appLog';
6
5
  SmartComponent({
7
6
  props: {
@@ -43,17 +42,17 @@ SmartComponent({
43
42
  mounted: function () {
44
43
  if (!this.id)
45
44
  return;
46
- if (contextRef.value[`#${this.id}`]) {
45
+ const repeated = registerToastContext(`#${this.id}`, this);
46
+ if (repeated) {
47
47
  console.error(`Toast component #${this.id} repeated!`);
48
48
  appLog.info(`Toast component #${this.id} repeated!`);
49
49
  }
50
- contextRef.value[`#${this.id}`] = getCurrentPage();
51
50
  appLog.info(`Toast #${this.id} mounted`);
52
51
  },
53
52
  destroyed: function () {
54
53
  if (!this.id)
55
54
  return;
56
- contextRef.value[`#${this.id}`] = null;
55
+ unregisterToastContext(`#${this.id}`, this);
57
56
  appLog.info(`Toast #${this.id} destroyed`);
58
57
  },
59
58
  methods: {
@@ -21,9 +21,27 @@ interface ToastOptions {
21
21
  width?: number;
22
22
  onClose?: () => void;
23
23
  }
24
+ interface ToastContextEntry {
25
+ page: ToastContext;
26
+ instance: WechatMiniprogram.Component.TrivialInstance | null;
27
+ }
28
+ /**
29
+ * selector -> 该 selector 下所有存活的 toast 实例。
30
+ * 一个 selector(id)可以同时挂在多个页面(多 webview)上,因此用数组记录,
31
+ * 而不再是「一个 selector 只对应一个 page」的单槽结构。
32
+ * 为向后兼容,仍容忍外部直接赋值为单个 page(非数组)的旧写法。
33
+ */
24
34
  export declare const contextRef: {
25
- value: Record<string, ToastContext | null>;
35
+ value: Record<string, ToastContext | ToastContextEntry[] | null>;
26
36
  };
37
+ /**
38
+ * 组件挂载时登记自身。同一 selector 在「同一个页面」上重复挂载才算冲突,
39
+ * 不同页面共用同一 selector 是被允许的(这正是「每页挂一个默认 toast」的场景)。
40
+ * @returns 是否与同页已有实例冲突(供组件层打印 repeated 提示)。
41
+ */
42
+ export declare function registerToastContext(selector: string, instance: WechatMiniprogram.Component.TrivialInstance): boolean;
43
+ /** 组件卸载时精确移除自身,不影响其它页面上同 selector 的实例。 */
44
+ export declare function unregisterToastContext(selector: string, instance: WechatMiniprogram.Component.TrivialInstance): void;
27
45
  declare function Toast(toastOptions: ToastOptions | ToastMessage): WechatMiniprogram.Component.TrivialInstance | undefined;
28
46
  declare namespace Toast {
29
47
  var loading: (options: ToastMessage | ToastOptions) => WechatMiniprogram.Component.TrivialInstance | undefined;
@@ -30,16 +30,68 @@ let currentOptions = Object.assign({}, defaultOptions);
30
30
  function parseOptions(message) {
31
31
  return isObj(message) ? message : { message };
32
32
  }
33
+ /**
34
+ * selector -> 该 selector 下所有存活的 toast 实例。
35
+ * 一个 selector(id)可以同时挂在多个页面(多 webview)上,因此用数组记录,
36
+ * 而不再是「一个 selector 只对应一个 page」的单槽结构。
37
+ * 为向后兼容,仍容忍外部直接赋值为单个 page(非数组)的旧写法。
38
+ */
33
39
  export const contextRef = {
34
40
  value: {},
35
41
  };
42
+ /**
43
+ * 组件挂载时登记自身。同一 selector 在「同一个页面」上重复挂载才算冲突,
44
+ * 不同页面共用同一 selector 是被允许的(这正是「每页挂一个默认 toast」的场景)。
45
+ * @returns 是否与同页已有实例冲突(供组件层打印 repeated 提示)。
46
+ */
47
+ export function registerToastContext(selector, instance) {
48
+ const existing = contextRef.value[selector];
49
+ const list = Array.isArray(existing) ? existing : [];
50
+ contextRef.value[selector] = list;
51
+ const page = getCurrentPage();
52
+ const samePage = list.find(entry => entry.page === page);
53
+ if (samePage) {
54
+ samePage.instance = instance;
55
+ return true;
56
+ }
57
+ list.push({ page, instance });
58
+ return false;
59
+ }
60
+ /** 组件卸载时精确移除自身,不影响其它页面上同 selector 的实例。 */
61
+ export function unregisterToastContext(selector, instance) {
62
+ const existing = contextRef.value[selector];
63
+ if (!Array.isArray(existing)) {
64
+ return;
65
+ }
66
+ contextRef.value[selector] = existing.filter(entry => entry.instance !== instance);
67
+ }
68
+ /**
69
+ * 解析目标实例所在的上下文:优先命中「当前页面」上的实例,
70
+ * 其次退回最后挂载的实例(栈顶,覆盖跨页/非当前页场景),
71
+ * 同时兼容外部直接赋值单个 page 的旧写法。
72
+ */
73
+ function resolveContextEntry(selector) {
74
+ const existing = contextRef.value[selector];
75
+ if (!existing) {
76
+ return null;
77
+ }
78
+ if (!Array.isArray(existing)) {
79
+ // 兼容旧结构:value[selector] 直接是一个 page
80
+ return { page: existing, instance: null };
81
+ }
82
+ if (!existing.length) {
83
+ return null;
84
+ }
85
+ const currentPage = getCurrentPage();
86
+ return existing.find(entry => entry.page === currentPage) || existing[existing.length - 1];
87
+ }
36
88
  function Toast(toastOptions) {
37
89
  const options = Object.assign(Object.assign({}, currentOptions), parseOptions(toastOptions));
38
90
  appLog.info('start open Toast');
39
- const context = (typeof options.context === 'function' ? options.context() : options.context) ||
40
- contextRef.value[options.selector] ||
41
- getCurrentPage();
42
- const toast = context.selectComponent(options.selector);
91
+ const explicitContext = typeof options.context === 'function' ? options.context() : options.context;
92
+ const entry = explicitContext ? null : resolveContextEntry(options.selector);
93
+ const context = explicitContext || (entry === null || entry === void 0 ? void 0 : entry.page) || getCurrentPage();
94
+ const toast = context.selectComponent(options.selector) || (entry === null || entry === void 0 ? void 0 : entry.instance) || null;
43
95
  appLog.info(`toast selector: ${options.selector}`);
44
96
  appLog.info(`toast component ${toast ? 'success' : 'fail'}`);
45
97
  if (!toast) {
@@ -255,6 +255,37 @@ Page({
255
255
  });
256
256
  ```
257
257
 
258
+ ### 控制某一列是否循环滚动 `v2.13.4`
259
+
260
+ 默认情况下,`year`(年)与 `12HourClock`(上午/下午)列不循环滚动,其余列(月、日、时、分)循环滚动。通过 `loop-map` 可以按列覆盖该默认行为,未配置的列保持默认。
261
+
262
+ ```html
263
+ <smart-datetime-picker
264
+ type="date"
265
+ value="{{ currentDate }}"
266
+ loop-map="{{ loopMap }}"
267
+ bind:input="onInput"
268
+ />
269
+ ```
270
+
271
+ ```js
272
+ Page({
273
+ data: {
274
+ currentDate: new Date().getTime(),
275
+ loopMap: {
276
+ day: false, // 日不循环
277
+ year: true, // 年循环
278
+ },
279
+ },
280
+
281
+ onInput(event) {
282
+ this.setData({
283
+ currentDate: event.detail,
284
+ });
285
+ },
286
+ });
287
+ ```
288
+
258
289
  ### 样式风格 `v2.3.7`
259
290
 
260
291
  `active-style` 可以修改选中项的样式;`column-styles` 可以设置每一列的样式;`font-styles` 可以设置每一列文字的样式。
@@ -326,6 +357,7 @@ Page({
326
357
  | columnStyles `v2.3.7` | 任意列的样式 | _Record\<string, string>_ | - |
327
358
  | font-styles `v2.3.7` | 任意列的字体样式 | _Record\<string, string>_ | - |
328
359
  | active-style `v2.3.7` | 选中项的样式 | _string_ | - |
360
+ | loop-map `v2.13.4` | 控制每一列是否循环滚动(`type` 可选值为 `year`, `month`, `day`, `hour`, `minute`, `12HourClock`),未配置的列保持默认行为:`year` 与 `12HourClock` 不循环,其余列循环 | _Record\<type, boolean>_ | - |
329
361
 
330
362
  ### Events
331
363
 
@@ -139,6 +139,10 @@ var defaultFormatter = function (type, value) {
139
139
  }, fontStyles: {
140
140
  type: Object,
141
141
  value: undefined,
142
+ }, loopMap: {
143
+ type: Object,
144
+ value: undefined,
145
+ observer: 'updateValue',
142
146
  } }),
143
147
  data: {
144
148
  part: 'am',
@@ -218,6 +222,16 @@ var defaultFormatter = function (type, value) {
218
222
  }
219
223
  return formatter(type, value);
220
224
  },
225
+ // 某一列是否循环滚动,loopMap 未配置该列时使用默认值(年份、12 小时制上下午不循环)
226
+ getColumnLoop: function (type) {
227
+ var _a;
228
+ var loopMap = this.data.loopMap;
229
+ var loop = (_a = loopMap === null || loopMap === void 0 ? void 0 : loopMap[type]) !== null && _a !== void 0 ? _a : (type === '12HourClock' ? loopMap === null || loopMap === void 0 ? void 0 : loopMap.part : undefined);
230
+ if (typeof loop === 'boolean') {
231
+ return loop;
232
+ }
233
+ return !['12HourClock', 'year'].includes(type);
234
+ },
221
235
  updateColumns: function (values) {
222
236
  var _this = this;
223
237
  var _a = this.data, locale = _a.locale, columnStyles = _a.columnStyles, fontStyles = _a.fontStyles;
@@ -232,7 +246,7 @@ var defaultFormatter = function (type, value) {
232
246
  style: columnStyles === null || columnStyles === void 0 ? void 0 : columnStyles[column.type],
233
247
  fontStyle: fontStyles === null || fontStyles === void 0 ? void 0 : fontStyles[column.type],
234
248
  activeIndex: activeIndex === -1 ? 0 : activeIndex,
235
- loop: !['12HourClock', 'year'].includes(column.type),
249
+ loop: _this.getColumnLoop(column.type),
236
250
  };
237
251
  });
238
252
  return this.setData({ columns: results });
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  var icons_1 = require("./icons");
7
7
  var component_1 = require("../common/component");
8
8
  var toast_1 = require("./toast");
9
- var utils_1 = require("../common/utils");
10
9
  var appLog_1 = __importDefault(require("../common/appLog"));
11
10
  (0, component_1.SmartComponent)({
12
11
  props: {
@@ -48,17 +47,17 @@ var appLog_1 = __importDefault(require("../common/appLog"));
48
47
  mounted: function () {
49
48
  if (!this.id)
50
49
  return;
51
- if (toast_1.contextRef.value["#".concat(this.id)]) {
50
+ var repeated = (0, toast_1.registerToastContext)("#".concat(this.id), this);
51
+ if (repeated) {
52
52
  console.error("Toast component #".concat(this.id, " repeated!"));
53
53
  appLog_1.default.info("Toast component #".concat(this.id, " repeated!"));
54
54
  }
55
- toast_1.contextRef.value["#".concat(this.id)] = (0, utils_1.getCurrentPage)();
56
55
  appLog_1.default.info("Toast #".concat(this.id, " mounted"));
57
56
  },
58
57
  destroyed: function () {
59
58
  if (!this.id)
60
59
  return;
61
- toast_1.contextRef.value["#".concat(this.id)] = null;
60
+ (0, toast_1.unregisterToastContext)("#".concat(this.id), this);
62
61
  appLog_1.default.info("Toast #".concat(this.id, " destroyed"));
63
62
  },
64
63
  methods: {
@@ -21,9 +21,27 @@ interface ToastOptions {
21
21
  width?: number;
22
22
  onClose?: () => void;
23
23
  }
24
+ interface ToastContextEntry {
25
+ page: ToastContext;
26
+ instance: WechatMiniprogram.Component.TrivialInstance | null;
27
+ }
28
+ /**
29
+ * selector -> 该 selector 下所有存活的 toast 实例。
30
+ * 一个 selector(id)可以同时挂在多个页面(多 webview)上,因此用数组记录,
31
+ * 而不再是「一个 selector 只对应一个 page」的单槽结构。
32
+ * 为向后兼容,仍容忍外部直接赋值为单个 page(非数组)的旧写法。
33
+ */
24
34
  export declare const contextRef: {
25
- value: Record<string, ToastContext | null>;
35
+ value: Record<string, ToastContext | ToastContextEntry[] | null>;
26
36
  };
37
+ /**
38
+ * 组件挂载时登记自身。同一 selector 在「同一个页面」上重复挂载才算冲突,
39
+ * 不同页面共用同一 selector 是被允许的(这正是「每页挂一个默认 toast」的场景)。
40
+ * @returns 是否与同页已有实例冲突(供组件层打印 repeated 提示)。
41
+ */
42
+ export declare function registerToastContext(selector: string, instance: WechatMiniprogram.Component.TrivialInstance): boolean;
43
+ /** 组件卸载时精确移除自身,不影响其它页面上同 selector 的实例。 */
44
+ export declare function unregisterToastContext(selector: string, instance: WechatMiniprogram.Component.TrivialInstance): void;
27
45
  declare function Toast(toastOptions: ToastOptions | ToastMessage): WechatMiniprogram.Component.TrivialInstance | undefined;
28
46
  declare namespace Toast {
29
47
  var loading: (options: ToastMessage | ToastOptions) => WechatMiniprogram.Component.TrivialInstance | undefined;
@@ -14,7 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.contextRef = void 0;
17
+ exports.unregisterToastContext = exports.registerToastContext = exports.contextRef = void 0;
18
18
  var appLog_1 = __importDefault(require("../common/appLog"));
19
19
  var utils_1 = require("../common/utils");
20
20
  var validator_1 = require("../common/validator");
@@ -47,16 +47,70 @@ var currentOptions = __assign({}, defaultOptions);
47
47
  function parseOptions(message) {
48
48
  return (0, validator_1.isObj)(message) ? message : { message: message };
49
49
  }
50
+ /**
51
+ * selector -> 该 selector 下所有存活的 toast 实例。
52
+ * 一个 selector(id)可以同时挂在多个页面(多 webview)上,因此用数组记录,
53
+ * 而不再是「一个 selector 只对应一个 page」的单槽结构。
54
+ * 为向后兼容,仍容忍外部直接赋值为单个 page(非数组)的旧写法。
55
+ */
50
56
  exports.contextRef = {
51
57
  value: {},
52
58
  };
59
+ /**
60
+ * 组件挂载时登记自身。同一 selector 在「同一个页面」上重复挂载才算冲突,
61
+ * 不同页面共用同一 selector 是被允许的(这正是「每页挂一个默认 toast」的场景)。
62
+ * @returns 是否与同页已有实例冲突(供组件层打印 repeated 提示)。
63
+ */
64
+ function registerToastContext(selector, instance) {
65
+ var existing = exports.contextRef.value[selector];
66
+ var list = Array.isArray(existing) ? existing : [];
67
+ exports.contextRef.value[selector] = list;
68
+ var page = (0, utils_1.getCurrentPage)();
69
+ var samePage = list.find(function (entry) { return entry.page === page; });
70
+ if (samePage) {
71
+ samePage.instance = instance;
72
+ return true;
73
+ }
74
+ list.push({ page: page, instance: instance });
75
+ return false;
76
+ }
77
+ exports.registerToastContext = registerToastContext;
78
+ /** 组件卸载时精确移除自身,不影响其它页面上同 selector 的实例。 */
79
+ function unregisterToastContext(selector, instance) {
80
+ var existing = exports.contextRef.value[selector];
81
+ if (!Array.isArray(existing)) {
82
+ return;
83
+ }
84
+ exports.contextRef.value[selector] = existing.filter(function (entry) { return entry.instance !== instance; });
85
+ }
86
+ exports.unregisterToastContext = unregisterToastContext;
87
+ /**
88
+ * 解析目标实例所在的上下文:优先命中「当前页面」上的实例,
89
+ * 其次退回最后挂载的实例(栈顶,覆盖跨页/非当前页场景),
90
+ * 同时兼容外部直接赋值单个 page 的旧写法。
91
+ */
92
+ function resolveContextEntry(selector) {
93
+ var existing = exports.contextRef.value[selector];
94
+ if (!existing) {
95
+ return null;
96
+ }
97
+ if (!Array.isArray(existing)) {
98
+ // 兼容旧结构:value[selector] 直接是一个 page
99
+ return { page: existing, instance: null };
100
+ }
101
+ if (!existing.length) {
102
+ return null;
103
+ }
104
+ var currentPage = (0, utils_1.getCurrentPage)();
105
+ return existing.find(function (entry) { return entry.page === currentPage; }) || existing[existing.length - 1];
106
+ }
53
107
  function Toast(toastOptions) {
54
108
  var options = __assign(__assign({}, currentOptions), parseOptions(toastOptions));
55
109
  appLog_1.default.info('start open Toast');
56
- var context = (typeof options.context === 'function' ? options.context() : options.context) ||
57
- exports.contextRef.value[options.selector] ||
58
- (0, utils_1.getCurrentPage)();
59
- var toast = context.selectComponent(options.selector);
110
+ var explicitContext = typeof options.context === 'function' ? options.context() : options.context;
111
+ var entry = explicitContext ? null : resolveContextEntry(options.selector);
112
+ var context = explicitContext || (entry === null || entry === void 0 ? void 0 : entry.page) || (0, utils_1.getCurrentPage)();
113
+ var toast = context.selectComponent(options.selector) || (entry === null || entry === void 0 ? void 0 : entry.instance) || null;
60
114
  appLog_1.default.info("toast selector: ".concat(options.selector));
61
115
  appLog_1.default.info("toast component ".concat(toast ? 'success' : 'fail'));
62
116
  if (!toast) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuya-miniapp/smart-ui",
3
- "version": "2.13.3",
3
+ "version": "2.13.4-beta-1",
4
4
  "author": "MiniApp Team",
5
5
  "license": "MIT",
6
6
  "miniprogram": "lib",