vanilla-jui 1.3.8 → 1.3.10

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 (77) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.umd.js +1 -1
  3. package/dist/style.css +1 -1
  4. package/package.json +3 -2
  5. package/src/components/accordion.js +373 -0
  6. package/src/components/components.js +0 -0
  7. package/src/components/drop.js +437 -0
  8. package/src/components/flow.js +1133 -0
  9. package/src/components/form.js +585 -0
  10. package/src/components/icons.js +83 -0
  11. package/src/components/menu.js +422 -0
  12. package/src/components/modal.js +1174 -0
  13. package/src/components/offcanvas.js +332 -0
  14. package/src/components/pagination.js +536 -0
  15. package/src/components/parabola.js +295 -0
  16. package/src/components/sticky.js +271 -0
  17. package/src/components/swiper.js +1123 -0
  18. package/src/components/tabs.js +746 -0
  19. package/src/components/theme.js +353 -0
  20. package/src/components/toast.js +329 -0
  21. package/src/components/toc.js +226 -0
  22. package/src/components/tooltip.js +86 -0
  23. package/src/components/validator.js +586 -0
  24. package/src/core/Component.js +248 -0
  25. package/src/css/accordion.css +72 -0
  26. package/src/css/animation.css +177 -0
  27. package/src/css/avatar.css +62 -0
  28. package/src/css/badge.css +66 -0
  29. package/src/css/breadcrumb.css +106 -0
  30. package/src/css/button.css +296 -0
  31. package/src/css/card.css +151 -0
  32. package/src/css/color.css +1176 -0
  33. package/src/css/color_p3.css +1184 -0
  34. package/src/css/components.css +26 -0
  35. package/src/css/content.css +716 -0
  36. package/src/css/divider.css +9 -0
  37. package/src/css/drop.css +28 -0
  38. package/src/css/flow.css +86 -0
  39. package/src/css/font.css +23 -0
  40. package/src/css/form.css +919 -0
  41. package/src/css/gradient.css +211 -0
  42. package/src/css/icon.css +63 -0
  43. package/src/css/index.css +12 -0
  44. package/src/css/layout.css +782 -0
  45. package/src/css/loading.css +38 -0
  46. package/src/css/menu.css +564 -0
  47. package/src/css/modal.css +111 -0
  48. package/src/css/offcanvas.css +104 -0
  49. package/src/css/pagination.css +25 -0
  50. package/src/css/popup.css +69 -0
  51. package/src/css/skeleton.css +76 -0
  52. package/src/css/status.css +56 -0
  53. package/src/css/swiper.css +160 -0
  54. package/src/css/table.css +52 -0
  55. package/src/css/tabs.css +95 -0
  56. package/src/css/tag.css +98 -0
  57. package/src/css/theme-palette.css +59 -0
  58. package/src/css/tip.css +84 -0
  59. package/src/css/toast.css +154 -0
  60. package/src/css/toast.min.css +1 -0
  61. package/src/css/toc.css +38 -0
  62. package/src/css/tokens.css +1324 -0
  63. package/src/css/toolbar.css +115 -0
  64. package/src/css/tooltip.css +19 -0
  65. package/src/icons/index.js +30 -0
  66. package/src/index.js +45 -0
  67. package/src/locales/index.js +45 -0
  68. package/src/locales/index.min.js +1 -0
  69. package/src/utilities/browser.js +111 -0
  70. package/src/utilities/cache.js +159 -0
  71. package/src/utilities/core.js +502 -0
  72. package/src/utilities/debug.js +103 -0
  73. package/src/utilities/dom.js +313 -0
  74. package/src/utilities/events.js +69 -0
  75. package/src/utilities/http.js +30 -0
  76. package/src/utilities/legacy-dom.js +179 -0
  77. package/src/utilities/storage.js +46 -0
