@burgantech/pseudo-ui 0.1.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 +336 -0
- package/dist/_virtual/_plugin-vue_export-helper.js +9 -0
- package/dist/adapters/vue/DynamicRenderer.vue.d.ts +8 -0
- package/dist/adapters/vue/DynamicRenderer.vue.js +7 -0
- package/dist/adapters/vue/DynamicRenderer.vue2.js +1302 -0
- package/dist/adapters/vue/ErrorBoundary.vue.d.ts +16 -0
- package/dist/adapters/vue/ErrorBoundary.vue.js +7 -0
- package/dist/adapters/vue/ErrorBoundary.vue2.js +27 -0
- package/dist/adapters/vue/NestedComponentWrapper.vue.d.ts +15 -0
- package/dist/adapters/vue/NestedComponentWrapper.vue.js +96 -0
- package/dist/adapters/vue/NestedComponentWrapper.vue2.js +4 -0
- package/dist/adapters/vue/PseudoView.vue.d.ts +18 -0
- package/dist/adapters/vue/PseudoView.vue.js +59 -0
- package/dist/adapters/vue/PseudoView.vue2.js +4 -0
- package/dist/adapters/vue/index.d.ts +6 -0
- package/dist/adapters/vue/index.js +17 -0
- package/dist/adapters/vue/injection.d.ts +3 -0
- package/dist/adapters/vue/injection.js +14 -0
- package/dist/adapters/vue/useFormContext.d.ts +4 -0
- package/dist/adapters/vue/useFormContext.js +35 -0
- package/dist/adapters/vue/useLookups.d.ts +4 -0
- package/dist/adapters/vue/useLookups.js +38 -0
- package/dist/engine/conditionalEngine.d.ts +6 -0
- package/dist/engine/conditionalEngine.js +56 -0
- package/dist/engine/dataClient.d.ts +5 -0
- package/dist/engine/dataClient.js +39 -0
- package/dist/engine/expressionResolver.d.ts +13 -0
- package/dist/engine/expressionResolver.js +136 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/schemaResolver.d.ts +14 -0
- package/dist/engine/schemaResolver.js +97 -0
- package/dist/engine/types.d.ts +155 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +23 -0
- package/dist/pseudo-ui.css +1 -0
- package/package.json +57 -0
- package/vocabularies/view-model-vocabulary.json +474 -0
- package/vocabularies/view-vocabulary.json +1104 -0
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# @burgantech/pseudo-ui
|
|
2
|
+
|
|
3
|
+
A **Server-Driven UI (SDUI)** rendering engine for TypeScript. Define your UI once with JSON — render it natively on any platform.
|
|
4
|
+
|
|
5
|
+
The SDK pairs a **JSON Schema** data contract with a **View JSON** component tree to produce fully interactive forms, summaries, and multi-step workflows without shipping new client code.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **30+ Material Design 3 components** — TextField, Dropdown, DatePicker, TabView, Dialog, Stepper, Carousel, and more
|
|
10
|
+
- **Delegate-driven architecture** — the SDK never makes HTTP calls; your app provides data via a simple interface
|
|
11
|
+
- **Expression engine** — `$form`, `$instance`, `$param`, `$ui`, `$lov`, `$lookup`, `$schema`, `$item`, `$context` namespaces for dynamic value resolution
|
|
12
|
+
- **Conditional engine** — `showIf` / `hideIf` / `enableIf` / `disableIf` with `allOf`, `anyOf`, `not` compound rules and 13 operators
|
|
13
|
+
- **Validation engine** — JSON Schema validation (`pattern`, `format`, `minLength`, `min/max`) plus async custom validation via delegate
|
|
14
|
+
- **LOV & Lookup** — List-of-Values dropdowns with cascade filtering and real-time data enrichment
|
|
15
|
+
- **Nested components** — reusable sub-components with isolated contexts, input contracts (`x-binding`), and two-way data flow
|
|
16
|
+
- **`$ui` state** — transient UI state (dialog visibility, active tab) that never pollutes form data
|
|
17
|
+
- **Accessibility** — `aria-required`, `aria-invalid`, `aria-label` on all inputs; `role="alert"` on errors
|
|
18
|
+
- **Error boundaries** — `Component` and `ForEach` nodes are wrapped to prevent child crashes from taking down the app
|
|
19
|
+
- **Multi-language** — all labels, errors, and enum values support `{ "en": "...", "tr": "...", "ar": "..." }`
|
|
20
|
+
- **View & ViewModel vocabularies** — included JSON Schema definitions for tooling and IDE auto-complete
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @burgantech/pseudo-ui
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Peer dependencies
|
|
29
|
+
|
|
30
|
+
The Vue adapter requires these in your project:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install vue@^3.5 primevue@^4.5 @primeuix/themes@^2 primeicons@^7
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### 1. Set up the delegate
|
|
39
|
+
|
|
40
|
+
The SDK uses **inversion of control** — you implement `PseudoViewDelegate` to supply data, load nested components, and handle actions:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import type { PseudoViewDelegate } from '@burgantech/pseudo-ui'
|
|
44
|
+
|
|
45
|
+
const delegate: PseudoViewDelegate = {
|
|
46
|
+
async requestData(ref, params) {
|
|
47
|
+
const res = await fetch(`/api/functions/${ref}`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: { 'Content-Type': 'application/json' },
|
|
50
|
+
body: JSON.stringify({ params }),
|
|
51
|
+
})
|
|
52
|
+
return res.json()
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
async loadComponent(ref) {
|
|
56
|
+
const [schema, view] = await Promise.all([
|
|
57
|
+
fetch(`/api/components/${ref}/schema.json`).then(r => r.json()),
|
|
58
|
+
fetch(`/api/components/${ref}/view.json`).then(r => r.json()),
|
|
59
|
+
])
|
|
60
|
+
return { schema, view }
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async onAction(action, formData, command) {
|
|
64
|
+
if (action === 'submit') {
|
|
65
|
+
await fetch('/api/submit', {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify(formData),
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Optional: custom server-side validation
|
|
74
|
+
async onValidationRequest(field, value, formData) {
|
|
75
|
+
const res = await fetch(`/api/validate/${field}`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
body: JSON.stringify({ value, formData }),
|
|
79
|
+
})
|
|
80
|
+
const { error } = await res.json()
|
|
81
|
+
return error ?? null
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Optional: capture SDK internal logs
|
|
85
|
+
onLog(level, message, error, context) {
|
|
86
|
+
console[level](`[pseudo-ui] ${message}`, context ?? '')
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. Provide the delegate and render
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import { ref, onMounted } from 'vue'
|
|
96
|
+
import { provideDelegate } from '@burgantech/pseudo-ui/vue'
|
|
97
|
+
import { PseudoView } from '@burgantech/pseudo-ui/vue'
|
|
98
|
+
import '@burgantech/pseudo-ui/vue/style.css'
|
|
99
|
+
|
|
100
|
+
import type { DataSchema, ViewDefinition } from '@burgantech/pseudo-ui'
|
|
101
|
+
|
|
102
|
+
provideDelegate(delegate)
|
|
103
|
+
|
|
104
|
+
const schema = ref<DataSchema>()
|
|
105
|
+
const view = ref<ViewDefinition>()
|
|
106
|
+
|
|
107
|
+
onMounted(async () => {
|
|
108
|
+
const [s, v] = await Promise.all([
|
|
109
|
+
fetch('/api/components/my-form/schema.json').then(r => r.json()),
|
|
110
|
+
fetch('/api/components/my-form/view.json').then(r => r.json()),
|
|
111
|
+
])
|
|
112
|
+
schema.value = s
|
|
113
|
+
view.value = v
|
|
114
|
+
})
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<template>
|
|
118
|
+
<PseudoView
|
|
119
|
+
v-if="schema && view"
|
|
120
|
+
:schema="schema"
|
|
121
|
+
:view="view"
|
|
122
|
+
lang="en"
|
|
123
|
+
@form-change="console.log('form data:', $event)"
|
|
124
|
+
@validation-change="console.log('is valid:', $event)"
|
|
125
|
+
/>
|
|
126
|
+
</template>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Package Exports
|
|
130
|
+
|
|
131
|
+
| Import path | Content |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `@burgantech/pseudo-ui` | Core engine: types, expression resolver, schema resolver, conditional engine |
|
|
134
|
+
| `@burgantech/pseudo-ui/vue` | Vue 3 adapter: `PseudoView`, `DynamicRenderer`, `provideDelegate` |
|
|
135
|
+
| `@burgantech/pseudo-ui/vue/style.css` | Component styles (Material Design 3 tokens) |
|
|
136
|
+
| `@burgantech/pseudo-ui/vocabularies/view-vocabulary.json` | View meta-schema (UI component tree definition) |
|
|
137
|
+
| `@burgantech/pseudo-ui/vocabularies/view-model-vocabulary.json` | ViewModel meta-schema (JSON Schema x- extensions) |
|
|
138
|
+
|
|
139
|
+
## Architecture
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
┌─────────────────────────────────────────────────────┐
|
|
143
|
+
│ Your Application │
|
|
144
|
+
│ │
|
|
145
|
+
│ ┌──────────────┐ implements ┌────────────────┐ │
|
|
146
|
+
│ │ App.vue │ ──────────────▶│ Delegate │ │
|
|
147
|
+
│ │ │ │ - requestData │ │
|
|
148
|
+
│ │ <PseudoView │ │ - loadComponent│ │
|
|
149
|
+
│ │ :schema │ │ - onAction │ │
|
|
150
|
+
│ │ :view │ │ - onLog │ │
|
|
151
|
+
│ │ lang="en"/>│ └────────────────┘ │
|
|
152
|
+
│ └──────┬───────┘ ▲ │
|
|
153
|
+
│ │ provides │ │
|
|
154
|
+
├─────────┼────────────────────────────────┼──────────┤
|
|
155
|
+
│ SDK │ │ │
|
|
156
|
+
│ ▼ │ │
|
|
157
|
+
│ ┌─────────────────┐ ┌──────────────┐ │ │
|
|
158
|
+
│ │ DynamicRenderer │ │ Expression │ │ │
|
|
159
|
+
│ │ (recursive) │ │ Resolver │ │ │
|
|
160
|
+
│ │ │ ├──────────────┤ │ │
|
|
161
|
+
│ │ 30+ MD3 widgets │ │ Conditional │ │ │
|
|
162
|
+
│ │ via PrimeVue 4 │ │ Engine │ │ │
|
|
163
|
+
│ │ │ ├──────────────┤ │ │
|
|
164
|
+
│ │ ErrorBoundary │ │ Schema │ │ │
|
|
165
|
+
│ │ for Component & │ │ Resolver │◀──┘ │
|
|
166
|
+
│ │ ForEach │ │ (validation) │ │
|
|
167
|
+
│ └─────────────────┘ └──────────────┘ │
|
|
168
|
+
└─────────────────────────────────────────────────────┘
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Data Model (MVVM)
|
|
172
|
+
|
|
173
|
+
| Layer | File | Purpose |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| **ViewModel** | `schema.json` | Data contract — field types, validation, LOV sources, conditionals, multi-lang labels |
|
|
176
|
+
| **View** | `view.json` | UI component tree — layout, binding, actions, transient UI state |
|
|
177
|
+
| **Model** | Backend | Persisted data, served via delegate's `requestData` |
|
|
178
|
+
|
|
179
|
+
### Schema example (ViewModel)
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"$id": "urn:amorphie:res:schema:shared:customer-form",
|
|
184
|
+
"type": "object",
|
|
185
|
+
"required": ["fullName", "city"],
|
|
186
|
+
"properties": {
|
|
187
|
+
"fullName": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"minLength": 2,
|
|
190
|
+
"x-labels": { "en": "Full Name", "tr": "Ad Soyad" },
|
|
191
|
+
"x-errorMessages": {
|
|
192
|
+
"required": { "en": "Required", "tr": "Zorunlu" }
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"city": {
|
|
196
|
+
"type": "string",
|
|
197
|
+
"x-labels": { "en": "City", "tr": "Şehir" },
|
|
198
|
+
"x-lov": {
|
|
199
|
+
"source": "urn:amorphie:func:domain:shared:get-cities",
|
|
200
|
+
"valueField": "$.response.data.code",
|
|
201
|
+
"displayField": "$.response.data.name"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"companyName": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"x-labels": { "en": "Company", "tr": "Şirket" },
|
|
207
|
+
"x-conditional": {
|
|
208
|
+
"showIf": {
|
|
209
|
+
"allOf": [
|
|
210
|
+
{ "field": "accountType", "operator": "equals", "value": "corporate" },
|
|
211
|
+
{ "field": "age", "operator": "greaterThan", "value": 17 }
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### View example
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"$schema": "https://amorphie.io/meta/view-vocabulary/1.0",
|
|
225
|
+
"dataSchema": "urn:amorphie:res:schema:shared:customer-form",
|
|
226
|
+
"uiState": { "showDialog": false },
|
|
227
|
+
"view": {
|
|
228
|
+
"type": "Column",
|
|
229
|
+
"gap": "md",
|
|
230
|
+
"children": [
|
|
231
|
+
{ "type": "TextField", "bind": "fullName" },
|
|
232
|
+
{ "type": "Dropdown", "bind": "city" },
|
|
233
|
+
{ "type": "TextField", "bind": "companyName" },
|
|
234
|
+
{
|
|
235
|
+
"type": "Button",
|
|
236
|
+
"label": { "en": "Submit", "tr": "Gönder" },
|
|
237
|
+
"variant": "filled",
|
|
238
|
+
"action": "submit"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Delegate Interface
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
interface PseudoViewDelegate {
|
|
249
|
+
/** Fetch data from backend (LOV items, lookup enrichment) */
|
|
250
|
+
requestData(ref: string, params?: Record<string, string>): Promise<unknown>
|
|
251
|
+
|
|
252
|
+
/** Load a nested component's schema + view by reference */
|
|
253
|
+
loadComponent(ref: string): Promise<{ schema: DataSchema; view: ViewDefinition }>
|
|
254
|
+
|
|
255
|
+
/** Handle user actions (submit, cancel, back, custom commands) */
|
|
256
|
+
onAction(action: string, formData: Record<string, unknown>, command?: string): Promise<void>
|
|
257
|
+
|
|
258
|
+
/** Optional: custom async validation after built-in checks pass */
|
|
259
|
+
onValidationRequest?(field: string, value: unknown, formData: Record<string, unknown>): Promise<string | null>
|
|
260
|
+
|
|
261
|
+
/** Optional: capture SDK logs (debug, info, warn, error) */
|
|
262
|
+
onLog?(level: LogLevel, message: string, error?: unknown, context?: Record<string, unknown>): void
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Supported Components
|
|
267
|
+
|
|
268
|
+
### Layout
|
|
269
|
+
`Column` · `Row` · `Stack` · `Grid` · `Expanded` · `SizedBox` · `Divider` · `Spacer`
|
|
270
|
+
|
|
271
|
+
### Input
|
|
272
|
+
`TextField` · `TextArea` · `NumberField` · `Dropdown` · `Checkbox` · `RadioGroup` · `DatePicker` · `TimePicker` · `Switch` · `Slider` · `SegmentedButton` · `SearchField` · `AutoComplete`
|
|
273
|
+
|
|
274
|
+
### Display
|
|
275
|
+
`Text` · `Image` · `Chip` · `ListTile` · `Avatar` · `RichText` · `LoadingIndicator`
|
|
276
|
+
|
|
277
|
+
### Surface & Overlay
|
|
278
|
+
`Card` · `Dialog` · `BottomSheet` · `SideSheet` · `Snackbar` · `Tooltip`
|
|
279
|
+
|
|
280
|
+
### Navigation
|
|
281
|
+
`TabView` · `AppBar` · `NavigationBar` · `NavigationDrawer`
|
|
282
|
+
|
|
283
|
+
### Container
|
|
284
|
+
`ExpansionPanel` · `Stepper`
|
|
285
|
+
|
|
286
|
+
### Action
|
|
287
|
+
`Button` · `IconButton` · `FAB` · `Menu` · `Toolbar` · `Carousel`
|
|
288
|
+
|
|
289
|
+
### Control
|
|
290
|
+
`ForEach` · `Component` (nested)
|
|
291
|
+
|
|
292
|
+
## Expression Namespaces
|
|
293
|
+
|
|
294
|
+
| Namespace | Source | Example |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| `$form.field` | User input data | `$form.firstName` |
|
|
297
|
+
| `$instance.field` | Backend persisted data | `$instance.status` |
|
|
298
|
+
| `$param.field` | Parent-bound data (nested components) | `$param.cityCode` |
|
|
299
|
+
| `$ui.key` | Transient UI state (not submitted) | `$ui.showDialog` |
|
|
300
|
+
| `$schema.field.label` | Schema label for current language | `$schema.city.label` |
|
|
301
|
+
| `$lov.field` | LOV items array | `$lov.city` |
|
|
302
|
+
| `$lov.field.display` | Localized display name for current value | `$lov.city.display` |
|
|
303
|
+
| `$lookup.prop.field` | Enrichment data | `$lookup.branch.address` |
|
|
304
|
+
| `$item.field` | ForEach iteration item | `$item.name` |
|
|
305
|
+
| `$context.lang` | Runtime context | `$context.lang` |
|
|
306
|
+
|
|
307
|
+
## Conditional Operators
|
|
308
|
+
|
|
309
|
+
`equals` · `notEquals` · `in` · `notIn` · `greaterThan` · `lessThan` · `greaterThanOrEquals` · `lessThanOrEquals` · `contains` · `startsWith` · `endsWith` · `isEmpty` · `isNotEmpty`
|
|
310
|
+
|
|
311
|
+
Compound rules: `allOf` (AND), `anyOf` (OR), `not` (negate) — recursive nesting supported.
|
|
312
|
+
|
|
313
|
+
## Validation Formats
|
|
314
|
+
|
|
315
|
+
Built-in `format` validators: `email` · `uri` / `url` · `date` · `date-time` · `time` · `phone` / `tel` · `iban`
|
|
316
|
+
|
|
317
|
+
## Vocabularies
|
|
318
|
+
|
|
319
|
+
The package includes JSON Schema vocabulary definitions for IDE auto-complete and tooling:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// Import as JSON modules
|
|
323
|
+
import viewVocab from '@burgantech/pseudo-ui/vocabularies/view-vocabulary.json'
|
|
324
|
+
import viewModelVocab from '@burgantech/pseudo-ui/vocabularies/view-model-vocabulary.json'
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
- **View Vocabulary** — defines all valid component types, properties, and their constraints
|
|
328
|
+
- **ViewModel Vocabulary** — defines all `x-*` extensions (`x-labels`, `x-lov`, `x-conditional`, etc.)
|
|
329
|
+
|
|
330
|
+
## Cross-Platform
|
|
331
|
+
|
|
332
|
+
This package provides the **TypeScript/Vue** implementation. A **Dart/Flutter** package (`pseudo_ui`) renders the same JSON schemas and views using Material 3 widgets. Both packages share the same vocabulary definitions, ensuring consistent behavior across platforms.
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentNode } from '../../engine/types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
node: ComponentNode;
|
|
4
|
+
item?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|