@svadmin/surface 0.9.0 → 0.11.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.
- package/README.md +9 -3
- package/STYLING.md +4 -2
- package/compatibility.json +18 -7
- package/dist/agent-contract.d.ts +1 -1
- package/dist/agent-contract.js +5 -2
- package/dist/builtin-schemas.js +1 -1
- package/dist/business-data.d.ts +16 -0
- package/dist/business-data.js +42 -0
- package/dist/business-definitions.d.ts +28 -0
- package/dist/business-definitions.js +53 -0
- package/dist/business.d.ts +3 -0
- package/dist/business.js +17 -0
- package/dist/components/ActivityFeedWidget.svelte +34 -0
- package/dist/components/ActivityFeedWidget.svelte.d.ts +4 -0
- package/dist/components/MetricWidget.svelte +2 -1
- package/dist/components/MetricWidget.svelte.d.ts +1 -0
- package/dist/components/ResourceDetailWidget.svelte +56 -0
- package/dist/components/ResourceDetailWidget.svelte.d.ts +4 -0
- package/dist/components/ResourceFormBody.svelte +1 -1
- package/dist/components/ResourceTableWidget.svelte +2 -1
- package/dist/components/ResourceTableWidget.svelte.d.ts +1 -0
- package/dist/components/SurfaceEditPreview.svelte +1 -1
- package/dist/components/SurfaceRenderer.svelte +2 -1
- package/dist/components/SurfaceRenderer.svelte.d.ts +1 -0
- package/dist/design-contract.d.ts +2 -0
- package/dist/design-contract.js +2 -0
- package/dist/localization.d.ts +11 -1
- package/dist/localization.js +20 -0
- package/dist/openui.js +17 -1
- package/dist/recipes.d.ts +20 -0
- package/dist/recipes.js +20 -0
- package/dist/styles/{editor.generated.js → editor.js} +1 -1
- package/dist/styles.css +41 -1260
- package/dist/types.d.ts +1 -1
- package/dist/validation.js +12 -6
- package/dist/workflows/catalog.js +1 -1
- package/package.json +15 -8
- package/dist/styled-system/css/conditions.js +0 -36
- package/dist/styled-system/css/css.d.ts +0 -22
- package/dist/styled-system/css/css.js +0 -45
- package/dist/styled-system/css/cva.d.ts +0 -6
- package/dist/styled-system/css/cva.js +0 -87
- package/dist/styled-system/css/cx.d.ts +0 -5
- package/dist/styled-system/css/cx.js +0 -15
- package/dist/styled-system/css/index.d.ts +0 -5
- package/dist/styled-system/css/index.js +0 -4
- package/dist/styled-system/css/sva.d.ts +0 -4
- package/dist/styled-system/css/sva.js +0 -46
- package/dist/styled-system/design-contract.d.ts +0 -13
- package/dist/styled-system/design-contract.js +0 -11
- package/dist/styled-system/helpers.js +0 -316
- package/dist/styled-system/patterns/aspect-ratio.d.ts +0 -20
- package/dist/styled-system/patterns/aspect-ratio.js +0 -38
- package/dist/styled-system/patterns/bleed.d.ts +0 -21
- package/dist/styled-system/patterns/bleed.js +0 -24
- package/dist/styled-system/patterns/box.d.ts +0 -20
- package/dist/styled-system/patterns/box.js +0 -15
- package/dist/styled-system/patterns/center.d.ts +0 -20
- package/dist/styled-system/patterns/center.js +0 -21
- package/dist/styled-system/patterns/circle.d.ts +0 -20
- package/dist/styled-system/patterns/circle.js +0 -25
- package/dist/styled-system/patterns/container.d.ts +0 -20
- package/dist/styled-system/patterns/container.js +0 -21
- package/dist/styled-system/patterns/cq.d.ts +0 -21
- package/dist/styled-system/patterns/cq.js +0 -21
- package/dist/styled-system/patterns/divider.d.ts +0 -22
- package/dist/styled-system/patterns/divider.js +0 -25
- package/dist/styled-system/patterns/flex.d.ts +0 -26
- package/dist/styled-system/patterns/flex.js +0 -26
- package/dist/styled-system/patterns/float.d.ts +0 -23
- package/dist/styled-system/patterns/float.js +0 -52
- package/dist/styled-system/patterns/grid-item.d.ts +0 -25
- package/dist/styled-system/patterns/grid-item.js +0 -25
- package/dist/styled-system/patterns/grid.d.ts +0 -24
- package/dist/styled-system/patterns/grid.js +0 -27
- package/dist/styled-system/patterns/hstack.d.ts +0 -21
- package/dist/styled-system/patterns/hstack.js +0 -24
- package/dist/styled-system/patterns/index.d.ts +0 -21
- package/dist/styled-system/patterns/index.js +0 -20
- package/dist/styled-system/patterns/link-overlay.d.ts +0 -20
- package/dist/styled-system/patterns/link-overlay.js +0 -24
- package/dist/styled-system/patterns/spacer.d.ts +0 -20
- package/dist/styled-system/patterns/spacer.js +0 -25
- package/dist/styled-system/patterns/square.d.ts +0 -20
- package/dist/styled-system/patterns/square.js +0 -24
- package/dist/styled-system/patterns/stack.d.ts +0 -23
- package/dist/styled-system/patterns/stack.js +0 -24
- package/dist/styled-system/patterns/visually-hidden.d.ts +0 -20
- package/dist/styled-system/patterns/visually-hidden.js +0 -18
- package/dist/styled-system/patterns/vstack.d.ts +0 -21
- package/dist/styled-system/patterns/vstack.js +0 -24
- package/dist/styled-system/patterns/wrap.d.ts +0 -24
- package/dist/styled-system/patterns/wrap.js +0 -25
- package/dist/styled-system/recipes/content-header.d.ts +0 -31
- package/dist/styled-system/recipes/content-header.js +0 -65
- package/dist/styled-system/recipes/content-page.d.ts +0 -34
- package/dist/styled-system/recipes/content-page.js +0 -43
- package/dist/styled-system/recipes/create-recipe.js +0 -82
- package/dist/styled-system/recipes/index.d.ts +0 -17
- package/dist/styled-system/recipes/index.js +0 -16
- package/dist/styled-system/recipes/metric-block.d.ts +0 -34
- package/dist/styled-system/recipes/metric-block.js +0 -76
- package/dist/styled-system/recipes/product-list.d.ts +0 -31
- package/dist/styled-system/recipes/product-list.js +0 -73
- package/dist/styled-system/recipes/product-section.d.ts +0 -31
- package/dist/styled-system/recipes/product-section.js +0 -49
- package/dist/styled-system/recipes/product-settings-row.d.ts +0 -34
- package/dist/styled-system/recipes/product-settings-row.js +0 -58
- package/dist/styled-system/recipes/product-settings.d.ts +0 -31
- package/dist/styled-system/recipes/product-settings.js +0 -57
- package/dist/styled-system/recipes/product-status.d.ts +0 -34
- package/dist/styled-system/recipes/product-status.js +0 -45
- package/dist/styled-system/recipes/product-toolbar.d.ts +0 -31
- package/dist/styled-system/recipes/product-toolbar.js +0 -41
- package/dist/styled-system/recipes/product-workspace.d.ts +0 -34
- package/dist/styled-system/recipes/product-workspace.js +0 -58
- package/dist/styled-system/recipes/surface-metric.d.ts +0 -38
- package/dist/styled-system/recipes/surface-metric.js +0 -63
- package/dist/styled-system/recipes/surface-table.d.ts +0 -34
- package/dist/styled-system/recipes/surface-table.js +0 -62
- package/dist/styled-system/recipes/ui-badge.d.ts +0 -31
- package/dist/styled-system/recipes/ui-badge.js +0 -38
- package/dist/styled-system/recipes/ui-button.d.ts +0 -32
- package/dist/styled-system/recipes/ui-button.js +0 -43
- package/dist/styled-system/recipes/ui-input.d.ts +0 -31
- package/dist/styled-system/recipes/ui-input.js +0 -49
- package/dist/styled-system/recipes/ui-textarea.d.ts +0 -31
- package/dist/styled-system/recipes/ui-textarea.js +0 -24
- package/dist/styled-system/styles.css +0 -1257
- package/dist/styled-system/tokens/index.d.ts +0 -9
- package/dist/styled-system/tokens/index.js +0 -268
- package/dist/styled-system/tokens/tokens.d.ts +0 -36
- package/dist/styled-system/types/composition.d.ts +0 -227
- package/dist/styled-system/types/conditions.d.ts +0 -236
- package/dist/styled-system/types/csstype.d.ts +0 -22570
- package/dist/styled-system/types/index.d.ts +0 -6
- package/dist/styled-system/types/parts.d.ts +0 -8
- package/dist/styled-system/types/pattern.d.ts +0 -78
- package/dist/styled-system/types/prop-type.d.ts +0 -223
- package/dist/styled-system/types/recipe.d.ts +0 -181
- package/dist/styled-system/types/selectors.d.ts +0 -59
- package/dist/styled-system/types/static-css.d.ts +0 -56
- package/dist/styled-system/types/style-props.d.ts +0 -8088
- package/dist/styled-system/types/system-types.d.ts +0 -151
- /package/dist/styles/{editor.generated.d.ts → editor.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -6,11 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
The declared minimum combination is Core 0.53.0, UI 0.73.0, and Svelte
|
|
10
|
+
5.56.10, matching the published UI peer requirements. `compatibility.json`
|
|
11
|
+
lists verification targets, not proof of a successful run. Acceptance requires
|
|
12
|
+
the `minimum-supported` strict install and consumer checks in `pack:check` to
|
|
13
|
+
pass against registry artifacts; workspace tarballs do not replace that check.
|
|
14
|
+
|
|
9
15
|
```bash
|
|
10
16
|
bun add @svadmin/surface @svadmin/core @svadmin/ui @tanstack/svelte-query svelte @sinclair/typebox
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
Import protocol types and validation from the DOM-free root entry. Import rendering code from the Svelte subpath. Hosts do not need
|
|
19
|
+
Import protocol types and validation from the DOM-free root entry. Import rendering code from the Svelte subpath. Hosts do not need CSS compiler plugins to consume the package's static component styles.
|
|
14
20
|
|
|
15
21
|
```ts
|
|
16
22
|
import { validateSurfaceSpec, type SurfacePolicy, type SurfaceSpec } from '@svadmin/surface';
|
|
@@ -137,7 +143,7 @@ The Svelte `SurfaceEditPreview` component accepts a controlled revision and untr
|
|
|
137
143
|
|
|
138
144
|
It leaves the current surface intact during streaming, offers a separate preview/back action, and revalidates at the explicit apply click. It calls `onApply(candidate)` only for a valid proposal. The host performs any persistence with server authorization and atomic revision compare-and-swap, then updates `revision` and clears `proposal`. Missing handlers, invalid proposals and pending callbacks disable application. A rejected callback reports an error without changing the controlled revision. Changing scope does not authorize an old proposal: the host must discard old proposals as described above.
|
|
139
145
|
|
|
140
|
-
|
|
146
|
+
Preview controls use local finite class helpers and maintained static CSS. `@svadmin/surface/editor.css` remains available for explicit CSS collection. Density/button variants and complete-color semantic CSS variable overrides are preserved. Widgets use local semantic recipes to retain compatibility with older UI peers; no UI-generated helpers are copied during build. The separate `@svadmin/surface/styles.css` entry remains available and rendering components also import it automatically; see `STYLING.md`.
|
|
141
147
|
|
|
142
148
|
## Built-in catalog
|
|
143
149
|
|
|
@@ -182,4 +188,4 @@ Limits are eight data sources, 24 widgets, 100 rows per page, eight filters, thr
|
|
|
182
188
|
|
|
183
189
|
Actual OpenUI Lang parsing, nested interactive forms, registered business actions, server persistence, SSR/Lite rendering, arbitrary URLs, client aggregation, Canvas and iframe execution are not implemented. Aggregated metrics should come from a policy-authorized backend summary resource and bind through `resource-one`. The current UI contract remains read-only even though its definition can be edited.
|
|
184
190
|
|
|
185
|
-
See `docs/architecture/openui-surface-phase2.md` for the original feature scope and `docs/architecture/surface-integration-provenance.md` for
|
|
191
|
+
See `docs/architecture/openui-surface-phase2.md` for the original feature scope and `docs/architecture/surface-integration-provenance.md` for historical integration evidence. 中文指南见文档站的“声明式 Surface”。
|
package/STYLING.md
CHANGED
|
@@ -24,8 +24,10 @@ import { styledSurfaceCatalog, STYLED_SURFACE_CATALOG_VERSION } from '@svadmin/s
|
|
|
24
24
|
|
|
25
25
|
以上只是 widget 片段;完整 spec 仍需要 schemaVersion、catalogVersion、数据源、绑定及布局等必要字段。
|
|
26
26
|
|
|
27
|
-
Surface
|
|
27
|
+
Surface 使用本地有限 recipe helper 和静态 styles.css;design-contract 统一从 @svadmin/ui/design-contract 导入,要求 UI >=0.73.0 <0.74.0。不复制 UI 生成物,也不依赖样式编译器。渲染组件自动导入语义样式;原有 styles.css 子路径仍可显式导入。UI 基础样式仍由宿主通过 @svadmin/ui/app.css 加载。构建仅执行 svelte-package;styles:check 检查全部有限变体及静态 CSS 边界。
|
|
28
|
+
|
|
29
|
+
editor.css 与本地 styles/editor.ts 一起维护,保留 density、button variant、禁用/焦点状态和语义变量覆盖;editor.css 子路径不变。宿主升级时应同时升级 UI 与 Surface,并重新执行消费侧构建。
|
|
28
30
|
|
|
29
31
|
AI 只能选择公开枚举,不能指定 class/style、任意颜色、recipe 定义或可执行代码。字段权限、只读查询、版本匹配和宿主确认要求不因样式变体而改变。
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
此次仅替换样式基础设施。OpenUI 入口、catalog/schema 校验、workflow 授权与执行、提案预览及宿主确认逻辑保持不变;去除样式编译器不代表增加新的协议能力。
|
package/compatibility.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"surface": "0.
|
|
2
|
+
"surface": "0.11.x",
|
|
3
3
|
"minimumSupported": {
|
|
4
|
-
"@svadmin/core": "0.
|
|
5
|
-
"@svadmin/ui": "0.
|
|
6
|
-
"svelte": "5.56.
|
|
4
|
+
"@svadmin/core": "0.53.0",
|
|
5
|
+
"@svadmin/ui": "0.73.0",
|
|
6
|
+
"svelte": "5.56.10"
|
|
7
7
|
},
|
|
8
8
|
"testedCombinations": [
|
|
9
9
|
{
|
|
10
10
|
"name": "minimum-supported",
|
|
11
|
-
"core": "0.
|
|
12
|
-
"ui": "0.
|
|
13
|
-
"svelte": "5.56.
|
|
11
|
+
"core": "0.53.0",
|
|
12
|
+
"ui": "0.73.0",
|
|
13
|
+
"svelte": "5.56.10"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"name": "workspace-packed",
|
|
@@ -38,6 +38,17 @@
|
|
|
38
38
|
"./server/sqlite": {
|
|
39
39
|
"runtime": "Node.js",
|
|
40
40
|
"verification": "Current CI Node version with real node:sqlite; no browser or distributed-store support implied."
|
|
41
|
+
},
|
|
42
|
+
"./business": {
|
|
43
|
+
"status": "experimental",
|
|
44
|
+
"requiredUiCapabilities": [
|
|
45
|
+
"ActivityFeed.title",
|
|
46
|
+
"ActivityFeed.emptyLabel",
|
|
47
|
+
"ActivityFeed.countLabel"
|
|
48
|
+
],
|
|
49
|
+
"minimumPublishedUi": null,
|
|
50
|
+
"verification": "same-checkout business + interactive packed consumer; current Surface workflow reports",
|
|
51
|
+
"releaseGate": "Coordinate a tested UI release and verify the peer floor before publishing this new entry."
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
54
|
}
|
package/dist/agent-contract.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface SurfaceCatalogManifest {
|
|
|
13
13
|
readonly catalogVersion: string;
|
|
14
14
|
readonly widgets: readonly {
|
|
15
15
|
readonly type: string;
|
|
16
|
-
readonly dataKind: 'none' | 'scalar' | 'items';
|
|
16
|
+
readonly dataKind: 'none' | 'scalar' | 'items' | 'record';
|
|
17
17
|
readonly description?: string;
|
|
18
18
|
readonly propsSchema: JsonObject;
|
|
19
19
|
readonly examples?: readonly JsonObject[];
|
package/dist/agent-contract.js
CHANGED
|
@@ -100,8 +100,10 @@ export function createSurfaceCatalogManifest(catalog) {
|
|
|
100
100
|
throw new Error(`Invalid or duplicate surface widget type "${widget.type}"`);
|
|
101
101
|
}
|
|
102
102
|
known.add(widget.type);
|
|
103
|
-
if (!['none', 'scalar', 'items'].includes(widget.dataKind))
|
|
103
|
+
if (!['none', 'scalar', 'items', 'record'].includes(widget.dataKind))
|
|
104
104
|
throw new Error('Invalid surface widget data kind');
|
|
105
|
+
if (widget.dataKind === 'record' && !widget.getReferencedFields)
|
|
106
|
+
throw new Error('Record widgets require an explicit field selector');
|
|
105
107
|
const propsSchema = surfaceSchemaToJson(widget.propsSchema);
|
|
106
108
|
if (!isClosedObject(propsSchema))
|
|
107
109
|
throw new Error(`Widget "${widget.type}" must use a closed object props schema`);
|
|
@@ -171,7 +173,8 @@ export function createSurfaceGenerationSpecSchema(catalog, policy) {
|
|
|
171
173
|
...(widget.dataKind === 'none' ? {} : {
|
|
172
174
|
binding: Type.Object({
|
|
173
175
|
sourceId: surfaceIdSchema,
|
|
174
|
-
pointer: widget.dataKind === 'items' ? Type.Literal('/items')
|
|
176
|
+
pointer: widget.dataKind === 'items' ? Type.Literal('/items')
|
|
177
|
+
: widget.dataKind === 'record' ? Type.Literal('') : literals([...scalarPointers]),
|
|
175
178
|
}, { additionalProperties: false }),
|
|
176
179
|
}),
|
|
177
180
|
}, { additionalProperties: false, ...(widget.description ? { description: widget.description } : {}) });
|
package/dist/builtin-schemas.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ActivityFeedProps } from './business-definitions.js';
|
|
2
|
+
import type { JsonValue } from './types.js';
|
|
3
|
+
import { type WidgetValueResult } from './widget-data.js';
|
|
4
|
+
export interface SurfaceActivityItem {
|
|
5
|
+
id: string;
|
|
6
|
+
action: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
user: {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
target?: string;
|
|
12
|
+
comment?: string;
|
|
13
|
+
status?: 'info' | 'success' | 'warning' | 'destructive';
|
|
14
|
+
}
|
|
15
|
+
/** 只映射选中的字段;不推断操作者、补造时间或把未知状态伪装成成功。 */
|
|
16
|
+
export declare function asSurfaceActivities(value: JsonValue, props: ActivityFeedProps, unknownActor: string): WidgetValueResult<SurfaceActivityItem[]>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { asRecordArray } from './widget-data.js';
|
|
2
|
+
/** 只映射选中的字段;不推断操作者、补造时间或把未知状态伪装成成功。 */
|
|
3
|
+
export function asSurfaceActivities(value, props, unknownActor) {
|
|
4
|
+
const records = asRecordArray(value);
|
|
5
|
+
if (!records.ok)
|
|
6
|
+
return records;
|
|
7
|
+
if (records.value.length > 100)
|
|
8
|
+
return { ok: false, message: 'Too many activity records' };
|
|
9
|
+
const items = [];
|
|
10
|
+
const ids = new Set();
|
|
11
|
+
for (const record of records.value) {
|
|
12
|
+
const idValue = record[props.idField];
|
|
13
|
+
const action = record[props.actionField];
|
|
14
|
+
const timestamp = record[props.timestampField];
|
|
15
|
+
if (!((typeof idValue === 'string' && idValue.length > 0 && idValue.length <= 128)
|
|
16
|
+
|| (typeof idValue === 'number' && Number.isFinite(idValue)))
|
|
17
|
+
|| typeof action !== 'string' || !action.trim() || action.length > 500
|
|
18
|
+
|| typeof timestamp !== 'string' || !timestamp.trim() || timestamp.length > 128) {
|
|
19
|
+
return { ok: false, message: 'Activity identity, action or timestamp is invalid' };
|
|
20
|
+
}
|
|
21
|
+
// 数字 1 与字符串 "1" 不得造成 keyed each 身份冲突。
|
|
22
|
+
const id = `${typeof idValue}:${idValue}`;
|
|
23
|
+
if (ids.has(id))
|
|
24
|
+
return { ok: false, message: 'Duplicate activity identity' };
|
|
25
|
+
ids.add(id);
|
|
26
|
+
const optional = [props.actorField, props.targetField, props.commentField, props.statusField]
|
|
27
|
+
.map((field) => field === undefined ? undefined : record[field]);
|
|
28
|
+
if (optional.some((item) => item != null && (typeof item !== 'string' || item.length > 4000))) {
|
|
29
|
+
return { ok: false, message: 'Optional activity text is invalid' };
|
|
30
|
+
}
|
|
31
|
+
const [actor, target, comment, status] = optional;
|
|
32
|
+
if (status != null && !['info', 'success', 'warning', 'destructive'].includes(String(status))) {
|
|
33
|
+
return { ok: false, message: 'Unknown activity status' };
|
|
34
|
+
}
|
|
35
|
+
items.push({ id, action, timestamp, user: { name: typeof actor === 'string' && actor.trim() ? actor : unknownActor },
|
|
36
|
+
...(typeof target === 'string' ? { target } : {}),
|
|
37
|
+
...(typeof comment === 'string' ? { comment } : {}),
|
|
38
|
+
...(typeof status === 'string' ? { status: status } : {}),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, value: items };
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Static } from '@sinclair/typebox';
|
|
2
|
+
import type { SurfaceCatalog } from './types.js';
|
|
3
|
+
export declare const resourceDetailPropsSchema: import("@sinclair/typebox").TObject<{
|
|
4
|
+
fields: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
5
|
+
field: import("@sinclair/typebox").TString;
|
|
6
|
+
label: import("@sinclair/typebox").TString;
|
|
7
|
+
format: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<string>[]>>;
|
|
8
|
+
}>>;
|
|
9
|
+
title: import("@sinclair/typebox").TString;
|
|
10
|
+
tone: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"info" | "warning" | "success" | "neutral" | "danger">[]>>;
|
|
11
|
+
density: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"compact" | "comfortable">[]>>;
|
|
12
|
+
}>;
|
|
13
|
+
export declare const activityFeedPropsSchema: import("@sinclair/typebox").TObject<{
|
|
14
|
+
idField: import("@sinclair/typebox").TString;
|
|
15
|
+
actionField: import("@sinclair/typebox").TString;
|
|
16
|
+
timestampField: import("@sinclair/typebox").TString;
|
|
17
|
+
actorField: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
18
|
+
targetField: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
19
|
+
commentField: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
20
|
+
statusField: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
21
|
+
title: import("@sinclair/typebox").TString;
|
|
22
|
+
tone: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"info" | "warning" | "success" | "neutral" | "danger">[]>>;
|
|
23
|
+
density: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"compact" | "comfortable">[]>>;
|
|
24
|
+
}>;
|
|
25
|
+
export type ResourceDetailProps = Static<typeof resourceDetailPropsSchema>;
|
|
26
|
+
export type ActivityFeedProps = Static<typeof activityFeedPropsSchema>;
|
|
27
|
+
/** 显式升级目录;旧目录、表单操作及默认权限不变。 */
|
|
28
|
+
export declare function createBusinessSurfaceDefinitions(base?: SurfaceCatalog): SurfaceCatalog;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
3
|
+
import { styledSurfaceDefinitions } from './builtin-definitions.js';
|
|
4
|
+
import { surfaceDesignContract } from './design-contract.js';
|
|
5
|
+
const field = Type.String({ minLength: 1, maxLength: 64, pattern: '^[A-Za-z][A-Za-z0-9_-]*$' });
|
|
6
|
+
const presentation = {
|
|
7
|
+
title: Type.String({ minLength: 1, maxLength: 80 }),
|
|
8
|
+
tone: Type.Optional(Type.Union(surfaceDesignContract.metric.tone.map((value) => Type.Literal(value)))),
|
|
9
|
+
density: Type.Optional(Type.Union(surfaceDesignContract.metric.density.map((value) => Type.Literal(value)))),
|
|
10
|
+
};
|
|
11
|
+
export const resourceDetailPropsSchema = Type.Object({
|
|
12
|
+
...presentation,
|
|
13
|
+
fields: Type.Array(Type.Object({
|
|
14
|
+
field, label: Type.String({ minLength: 1, maxLength: 60 }),
|
|
15
|
+
format: Type.Optional(Type.Union(['text', 'number', 'date', 'boolean'].map((value) => Type.Literal(value)))),
|
|
16
|
+
}, { additionalProperties: false }), { minItems: 1, maxItems: 16 }),
|
|
17
|
+
}, { additionalProperties: false });
|
|
18
|
+
export const activityFeedPropsSchema = Type.Object({
|
|
19
|
+
...presentation,
|
|
20
|
+
idField: field, actionField: field, timestampField: field,
|
|
21
|
+
actorField: Type.Optional(field), targetField: Type.Optional(field),
|
|
22
|
+
commentField: Type.Optional(field), statusField: Type.Optional(field),
|
|
23
|
+
}, { additionalProperties: false });
|
|
24
|
+
const definitions = [
|
|
25
|
+
{
|
|
26
|
+
type: 'resource-detail', dataKind: 'record', propsSchema: resourceDetailPropsSchema,
|
|
27
|
+
description: 'Read-only detail of one authorized record. Bind a permitted resource-one source with pointer "". Select up to sixteen distinct readable fields; never provide record values in props.',
|
|
28
|
+
examples: [{ title: 'Contact details', fields: [{ field: 'name', label: 'Name' }], density: 'compact' }],
|
|
29
|
+
getReferencedFields: (props) => {
|
|
30
|
+
const fields = Value.Decode(resourceDetailPropsSchema, props).fields.map((item) => item.field);
|
|
31
|
+
if (new Set(fields).size !== fields.length)
|
|
32
|
+
throw new Error('Detail fields must be distinct');
|
|
33
|
+
return fields;
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'activity-feed', dataKind: 'items', propsSchema: activityFeedPropsSchema,
|
|
38
|
+
description: 'Read-only activity timeline from an authorized resource-list /items binding. Explicitly map readable ID, action and timestamp fields; optional actor/target/comment/status fields also require read permission. No comment composer or mutations. Backend owns ordering and event authenticity; this is not proof of an audit trail.',
|
|
39
|
+
examples: [{ title: 'Recent activity', idField: 'id', actionField: 'action', timestampField: 'at', tone: 'info' }],
|
|
40
|
+
getReferencedFields: (props) => {
|
|
41
|
+
const p = Value.Decode(activityFeedPropsSchema, props);
|
|
42
|
+
return [p.idField, p.actionField, p.timestampField, p.actorField, p.targetField, p.commentField, p.statusField]
|
|
43
|
+
.filter((name) => typeof name === 'string');
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
/** 显式升级目录;旧目录、表单操作及默认权限不变。 */
|
|
48
|
+
export function createBusinessSurfaceDefinitions(base = styledSurfaceDefinitions) {
|
|
49
|
+
const reserved = new Set(definitions.map((widget) => widget.type));
|
|
50
|
+
if (base.widgets.some((widget) => reserved.has(widget.type)))
|
|
51
|
+
throw new Error('Business widget type is already registered');
|
|
52
|
+
return { version: `${base.version}+business/v1`, widgets: [...base.widgets, ...definitions] };
|
|
53
|
+
}
|
package/dist/business.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSurfaceCatalog, styledSurfaceCatalog } from './catalog.js';
|
|
2
|
+
import { createBusinessSurfaceDefinitions } from './business-definitions.js';
|
|
3
|
+
import ResourceDetailWidget from './components/ResourceDetailWidget.svelte';
|
|
4
|
+
import ActivityFeedWidget from './components/ActivityFeedWidget.svelte';
|
|
5
|
+
/** 同一份 DOM-free 定义用于模型提示、服务端校验及真实组件。 */
|
|
6
|
+
export function createBusinessSurfaceCatalog(base = styledSurfaceCatalog) {
|
|
7
|
+
const definitions = createBusinessSurfaceDefinitions(base);
|
|
8
|
+
const components = new Map(base.widgets.map((widget) => [widget.type, widget.component]));
|
|
9
|
+
components.set('resource-detail', ResourceDetailWidget);
|
|
10
|
+
components.set('activity-feed', ActivityFeedWidget);
|
|
11
|
+
return defineSurfaceCatalog({ ...definitions, widgets: definitions.widgets.map((widget) => {
|
|
12
|
+
const component = components.get(widget.type);
|
|
13
|
+
if (!component)
|
|
14
|
+
throw new Error(`Missing renderer for ${widget.type}`);
|
|
15
|
+
return { ...widget, component };
|
|
16
|
+
}) });
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
3
|
+
import ActivityFeed from '@svadmin/ui/components/ActivityFeed.svelte';
|
|
4
|
+
import { activityFeedPropsSchema } from '../business-definitions.js';
|
|
5
|
+
import { asSurfaceActivities } from '../business-data.js';
|
|
6
|
+
import type { SurfaceWidgetRendererProps } from '../catalog.js';
|
|
7
|
+
import { surfaceMetric } from '../recipes.js';
|
|
8
|
+
import { formatSurfaceMessage, resolveSurfaceMessages } from '../localization.js';
|
|
9
|
+
|
|
10
|
+
let { props, data, locale = 'en-US', messages }: SurfaceWidgetRendererProps = $props();
|
|
11
|
+
const baseProps = $derived(Object.fromEntries(Object.entries(props).filter(([key]) => key !== 'appearance')));
|
|
12
|
+
const p = $derived(Value.Decode(activityFeedPropsSchema, baseProps));
|
|
13
|
+
const appearance = $derived(typeof props['appearance'] === 'object' && props['appearance'] !== null ? props['appearance'] as { tone?: typeof p.tone; density?: typeof p.density } : undefined);
|
|
14
|
+
const tone = $derived(appearance?.tone ?? p.tone ?? 'neutral');
|
|
15
|
+
const density = $derived(appearance?.density ?? p.density ?? 'comfortable');
|
|
16
|
+
const classes = $derived(surfaceMetric({ tone, density }));
|
|
17
|
+
const text = $derived(resolveSurfaceMessages(locale, messages));
|
|
18
|
+
const activities = $derived(data.status === 'ready' ? asSurfaceActivities(data.value, p, text.activityUnknownActor) : null);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<section class={classes.root + ' ' + classes.card} aria-label={p.title} data-surface-business="activity-feed" data-surface-density={density}>
|
|
22
|
+
{#if data.status === 'loading'}
|
|
23
|
+
<p role="status">{text.activityLoading}</p>
|
|
24
|
+
{:else if data.status === 'empty'}
|
|
25
|
+
<p role="status">{text.activityEmpty}</p>
|
|
26
|
+
{:else if data.status === 'error'}
|
|
27
|
+
<p role="alert">{text.activityUnavailable}</p>
|
|
28
|
+
{:else if activities?.ok}
|
|
29
|
+
<ActivityFeed activities={activities.value} allowComment={false} title={p.title} emptyLabel={text.activityEmpty}
|
|
30
|
+
countLabel={formatSurfaceMessage(text.activityCount, { count: activities.value.length })} />
|
|
31
|
+
{:else}
|
|
32
|
+
<p role="alert">{text.activityInvalidData}</p>
|
|
33
|
+
{/if}
|
|
34
|
+
</section>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Value } from '@sinclair/typebox/value';
|
|
3
|
-
import { surfaceMetric } from '../
|
|
3
|
+
import { surfaceMetric } from '../recipes.js';
|
|
4
|
+
import '../styles.css';
|
|
4
5
|
import StatsCard from '@svadmin/ui/components/StatsCard.svelte';
|
|
5
6
|
import { styledMetricPropsSchema as metricPropsSchema } from '../builtin-schemas.js';
|
|
6
7
|
import type { SurfaceWidgetRendererProps } from '../catalog.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
3
|
+
import Card from '@svadmin/ui/components/ui/card/card.svelte';
|
|
4
|
+
import CardHeader from '@svadmin/ui/components/ui/card/card-header.svelte';
|
|
5
|
+
import CardTitle from '@svadmin/ui/components/ui/card/card-title.svelte';
|
|
6
|
+
import CardContent from '@svadmin/ui/components/ui/card/card-content.svelte';
|
|
7
|
+
import { resourceDetailPropsSchema } from '../business-definitions.js';
|
|
8
|
+
import type { SurfaceWidgetRendererProps } from '../catalog.js';
|
|
9
|
+
import type { JsonObject } from '../types.js';
|
|
10
|
+
import { surfaceMetric } from '../recipes.js';
|
|
11
|
+
import { resolveSurfaceMessages } from '../localization.js';
|
|
12
|
+
import { displayTableValue } from '../widget-data.js';
|
|
13
|
+
|
|
14
|
+
let { props, data, locale = 'en-US', messages }: SurfaceWidgetRendererProps = $props();
|
|
15
|
+
const baseProps = $derived(Object.fromEntries(Object.entries(props).filter(([key]) => key !== 'appearance')));
|
|
16
|
+
const p = $derived(Value.Decode(resourceDetailPropsSchema, baseProps));
|
|
17
|
+
const appearance = $derived(typeof props['appearance'] === 'object' && props['appearance'] !== null ? props['appearance'] as { tone?: typeof p.tone; density?: typeof p.density } : undefined);
|
|
18
|
+
const tone = $derived(appearance?.tone ?? p.tone ?? 'neutral');
|
|
19
|
+
const density = $derived(appearance?.density ?? p.density ?? 'comfortable');
|
|
20
|
+
const classes = $derived(surfaceMetric({ tone, density }));
|
|
21
|
+
const text = $derived(resolveSurfaceMessages(locale, messages));
|
|
22
|
+
const record = $derived(data.status === 'ready' && data.value !== null && typeof data.value === 'object' && !Array.isArray(data.value)
|
|
23
|
+
? data.value as JsonObject : null);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<Card class={classes.root + ' ' + classes.card} data-surface-business="resource-detail" data-surface-density={density}>
|
|
27
|
+
<CardHeader><CardTitle>{p.title}</CardTitle></CardHeader>
|
|
28
|
+
<CardContent>
|
|
29
|
+
{#if data.status === 'loading'}
|
|
30
|
+
<p role="status">{text.detailLoading}</p>
|
|
31
|
+
{:else if data.status === 'empty'}
|
|
32
|
+
<p role="status">{text.detailEmpty}</p>
|
|
33
|
+
{:else if data.status === 'error'}
|
|
34
|
+
<p role="alert">{text.detailUnavailable}</p>
|
|
35
|
+
{:else if record}
|
|
36
|
+
<dl aria-label={p.title} data-density={density}>
|
|
37
|
+
{#each p.fields as item, index (`${index}:${item.field}`)}
|
|
38
|
+
<div>
|
|
39
|
+
<dt>{item.label}</dt>
|
|
40
|
+
<dd>{displayTableValue(record[item.field], { format: item.format, locale, messages: text })}</dd>
|
|
41
|
+
</div>
|
|
42
|
+
{/each}
|
|
43
|
+
</dl>
|
|
44
|
+
{:else}
|
|
45
|
+
<p role="alert">{text.detailInvalidData}</p>
|
|
46
|
+
{/if}
|
|
47
|
+
</CardContent>
|
|
48
|
+
</Card>
|
|
49
|
+
|
|
50
|
+
<style>
|
|
51
|
+
dl { display: grid; gap: 1rem; margin: 0; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); }
|
|
52
|
+
dl > div { min-width: 0; }
|
|
53
|
+
dt { color: var(--muted-foreground); font-size: .8125rem; }
|
|
54
|
+
dd { margin: .25rem 0 0; color: var(--foreground); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
|
|
55
|
+
dl[data-density='compact'] { gap: .5rem; }
|
|
56
|
+
</style>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SurfaceWidgetRendererProps } from '../catalog.js';
|
|
2
|
+
declare const ResourceDetailWidget: import("svelte").Component<SurfaceWidgetRendererProps, {}, "">;
|
|
3
|
+
type ResourceDetailWidget = ReturnType<typeof ResourceDetailWidget>;
|
|
4
|
+
export default ResourceDetailWidget;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { SurfaceWorkflowHost } from '../workflows/context.js';
|
|
7
7
|
import type { SurfaceActionDescriptor } from '../workflows/types.js';
|
|
8
8
|
import type { JsonObject } from '../types.js';
|
|
9
|
-
import { editorClasses, editorButtonClasses } from '../styles/editor.
|
|
9
|
+
import { editorClasses, editorButtonClasses } from '../styles/editor.js';
|
|
10
10
|
import '../styles/editor.css';
|
|
11
11
|
let { widgetId, host, action, title, locale }: {
|
|
12
12
|
widgetId: string; host: SurfaceWorkflowHost; action: SurfaceActionDescriptor; title: string; locale: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Value } from '@sinclair/typebox/value';
|
|
3
|
-
import { surfaceTable } from '../
|
|
3
|
+
import { surfaceTable } from '../recipes.js';
|
|
4
|
+
import '../styles.css';
|
|
4
5
|
import CardContent from '@svadmin/ui/components/ui/card/card-content.svelte';
|
|
5
6
|
import CardHeader from '@svadmin/ui/components/ui/card/card-header.svelte';
|
|
6
7
|
import CardTitle from '@svadmin/ui/components/ui/card/card-title.svelte';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { SurfaceRenderCatalog } from '../catalog.js';
|
|
7
7
|
import type { SurfaceDataProvider, SurfacePolicy } from '../types.js';
|
|
8
8
|
import SurfaceRenderer from './SurfaceRenderer.svelte';
|
|
9
|
-
import { editorClasses, editorButtonClasses } from '../styles/editor.
|
|
9
|
+
import { editorClasses, editorButtonClasses } from '../styles/editor.js';
|
|
10
10
|
import '../styles/editor.css';
|
|
11
11
|
|
|
12
12
|
export interface SurfaceEditPreviewProps {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
import { validateSurfaceSpec } from '../validation.js';
|
|
17
17
|
import { createSurfaceSourceCache, sameSourceIdentity, snapshotSurfaceSource } from '../source-cache.js';
|
|
18
18
|
import type { SurfaceSourceRequest } from '../source-cache.js';
|
|
19
|
-
import { surfaceMetric } from '../
|
|
19
|
+
import { surfaceMetric } from '../recipes.js';
|
|
20
|
+
import '../styles.css';
|
|
20
21
|
import { withoutSurfaceAppearance } from '../workflows/catalog.js';
|
|
21
22
|
|
|
22
23
|
function appearanceClasses(props: JsonObject): string {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SurfaceRenderCatalog } from '../catalog.js';
|
|
2
2
|
import type { SurfaceMessages } from '../localization.js';
|
|
3
3
|
import type { SurfaceDataError, SurfaceDataProvider, SurfacePolicy, SurfaceValidationIssue } from '../types.js';
|
|
4
|
+
import '../styles.css';
|
|
4
5
|
export type SurfaceRendererError = {
|
|
5
6
|
readonly type: 'validation';
|
|
6
7
|
readonly issues: readonly SurfaceValidationIssue[];
|
package/dist/localization.d.ts
CHANGED
|
@@ -11,8 +11,18 @@ export interface SurfaceMessages {
|
|
|
11
11
|
readonly tableNoRecords: string;
|
|
12
12
|
readonly tableInvalidData: string;
|
|
13
13
|
readonly tableUnavailable: string;
|
|
14
|
+
readonly detailLoading?: string;
|
|
15
|
+
readonly detailEmpty?: string;
|
|
16
|
+
readonly detailUnavailable?: string;
|
|
17
|
+
readonly detailInvalidData?: string;
|
|
18
|
+
readonly activityLoading?: string;
|
|
19
|
+
readonly activityEmpty?: string;
|
|
20
|
+
readonly activityUnavailable?: string;
|
|
21
|
+
readonly activityInvalidData?: string;
|
|
22
|
+
readonly activityUnknownActor?: string;
|
|
23
|
+
readonly activityCount?: string;
|
|
14
24
|
readonly booleanTrue: string;
|
|
15
25
|
readonly booleanFalse: string;
|
|
16
26
|
}
|
|
17
|
-
export declare function resolveSurfaceMessages(locale: string, overrides?: Partial<SurfaceMessages>): SurfaceMessages
|
|
27
|
+
export declare function resolveSurfaceMessages(locale: string, overrides?: Partial<SurfaceMessages>): Required<SurfaceMessages>;
|
|
18
28
|
export declare function formatSurfaceMessage(template: string, params: Readonly<Record<string, string | number>>): string;
|
package/dist/localization.js
CHANGED
|
@@ -11,6 +11,16 @@ const englishMessages = {
|
|
|
11
11
|
tableNoRecords: 'No records',
|
|
12
12
|
tableInvalidData: 'Table data is invalid',
|
|
13
13
|
tableUnavailable: 'Table data is unavailable',
|
|
14
|
+
detailLoading: 'Loading record',
|
|
15
|
+
detailEmpty: 'No record',
|
|
16
|
+
detailUnavailable: 'Record is unavailable',
|
|
17
|
+
detailInvalidData: 'Record data is invalid',
|
|
18
|
+
activityLoading: 'Loading activity',
|
|
19
|
+
activityEmpty: 'No recent activity',
|
|
20
|
+
activityUnavailable: 'Activity is unavailable',
|
|
21
|
+
activityInvalidData: 'Activity data is invalid',
|
|
22
|
+
activityUnknownActor: 'Unknown actor',
|
|
23
|
+
activityCount: '{count} events',
|
|
14
24
|
booleanTrue: 'Yes',
|
|
15
25
|
booleanFalse: 'No',
|
|
16
26
|
};
|
|
@@ -27,6 +37,16 @@ const simplifiedChineseMessages = {
|
|
|
27
37
|
tableNoRecords: '暂无记录',
|
|
28
38
|
tableInvalidData: '表格数据无效',
|
|
29
39
|
tableUnavailable: '表格数据不可用',
|
|
40
|
+
detailLoading: '正在加载记录',
|
|
41
|
+
detailEmpty: '暂无记录',
|
|
42
|
+
detailUnavailable: '记录不可用',
|
|
43
|
+
detailInvalidData: '记录数据无效',
|
|
44
|
+
activityLoading: '正在加载动态',
|
|
45
|
+
activityEmpty: '暂无动态',
|
|
46
|
+
activityUnavailable: '动态不可用',
|
|
47
|
+
activityInvalidData: '动态数据无效',
|
|
48
|
+
activityUnknownActor: '未知操作者',
|
|
49
|
+
activityCount: '{count} 条动态',
|
|
30
50
|
booleanTrue: '是',
|
|
31
51
|
booleanFalse: '否',
|
|
32
52
|
};
|
package/dist/openui.js
CHANGED
|
@@ -43,6 +43,21 @@ function element(value) {
|
|
|
43
43
|
&& typeof value.typeName === 'string'
|
|
44
44
|
&& !!value.props && typeof value.props === 'object';
|
|
45
45
|
}
|
|
46
|
+
function surfaceDataSource(value) {
|
|
47
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
48
|
+
return false;
|
|
49
|
+
const source = value;
|
|
50
|
+
if (typeof source['id'] !== 'string' || typeof source['resource'] !== 'string')
|
|
51
|
+
return false;
|
|
52
|
+
if (source['type'] === 'resource-one') {
|
|
53
|
+
return typeof source['recordId'] === 'string' || typeof source['recordId'] === 'number';
|
|
54
|
+
}
|
|
55
|
+
if (source['type'] !== 'resource-list')
|
|
56
|
+
return false;
|
|
57
|
+
if (source['pageSize'] !== undefined && typeof source['pageSize'] !== 'number')
|
|
58
|
+
return false;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
46
61
|
export function createSurfaceOpenUIStream(options) {
|
|
47
62
|
const { catalog, policy } = options;
|
|
48
63
|
const schema = createSurfaceOpenUISchema(catalog);
|
|
@@ -77,7 +92,8 @@ export function createSurfaceOpenUIStream(options) {
|
|
|
77
92
|
for (const source of sources) {
|
|
78
93
|
if (source === null && !final)
|
|
79
94
|
continue;
|
|
80
|
-
if (!element(source) || source.typeName !== 'Source' || source.partial
|
|
95
|
+
if (!element(source) || source.typeName !== 'Source' || source.partial
|
|
96
|
+
|| !isJsonValue(source.props['value']) || !surfaceDataSource(source.props['value'])) {
|
|
81
97
|
throw new SurfaceOpenUIError('syntax', 'Invalid Source node');
|
|
82
98
|
}
|
|
83
99
|
dataSources.push(source.props['value']);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SurfaceDensity, SurfaceTone } from './design-contract.js';
|
|
2
|
+
export declare function surfaceMetric({ tone, density, }?: {
|
|
3
|
+
tone?: SurfaceTone | undefined;
|
|
4
|
+
density?: SurfaceDensity | undefined;
|
|
5
|
+
}): {
|
|
6
|
+
root: string;
|
|
7
|
+
card: string;
|
|
8
|
+
description: string;
|
|
9
|
+
state: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function surfaceTable({ density }?: {
|
|
12
|
+
density?: SurfaceDensity | undefined;
|
|
13
|
+
}): {
|
|
14
|
+
root: string;
|
|
15
|
+
header: string;
|
|
16
|
+
content: string;
|
|
17
|
+
head: string;
|
|
18
|
+
cell: string;
|
|
19
|
+
state: string;
|
|
20
|
+
};
|
package/dist/recipes.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// 有限语义变体与静态 CSS 配套;不接受模型提供的 class/style。
|
|
2
|
+
export function surfaceMetric({ tone = 'neutral', density = 'comfortable', } = {}) {
|
|
3
|
+
return {
|
|
4
|
+
root: 'svadmin-surface-metric__root',
|
|
5
|
+
card: `svadmin-surface-metric__card svadmin-surface-tone--${tone} svadmin-surface-density--${density}`,
|
|
6
|
+
description: 'svadmin-surface-metric__description',
|
|
7
|
+
state: `svadmin-surface-metric__state svadmin-surface-tone--${tone} svadmin-surface-density--${density}`,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function surfaceTable({ density = 'comfortable' } = {}) {
|
|
11
|
+
const variant = `svadmin-surface-density--${density}`;
|
|
12
|
+
return {
|
|
13
|
+
root: 'svadmin-surface-table__root',
|
|
14
|
+
header: `svadmin-surface-table__header ${variant}`,
|
|
15
|
+
content: `svadmin-surface-table__content ${variant}`,
|
|
16
|
+
head: `svadmin-surface-table__head ${variant}`,
|
|
17
|
+
cell: `svadmin-surface-table__cell ${variant}`,
|
|
18
|
+
state: `svadmin-surface-table__state ${variant}`,
|
|
19
|
+
};
|
|
20
|
+
}
|