@skopon-cool/form-sdk 0.2.0 → 0.2.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.
Files changed (62) hide show
  1. package/README.md +96 -9
  2. package/dist/adapter/a2uiAdapter.d.ts +1 -0
  3. package/dist/adapter/a2uiAdapter.d.ts.map +1 -1
  4. package/dist/adapter/formSchema.d.ts +2 -1
  5. package/dist/adapter/formSchema.d.ts.map +1 -1
  6. package/dist/catalog/a2uiCustomCatalog.d.ts.map +1 -1
  7. package/dist/catalog/skoponCaseSelect.d.ts.map +1 -1
  8. package/dist/catalog/skoponResumeSelect.d.ts.map +1 -1
  9. package/dist/catalog/useSkoponBoundField.d.ts +2 -0
  10. package/dist/catalog/useSkoponBoundField.d.ts.map +1 -1
  11. package/dist/client/apiEnvelope.d.ts +7 -0
  12. package/dist/client/apiEnvelope.d.ts.map +1 -0
  13. package/dist/client/authStorage.d.ts +19 -0
  14. package/dist/client/authStorage.d.ts.map +1 -0
  15. package/dist/client/caseSearchClient.d.ts +11 -0
  16. package/dist/client/caseSearchClient.d.ts.map +1 -0
  17. package/dist/client/formClient.d.ts +2 -2
  18. package/dist/client/formClient.d.ts.map +1 -1
  19. package/dist/client/resumeSearchClient.d.ts +9 -0
  20. package/dist/client/resumeSearchClient.d.ts.map +1 -0
  21. package/dist/client/skoponSdkClients.d.ts +22 -0
  22. package/dist/client/skoponSdkClients.d.ts.map +1 -0
  23. package/dist/components/AskUserFormCard.d.ts.map +1 -1
  24. package/dist/components/SkoponFormRenderer.d.ts +2 -0
  25. package/dist/components/SkoponFormRenderer.d.ts.map +1 -1
  26. package/dist/form-sdk.css +1 -1
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1097 -826
  30. package/dist/submit/validateFormSubmit.d.ts +10 -0
  31. package/dist/submit/validateFormSubmit.d.ts.map +1 -0
  32. package/package.json +1 -1
  33. package/src/adapter/a2uiAdapter.ts +3 -4
  34. package/src/adapter/formSchema.test.ts +29 -1
  35. package/src/adapter/formSchema.ts +12 -3
  36. package/src/adapter/resolveSurface.ts +3 -3
  37. package/src/catalog/a2uiCustomCatalog.tsx +49 -3
  38. package/src/catalog/skoponCaseSelect.tsx +2 -1
  39. package/src/catalog/skoponResumeSelect.tsx +1 -0
  40. package/src/catalog/skoponSelectLoading.test.tsx +182 -0
  41. package/src/catalog/textFieldPreview.test.tsx +31 -0
  42. package/src/catalog/useSkoponBoundField.test.ts +14 -1
  43. package/src/catalog/useSkoponBoundField.ts +14 -0
  44. package/src/client/apiEnvelope.ts +21 -0
  45. package/src/client/authStorage.test.ts +61 -0
  46. package/src/client/authStorage.ts +70 -0
  47. package/src/client/caseSearchClient.ts +107 -0
  48. package/src/client/formClient.test.ts +25 -0
  49. package/src/client/formClient.ts +7 -25
  50. package/src/client/resumeSearchClient.test.ts +87 -0
  51. package/src/client/resumeSearchClient.ts +108 -0
  52. package/src/client/skoponSdkClients.ts +51 -0
  53. package/src/components/AskUserFormCard.tsx +7 -0
  54. package/src/components/SkoponFormRenderer.tsx +18 -6
  55. package/src/index.ts +21 -0
  56. package/src/styles/index.css +3 -3
  57. package/src/submit/submit.test.ts +40 -0
  58. package/src/submit/validateFormSubmit.ts +111 -0
  59. package/dist/catalog/textFieldPreview.test.d.ts +0 -2
  60. package/dist/catalog/textFieldPreview.test.d.ts.map +0 -1
  61. package/dist/components/SkoponA2uiStreamRenderer.test.d.ts +0 -2
  62. package/dist/components/SkoponA2uiStreamRenderer.test.d.ts.map +0 -1
