@svadmin/lite 0.23.2 → 0.25.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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { assertSchemaFormSchema, isSchemaFormNodeVisible, type SchemaFormSchema as JsonSchema } from '@svadmin/core/schema-form';
2
+ import { assertSchemaFormSchema, isSchemaFormNodeVisible, resolveSchemaFormLayout, type SchemaFormLayoutGroup, type SchemaFormSchema as JsonSchema } from '@svadmin/core/schema-form';
3
3
  import { useTranslation } from '@svadmin/core/i18n';
4
4
  import { encodeSchemaFormArrayAction, schemaFormActionName, schemaFormFieldName } from '../schema-form-data';
5
5
 
@@ -95,6 +95,21 @@
95
95
  return schemaFormFieldName(path);
96
96
  }
97
97
 
98
+ function groupsFor(node: JsonSchema): readonly SchemaFormLayoutGroup[] {
99
+ if (resolved.invalid) return [];
100
+ try {
101
+ const plan = resolveSchemaFormLayout(node);
102
+ if (plan.groups.length > 0) return plan.groups;
103
+ } catch {
104
+ // 嵌套节点若超出封闭 schema,退回按字段顺序平铺。
105
+ }
106
+ return [{
107
+ key: '',
108
+ columns: 1,
109
+ fields: Object.keys(node.properties ?? {}).map((key) => ({ key, span: 1 })),
110
+ }];
111
+ }
112
+
98
113
  function enumToken(index: number): string {
99
114
  return `__json_enum_${index}`;
100
115
  }
@@ -137,9 +152,7 @@
137
152
  <fieldset class="lite-schema-fieldset">
138
153
  <legend>{title}</legend>
