@shelchin/pda-sveltekit
v0.1.0
Published
Auto-render PDA applications with SvelteKit - schema-driven UI with customizable renderers
Readme
@shelchin/pda-sveltekit
PDA 应用的 Svelte UI 渲染层。传入一个 PDA 应用定义,自动生成完整的表单、进度、交互弹窗和结果展示界面。支持三种输入模式、六种布局、以及三层渲染器自定义。
特性
- Schema 驱动 — 从 Zod Schema 自动生成表单,零模板代码
- 三种输入模式 — 引导式(新手)、标准、专家
- 六种布局 — vertical / horizontal / progressive / split / compact / grid
- 三层渲染器 — 字段级、类型级、全局级自定义,逐级覆盖
- 交互弹窗 — 确认、输入、单选、多选、多步工作流
- 错误引导 — 结构化错误 + 可操作建议
- CSS 变量主题 — 通过
--pda-*变量定制外观
安装
bun add @shelchin/pda-sveltekitPeer 依赖: Svelte ^5, Zod ^3.23
快速开始
零配置用法
一行代码渲染完整应用 UI:
<script lang="ts">
import { PDAApp } from '@shelchin/pda-sveltekit';
import { myApp } from '$lib/my-app'; // PDA 应用定义
</script>
<PDAApp app={myApp} />这会自动生成:标题、表单字段、提交按钮、进度条、交互弹窗、结果展示。
带配置的用法
<PDAApp
app={myApp}
layout="progressive"
showHeader={true}
submitLabel="开始查询"
onComplete={(result) => console.log(result)}
onError={(error) => console.error(error)}
/>输入模式
通过 inputMode 和 showModeSwitch 启用三种模式切换:
<script lang="ts">
import { PDAApp, type InputMode } from '@shelchin/pda-sveltekit';
let mode = $state<InputMode>('standard');
</script>
<PDAApp
app={myApp}
inputMode={mode}
showModeSwitch={true}
onModeChange={(m) => mode = m}
/>| 模式 | 说明 | 适合 |
|------|------|------|
| guided | 逐步引导,一次一个字段 | 新手用户 |
| standard | 所有字段可见,分组展示 | 日常使用 |
| expert | 紧凑布局,一目了然 | 高级用户 |
切换模式时表单数据不丢失。
布局模式
<PDAApp app={myApp} layout="progressive" />| 布局 | 说明 |
|------|------|
| vertical | 默认,所有区域上下排列 |
| horizontal | 左右分栏,输入在左,状态/结果在右 |
| progressive | 焦点区域放大,其他区域淡出 |
| split | 网格布局,输入+状态左列,结果右列 |
| compact | 最小间距,所有内容紧凑排列 |
| grid | 表单字段自适应网格 |
可用 LayoutSwitcher 组件让用户自由切换:
<script>
import { LayoutSwitcher } from '@shelchin/pda-sveltekit';
let layout = $state('vertical');
</script>
<LayoutSwitcher value={layout} onChange={(l) => layout = l} />
<PDAApp app={myApp} {layout} />自定义渲染器
渲染器优先级(三层覆盖)
查找某个字段的渲染器时,按以下顺序:
- Props 传入 —
PDAApp.inputRenderers['fieldName'](最高优先级) - 全局注册 —
registerInputRenderer('address', AddressInput) - 内置默认 — string → StringInput, number → NumberInput 等
自定义输入渲染器
<!-- AddressInput.svelte -->
<script lang="ts">
import type { InputRendererProps } from '@shelchin/pda-sveltekit';
let { field, value, onChange, disabled, error }: InputRendererProps<string> = $props();
</script>
<div>
<label>{field.label}</label>
<textarea
{value}
oninput={(e) => onChange(e.currentTarget.value)}
placeholder={field.placeholder}
{disabled}
rows="4"
/>
{#if error}
<span class="error">{error}</span>
{/if}
</div>通过 Props 传入:
<PDAApp
app={myApp}
inputRenderers={{
addresses: AddressInput,
networks: NetworkSelector,
}}
/>或全局注册(影响所有 PDAApp 实例):
import { registerInputRenderer } from '@shelchin/pda-sveltekit';
registerInputRenderer('address', AddressInput);自定义输出渲染器
用 root key 替换整个结果展示区域:
<PDAApp
app={myApp}
outputRenderers={{
root: BalanceResultView, // 接管整个输出区域
}}
/><!-- BalanceResultView.svelte -->
<script lang="ts">
import type { RootOutputRendererProps } from '@shelchin/pda-sveltekit';
let { data, schema, appId }: RootOutputRendererProps<MyData> = $props();
</script>
<div class="results">
{#each data.results as item}
<div class="card">{item.address}: {item.balance}</div>
{/each}
</div>自定义表单渲染器
完全替换默认的逐字段表单:
<PDAApp
app={myApp}
formRenderer={MyCustomForm}
/><!-- MyCustomForm.svelte -->
<script lang="ts">
import type { FormRendererProps } from '@shelchin/pda-sveltekit';
let { value, onChange, disabled, mode }: FormRendererProps = $props();
</script>
<form>
<section>
<input bind:value={value.name} {disabled} />
</section>
<button onclick={() => onChange(value)} {disabled}>更新</button>
</form>错误引导
提供结构化错误信息和可操作建议:
<PDAApp
app={myApp}
errorGuide={(error) => ({
code: 'INSUFFICIENT_BALANCE',
message: '余额不足',
details: '当前钱包余额不足以完成此交易',
suggestions: [
{ label: '获取测试代币', type: 'external', url: 'https://faucet.example.com' },
{ label: '重试', type: 'retry' },
],
severity: 'error',
})}
/>Schema 中的 UI 提示
Zod 的 describe() 支持 JSON 格式的 UI 提示:
z.object({
address: z.string().describe(JSON.stringify({
label: '钱包地址',
placeholder: '0x...',
type: 'address', // 用于渲染器查找
widget: 'textarea', // StringInput 的 widget 提示
})),
network: z.enum(['ethereum', 'polygon']).describe('选择网络'),
})纯文本 describe 会作为 label 使用。
内置输入组件
| 组件 | 对应类型 | 说明 |
|------|---------|------|
| StringInput | string | 支持 textarea(通过 widget 提示) |
| NumberInput | number | HTML5 数字输入 |
| BooleanInput | boolean | 复选框 |
| EnumInput | enum | ≤5 选项用 chips,>5 用 dropdown |
| ArrayInput | array | 动态增减项,嵌套渲染 |
CSS 变量主题
:root {
--pda-bg-primary: #fff;
--pda-bg-secondary: #f5f5f7;
--pda-text-primary: #1d1d1f;
--pda-text-secondary: #86868b;
--pda-border: rgba(0, 0, 0, 0.08);
--pda-accent: #007aff;
--pda-success: #34c759;
--pda-warning: #ff9500;
--pda-error: #ff3b30;
}注意事项
- Svelte 5 必需 — 使用
$state、$derived、$props()等 rune 语法 - root 输出渲染器会完全接管 — 设置
outputRenderers.root后,字段级输出渲染器不生效 - Enum 自动切换 widget — >5 个选项自动从 chips 变为 dropdown,无法覆盖(除非用自定义渲染器)
- progress.total 必须提供 — 不提供会导致百分比显示 NaN
- 长数组性能 — ArrayInput 无虚拟化,1000+ 项会卡顿
- 深层嵌套 — 对象嵌套超过 5 层可能变慢
- File 类型未实现 — FieldType 定义了
file但没有内置 FileInput 组件 - 交互状态不持久 — 长时间交互被中断(关闭浏览器)会丢失状态
