@skopon-cool/form-sdk 0.4.9 → 0.5.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.
@@ -105,6 +105,93 @@
105
105
  text-align: center;
106
106
  }
107
107
 
108
+ /* 网页展示 */
109
+ .skopon-web-preview {
110
+ min-width: 0;
111
+ max-width: 100%;
112
+ overflow-x: hidden;
113
+ }
114
+
115
+ .skopon-web-frame-shell {
116
+ position: relative;
117
+ box-sizing: border-box;
118
+ width: 100%;
119
+ max-width: 100%;
120
+ min-width: 0;
121
+ overflow: hidden;
122
+ border: 1px solid var(--color-border-subtle);
123
+ border-radius: var(--radius-md);
124
+ background: var(--color-bg-subtle);
125
+ }
126
+
127
+ .skopon-web-frame-shell--empty {
128
+ border-style: dashed;
129
+ border-color: var(--color-border);
130
+ }
131
+
132
+ .skopon-web-frame {
133
+ display: block;
134
+ box-sizing: border-box;
135
+ width: 100%;
136
+ max-width: 100%;
137
+ min-width: 0;
138
+ border: 0;
139
+ background: var(--color-surface);
140
+ }
141
+
142
+ .skopon-web-empty {
143
+ display: flex;
144
+ width: 100%;
145
+ height: 100%;
146
+ align-items: center;
147
+ justify-content: center;
148
+ padding: var(--space-4);
149
+ color: var(--color-text-muted);
150
+ font-size: var(--font-size-sm);
151
+ text-align: center;
152
+ }
153
+
154
+ .skopon-web-expand-button.ant-btn {
155
+ position: absolute;
156
+ right: var(--space-3);
157
+ bottom: var(--space-3);
158
+ z-index: 1;
159
+ width: var(--button-height-md);
160
+ min-width: var(--button-height-md);
161
+ height: var(--button-height-md);
162
+ padding: 0;
163
+ color: var(--color-primary);
164
+ border-color: var(--color-border);
165
+ background: color-mix(in srgb, var(--color-surface) 92%, transparent);
166
+ box-shadow: var(--shadow-md);
167
+ }
168
+
169
+ .skopon-web-expand-button.ant-btn:hover {
170
+ color: var(--color-primary-hover);
171
+ border-color: var(--color-primary-hover);
172
+ background: var(--color-surface);
173
+ }
174
+
175
+ .skopon-web-expand-button.ant-btn:focus-visible {
176
+ outline: none;
177
+ box-shadow: var(--shadow-focus), var(--shadow-md);
178
+ }
179
+
180
+ .skopon-web-modal .ant-modal-content {
181
+ overflow: hidden;
182
+ border-radius: var(--radius-lg);
183
+ }
184
+
185
+ .skopon-web-modal .ant-modal-body {
186
+ padding: 0;
187
+ overflow: hidden;
188
+ }
189
+
190
+ .skopon-web-frame--modal {
191
+ height: min(80vh, 900px);
192
+ height: min(80dvh, 900px);
193
+ }
194
+
108
195
  .form-file-upload-preview {
109
196
  margin-top: var(--space-1);
110
197
  }
@@ -734,6 +734,20 @@ describe('submitFormJson', () => {
734
734
  })
735
735
 
736
736
  describe('validateFormSubmitValues', () => {
737
+ it('ignores display blocks even if they contain input-like metadata', () => {
738
+ const blocks = [
739
+ {
740
+ id: 'web-1',
741
+ type: 'web' as const,
742
+ name: 'embedded_page',
743
+ label: '网页',
744
+ required: true,
745
+ webUrl: 'https://example.com',
746
+ },
747
+ ]
748
+ expect(validateFormSubmitValues(blocks, {})).toBeNull()
749
+ })
750
+
737
751
  it('rejects empty required text field', () => {
738
752
  const blocks = [
739
753
  { id: 'b1', type: 'text' as const, name: 'title', label: '标题', required: true },
@@ -1,5 +1,5 @@
1
1
  import type { FormBlock } from '../types/index'
2
- import { isLayoutBlockType, isMediaBlockType } from '../types/index'
2
+ import { isInputBlockType } from '../types/index'
3
3
  import { EMAIL_REGEXP } from '../adapter/a2uiAdapter'
4
4
  import { coerceToggleValue } from '../adapter/formSchema'
5
5
  import { asStringArray } from '../catalog/useSkoponBoundField'
@@ -52,9 +52,9 @@ export function validateFormSubmitValues(
52
52
  values: Record<string, unknown>,
53
53
  ): FormSubmitValidationError | null {
54
54
  for (const block of blocks) {
55
+ if (!isInputBlockType(block.type)) continue
55
56
  const name = block.name?.trim()
56
57
  if (!name) continue
57
- if (isLayoutBlockType(block.type) || isMediaBlockType(block.type)) continue
58
58
 
59
59
  const raw = values[name]
60
60
  const label = block.label?.trim() || name
@@ -16,6 +16,7 @@ export type A2uiComponentName =
16
16
  | 'DateTimeInput'
17
17
  | 'FileUpload'
18
18
  | 'SkoponMedia'
19
+ | 'SkoponWeb'
19
20
  | 'SkoponSelect'
20
21
  | 'SkoponResumeSelect'
21
22
  | 'SkoponCaseSelect'
@@ -62,6 +63,7 @@ export type FormBlockType =
62
63
  | 'image'
63
64
  | 'video'
64
65
  | 'audio'
66
+ | 'web'
65
67
 
66
68
  export type FormMediaSize = 'huge' | 'large' | 'medium' | 'small' | 'icon'
67
69
 
@@ -131,6 +133,10 @@ export interface FormBlock {
131
133
  mediaUrl?: string
132
134
  mediaUrls?: string[]
133
135
  mediaSize?: FormMediaSize
136
+ /** 网页展示块:iframe 地址,仅支持 HTTP(S) 或同源相对地址 */
137
+ webUrl?: string
138
+ /** 网页展示块:行内 iframe 高度(px) */
139
+ webHeight?: number
134
140
  fileAcceptTypes?: string[]
135
141
  fileAcceptExtensions?: string[]
136
142
  filePlaceholderIcon?: FormFilePlaceholderIcon
@@ -173,16 +179,20 @@ export function isMediaBlockType(type: FormBlockType): boolean {
173
179
  return type === 'image' || type === 'video' || type === 'audio'
174
180
  }
175
181
 
182
+ export function isWebBlockType(type: FormBlockType): boolean {
183
+ return type === 'web'
184
+ }
185
+
176
186
  export function isLayoutBlockType(type: FormBlockType): boolean {
177
187
  return type === 'heading' || type === 'paragraph'
178
188
  }
179
189
 
180
190
  export function isInputBlockType(type: FormBlockType): boolean {
181
- return !isLayoutBlockType(type) && !isMediaBlockType(type)
191
+ return !isLayoutBlockType(type) && !isMediaBlockType(type) && !isWebBlockType(type)
182
192
  }
183
193
 
184
194
  export function isDisplayBlockType(type: FormBlockType): boolean {
185
- return isLayoutBlockType(type) || isMediaBlockType(type)
195
+ return isLayoutBlockType(type) || isMediaBlockType(type) || isWebBlockType(type)
186
196
  }
187
197
 
188
198
  export function isOptionsBlockType(type: FormBlockType): boolean {