139
154
  {#if node.description}<p class="lite-schema-description">{node.description}</p>{/if}
140
- {#each Object.entries(node.properties ?? {}) as [key, child] (key)}
141
- {@render renderField(child, [...path, key], child.title ?? key, node.required?.includes(key) ?? false)}
142
- {/each}
155
+ {@render renderGroups(node, path)}
143
156
  </fieldset>
144
157
  {:else if node.type === 'array'}
145
158
  {@const items = Array.isArray(current) ? current : []}
@@ -194,9 +207,32 @@
194
207
  {/if}
195
208
  {/snippet}
196
209
 
197
- {#each Object.entries(resolved.invalid ? {} : schema.properties ?? {}) as [key, node] (key)}
198
- {@render renderField(node, [key], node.title ?? key, schema.required?.includes(key) ?? false)}
210
+ {@render renderGroups(schema, [])}
211
+
212
+ {#snippet renderGroups(node: JsonSchema, path: string[])}
213
+ {#each groupsFor(node) as group (group.key)}
214
+ <div class="lite-schema-group" data-svadmin-form-group={group.key}>
215
+ {#if group.title}
216
+ <h3 class="lite-schema-group-title">{group.step !== undefined ? `${group.step}. ${group.title}` : group.title}</h3>
217
+ {#if group.description}<p class="lite-schema-description">{group.description}</p>{/if}
218
+ {/if}
219
+ <div class={group.columns > 1 ? 'lite-schema-row' : 'lite-schema-stack'}>
220
+ {#each group.fields as field (field.key)}
221
+ {@const child = node.properties?.[field.key]}
222
+ {#if child}
223
+ <div
224
+ class="lite-schema-col"
225
+ style:width={group.columns > 1 ? `${Math.min(100, (field.span / group.columns) * 100)}%` : undefined}
226
+ >
227
+ {@render renderField(child, [...path, field.key], child.title ?? field.key, node.required?.includes(field.key) ?? false)}
228
+ </div>
229
+ {/if}
230
+ {/each}
231
+ </div>
232
+ </div>
199
233
  {/each}
234
+ {/snippet}
235
+
200
236
  <div class="lite-schema-footer">
201
237
  <button type="submit" disabled={resolved.invalid} class="lite-btn lite-btn-primary lite-btn-sm">{submitText}</button>
202
238
  </div>
@@ -208,6 +244,12 @@
208
244
  .lite-schema-form-header { padding-bottom: 8px; border-bottom: 1px solid #E5E6EB; margin-bottom: 12px; font-size: 14px; }
209
245
  .lite-schema-fieldset { border: 1px solid #E5E6EB; border-radius: 4px; padding: 12px; margin: 0 0 12px; }
210
246
  .lite-schema-field { margin-bottom: 12px; }
247
+ .lite-schema-group { margin-bottom: 12px; }
248
+ .lite-schema-group-title { margin: 0 0 8px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: #4E5969; }
249
+ /* IE11 基线:用 inline-block 栅格替代 CSS Grid/flex gap。 */
250
+ .lite-schema-row { font-size: 0; }
251
+ .lite-schema-col { display: inline-block; vertical-align: top; box-sizing: border-box; padding-right: 12px; font-size: 13px; }
252
+ .lite-schema-col:last-child { padding-right: 0; }
211
253
  .lite-schema-fieldset > .lite-schema-field:last-child { margin-bottom: 0; }
212
254
  .lite-schema-footer { display: flex; justify-content: flex-end; padding-top: 12px; border-top: 1px solid #E5E6EB; }
213
255
  .lite-label { display: block; font-size: 12px; color: #4E5969; margin-bottom: 4px; }
@@ -141,9 +141,32 @@ function decodeFields(schema, form, validate) {
141
141
  if (schema.type !== 'object' && !schema.properties)
142
142
  return fail();
143
143
  // 控制字段只允许无条件标量,因此可先解码,不依赖属性声明顺序。
144
+ // 组合器条件可能引用多个控制字段(含 equalsField),必须全部预解码。
145
+ function conditionPaths(condition, paths) {
146
+ if (!condition)
147
+ return;
148
+ if (condition.path)
149
+ paths.push([...condition.path]);
150
+ if (condition.equalsField)
151
+ paths.push([...condition.equalsField]);
152
+ for (const child of condition.all ?? [])
153
+ conditionPaths(child, paths);
154
+ for (const child of condition.any ?? [])
155
+ conditionPaths(child, paths);
156
+ conditionPaths(condition.not, paths);
157
+ }
144
158
  function collectControllers(node) {
145
- const path = node.visibleWhen?.path;
146
- if (path && !controllerValues.has(JSON.stringify(path))) {
159
+ const paths = [];
160
+ conditionPaths(node.visibleWhen, paths);
161
+ for (const path of paths)
162
+ collectControllerPath(path);
163
+ for (const child of Object.values(node.properties ?? {}))
164
+ collectControllers(child);
165
+ if (node.items)
166
+ collectControllers(node.items);
167
+ }
168
+ function collectControllerPath(path) {
169
+ if (!controllerValues.has(JSON.stringify(path))) {
147
170
  let definition = schema;
148
171
  for (const key of path) {
149
172
  const next = definition.properties?.[key];
@@ -167,10 +190,6 @@ function decodeFields(schema, form, validate) {
167
190
  }
168
191
  });
169
192
  }
170
- for (const child of Object.values(node.properties ?? {}))
171
- collectControllers(child);
172
- if (node.items)
173
- collectControllers(node.items);
174
193
  }
175
194
  collectControllers(schema);
176
195
  const result = visit(schema, [], 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/lite",
3
- "version": "0.23.2",
3
+ "version": "0.25.0",
4
4
  "description": "SSR-first lightweight admin UI for @svadmin with optional progressive enhancement",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "svelte": "^5.57.1",
59
- "@svadmin/core": ">=0.34.2 <0.73.0",
59
+ "@svadmin/core": ">=0.34.2 <0.75.0",
60
60
  "@sveltejs/kit": "^3.0.0",
61
61
  "qrcode": "^1.5.4"
62
62
  },