@@ -0,0 +1,586 @@
1
+ import { jsx } from 'vanilla-signal';
2
+
3
+ import { resolveProps, validateParam } from '../utilities/core.js';
4
+ import { all, q, requireRenderDOM, resolveElement } from '../utilities/dom.js';
5
+ import { createEventManager } from '../utilities/events.js';
6
+
7
+ const VALIDATOR_OPTIONS_SCHEMA = {
8
+ rules: { default: {}, type: 'object' },
9
+ messages: { default: {}, type: 'object' },
10
+ onSubmit: { default: null, types: ['function', 'null'] },
11
+ };
12
+
13
+ /**
14
+ * @typedef {object} ValidatorRule
15
+ * @property {boolean} [required] 是否必填。
16
+ * @property {number} [minLength] 最短字符数。
17
+ * @property {number} [maxLength] 最长字符数。
18
+ * @property {string} [equalTo] 需要与之相等的表单字段 name。
19
+ * @property {boolean} [email] 是否按邮箱格式校验。
20
+ * @property {boolean} [checked] 复选框是否必须处于指定状态。
21
+ * @property {boolean} [selected] 下拉选择框是否必须选择非空值。
22
+ * @property {boolean} [multiple] 多选下拉框是否必须至少选择一项。
23
+ * @property {number} [min] 多选下拉框最少选择项数。
24
+ * @property {number} [max] 多选下拉框最多选择项数。
25
+ * @property {boolean} [noSpace] 是否禁止空格。
26
+ * @property {boolean} [noChinese] 是否禁止中文。
27
+ * @property {boolean} [noSpecial] 是否禁止特殊字符。
28
+ * @property {string|RegExp} [pattern] 自定义正则。
29
+ * @property {boolean} [file] 文件是否必选。
30
+ * @property {number} [minSize] 文件最小字节数。
31
+ * @property {number} [maxSize] 文件最大字节数。
32
+ * @property {string} [accept] 允许的文件类型,逗号分隔(如 ".jpg,.png" 或 "image/*")。
33
+ * @property {Function} [validate] 自定义验证函数,接收 element,返回 boolean(通过/失败)或 string(错误信息)。
34
+ */
35
+
36
+ /**
37
+ * @typedef {object} ValidatorOptions
38
+ * @property {Record<string, ValidatorRule>} [rules] 以字段 name 为 key 的校验规则。
39
+ * @property {Record<string, Record<string, string>>} [messages] 以字段 name 和规则名为 key 的错误文案。
40
+ * @property {Function|null} [onSubmit] 全部校验通过后的回调。
41
+ */
42
+
43
+ /**
44
+ * 表单校验组件。
45
+ *
46
+ * 支持绑定表单 submit/reset 事件,也可以手动调用 validate/reset。
47
+ */
48
+ export class Validator {
49
+ /**
50
+ * 创建表单校验实例。
51
+ * @param {HTMLFormElement|Node|string|Array} element 表单元素、选择器或 JSX/h 返回节点。
52
+ * @param {ValidatorOptions} [options={}] 校验配置。
53
+ * @param {boolean} [bindEvents=false] 是否自动绑定 submit/reset 事件。
54
+ */
55
+ constructor(element, options = {}, bindEvents = false) {
56
+ requireRenderDOM('Validator');
57
+
58
+ this.options = resolveProps(
59
+ options,
60
+ VALIDATOR_OPTIONS_SCHEMA,
61
+ 'Validator.options'
62
+ );
63
+ this._validateOptions(element, this.options, bindEvents);
64
+ this._init(element);
65
+
66
+ if (bindEvents) {
67
+ this._bindEvents();
68
+ }
69
+ }
70
+
71
+ /**
72
+ * 校验构造参数。
73
+ * @private
74
+ * @param {HTMLFormElement|Node|string|Array} element 表单元素、选择器或 JSX/h 返回节点。
75
+ * @param {ValidatorOptions} options 已归一化配置。
76
+ * @param {boolean} bindEvents 是否绑定事件。
77
+ * @returns {void}
78
+ */
79
+ _validateOptions(element, options, bindEvents) {
80
+ validateParam('bindEvents', bindEvents, 'boolean', 'Validator');
81
+ }
82
+
83
+ /**
84
+ * 初始化实例状态。
85
+ * @private
86
+ * @param {HTMLFormElement|Node|string|Array} element 表单元素、选择器或 JSX/h 返回节点。
87
+ * @returns {void}
88
+ */
89
+ _init(element) {
90
+ this.root = resolveElement(element, 'Validator.element');
91
+
92
+ this.runtime = {
93
+ valid: true,
94
+ message: '',
95
+ destroyed: false,
96
+ };
97
+ this.cleanup = {
98
+ events: createEventManager(),
99
+ };
100
+ }
101
+
102
+ /**
103
+ * 绑定表单提交与重置事件。
104
+ * @private
105
+ * @returns {void}
106
+ */
107
+ _bindEvents() {
108
+ this._unbindEvents();
109
+
110
+ this.cleanup.events.on('submit', this.root, 'submit', (e) => {
111
+ e.preventDefault();
112
+ this.validate();
113
+ });
114
+ this.cleanup.events.on('reset', this.root, 'reset', () => {
115
+ this.reset();
116
+ });
117
+ }
118
+
119
+ /**
120
+ * 解绑表单事件。
121
+ * @private
122
+ * @returns {void}
123
+ */
124
+ _unbindEvents() {
125
+ this.cleanup?.events.clear();
126
+ }
127
+
128
+ /**
129
+ * 执行表单校验。
130
+ * @returns {boolean} 表单是否通过校验。
131
+ */
132
+ validate() {
133
+ this.runtime.valid = true;
134
+ this.runtime.message = '';
135
+ for (const element of this.root.elements) {
136
+ const nameAttr = element.name;
137
+ if (this.options.rules[nameAttr]) {
138
+ this.runtime.valid = this._validateRule(element, nameAttr);
139
+ if (!this.runtime.valid) break;
140
+ }
141
+ }
142
+ if (this.runtime.valid && this.options.onSubmit) {
143
+ this.options.onSubmit();
144
+ }
145
+ return this.runtime.valid;
146
+ }
147
+
148
+ /**
149
+ * 按字段规则校验单个表单控件。
150
+ * @private
151
+ * @param {HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement} element 表单控件。
152
+ * @param {string} nameAttr 字段 name。
153
+ * @returns {boolean}
154
+ */
155
+ _validateRule(element, nameAttr) {
156
+ const rules = this.options.rules[nameAttr];
157
+ // 检查是否自定义了浏览器默认的验证规则
158
+ if (
159
+ element.hasAttribute('required') ||
160
+ element.hasAttribute('minlength') ||
161
+ element.hasAttribute('maxlength') ||
162
+ element.hasAttribute('pattern') ||
163
+ element.hasAttribute('min') ||
164
+ element.hasAttribute('max') ||
165
+ element.hasAttribute('step')
166
+ ) {
167
+ return this.runtime.valid;
168
+ }
169
+ // 如果没有,则使用下面规则进行验证
170
+ for (const rule in rules) {
171
+ switch (rule) {
172
+ // 验证项1:必填
173
+ case 'required':
174
+ this.runtime.valid = this._validateRequired(element, rules[rule]);
175
+ break;
176
+ // 验证项2:最短长度
177
+ case 'minLength':
178
+ this.runtime.valid = this._validateMinLength(element, rules[rule]);
179
+ break;
180
+ // 验证项3:最长长度
181
+ case 'maxLength':
182
+ this.runtime.valid = this._validateMaxLength(element, rules[rule]);
183
+ break;
184
+ // 验证项4:密码是否一致
185
+ case 'equalTo':
186
+ this.runtime.valid = this._validateEqualTo(element, rules[rule]);
187
+ break;
188
+ // 验证项5:邮箱合法性
189
+ case 'email':
190
+ this.runtime.valid = this._validateEmail(element);
191
+ break;
192
+ // 验证项6:复选框是否选中
193
+ case 'checked':
194
+ this.runtime.valid = this._validateCheck(element, rules[rule]);
195
+ break;
196
+ // 验证项7:下拉选择框是否已选择非空值
197
+ case 'selected':
198
+ this.runtime.valid = this._validateSelected(element, rules[rule]);
199
+ break;
200
+ // 验证项7b:多选下拉框是否至少选择了一项
201
+ case 'multiple':
202
+ this.runtime.valid = this._validateMultiple(element, rules[rule]);
203
+ break;
204
+ // 验证项7c:多选下拉框最少选择项数
205
+ case 'min':
206
+ this.runtime.valid = this._validateSelectMin(element, rules[rule]);
207
+ break;
208
+ // 验证项7d:多选下拉框最多选择项数
209
+ case 'max':
210
+ this.runtime.valid = this._validateSelectMax(element, rules[rule]);
211
+ break;
212
+ /**
213
+ * 验证项8:是否包含空格
214
+ * @since 1.0.0
215
+ */
216
+ case 'noSpace':
217
+ this.runtime.valid = this._validateNoSpace(element, rules[rule]);
218
+ break;
219
+ /**
220
+ * 验证项9: 不支持中文
221
+ * @since 1.0.0
222
+ */
223
+ case 'noChinese':
224
+ this.runtime.valid = !/[\u4e00-\u9fa5]/.test(element.value);
225
+ break;
226
+ /**
227
+ * 验证项10: 不支持特殊字符
228
+ * @since 1.0.0
229
+ */
230
+ case 'noSpecial':
231
+ this.runtime.valid = !/[@#$%^&*]+/g.test(element.value);
232
+ break;
233
+ /**
234
+ * 验证项11: 自定义正则表达式
235
+ * @since 1.0.0
236
+ */
237
+ case 'pattern':
238
+ this.runtime.valid = new RegExp(rules[rule]).test(element.value);
239
+ break;
240
+ // 验证项12:文件必选
241
+ case 'file':
242
+ this.runtime.valid = this._validateFile(element, rules[rule]);
243
+ break;
244
+ // 验证项13:文件最小大小
245
+ case 'minSize':
246
+ this.runtime.valid = this._validateMinSize(element, rules[rule]);
247
+ break;
248
+ // 验证项14:文件最大大小
249
+ case 'maxSize':
250
+ this.runtime.valid = this._validateMaxSize(element, rules[rule]);
251
+ break;
252
+ // 验证项16:文件类型
253
+ case 'accept':
254
+ this.runtime.valid = this._validateAccept(element, rules[rule]);
255
+ break;
256
+ // 验证项17:自定义验证函数
257
+ case 'validate':
258
+ this.runtime.valid = this._validateCustom(element, rules[rule]);
259
+ break;
260
+ }
261
+ if (!this.runtime.valid) {
262
+ /**
263
+ * messages配置规则,保持与rules配置规则一致
264
+ * @since 1.0.0
265
+ */
266
+ this._errorMsg(element, nameAttr, rule);
267
+ break;
268
+ } else {
269
+ this._success(element);
270
+ }
271
+ }
272
+ return this.runtime.valid;
273
+ }
274
+
275
+ /**
276
+ * 校验必填项。
277
+ * @private
278
+ * @param {HTMLInputElement|HTMLTextAreaElement} element 表单控件。
279
+ * @param {boolean} required 是否必填。
280
+ * @returns {boolean}
281
+ */
282
+ _validateRequired(element, required) {
283
+ return element.value.trim().length >= 1 && required === true;
284
+ }
285
+
286
+ /**
287
+ * 校验最短长度。
288
+ * @private
289
+ * @param {HTMLInputElement|HTMLTextAreaElement} element 表单控件。
290
+ * @param {number} minLength 最短长度。
291
+ * @returns {boolean}
292
+ */
293
+ _validateMinLength(element, minLength) {
294
+ return element.value.length >= minLength;
295
+ }
296
+
297
+ /**
298
+ * 校验最长长度。
299
+ * @private
300
+ * @param {HTMLInputElement|HTMLTextAreaElement} element 表单控件。
301
+ * @param {number} maxLength 最长长度。
302
+ * @returns {boolean}
303
+ */
304
+ _validateMaxLength(element, maxLength) {
305
+ return element.value.length <= maxLength;
306
+ }
307
+
308
+ /**
309
+ * 校验邮箱格式。
310
+ * @private
311
+ * @param {HTMLInputElement} element 表单控件。
312
+ * @returns {boolean}
313
+ */
314
+ _validateEmail(element) {
315
+ const emailPattern = /^([\w-.]+@([\w-]+\.)+[\w-]{2,4})?$/;
316
+ return emailPattern.test(element.value);
317
+ }
318
+
319
+ /**
320
+ * 校验当前字段是否等于另一个字段。
321
+ * @private
322
+ * @param {HTMLInputElement} element 当前控件。
323
+ * @param {string} targetName 目标字段 name。
324
+ * @returns {boolean}
325
+ */
326
+ _validateEqualTo(element, targetName) {
327
+ const targetElement = this.root.elements[targetName];
328
+ if (targetElement === undefined || targetElement === null) {
329
+ throw new Error(`Validator: target element "${targetName}" not found.`);
330
+ }
331
+ return element.value === targetElement.value;
332
+ }
333
+
334
+ /**
335
+ * 校验复选框状态。
336
+ * @private
337
+ * @param {HTMLInputElement} element 复选框控件。
338
+ * @param {boolean} checked 期望状态。
339
+ * @returns {boolean}
340
+ */
341
+ _validateCheck(element, checked) {
342
+ if (element.type !== 'checkbox') {
343
+ throw new Error(
344
+ `Validator: element expects a checkbox input, but ${element.type} given.`
345
+ );
346
+ }
347
+ return element.checked === checked;
348
+ }
349
+
350
+ /**
351
+ * 校验下拉选择框是否选择了非空值。
352
+ * @private
353
+ * @param {HTMLSelectElement} element 下拉选择框。
354
+ * @param {boolean} selected 是否必须选择。
355
+ * @returns {boolean}
356
+ */
357
+ _validateSelected(element, selected) {
358
+ if (element.tagName !== 'SELECT') {
359
+ throw new Error(
360
+ `Validator: element expects a select element, but ${element.tagName.toLowerCase()} given.`
361
+ );
362
+ }
363
+ if (selected !== true) return true;
364
+
365
+ const values = Array.from(element.selectedOptions).map((option) =>
366
+ option.value.trim()
367
+ );
368
+ return values.some((value) => value.length > 0);
369
+ }
370
+
371
+ /**
372
+ * 校验多选下拉框是否至少选择了一项。
373
+ * @private
374
+ * @param {HTMLSelectElement} element 多选下拉框。
375
+ * @param {boolean} multiple 是否必须选择至少一项。
376
+ * @returns {boolean}
377
+ */
378
+ _validateMultiple(element, multiple) {
379
+ if (element.tagName !== 'SELECT') {
380
+ throw new Error(
381
+ `Validator: element expects a select element, but ${element.tagName.toLowerCase()} given.`
382
+ );
383
+ }
384
+ if (multiple !== true) return true;
385
+ return element.selectedOptions.length > 0;
386
+ }
387
+
388
+ /**
389
+ * 校验多选下拉框最少选择项数。
390
+ * @private
391
+ * @param {HTMLSelectElement} element 多选下拉框。
392
+ * @param {number} min 最少选择项数。
393
+ * @returns {boolean}
394
+ */
395
+ _validateSelectMin(element, min) {
396
+ if (element.tagName !== 'SELECT') return true;
397
+ return element.selectedOptions.length >= min;
398
+ }
399
+
400
+ /**
401
+ * 校验多选下拉框最多选择项数。
402
+ * @private
403
+ * @param {HTMLSelectElement} element 多选下拉框。
404
+ * @param {number} max 最多选择项数。
405
+ * @returns {boolean}
406
+ */
407
+ _validateSelectMax(element, max) {
408
+ if (element.tagName !== 'SELECT') return true;
409
+ return element.selectedOptions.length <= max;
410
+ }
411
+
412
+ /**
413
+ * 校验是否禁止空格。
414
+ * @private
415
+ * @param {HTMLInputElement|HTMLTextAreaElement} element 表单控件。
416
+ * @param {boolean} noSpace 是否禁止空格。
417
+ * @returns {boolean}
418
+ */
419
+ _validateNoSpace(element, noSpace) {
420
+ return !/\s/.test(element.value) || noSpace !== true;
421
+ }
422
+
423
+ /**
424
+ * 校验文件是否已选择。
425
+ * @private
426
+ * @param {HTMLInputElement} element 文件控件。
427
+ * @param {boolean} required 是否必选。
428
+ * @returns {boolean}
429
+ */
430
+ _validateFile(element, required) {
431
+ if (element.type !== 'file') return true;
432
+ return required === true ? element.files.length > 0 : true;
433
+ }
434
+
435
+ /**
436
+ * 校验文件最小大小。
437
+ * @private
438
+ * @param {HTMLInputElement} element 文件控件。
439
+ * @param {number} minSize 最小字节数。
440
+ * @returns {boolean}
441
+ */
442
+ _validateMinSize(element, minSize) {
443
+ if (element.type !== 'file' || !element.files.length) return true;
444
+ return element.files[0].size >= minSize;
445
+ }
446
+
447
+ /**
448
+ * 校验文件最大大小。
449
+ * @private
450
+ * @param {HTMLInputElement} element 文件控件。
451
+ * @param {number} maxSize 最大字节数。
452
+ * @returns {boolean}
453
+ */
454
+ _validateMaxSize(element, maxSize) {
455
+ if (element.type !== 'file' || !element.files.length) return true;
456
+ return element.files[0].size <= maxSize;
457
+ }
458
+
459
+ /**
460
+ * 校验文件类型。
461
+ * @private
462
+ * @param {HTMLInputElement} element 文件控件。
463
+ * @param {string} accept 允许的 MIME 类型或扩展名(逗号分隔,如 ".jpg,.png" 或 "image/*")。
464
+ * @returns {boolean}
465
+ */
466
+ _validateAccept(element, accept) {
467
+ if (element.type !== 'file' || !element.files.length) return true;
468
+ const file = element.files[0];
469
+ const allowed = accept.split(',').map((s) => s.trim().toLowerCase());
470
+
471
+ return allowed.some((rule) => {
472
+ if (rule.startsWith('.')) {
473
+ return file.name.toLowerCase().endsWith(rule);
474
+ }
475
+ if (rule.endsWith('/*')) {
476
+ return file.type.startsWith(rule.replace('/*', '/'));
477
+ }
478
+ return file.type === rule;
479
+ });
480
+ }
481
+
482
+ /**
483
+ * 执行自定义验证函数。
484
+ * @private
485
+ * @param {HTMLElement} element 表单控件。
486
+ * @param {Function} fn 验证函数,接收 element,返回 boolean 或 string(错误信息)。
487
+ * @returns {boolean}
488
+ */
489
+ _validateCustom(element, fn) {
490
+ if (typeof fn !== 'function') return true;
491
+ const result = fn(element);
492
+ if (typeof result === 'string') {
493
+ return false;
494
+ }
495
+ return !!result;
496
+ }
497
+
498
+ /**
499
+ * 显示错误信息并设置无效状态。
500
+ * @private
501
+ * @param {HTMLElement} element 表单控件。
502
+ * @param {string} nameAttr 字段 name。
503
+ * @param {string} rule 失败的规则名。
504
+ * @returns {void}
505
+ */
506
+ _errorMsg(element, nameAttr, rule) {
507
+ if (element.type !== 'checkbox') {
508
+ element.classList.remove('is-valid');
509
+ element.classList.add('is-invalid');
510
+ }
511
+ const error =
512
+ this.options.messages[nameAttr] && this.options.messages[nameAttr][rule];
513
+ if (error) {
514
+ // 记录上一次报错信息
515
+ this.runtime.message = error;
516
+
517
+ const formControl = element.closest('.form-control');
518
+ let help = formControl ? q('.help-block', formControl) : null;
519
+ if (!help) {
520
+ help = jsx('div', {
521
+ className: 'help-block is-invalid',
522
+ children: error,
523
+ });
524
+ formControl.appendChild(help);
525
+ }
526
+ }
527
+ }
528
+
529
+ /**
530
+ * 清理错误信息并设置通过状态。
531
+ * @private
532
+ * @param {HTMLElement} element 表单控件。
533
+ * @returns {void}
534
+ */
535
+ _success(element) {
536
+ const formControl = element.closest('.form-control');
537
+ const helpBlock = formControl ? q('.help-block', formControl) : null;
538
+ if (helpBlock) {
539
+ formControl.removeChild(helpBlock);
540
+ }
541
+ if (element.type !== 'checkbox') {
542
+ element.classList.remove('is-invalid');
543
+ element.classList.add('is-valid');
544
+ }
545
+ }
546
+
547
+ /**
548
+ * 重置表单与校验状态。
549
+ * @returns {void}
550
+ */
551
+ reset() {
552
+ this.root.reset();
553
+ for (const element of this.root.elements) {
554
+ element.classList.remove('is-valid');
555
+ element.classList.remove('is-invalid');
556
+ }
557
+ const helpBlocks = all('.help-block', this.root);
558
+ for (const help of helpBlocks) {
559
+ help.remove();
560
+ }
561
+ this.runtime.valid = true;
562
+ this.runtime.message = '';
563
+ }
564
+
565
+ /**
566
+ * 销毁当前校验实例。
567
+ * @returns {void}
568
+ */
569
+ destroy() {
570
+ if (this.runtime.destroyed) return;
571
+ this.runtime.destroyed = true;
572
+
573
+ this._unbindEvents();
574
+ this.reset();
575
+ this.root = null;
576
+ this.options = null;
577
+ this.runtime.valid = null;
578
+ this.runtime.message = null;
579
+ this.cleanup?.events.clear();
580
+ this.cleanup = null;
581
+ }
582
+ }
583
+
584
+ export function createValidator(element, options = {}, bindEvents = false) {
585
+ return new Validator(element, options, bindEvents);
586
+ }