@@ -0,0 +1,111 @@
1
+ import type { FormBlock } from '../types/index'
2
+ import { isLayoutBlockType, isMediaBlockType } from '../types/index'
3
+ import { EMAIL_REGEXP } from '../adapter/a2uiAdapter'
4
+ import { coerceToggleValue } from '../adapter/formSchema'
5
+ import { asStringArray } from '../catalog/useSkoponBoundField'
6
+
7
+ export interface FormSubmitValidationError {
8
+ fieldName: string
9
+ label: string
10
+ message: string
11
+ }
12
+
13
+ export function matchesValidationRegexp(value: string, pattern: string): boolean {
14
+ if (!pattern.trim()) return true
15
+ try {
16
+ return new RegExp(pattern).test(value)
17
+ } catch {
18
+ return true
19
+ }
20
+ }
21
+
22
+ function isBlankString(raw: unknown): boolean {
23
+ if (raw === undefined || raw === null) return true
24
+ if (typeof raw === 'string') return !raw.trim()
25
+ return false
26
+ }
27
+
28
+ function isEmptySubmitValue(block: FormBlock, raw: unknown): boolean {
29
+ switch (block.type) {
30
+ case 'multiselect':
31
+ case 'checkbox':
32
+ case 'file':
33
+ case 'resume_multiselect':
34
+ case 'case_multiselect':
35
+ return asStringArray(raw).length === 0
36
+ case 'toggle':
37
+ return !coerceToggleValue(raw)
38
+ case 'number':
39
+ return raw === undefined || raw === null || raw === ''
40
+ default:
41
+ return isBlankString(raw)
42
+ }
43
+ }
44
+
45
+ function shouldEnforceMinCount(block: FormBlock, count: number): boolean {
46
+ return block.required === true || count > 0
47
+ }
48
+
49
+ /** 提交前校验 renderBlocks 对应字段值;首个错误即返回。 */
50
+ export function validateFormSubmitValues(
51
+ blocks: FormBlock[],
52
+ values: Record<string, unknown>,
53
+ ): FormSubmitValidationError | null {
54
+ for (const block of blocks) {
55
+ const name = block.name?.trim()
56
+ if (!name) continue
57
+ if (isLayoutBlockType(block.type) || isMediaBlockType(block.type)) continue
58
+
59
+ const raw = values[name]
60
+ const label = block.label?.trim() || name
61
+
62
+ if (block.required && isEmptySubmitValue(block, raw)) {
63
+ return { fieldName: name, label, message: `请填写「${label}」` }
64
+ }
65
+
66
+ if (block.type === 'email' && !isBlankString(raw)) {
67
+ const text = String(raw).trim()
68
+ if (!matchesValidationRegexp(text, EMAIL_REGEXP)) {
69
+ return { fieldName: name, label, message: `「${label}」格式不正确` }
70
+ }
71
+ }
72
+
73
+ if (block.type === 'file') {
74
+ const min = block.fileMinCount ?? 0
75
+ const count = asStringArray(raw).length
76
+ if (min > 0 && count < min && shouldEnforceMinCount(block, count)) {
77
+ return {
78
+ fieldName: name,
79
+ label,
80
+ message: `「${label}」至少需要 ${min} 个文件`,
81
+ }
82
+ }
83
+ }
84
+
85
+ if (block.type === 'resume_multiselect') {
86
+ const min = block.resumeMinCount
87
+ const count = asStringArray(raw).length
88
+ if (min !== undefined && count < min && shouldEnforceMinCount(block, count)) {
89
+ return {
90
+ fieldName: name,
91
+ label,
92
+ message: `「${label}」至少选择 ${min} 项`,
93
+ }
94
+ }
95
+ }
96
+
97
+ if (block.type === 'case_multiselect') {
98
+ const min = block.caseMinCount
99
+ const count = asStringArray(raw).length
100
+ if (min !== undefined && count < min && shouldEnforceMinCount(block, count)) {
101
+ return {
102
+ fieldName: name,
103
+ label,
104
+ message: `「${label}」至少选择 ${min} 项`,
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ return null
111
+ }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=textFieldPreview.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"textFieldPreview.test.d.ts","sourceRoot":"","sources":["../../src/catalog/textFieldPreview.test.tsx"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=SkoponA2uiStreamRenderer.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SkoponA2uiStreamRenderer.test.d.ts","sourceRoot":"","sources":["../../src/components/SkoponA2uiStreamRenderer.test.tsx"],"names":[],"mappings":""}