litmoss-form 0.0.0-stage → 5.1.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.
Potentially problematic release.
This version of litmoss-form might be problematic. Click here for more details.
- package/LICENSE +201 -0
- package/README.md +221 -3
- package/dist/components/form/context.d.ts +15 -0
- package/dist/components/form/entries.d.ts +28 -0
- package/dist/components/form/form.d.ts +10 -0
- package/dist/components/form/form.types.d.ts +286 -0
- package/dist/components/form/formBase.d.ts +1 -0
- package/dist/components/form/formUtil.d.ts +51 -0
- package/dist/components/form/useForm.d.ts +12 -0
- package/dist/components/form/useFormItemValue.d.ts +16 -0
- package/dist/components/form/useHelperInfo.d.ts +14 -0
- package/dist/components/inject.d.ts +11 -0
- package/dist/components/mounter/index.d.ts +2 -0
- package/dist/components/mounter/mounter.d.ts +2 -0
- package/dist/components/mounter/mounter.types.d.ts +4 -0
- package/dist/components/util.d.ts +5 -0
- package/dist/components/validation/index.d.ts +1 -0
- package/dist/components/validation/validate.d.ts +8 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +271 -0
- package/package.json +101 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,221 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# litmoss-form
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
[](https://coveralls.io/github/liuxian496/litmoss)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
litmoss-form 是一个基于 React 的轻量表单库,提供跨组件的表单数据管理能力。核心理念是将**表单数据的状态管理**与**表单UI控制**解耦。
|
|
9
|
+
|
|
10
|
+
- **跨组件收集表单数据**:表单项通过 `path` 自动注册到 `Form` 上下文,无需逐层传递 props
|
|
11
|
+
- **一次受控**:只要**表单组件**封装时实现一次受控逻辑,页面开发即可免去重复的受控代码编写,使开发者专注于业务逻辑的编排。
|
|
12
|
+
- **命令式 API**:通过 `useForm`暴露`getValues`、`setValues`、`validate` 等方法
|
|
13
|
+
- **可扩展的校验体系**:通过 `initLittenForm` 全局注入通用校验断言和默认提示文案,同时支持自定义校验
|
|
14
|
+
- **两种校验模式**:全部校验(`all`)与分步校验(`step`)
|
|
15
|
+
- **校验失败时**,自动聚焦到第一个出错的表单项
|
|
16
|
+
- **UI无关**:不依赖与任何UI库,任何UI库都能通过封装**表单组件**继承
|
|
17
|
+
|
|
18
|
+
## 在线文档
|
|
19
|
+
|
|
20
|
+
[github pages:https://liuxian496.github.io/litmoss-form/](https://liuxian496.github.io/litmoss-form/)
|
|
21
|
+
|
|
22
|
+
[chromatic:https://6ac70cb7870df8e554fb05ff-jcxiqbiehx.chromatic.com/](https://6ac70cb7870df8e554fb05ff-jcxiqbiehx.chromatic.com/)
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i litmoss-form
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
以下依赖为 `peerDependencies`,打包时不包含,需要在使用的项目中自行安装:
|
|
31
|
+
|
|
32
|
+
| 依赖 | 版本 |
|
|
33
|
+
|----------------------|------------|
|
|
34
|
+
| `react` | `^18.2.0` |
|
|
35
|
+
| `react-dom` | `^18.2.0` |
|
|
36
|
+
| `classnames` | `^2.5.1` |
|
|
37
|
+
| `cyndi` | `^1.0.0` |
|
|
38
|
+
| `exception-boundary` | `^2.0.1` |
|
|
39
|
+
| `litmoss-hooks` | `^3.0.0` |
|
|
40
|
+
| `lodash` | `^4.17.21` |
|
|
41
|
+
|
|
42
|
+
## 快速开始
|
|
43
|
+
|
|
44
|
+
### 1. 在工程入口全局初始化
|
|
45
|
+
|
|
46
|
+
在应用启动时调用一次 `initLittenForm`,注入通用校验断言与默认提示文案:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { BaseValidationType, initLittenForm } from 'litmoss-form';
|
|
50
|
+
|
|
51
|
+
export const ValidationType = {
|
|
52
|
+
...BaseValidationType,
|
|
53
|
+
StringRequired: 'stringRequired',
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
export type ValidationType =
|
|
57
|
+
(typeof ValidationType)[keyof typeof ValidationType];
|
|
58
|
+
|
|
59
|
+
initLittenForm<ValidationType, string, unknown>({
|
|
60
|
+
commonValidationAssert: (value, type) => {
|
|
61
|
+
switch (type) {
|
|
62
|
+
case ValidationType.StringRequired:
|
|
63
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
64
|
+
default:
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
getDefaultHelperInfo: (type) => {
|
|
69
|
+
switch (type) {
|
|
70
|
+
case ValidationType.StringRequired:
|
|
71
|
+
return '该项为必填项';
|
|
72
|
+
default:
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. 使用表单
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { Form, useForm } from 'litmoss-form';
|
|
83
|
+
|
|
84
|
+
function Demo() {
|
|
85
|
+
const [formRef, form] = useForm();
|
|
86
|
+
|
|
87
|
+
function handleSubmit() {
|
|
88
|
+
const errors = form?.validate({ focusOnError: true }) ?? [];
|
|
89
|
+
if (errors.length > 0) return;
|
|
90
|
+
|
|
91
|
+
console.log(form?.getValues());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<Form ref={formRef}>
|
|
97
|
+
{/* 使用 useFormItemValue 封装的表单项组件 */}
|
|
98
|
+
<MyTextField
|
|
99
|
+
path="name"
|
|
100
|
+
validations={[{ type: ValidationType.StringRequired }]}
|
|
101
|
+
/>
|
|
102
|
+
</Form>
|
|
103
|
+
<button onClick={handleSubmit}>提交</button>
|
|
104
|
+
</>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API 概览
|
|
110
|
+
|
|
111
|
+
### 组件
|
|
112
|
+
|
|
113
|
+
| 组件 | 说明 |
|
|
114
|
+
|-----------|--------------------------------------------------------------------|
|
|
115
|
+
| `Form` | 表单容器,通过 `ref` 暴露 `FormRef`。`validationMode` 默认为 `all` |
|
|
116
|
+
| `Mounter` | 挂载完成后触发一次 `onDidMount` 回调,可用于级联表单等场景的初始化 |
|
|
117
|
+
|
|
118
|
+
### Hooks
|
|
119
|
+
|
|
120
|
+
| Hook | 说明 |
|
|
121
|
+
|--------------------|-----------------------------------------------------------------|
|
|
122
|
+
| `useForm` | 返回 `[ref, form]`,`ref` 传给 `Form`,`form` 为 `FormRef` 实例 |
|
|
123
|
+
| `useFormItemValue` | 管理表单项的值,并向 `Form` 上下文注册 / 卸载该表单项 |
|
|
124
|
+
| `useHelperInfo` | 根据 `validations` 执行校验,管理校验提示文本 |
|
|
125
|
+
|
|
126
|
+
### FormRef
|
|
127
|
+
|
|
128
|
+
| 方法 | 说明 |
|
|
129
|
+
|---------------------------------|---------------------------------------------------|
|
|
130
|
+
| `getValues()` | 获取表单当前全部数据 |
|
|
131
|
+
| `setValues(args)` | 批量设置表单数据,`args` 形如 `[{ path, value }]` |
|
|
132
|
+
| `getValueByPath(path)` | 获取指定表单项的值 |
|
|
133
|
+
| `setValueByPath(path, value)` | 设置指定表单项的值 |
|
|
134
|
+
| `setHelpTextByPath(path, text)` | 设置指定表单项的提示信息 |
|
|
135
|
+
| `validate({ focusOnError? })` | 校验表单,返回失败项的 `{ path, helpInfo }` 数组 |
|
|
136
|
+
| `validateField(path)` | 通过属性路径验证表单项的方法,并返回验证结果 |
|
|
137
|
+
| `focusFieldByPath(path)` | 聚焦指定表单项 |
|
|
138
|
+
|
|
139
|
+
### 常量
|
|
140
|
+
|
|
141
|
+
| 常量 | 说明 |
|
|
142
|
+
|--------------------------------|--------------------------------------------------|
|
|
143
|
+
| `ValidationMode.all` | 全部校验,收集所有失败项 |
|
|
144
|
+
| `ValidationMode.step` | 分步校验,按注册顺序校验,遇到第一个失败项即停止 |
|
|
145
|
+
| `BaseValidationType.Customize` | 自定义校验类型,需配合 `validationAssert` 使用 |
|
|
146
|
+
|
|
147
|
+
### 初始化
|
|
148
|
+
|
|
149
|
+
| 方法 | 说明 |
|
|
150
|
+
|----------------------------|--------------------------------------------------------------------------------------|
|
|
151
|
+
| `initLittenForm(injector)` | 全局注入 `commonValidationAssert`、`getDefaultHelperInfo`、`getInitialValue`(可选) |
|
|
152
|
+
|
|
153
|
+
## 自定义表单组件
|
|
154
|
+
|
|
155
|
+
组合使用 `useHelperInfo` 与 `useFormItemValue` 即可封装一个受控、支持校验提示的表单组件:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { useRef } from 'react';
|
|
159
|
+
import { useFormItemValue, useHelperInfo } from 'litmoss-form';
|
|
160
|
+
import type { FormItemProps } from 'litmoss-form';
|
|
161
|
+
|
|
162
|
+
export const MyTextField = ({
|
|
163
|
+
path,
|
|
164
|
+
initialValue,
|
|
165
|
+
validations = [],
|
|
166
|
+
}: FormItemProps<string, ValidationType>) => {
|
|
167
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
168
|
+
|
|
169
|
+
const [helperText, verifyFormItem, setHelperText] = useHelperInfo<
|
|
170
|
+
string,
|
|
171
|
+
ValidationType
|
|
172
|
+
>(validations);
|
|
173
|
+
|
|
174
|
+
const [value, setValue] = useFormItemValue<string>(
|
|
175
|
+
path,
|
|
176
|
+
initialValue,
|
|
177
|
+
verifyFormItem,
|
|
178
|
+
setHelperText,
|
|
179
|
+
inputRef
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
<input
|
|
185
|
+
ref={inputRef}
|
|
186
|
+
value={value ?? ''}
|
|
187
|
+
onChange={(e) => setValue(e.target.value)}
|
|
188
|
+
onBlur={() => verifyFormItem(value)}
|
|
189
|
+
/>
|
|
190
|
+
<div>{helperText}</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
更多细节见 [自定义表单组件封装指南](./docs/custom-form-component-guide.md)。
|
|
197
|
+
|
|
198
|
+
## 文档
|
|
199
|
+
|
|
200
|
+
- [接入文档](./docs/integration.md)
|
|
201
|
+
- [自定义表单组件封装指南](./docs/custom-form-component-guide.md)
|
|
202
|
+
|
|
203
|
+
## 本地开发
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
npm i # 安装依赖
|
|
207
|
+
npm run storybook # 启动 Storybook(端口 6006)
|
|
208
|
+
npm test # 运行测试
|
|
209
|
+
npm run test-storybook -- --coverage # 运行 Storybook 测试并生成覆盖率
|
|
210
|
+
npm run lint # 代码检查
|
|
211
|
+
npm run build # 构建
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
[Apache-2.0](./LICENSE)
|
|
217
|
+
|
|
218
|
+
## 如果你想请我喝一咖啡(Buy Me a Coffee)
|
|
219
|
+
|
|
220
|
+
<img src=".\\public\\wechat.jpg" height="360">
|
|
221
|
+
<img src=".\\public\\alipay.jpg" height="360">
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FormContextProps, FormRegister } from './form.types';
|
|
2
|
+
/**
|
|
3
|
+
* 用于管理表单状态和操作的 React Context
|
|
4
|
+
* 通过 `FormContextProps` 提供表单相关的属性和方法
|
|
5
|
+
* @see FormContextProps
|
|
6
|
+
*/
|
|
7
|
+
export declare const FormContext: import('react').Context<FormContextProps | undefined>;
|
|
8
|
+
/**
|
|
9
|
+
* 初始化表单上下文
|
|
10
|
+
*
|
|
11
|
+
* @param formRegister - 用于存储表单项注册信息的对象,以 valuePath 作为键
|
|
12
|
+
* @param setErrorMsg - 用于更新错误信息的 React 状态设置函数
|
|
13
|
+
* @returns {FormContextProps} 返回包含注册、检查和卸载表单项方法的表单上下文
|
|
14
|
+
*/
|
|
15
|
+
export declare function initContext(formRegister: FormRegister, setErrorMsg: React.Dispatch<React.SetStateAction<string | undefined>>): FormContextProps;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 当在表单中无法找到对应的值路径时的警告信息
|
|
3
|
+
* @param path 表单项的值在表单数据中的属性路径
|
|
4
|
+
* @returns 警告信息字符串
|
|
5
|
+
*/
|
|
6
|
+
export declare function valuePathNotFoundEntry(path: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* 当setValues函数的第一个参数不是FormArgs数组时的警告信息
|
|
9
|
+
* @returns 警告信息字符串
|
|
10
|
+
*/
|
|
11
|
+
export declare function setValuesFirstParamNotArray(): string;
|
|
12
|
+
/**
|
|
13
|
+
* 当在表单项注册器中找不到set方法时的警告信息
|
|
14
|
+
* @returns 警告信息字符串
|
|
15
|
+
*/
|
|
16
|
+
export declare function setMethodNotFound(): string;
|
|
17
|
+
/**
|
|
18
|
+
* 当在表单项注册器中找不到commonValidationAssert方法时的警告信息
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
export declare function commonValidationAssertNotFound(): string;
|
|
22
|
+
/**
|
|
23
|
+
* 当尝试聚焦一个不存在的表单项时的警告信息
|
|
24
|
+
* @param path 表单项的唯一路径(path)
|
|
25
|
+
* @returns 警告信息字符串
|
|
26
|
+
*/
|
|
27
|
+
export declare function focusNotFound(path: string): string;
|
|
28
|
+
export declare function validationAssertNotFoundEntry(path: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FormProps } from './form.types';
|
|
2
|
+
/**
|
|
3
|
+
* Form 是一个 React 组件,提供基于React Context的表单管理系统
|
|
4
|
+
* 通过 `ref` 暴露命令式方法,用于操作表单状态,如清空、获取、设置和校验表单值。
|
|
5
|
+
*
|
|
6
|
+
* @param {FormProps} props - 表单组件的属性
|
|
7
|
+
* @param {React.Ref<FormRef>} ref - 用于访问命令式表单方法的 ref
|
|
8
|
+
* @returns {JSX.Element} 渲染后的表单组件
|
|
9
|
+
*/
|
|
10
|
+
export declare const Form: import('react').ForwardRefExoticComponent<FormProps & import('react').RefAttributes<unknown>>;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
|
+
import { UserControlProps } from 'litmoss-hooks/dist/control/userControl/userControl.types';
|
|
3
|
+
/**
|
|
4
|
+
* 表单组件属性
|
|
5
|
+
* @property children - 表单组件的子组件
|
|
6
|
+
* @property {ValidationMode} validationMode - 表单验证模式,支持分步验证或全部验证。默认全部验证(暂时只支持全部验证)
|
|
7
|
+
*/
|
|
8
|
+
export interface FormProps extends UserControlProps {
|
|
9
|
+
/**
|
|
10
|
+
* 表单组件的子组件
|
|
11
|
+
*/
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* 表单验证模式,支持分步验证或全部验证。默认全部验证(暂时只支持全部验证)
|
|
15
|
+
*/
|
|
16
|
+
validationMode?: ValidationMode;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 表单项验证后的提示信息,可以是string, JSX.Element,或者Undefined
|
|
20
|
+
* 通常使用string就可以满足基本的验证提示需求
|
|
21
|
+
*/
|
|
22
|
+
export type FormHelperInfo = string | JSX.Element | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* 表单验证模式常量
|
|
25
|
+
*
|
|
26
|
+
* @property {"all"} all - 全部验证,验证所有表单项并收集所有无效项的提示信息
|
|
27
|
+
* @property {"step"} step - 分步验证,按注册顺序验证表单项,遇到第一个无效项时停止验证并返回提示信息
|
|
28
|
+
*
|
|
29
|
+
* 用于 FormProps.validationMode 属性,或表单相关 API 的验证模式参数。
|
|
30
|
+
*
|
|
31
|
+
* 示例:
|
|
32
|
+
* validationMode={ValidationMode.all}
|
|
33
|
+
* validationMode={ValidationMode.step}
|
|
34
|
+
*/
|
|
35
|
+
export declare const ValidationMode: {
|
|
36
|
+
/**
|
|
37
|
+
* 全部验证,验证所有表单项并收集所有无效项的提示信息
|
|
38
|
+
*/
|
|
39
|
+
readonly all: "all";
|
|
40
|
+
/**
|
|
41
|
+
* 分步验证,按注册顺序验证表单项,遇到第一个无效项时停止验证并返回提示信息
|
|
42
|
+
*/
|
|
43
|
+
readonly step: "step";
|
|
44
|
+
};
|
|
45
|
+
export type ValidationMode = (typeof ValidationMode)[keyof typeof ValidationMode];
|
|
46
|
+
/**
|
|
47
|
+
* 表单数据
|
|
48
|
+
*/
|
|
49
|
+
export interface FormValues {
|
|
50
|
+
[index: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 可聚焦的表单控件接口
|
|
54
|
+
* 任何实现了该接口的表单控件都应提供一个 focus 方法,用于将焦点设置到该控件上
|
|
55
|
+
* 这对于表单验证失败时自动聚焦非常有用
|
|
56
|
+
* @property focus - 将焦点设置到该控件的方法
|
|
57
|
+
*/
|
|
58
|
+
export interface FocusableField {
|
|
59
|
+
focus: () => void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 表单项注册器
|
|
63
|
+
* @property path - 表单项的值路径,值路径需要唯一
|
|
64
|
+
* @property get - 获取表单项值的方法
|
|
65
|
+
* @property set - 设置表单项值的方法
|
|
66
|
+
* @property validate - 可选的验证函数,接受表单项值作为参数,返回一个字符串或JSX元素表示验证结果
|
|
67
|
+
* @property setHelperText - 可选的设置帮助信息的方法,接受一个字符串或JSX元素作为参数
|
|
68
|
+
* @property fieldRef - 可选的表单控件引用,用于访问控件的实例方法,如 focus
|
|
69
|
+
*/
|
|
70
|
+
export interface FormItemRegister {
|
|
71
|
+
path: string;
|
|
72
|
+
get?: <V>() => V | undefined;
|
|
73
|
+
set?: <T>(value: T) => void;
|
|
74
|
+
validate?: <V>(value: V) => FormHelperInfo;
|
|
75
|
+
setHelperText?: Dispatch<SetStateAction<FormHelperInfo>>;
|
|
76
|
+
fieldRef?: React.RefObject<FocusableField | null>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 表单注册器
|
|
80
|
+
* @property [index: string] - 以表单项的值路径作为键,值为对应的表单项注册器
|
|
81
|
+
*/
|
|
82
|
+
export interface FormRegister {
|
|
83
|
+
[index: string]: FormItemRegister;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 表单上下文属性
|
|
87
|
+
* @property register - 注册表单项
|
|
88
|
+
* @property checkValuePath - 检查指定路径的表单项值
|
|
89
|
+
* @property uninstall - 卸载指定路径的表单项
|
|
90
|
+
*/
|
|
91
|
+
export interface FormContextProps {
|
|
92
|
+
/**
|
|
93
|
+
* 注册表单项
|
|
94
|
+
* @param props 表单项注册器
|
|
95
|
+
* @returns void
|
|
96
|
+
*/
|
|
97
|
+
register: (props: FormItemRegister) => void;
|
|
98
|
+
/**
|
|
99
|
+
* 检查表单项的值路径是否唯一
|
|
100
|
+
* @param path - 要校验的表单项的值路径
|
|
101
|
+
*/
|
|
102
|
+
checkValuePath: (path: string) => void;
|
|
103
|
+
/**
|
|
104
|
+
* 卸载表单项
|
|
105
|
+
* @param path - 要卸载的表单项的值路径
|
|
106
|
+
* @returns void
|
|
107
|
+
*/
|
|
108
|
+
uninstall: (path: string) => void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 表单控件属性
|
|
112
|
+
*/
|
|
113
|
+
export interface FormControlProps {
|
|
114
|
+
/**
|
|
115
|
+
* 设置用于获取value属性的属性路径
|
|
116
|
+
*/
|
|
117
|
+
valuePath: string;
|
|
118
|
+
/**
|
|
119
|
+
* 子组件
|
|
120
|
+
*/
|
|
121
|
+
children: JSX.Element;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 表单项属性
|
|
125
|
+
* @property path - 表单项的唯一路径
|
|
126
|
+
* @property initialValue - 表单项的初始值, 不是默认值
|
|
127
|
+
* @property validations - 表单项的验证规则
|
|
128
|
+
*/
|
|
129
|
+
export interface FormItemProps<T, V> {
|
|
130
|
+
path: string;
|
|
131
|
+
initialValue?: T;
|
|
132
|
+
validations?: FormItemValidations<V>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 表单项参数
|
|
136
|
+
* 用于设置表单项的值
|
|
137
|
+
* 包含属性路径和对应的值
|
|
138
|
+
* @property path - 表单项的唯一路径
|
|
139
|
+
* @property value - 表单项的值
|
|
140
|
+
*/
|
|
141
|
+
export interface FormItemArgs {
|
|
142
|
+
path: string;
|
|
143
|
+
value: unknown;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 表单参数
|
|
147
|
+
* 包含一个数组,每个元素是一个[表单项参数](#FormItemArgs)
|
|
148
|
+
*/
|
|
149
|
+
export type FormArgs = FormItemArgs[];
|
|
150
|
+
/**
|
|
151
|
+
* 包含表单组件基础校验类型的对象。
|
|
152
|
+
*
|
|
153
|
+
* @property {string} Customize - 表示自定义校验类型。
|
|
154
|
+
*/
|
|
155
|
+
export declare const BaseValidationType: {
|
|
156
|
+
/**
|
|
157
|
+
* 表示自定义校验类型
|
|
158
|
+
*/
|
|
159
|
+
readonly Customize: "customize";
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* 表单默认验证类型
|
|
163
|
+
*/
|
|
164
|
+
export type BaseValidationType = (typeof BaseValidationType)[keyof typeof BaseValidationType];
|
|
165
|
+
/**
|
|
166
|
+
* 扩展验证类型
|
|
167
|
+
*/
|
|
168
|
+
export interface ExtendedValidation {
|
|
169
|
+
[key: string]: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 表单项的验证对象
|
|
173
|
+
* 包含帮助信息、验证类型和可选的验证断言函数,一个表单项可以包含多个验证对象
|
|
174
|
+
* @property helpInfo - 帮助信息,可以是字符串或JSX元素
|
|
175
|
+
* @property type - 验证类型,使用[ValidationType](#ValidationType)枚举
|
|
176
|
+
* @property validationAssert - 可选的验证断言函数,接受表单项值作为参数,返回一个布尔值表示验证是否通过
|
|
177
|
+
*/
|
|
178
|
+
export interface FormItemValidation<T> {
|
|
179
|
+
helpInfo?: FormHelperInfo;
|
|
180
|
+
type: T;
|
|
181
|
+
validationAssert?: (value: unknown) => boolean;
|
|
182
|
+
}
|
|
183
|
+
export type FormItemValidations<T> = FormItemValidation<T>[];
|
|
184
|
+
/**
|
|
185
|
+
* 表单项帮助信息
|
|
186
|
+
* @package helpInfo - 帮助信息,可以是字符串或JSX元素
|
|
187
|
+
* @property path - 表单项的值路径
|
|
188
|
+
*/
|
|
189
|
+
export interface FormItemHelper {
|
|
190
|
+
helpInfo?: FormHelperInfo;
|
|
191
|
+
path: string;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 表单ref对象
|
|
195
|
+
* 包含操作表单的方法
|
|
196
|
+
* @property getValues - 获取表单当前数据的方法,返回一个包含所有表单项数据的对象
|
|
197
|
+
* @property setValues - 设置表单数据的方法,接受一个包含表单项路径和值的数组作为参数
|
|
198
|
+
* @property getValueByPath - 通过属性路径获取表单项对应控件的值的方法,接受一个字符串类型的路径作为参数,返回对应的值
|
|
199
|
+
* @property setValueByPath - 通过属性路径设置表单项对应控件的值的方法,接受一个字符串类型的路径和一个值作为参数
|
|
200
|
+
* @property setHelpTextByPath - 通过属性路径设置表单项的帮助信息的方法,接受一个字符串类型的路径和一个错误消息(字符串或JSX元素)作为参数
|
|
201
|
+
*/
|
|
202
|
+
export interface FormRef {
|
|
203
|
+
/**
|
|
204
|
+
* 获取表单当前数据
|
|
205
|
+
* @returns 表单当前数据对象
|
|
206
|
+
*/
|
|
207
|
+
getValues: () => FormValues;
|
|
208
|
+
/**
|
|
209
|
+
* 设置表单数据
|
|
210
|
+
* @param {FormArgs} args 表单项参数数组
|
|
211
|
+
* @returns void
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* myForm.setValues([{ path: "name", value: "Tom" }, { path: "age", value: 18 }]);
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
setValues: (args: FormArgs) => FormItemHelper[];
|
|
218
|
+
/**
|
|
219
|
+
* 通过属性路径获取表单项对应控件的值的方法
|
|
220
|
+
* @param path 表单项的值路径
|
|
221
|
+
* @returns 表单项的值
|
|
222
|
+
*/
|
|
223
|
+
getValueByPath: (path: string) => unknown;
|
|
224
|
+
/**
|
|
225
|
+
* 通过属性路径设置表单项对应控件的值的方法
|
|
226
|
+
* @param path 表单项的值路径
|
|
227
|
+
* @param value 表单项对应控件的值
|
|
228
|
+
* @returns void
|
|
229
|
+
*/
|
|
230
|
+
setValueByPath: (path: string, value: unknown) => FormHelperInfo;
|
|
231
|
+
/**
|
|
232
|
+
* 通过属性路径设置表单项对应控件的帮助信息的方法
|
|
233
|
+
* @param path 表单项的值路径
|
|
234
|
+
* @param {FormHelperInfo} helpText 帮助信息
|
|
235
|
+
* @returns void
|
|
236
|
+
*/
|
|
237
|
+
setHelpTextByPath: (path: string, helpText: FormHelperInfo) => void;
|
|
238
|
+
/**
|
|
239
|
+
* 表单验证
|
|
240
|
+
* @param options - 验证选项
|
|
241
|
+
* @param options.focusOnError - 验证失败时是否自动聚焦到第一个错误表单项(可选,默认为 false)
|
|
242
|
+
* @returns 表单验证结果数组,包含每个验证失败表项的帮助信息和路径
|
|
243
|
+
*/
|
|
244
|
+
validate: (options?: {
|
|
245
|
+
focusOnError?: boolean;
|
|
246
|
+
}) => FormItemHelper[];
|
|
247
|
+
/**
|
|
248
|
+
* 通过属性路径验证表单项的方法
|
|
249
|
+
* @param path 表单项的值路径
|
|
250
|
+
* @returns 验证结果,包含帮助信息和路径
|
|
251
|
+
*/
|
|
252
|
+
validateField: (path: string) => FormItemHelper;
|
|
253
|
+
/**
|
|
254
|
+
* 通过属性路径聚焦表单项对应控件的方法
|
|
255
|
+
* @param path 表单项的值路径
|
|
256
|
+
* @returns void
|
|
257
|
+
*/
|
|
258
|
+
focusFieldByPath: (path: string) => void;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 表单注入器
|
|
262
|
+
* @property commonValidationAssert - 可选的公共验证断言函数,接受一个值和一个验证类型作为参数,返回一个布尔值表示验证是否通过
|
|
263
|
+
* @property getDefaultHelperInfo - 可选的获取默认帮助信息的方法,接受一个值和一个验证类型作为参数,返回一个字符串或JSX元素表示默认帮助信息
|
|
264
|
+
* @property getInitialValue - 可选的获取初始值的方法,接受一个控件类型作为参数,返回对应的初始值
|
|
265
|
+
*/
|
|
266
|
+
export interface FormInjector<VT = unknown, CT = unknown, T = unknown> {
|
|
267
|
+
/**
|
|
268
|
+
* 公共验证断言函数,接受一个值和一个验证类型作为参数,返回一个布尔值表示验证是否通过
|
|
269
|
+
* @param value - 要验证的值
|
|
270
|
+
* @param validationType - 验证类型
|
|
271
|
+
* @returns 验证结果,true表示验证通过,false表示验证失败
|
|
272
|
+
*/
|
|
273
|
+
commonValidationAssert?(value: unknown, validationType: VT): boolean;
|
|
274
|
+
/**
|
|
275
|
+
* 获取默认帮助信息的方法,接受一个验证类型作为参数,返回一个字符串或JSX元素表示默认帮助信息
|
|
276
|
+
* @param validationType - 验证类型
|
|
277
|
+
* @returns 默认帮助信息,字符串或JSX元素
|
|
278
|
+
*/
|
|
279
|
+
getDefaultHelperInfo?(validationType: VT): FormHelperInfo;
|
|
280
|
+
/**
|
|
281
|
+
* 获取初始值的方法,接受一个控件类型作为参数,返回对应的初始值
|
|
282
|
+
* @param controlType - 控件类型
|
|
283
|
+
* @returns 控件的初始值
|
|
284
|
+
*/
|
|
285
|
+
getInitialValue?(controlType: CT): T;
|
|
286
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getVisualStates(cls?: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FormArgs, FormHelperInfo, FormItemHelper, FormRegister, FormValues } from './form.types';
|
|
2
|
+
/**
|
|
3
|
+
* 获取表单当前数据
|
|
4
|
+
* @param {FormRegister} formRegister 表单注册器
|
|
5
|
+
* @returns 表单当前数据对象
|
|
6
|
+
*/
|
|
7
|
+
export declare function getValues(formRegister: FormRegister): FormValues;
|
|
8
|
+
/**
|
|
9
|
+
* 通过属性路径获取表单项对应控件的值的方法
|
|
10
|
+
* @param path 表单项的值在表单数据中的属性路径
|
|
11
|
+
* @param {FormRegister} formRegister 表单注册器
|
|
12
|
+
* @returns 表单项对应控件的值
|
|
13
|
+
*/
|
|
14
|
+
export declare function getValueByPath(path: string, formRegister: FormRegister): unknown;
|
|
15
|
+
/**
|
|
16
|
+
* 设置表单项的帮助信息
|
|
17
|
+
* @param path 表单项的值在表单数据中的属性路径
|
|
18
|
+
* @param helperText 帮助信息
|
|
19
|
+
* @param formRegister 表单注册器
|
|
20
|
+
*/
|
|
21
|
+
export declare function setHelpTextByPath(path: string, helperText: FormHelperInfo, formRegister: FormRegister): void;
|
|
22
|
+
/**
|
|
23
|
+
* 设置表单数据
|
|
24
|
+
* @param {FormArgs} args 表单项参数数组
|
|
25
|
+
* @param {FormRegister} formRegister 表单注册器
|
|
26
|
+
*/
|
|
27
|
+
export declare function setValues(args: FormArgs, formRegister: FormRegister): FormItemHelper[];
|
|
28
|
+
/**
|
|
29
|
+
* 通过属性路径设置指定表单项的值,并自动触发该表单项的验证逻辑。
|
|
30
|
+
*
|
|
31
|
+
* @param path 表单项的唯一路径(valuePath)
|
|
32
|
+
* @param value 需要设置的新值
|
|
33
|
+
* @param formRegister 当前表单的注册器对象
|
|
34
|
+
*/
|
|
35
|
+
export declare function setValueByPath(path: string, value: unknown, formRegister: FormRegister): FormHelperInfo;
|
|
36
|
+
/**
|
|
37
|
+
* 通过属性路径验证表单项的方法
|
|
38
|
+
* @param path 表单项的唯一路径(valuePath)
|
|
39
|
+
* @param formRegister 当前表单的注册器对象
|
|
40
|
+
* @returns 验证结果,包含帮助信息和路径
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateField(path: string, formRegister: FormRegister): {
|
|
43
|
+
helpInfo: FormHelperInfo;
|
|
44
|
+
path: string;
|
|
45
|
+
} | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* 聚焦指定表单项
|
|
48
|
+
* @param path 表单项的唯一路径(valuePath)
|
|
49
|
+
* @param formRegister 当前表单的注册器对象
|
|
50
|
+
*/
|
|
51
|
+
export declare function focusFieldByPath(path: string, formRegister: FormRegister): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FormRef } from './form.types';
|
|
2
|
+
/**
|
|
3
|
+
* 获取表单ref的自定义hook
|
|
4
|
+
*
|
|
5
|
+
* @returns {[React.MutableRefObject<FormRef | undefined>, FormRef | undefined]} 返回一个元组:
|
|
6
|
+
* - ref:表单的可变 ref 对象
|
|
7
|
+
* - myForm:`FormRef` 类型的表单对象,包含操作表单的方法
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const [ref, myForm] = useForm();
|
|
11
|
+
*/
|
|
12
|
+
export declare function useForm(): [React.MutableRefObject<FormRef | undefined>, FormRef | undefined];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { FocusableField, FormHelperInfo } from './form.types';
|
|
3
|
+
/**
|
|
4
|
+
* 提供一个自定义hook, 用来实现表单项的受控逻辑
|
|
5
|
+
*
|
|
6
|
+
* @template T - 表单项值的类型,默认FormItemValue
|
|
7
|
+
* @template V - 传递给校验函数的值类型,默认string
|
|
8
|
+
*
|
|
9
|
+
* @param valuePath - 唯一标识表单项的路径
|
|
10
|
+
* @param initialValue - 表单项的初始值
|
|
11
|
+
* @param onGetErrorMessage - 可选,校验值并返回错误信息或 JSX 元素的函数
|
|
12
|
+
* @param setHelperText - 可选,用于设置校验提示文本的 setter
|
|
13
|
+
*
|
|
14
|
+
* @returns 包含当前值和用于更新值的 setter 的元组
|
|
15
|
+
*/
|
|
16
|
+
export declare function useFormItemValue<T, V = string>(valuePath: string, initialValue?: T, onGetErrorMessage?: (value: V, path: string) => FormHelperInfo, setHelperText?: Dispatch<SetStateAction<FormHelperInfo>>, fieldRef?: React.RefObject<FocusableField | null>): [T, Dispatch<SetStateAction<T>>];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { FormHelperInfo, FormItemValidation } from './form.types';
|
|
3
|
+
/**
|
|
4
|
+
* 提供一个自定义hook,管理表单项的帮助信息
|
|
5
|
+
*
|
|
6
|
+
* @template T - 帮助信息的类型,默认为 `FormHelperInfo`
|
|
7
|
+
* @template VT - 验证类型,默认为 `ValidationType`
|
|
8
|
+
* @template V - 需要校验的值类型,默认为 `string`
|
|
9
|
+
* @param validations - 需要应用于值的校验规则数组。
|
|
10
|
+
* @returns 返回一个元组,包含:
|
|
11
|
+
* - 校验并更新帮助信息的函数 (`(value: V) => T`),
|
|
12
|
+
* - 更新帮助信息状态的set函数 (`Dispatch<SetStateAction<FormHelperInfo>>`)。
|
|
13
|
+
*/
|
|
14
|
+
export declare function useHelperInfo<V, VT>(validations: FormItemValidation<VT>[]): [FormHelperInfo, (value: V) => FormHelperInfo, Dispatch<SetStateAction<FormHelperInfo>>];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FormInjector } from './form/form.types';
|
|
2
|
+
/**
|
|
3
|
+
* 表单能力注入对象。
|
|
4
|
+
* 目的是扩展Litten Form的能力,并将具体的功能实现解耦,使得Litten Form可以适配不同的组件库或者自定义的表单实现(比如验证)。
|
|
5
|
+
*/
|
|
6
|
+
export declare const formInjector: FormInjector;
|
|
7
|
+
/**
|
|
8
|
+
* 为litten form注入一些定制能力。
|
|
9
|
+
* @param args 注入实现集合,包含公共校验断言、默认帮助信息和初始值处理方法。
|
|
10
|
+
*/
|
|
11
|
+
export declare function initLittenForm<VT, CT, T>(args: FormInjector<VT, CT, T>): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validate } from '../validation/validate';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ValidationMode, FormItemHelper, FormRegister } from '../form/form.types';
|
|
2
|
+
/**
|
|
3
|
+
* 验证所有注册的表单项,并收集验证后的提示信息
|
|
4
|
+
*
|
|
5
|
+
* @param {FormRegister} formRegister - 表单注册器
|
|
6
|
+
* @returns {FormItemHelper[]} 验证后的提示信息
|
|
7
|
+
*/
|
|
8
|
+
export declare function validate(formRegister: FormRegister, validationMode: ValidationMode): FormItemHelper[];
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Form } from './components/form/form';
|
|
2
|
+
export { useForm } from './components/form/useForm';
|
|
3
|
+
export { useFormItemValue } from './components/form/useFormItemValue';
|
|
4
|
+
export { useHelperInfo } from './components/form/useHelperInfo';
|
|
5
|
+
export { initLittenForm } from './components/inject';
|
|
6
|
+
export { BaseValidationType, ValidationMode, } from './components/form/form.types';
|
|
7
|
+
export { Mounter } from './components/mounter';
|
|
8
|
+
export type { FormProps, FormRef, FormHelperInfo, FormItemProps, } from './components/form/form.types';
|
|
9
|
+
export type { MounterProps } from './components/mounter/mounter.types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { createContext as e, forwardRef as t, useContext as n, useEffect as r, useImperativeHandle as i, useRef as a, useState as o } from "react";
|
|
2
|
+
import { ExceptionBoundary as s } from "exception-boundary";
|
|
3
|
+
import { ControlType as c } from "litmoss-hooks/dist/enum";
|
|
4
|
+
import l from "classnames";
|
|
5
|
+
import { getPrefixNs as u } from "cyndi/dist/getPrefixNs";
|
|
6
|
+
import { jsx as d } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/form/form.types.ts
|
|
8
|
+
var f = {
|
|
9
|
+
all: "all",
|
|
10
|
+
step: "step"
|
|
11
|
+
}, p = { Customize: "customize" };
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/components/validation/validate.ts
|
|
14
|
+
function m(e) {
|
|
15
|
+
let t = [];
|
|
16
|
+
return Object.keys(e).forEach((n) => {
|
|
17
|
+
let r = e[n], i = r?.validate, a = r?.get;
|
|
18
|
+
if (i && a) {
|
|
19
|
+
let e = i(a());
|
|
20
|
+
e !== void 0 && t.push({
|
|
21
|
+
helpInfo: e,
|
|
22
|
+
path: n
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}), t;
|
|
26
|
+
}
|
|
27
|
+
function h(e) {
|
|
28
|
+
let t = [], n = Object.keys(e), r = n.length;
|
|
29
|
+
for (let i = 0; i < r; i++) {
|
|
30
|
+
let r = n[i], a = e[r], o = a?.validate, s = a?.get;
|
|
31
|
+
if (o && s) {
|
|
32
|
+
let e = o(s());
|
|
33
|
+
if (e !== void 0) {
|
|
34
|
+
t.push({
|
|
35
|
+
helpInfo: e,
|
|
36
|
+
path: r
|
|
37
|
+
});
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return t;
|
|
43
|
+
}
|
|
44
|
+
function g(e, t) {
|
|
45
|
+
let n = [];
|
|
46
|
+
return n = t === f.all ? m(e) : h(e), n;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/components/form/context.ts
|
|
50
|
+
var _ = e(void 0);
|
|
51
|
+
function v(e, t) {
|
|
52
|
+
return {
|
|
53
|
+
register: (t) => {
|
|
54
|
+
let { path: n } = t;
|
|
55
|
+
e[n] = { ...t };
|
|
56
|
+
},
|
|
57
|
+
uninstall: (t) => {
|
|
58
|
+
delete e[t];
|
|
59
|
+
},
|
|
60
|
+
checkValuePath: (n) => {
|
|
61
|
+
Object.prototype.hasOwnProperty.call(e, n) === !1 ? e[n] = { path: n } : t(`The valuePath "${n}" is used by other FormControl, Please check your form.`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/components/form/formBase.ts
|
|
67
|
+
function y(e, t) {
|
|
68
|
+
return u(e, t, "litten");
|
|
69
|
+
}
|
|
70
|
+
function b(e) {
|
|
71
|
+
let t = y("form", e);
|
|
72
|
+
return l(t);
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/components/util.ts
|
|
76
|
+
function x(e) {
|
|
77
|
+
console.warn(`[litten warning]: ${e}`);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/components/form/entries.ts
|
|
81
|
+
function S(e) {
|
|
82
|
+
return `Can not find the path "${e}" in form. Please check if the form item is registered.`;
|
|
83
|
+
}
|
|
84
|
+
function C() {
|
|
85
|
+
return "The first parameter of setValues should be an array of FormArgs";
|
|
86
|
+
}
|
|
87
|
+
function w() {
|
|
88
|
+
return "Can not find set method in form item register.";
|
|
89
|
+
}
|
|
90
|
+
function T() {
|
|
91
|
+
return "Can not find commonValidationAssert in formInjector, please provide it to support validation types other than Customize.";
|
|
92
|
+
}
|
|
93
|
+
function E(e) {
|
|
94
|
+
return `The fieldRef for path "${e}" does not have a focus method.`;
|
|
95
|
+
}
|
|
96
|
+
function D(e) {
|
|
97
|
+
return `When using custom validation, validationAssert must be configured. The path missing this setting is: ${e}`;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/components/form/formUtil.ts
|
|
101
|
+
function O(e) {
|
|
102
|
+
let t = {};
|
|
103
|
+
return Object.keys(e).forEach((n) => {
|
|
104
|
+
let r = e[n]?.get;
|
|
105
|
+
r && (t[n] = r());
|
|
106
|
+
}), t;
|
|
107
|
+
}
|
|
108
|
+
function k(e, t) {
|
|
109
|
+
let n = t[e], r;
|
|
110
|
+
if (n !== void 0) {
|
|
111
|
+
let e = n.get;
|
|
112
|
+
e && (r = e());
|
|
113
|
+
} else x(S(e));
|
|
114
|
+
return r;
|
|
115
|
+
}
|
|
116
|
+
function A(e, t, n) {
|
|
117
|
+
let r = n[e];
|
|
118
|
+
r === void 0 ? x(S(e)) : r.setHelperText?.(t);
|
|
119
|
+
}
|
|
120
|
+
function j(e, t) {
|
|
121
|
+
let n = [];
|
|
122
|
+
return Array.isArray(e) ? (e.forEach((e) => {
|
|
123
|
+
let { path: r, value: i } = e, a = t[r]?.set, o = t[r]?.validate;
|
|
124
|
+
if (a) {
|
|
125
|
+
a(i);
|
|
126
|
+
let e = o?.(i);
|
|
127
|
+
e !== void 0 && n.push({
|
|
128
|
+
helpInfo: e,
|
|
129
|
+
path: r
|
|
130
|
+
});
|
|
131
|
+
} else x(w());
|
|
132
|
+
}), n) : (x(C()), n);
|
|
133
|
+
}
|
|
134
|
+
function M(e, t, n) {
|
|
135
|
+
let r = n[e];
|
|
136
|
+
if (r !== void 0) {
|
|
137
|
+
let { set: e, validate: n } = r;
|
|
138
|
+
if (e) return e(t), n?.(t);
|
|
139
|
+
x(w());
|
|
140
|
+
} else x(S(e));
|
|
141
|
+
}
|
|
142
|
+
function N(e, t) {
|
|
143
|
+
let n = t[e];
|
|
144
|
+
if (n !== void 0) {
|
|
145
|
+
let { validate: t, get: r } = n, i = r?.();
|
|
146
|
+
return {
|
|
147
|
+
helpInfo: t?.(i),
|
|
148
|
+
path: e
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
x(S(e));
|
|
152
|
+
}
|
|
153
|
+
function P(e, t) {
|
|
154
|
+
let n = t[e];
|
|
155
|
+
n ? n.fieldRef?.current?.focus ? n.fieldRef.current.focus() : x(E(e)) : x(S(e));
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/components/form/form.tsx
|
|
159
|
+
var F = t(({ children: e, prefixCls: t, validationMode: n = f.all, ...r }, a) => {
|
|
160
|
+
let [c] = o({}), [l, u] = o(), [p] = o(() => v(c, u));
|
|
161
|
+
return i(a, () => ({
|
|
162
|
+
getValues: () => O(c),
|
|
163
|
+
getValueByPath: (e) => k(e, c),
|
|
164
|
+
setHelpTextByPath: (e, t) => {
|
|
165
|
+
A(e, t, c);
|
|
166
|
+
},
|
|
167
|
+
setValues: (e) => j(e, c),
|
|
168
|
+
setValueByPath: (e, t) => M(e, t, c),
|
|
169
|
+
validate: (e) => {
|
|
170
|
+
let { focusOnError: t } = e || {}, r = g(c, n);
|
|
171
|
+
if (t !== !1 && r.length > 0) {
|
|
172
|
+
let e = r[0];
|
|
173
|
+
P(e.path, c);
|
|
174
|
+
}
|
|
175
|
+
return r;
|
|
176
|
+
},
|
|
177
|
+
validateField: (e) => N(e, c),
|
|
178
|
+
focusFieldByPath: (e) => {
|
|
179
|
+
P(e, c);
|
|
180
|
+
}
|
|
181
|
+
}), [c, n]), /* @__PURE__ */ d(s, {
|
|
182
|
+
errorMsg: l,
|
|
183
|
+
children: /* @__PURE__ */ d(_.Provider, {
|
|
184
|
+
value: p,
|
|
185
|
+
children: /* @__PURE__ */ d("div", {
|
|
186
|
+
className: b(t),
|
|
187
|
+
...r,
|
|
188
|
+
children: e
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
F.displayName = c.Form;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/components/form/useForm.ts
|
|
196
|
+
function I() {
|
|
197
|
+
let e = a(), [t, n] = o();
|
|
198
|
+
return r(() => (n(e.current), () => {
|
|
199
|
+
e.current = void 0;
|
|
200
|
+
}), [e]), [e, t];
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/components/form/useFormItemValue.ts
|
|
204
|
+
function L(e, t, i, a, s) {
|
|
205
|
+
let c = n(_), [l, u] = o(t);
|
|
206
|
+
return r(() => (c?.checkValuePath(e), () => {
|
|
207
|
+
c?.uninstall(e);
|
|
208
|
+
}), [c, e]), r(() => {
|
|
209
|
+
c?.register({
|
|
210
|
+
path: e,
|
|
211
|
+
get: () => l,
|
|
212
|
+
set: u,
|
|
213
|
+
validate: (t) => i?.(t, e),
|
|
214
|
+
setHelperText: a,
|
|
215
|
+
fieldRef: s
|
|
216
|
+
});
|
|
217
|
+
}, [
|
|
218
|
+
c,
|
|
219
|
+
i,
|
|
220
|
+
l,
|
|
221
|
+
e,
|
|
222
|
+
a,
|
|
223
|
+
s
|
|
224
|
+
]), [l, u];
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/components/inject.ts
|
|
228
|
+
var R = {};
|
|
229
|
+
function z(e) {
|
|
230
|
+
let { commonValidationAssert: t, getDefaultHelperInfo: n, getInitialValue: r } = e;
|
|
231
|
+
R.commonValidationAssert = t, R.getDefaultHelperInfo = n, R.getInitialValue = r;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/components/form/useHelperInfo.ts
|
|
235
|
+
function B(e) {
|
|
236
|
+
let [t, n] = o();
|
|
237
|
+
function r(t, r) {
|
|
238
|
+
let i, a = !1, o = e.length;
|
|
239
|
+
for (let n = 0; n < o; n++) {
|
|
240
|
+
let { helpInfo: o, type: s, validationAssert: c } = e[n];
|
|
241
|
+
if (s === p.Customize) {
|
|
242
|
+
if (c !== void 0) a = c(t) === !1;
|
|
243
|
+
else {
|
|
244
|
+
x(D(r));
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
} else if (R.commonValidationAssert) a = R.commonValidationAssert(t, s) === !1;
|
|
248
|
+
else {
|
|
249
|
+
x(T());
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
if (a) {
|
|
253
|
+
i = o ?? (R.getDefaultHelperInfo ? R.getDefaultHelperInfo(s) : void 0);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return n(i), i;
|
|
258
|
+
}
|
|
259
|
+
return [
|
|
260
|
+
t,
|
|
261
|
+
r,
|
|
262
|
+
n
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/components/mounter/mounter.tsx
|
|
267
|
+
var V = ({ onDidMount: e }) => (r(() => {
|
|
268
|
+
e?.();
|
|
269
|
+
}, []), null);
|
|
270
|
+
//#endregion
|
|
271
|
+
export { p as BaseValidationType, F as Form, V as Mounter, f as ValidationMode, z as initLittenForm, I as useForm, L as useFormItemValue, B as useHelperInfo };
|
package/package.json
CHANGED
|
@@ -1,6 +1,101 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "litmoss-form",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "litmoss-form",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "5.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"react",
|
|
13
|
+
"javascript",
|
|
14
|
+
"typescript",
|
|
15
|
+
"component",
|
|
16
|
+
"form"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "tsc && vite build",
|
|
21
|
+
"check-type": "tsc -b",
|
|
22
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
23
|
+
"lint:fix": "eslint --fix src/",
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"storybook": "storybook dev -p 6006",
|
|
26
|
+
"test": "vitest",
|
|
27
|
+
"test-storybook": "vitest --project=storybook",
|
|
28
|
+
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -s -r 0",
|
|
29
|
+
"build-storybook": "storybook build",
|
|
30
|
+
"prepare": "husky"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"classnames": "^2.5.1",
|
|
34
|
+
"cyndi": "^1.0.0",
|
|
35
|
+
"exception-boundary": "^2.0.1",
|
|
36
|
+
"litmoss-hooks": "^3.0.0",
|
|
37
|
+
"number-precision": "^1.6.0",
|
|
38
|
+
"react": "^18.2.0",
|
|
39
|
+
"react-dom": "^18.2.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@chromatic-com/storybook": "^5.1.1",
|
|
43
|
+
"@commitlint/cli": "^19.3.0",
|
|
44
|
+
"@commitlint/config-conventional": "^19.2.2",
|
|
45
|
+
"@storybook/addon-docs": "^10.3.3",
|
|
46
|
+
"@storybook/addon-vitest": "^10.3.3",
|
|
47
|
+
"@storybook/react-vite": "^10.3.3",
|
|
48
|
+
"@types/react": "^18.2.66",
|
|
49
|
+
"@types/react-dom": "^18.2.22",
|
|
50
|
+
"@vitejs/plugin-react": "^6.0.0",
|
|
51
|
+
"@vitest/browser-playwright": "^4.1.2",
|
|
52
|
+
"@vitest/coverage-istanbul": "^4.1.2",
|
|
53
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
54
|
+
"classnames": "^2.5.1",
|
|
55
|
+
"conventional-changelog": "^6.0.0",
|
|
56
|
+
"cyndi": "^1.0.0",
|
|
57
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
58
|
+
"eslint": "^9.39.4",
|
|
59
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
60
|
+
"eslint-plugin-import": "^2.32.0",
|
|
61
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
62
|
+
"eslint-plugin-react-refresh": "^0.4.6",
|
|
63
|
+
"eslint-plugin-storybook": "^10.3.3",
|
|
64
|
+
"exception-boundary": "^2.0.1",
|
|
65
|
+
"husky": "^9.0.11",
|
|
66
|
+
"less": "^4.2.0",
|
|
67
|
+
"litmoss": "^3.0.0",
|
|
68
|
+
"litmoss-hooks": "^3.0.0",
|
|
69
|
+
"number-precision": "^1.6.0",
|
|
70
|
+
"playwright": "^1.59.0",
|
|
71
|
+
"react": "^18.2.0",
|
|
72
|
+
"react-dom": "^18.2.0",
|
|
73
|
+
"rollup-plugin-visualizer": "^5.12.0",
|
|
74
|
+
"storybook": "^10.3.3",
|
|
75
|
+
"typescript": "~5.9.3",
|
|
76
|
+
"typescript-eslint": "^8.56.1",
|
|
77
|
+
"vite": "^8.0.0",
|
|
78
|
+
"vite-plugin-dts": "^4.5.4",
|
|
79
|
+
"vitest": "^4.1.2"
|
|
80
|
+
},
|
|
81
|
+
"husky": {
|
|
82
|
+
"hooks": {
|
|
83
|
+
"pre-commit": "npm run test",
|
|
84
|
+
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"commitlint": {
|
|
88
|
+
"extends": [
|
|
89
|
+
"@commitlint/config-conventional"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"config": {
|
|
93
|
+
"commitizen": {
|
|
94
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"repository": {
|
|
98
|
+
"type": "git",
|
|
99
|
+
"url": "git+https://github.com/liuxian496/litmoss-form.git"
|
|
100
|
+
}
|
|
101
|
+
}
|