@nan0web/ui 1.9.0 → 1.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 +97 -12
- package/package.json +54 -25
- package/src/App/Command/DepsCommand.js +3 -4
- package/src/Frame/Props.js +12 -18
- package/src/InterfaceTemplate/InterfaceTemplate.js +9 -7
- package/src/Model/index.js +86 -2
- package/src/StdIn.js +2 -6
- package/src/cli.js +1 -0
- package/src/core/Form/Form.js +8 -7
- package/src/core/Form/Message.js +1 -1
- package/src/core/GeneratorRunner.js +77 -7
- package/src/core/InputAdapter.js +3 -1
- package/src/core/Intent.js +214 -16
- package/src/core/IntentErrorModel.js +6 -1
- package/src/core/Message/Message.js +4 -7
- package/src/core/Message/OutputMessage.js +4 -9
- package/src/core/Stream.js +16 -5
- package/src/core/StreamEntry.js +20 -28
- package/src/core/index.js +2 -1
- package/src/domain/Content.js +196 -0
- package/src/domain/Document.js +17 -0
- package/src/domain/FooterModel.js +37 -19
- package/src/domain/HeaderModel.js +47 -21
- package/src/domain/HeroModel.js +24 -22
- package/src/domain/LayoutModel.js +43 -0
- package/src/domain/ModelAsApp.js +46 -0
- package/src/domain/SandboxModel.js +19 -16
- package/src/domain/app/GalleryCommand.js +53 -0
- package/src/domain/app/GalleryRenderIntent.js +77 -0
- package/src/domain/app/SnapshotAuditor.js +401 -0
- package/src/domain/app/SnapshotRunner.js +264 -0
- package/src/domain/app/UIApp.js +78 -0
- package/src/domain/components/BreadcrumbModel.js +10 -6
- package/src/domain/components/FeatureGridModel.js +62 -0
- package/src/domain/components/MarkdownModel.js +24 -0
- package/src/domain/components/ShellModel.js +243 -0
- package/src/domain/components/TableModel.js +10 -6
- package/src/domain/components/ToastModel.js +10 -6
- package/src/domain/components/index.js +3 -1
- package/src/domain/index.js +14 -4
- package/src/index.js +21 -2
- package/src/inspect.js +2 -0
- package/src/test/ScenarioAdapter.js +59 -0
- package/src/test/ScenarioTest.js +51 -0
- package/src/test/ScenarioTest.story.js +56 -0
- package/src/testing/CrashReporter.js +56 -0
- package/src/testing/GalleryGenerator.js +29 -0
- package/src/testing/LogicInspector.js +55 -0
- package/src/testing/SnapshotRunner.js +22 -0
- package/src/testing/SpecAdapter.js +115 -0
- package/src/testing/SpecRunner.js +121 -0
- package/src/testing/VisualAdapter.js +46 -0
- package/src/testing/index.js +7 -0
- package/src/testing/verifySnapshot.js +17 -0
- package/types/App/Command/DepsCommand.d.ts +0 -2
- package/types/Model/index.d.ts +56 -4
- package/types/StdIn.d.ts +3 -3
- package/types/cli.d.ts +1 -0
- package/types/core/Form/Form.d.ts +2 -2
- package/types/core/GeneratorRunner.d.ts +18 -1
- package/types/core/InputAdapter.d.ts +2 -1
- package/types/core/Intent.d.ts +232 -26
- package/types/core/IntentErrorModel.d.ts +4 -0
- package/types/core/Message/Message.d.ts +2 -2
- package/types/core/Message/OutputMessage.d.ts +0 -2
- package/types/core/index.d.ts +2 -1
- package/types/domain/Content.d.ts +340 -0
- package/types/domain/Document.d.ts +21 -0
- package/types/domain/FooterModel.d.ts +22 -12
- package/types/domain/HeaderModel.d.ts +36 -13
- package/types/domain/HeroModel.d.ts +19 -17
- package/types/domain/LayoutModel.d.ts +34 -0
- package/types/domain/ModelAsApp.d.ts +23 -0
- package/types/domain/SandboxModel.d.ts +10 -0
- package/types/domain/app/GalleryCommand.d.ts +55 -0
- package/types/domain/app/GalleryRenderIntent.d.ts +31 -0
- package/types/domain/app/SnapshotAuditor.d.ts +99 -0
- package/types/domain/app/SnapshotRunner.d.ts +45 -0
- package/types/domain/app/UIApp.d.ts +60 -0
- package/types/domain/components/BreadcrumbModel.d.ts +6 -8
- package/types/domain/components/FeatureGridModel.d.ts +50 -0
- package/types/domain/components/MarkdownModel.d.ts +19 -0
- package/types/domain/components/ShellModel.d.ts +56 -0
- package/types/domain/components/TableModel.d.ts +4 -0
- package/types/domain/components/ToastModel.d.ts +4 -0
- package/types/domain/components/index.d.ts +3 -0
- package/types/domain/index.d.ts +10 -4
- package/types/index.d.ts +19 -1
- package/types/inspect.d.ts +2 -0
- package/types/test/ScenarioAdapter.d.ts +43 -0
- package/types/test/ScenarioTest.d.ts +24 -0
- package/types/test/ScenarioTest.story.d.ts +1 -0
- package/types/testing/CrashReporter.d.ts +13 -0
- package/types/testing/GalleryGenerator.d.ts +1 -0
- package/types/testing/LogicInspector.d.ts +22 -0
- package/types/testing/SnapshotRunner.d.ts +7 -0
- package/types/testing/SpecAdapter.d.ts +57 -0
- package/types/testing/SpecRunner.d.ts +41 -0
- package/types/testing/VisualAdapter.d.ts +9 -0
- package/types/testing/index.d.ts +7 -0
- package/types/testing/verifySnapshot.d.ts +14 -0
- package/src/README.md.js +0 -436
- package/types/App/Command/Options.d.ts +0 -43
- package/types/App/Command/index.d.ts +0 -8
- package/types/App/User/Command/Options.d.ts +0 -34
- package/types/core/Message/InputMessage.d.ts +0 -71
- package/types/domain/components/HeroModel.d.ts +0 -24
- package/types/domain/components/ShowcaseAppModel.d.ts +0 -32
package/README.md
CHANGED
|
@@ -90,9 +90,9 @@ const form = new UiForm({
|
|
|
90
90
|
message: 'Hello!',
|
|
91
91
|
},
|
|
92
92
|
})
|
|
93
|
-
const errors = form.validate()
|
|
94
|
-
console.info(errors.
|
|
95
|
-
console.info(errors.
|
|
93
|
+
const { isValid, errors } = form.validate()
|
|
94
|
+
console.info(Object.keys(errors).length) // ← 1
|
|
95
|
+
console.info(errors.email) // ← Invalid email format
|
|
96
96
|
```
|
|
97
97
|
### Components
|
|
98
98
|
|
|
@@ -148,27 +148,112 @@ const frame = new Frame({
|
|
|
148
148
|
const rendered = frame.render()
|
|
149
149
|
console.info(rendered.includes('Frame content')) // ← true
|
|
150
150
|
```
|
|
151
|
-
### Models
|
|
151
|
+
### Domain Models (v1.9.0)
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
v1.9.0 introduces a comprehensive set of domain models for layout and components.
|
|
154
|
+
These models follow the **Model-as-Schema** pattern.
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
#### Layout Models
|
|
157
|
+
- `HeaderModel` — title, logo, navigation actions
|
|
158
|
+
- `FooterModel` — copyright, version, social links
|
|
159
|
+
- `HeroModel` — prominent call-to-action
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
#### HTML5 Base Elements
|
|
162
|
+
Fully typed zero-cost support for standard tags: `div`, `span`, `p`, `h1`-`h6`, `a`, `ul`, `table`, etc., plus SVG basics (`svg`, `path`, `rect`). Data must be standard `camelCase`.
|
|
163
|
+
|
|
164
|
+
#### Component Models
|
|
165
|
+
- `PricingModel` / `PricingSection` — plans with features and prices
|
|
166
|
+
- `FeatureGrid` — grid of feature highlights
|
|
167
|
+
- `ProfileDropdown` — user avatar and settings menu
|
|
168
|
+
- `CommentModel` & `TestimonialModel` — social proof
|
|
169
|
+
- `StatsModel` — data visualizations
|
|
170
|
+
- `TimelineModel` — event history
|
|
171
|
+
|
|
172
|
+
How to use the new Header and Hero models?
|
|
158
173
|
```js
|
|
159
174
|
import { Model } from '@nan0web/ui'
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
const { HeaderModel, HeroModel } = Model
|
|
176
|
+
const header = new HeaderModel({
|
|
177
|
+
title: 'NaN•Web',
|
|
178
|
+
logo: '/logo.svg',
|
|
179
|
+
actions: [{ title: 'Docs', href: '/docs' }],
|
|
180
|
+
})
|
|
181
|
+
const hero = new HeroModel({
|
|
182
|
+
title: 'One Logic — Many UI',
|
|
183
|
+
actions: [{ title: 'Get Started', href: '/start' }],
|
|
184
|
+
})
|
|
185
|
+
console.info(header.title) // ← NaN•Web
|
|
186
|
+
console.info(hero.actions[0].title) // ← Get Started
|
|
163
187
|
```
|
|
164
|
-
###
|
|
188
|
+
### Intent Generators (v1.11.0)
|
|
189
|
+
|
|
190
|
+
From v1.11.0, Intent creators are standard named functions generating
|
|
191
|
+
strict interactions (ask, progress, show, render, result).
|
|
192
|
+
|
|
193
|
+
- `ask(field, schema)` — requests input from the environment.
|
|
194
|
+
- `progress(message)` — updates a visual loader.
|
|
195
|
+
- `show(message, level, data)` — displays a notification (replaces deprecated `log`).
|
|
196
|
+
- `render(component, props)` — renders a specific component view.
|
|
197
|
+
- `result(data)` — ends the model execution cleanly.
|
|
198
|
+
|
|
199
|
+
How to use Intent generators? (v1.11.0)
|
|
200
|
+
```js
|
|
201
|
+
import { ask, show, result } from '@nan0web/ui'
|
|
202
|
+
const nameIntent = ask('name', { help: 'Your name' })
|
|
203
|
+
const msgIntent = show('Processing...', 'info')
|
|
204
|
+
const endIntent = result({ ok: true })
|
|
205
|
+
```
|
|
206
|
+
### Testing UI (v1.11.0 Deterministic Testing)
|
|
165
207
|
|
|
166
208
|
Core unit-tested to ensure stability in different environments.
|
|
209
|
+
With **v1.11.0**, the architecture formally introduces `ScenarioTest` for zero-I/O deterministic testing.
|
|
210
|
+
|
|
211
|
+
By lifting the asynchronous logic and providing an explicit scenario array, models are evaluated instantly without waiting on user prompt delays.
|
|
167
212
|
|
|
213
|
+
How to test Model pipelines deterministically?
|
|
214
|
+
```js
|
|
215
|
+
import { ModelAsApp, ask, result, show } from '@nan0web/ui'
|
|
216
|
+
import { ScenarioTest } from '@nan0web/ui/test/ScenarioTest.js'
|
|
217
|
+
const { ModelAsApp, ask, result, show } = await import('./index.js')
|
|
218
|
+
const { ScenarioTest } = await import('./test/ScenarioTest.js')
|
|
219
|
+
class ShoppingCartApp extends ModelAsApp {
|
|
220
|
+
*run() {
|
|
221
|
+
const product = yield ask('product', { help: 'Select product' })
|
|
222
|
+
if (product?.value === 'laptop') {
|
|
223
|
+
yield show('Good choice!', 'ok')
|
|
224
|
+
}
|
|
225
|
+
const confirm = yield ask('confirm', { help: 'Confirm purchase?' })
|
|
226
|
+
return result({ product: product?.value, confirm: confirm?.value })
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const res = await ScenarioTest.run(ShoppingCartApp, [
|
|
230
|
+
{ field: 'product', value: 'laptop' },
|
|
231
|
+
{ field: 'confirm', value: true }
|
|
232
|
+
])
|
|
233
|
+
```
|
|
234
|
+
You can also verify exceptions and validation rules by observing the final error in ScenarioTest.
|
|
235
|
+
|
|
236
|
+
How to test validation errors with ScenarioTest?
|
|
237
|
+
```js
|
|
238
|
+
import { ModelAsApp, ask, result } from '@nan0web/ui'
|
|
239
|
+
import { ScenarioTest } from '@nan0web/ui/test/ScenarioTest.js'
|
|
240
|
+
const { ModelAsApp, ask, result } = await import('./index.js')
|
|
241
|
+
const { ScenarioTest } = await import('./test/ScenarioTest.js')
|
|
242
|
+
class ValidatedApp extends ModelAsApp {
|
|
243
|
+
*run() {
|
|
244
|
+
const code = yield ask('code', { help: 'Enter code', required: true })
|
|
245
|
+
if (!code?.value) throw new Error('Code is mandatory')
|
|
246
|
+
return result({ code: code?.value })
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const res = await ScenarioTest.run(ValidatedApp, [
|
|
250
|
+
{ field: 'code', value: '' } // Simulating empty response
|
|
251
|
+
])
|
|
252
|
+
```
|
|
168
253
|
All components, adapters, and models are designed to be testable
|
|
169
254
|
with minimal setup.
|
|
170
255
|
|
|
171
|
-
How to test UI components with assertions?
|
|
256
|
+
How to test visual UI components with assertions?
|
|
172
257
|
```js
|
|
173
258
|
import { Welcome } from '@nan0web/ui'
|
|
174
259
|
const output = Welcome({ user: { name: 'Test' } })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nan0web/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "NaN•Web UI. One application logic (algorithm) and many UI.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"src/**/*.js",
|
|
10
10
|
"!src/**/*.test.js",
|
|
11
|
+
"!src/README.md.js",
|
|
11
12
|
"types/**/*.d.ts"
|
|
12
13
|
],
|
|
13
14
|
"exports": {
|
|
@@ -24,25 +25,42 @@
|
|
|
24
25
|
"types": "./types/core/index.d.ts"
|
|
25
26
|
},
|
|
26
27
|
"./domain": {
|
|
27
|
-
"import": "./src/domain/index.js"
|
|
28
|
+
"import": "./src/domain/index.js",
|
|
29
|
+
"types": "./types/domain/index.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./inspect": {
|
|
32
|
+
"import": "./src/inspect.js",
|
|
33
|
+
"types": "./types/inspect.d.ts"
|
|
34
|
+
},
|
|
35
|
+
"./testing": {
|
|
36
|
+
"import": "./src/testing/index.js",
|
|
37
|
+
"types": "./types/testing/index.d.ts"
|
|
28
38
|
},
|
|
29
39
|
"./cli-app": "./apps/cli/src/index.js",
|
|
30
40
|
"./mobile-app": "./apps/mobile/src/index.js",
|
|
31
|
-
"./web-app": "./apps/web/src/App.jsx"
|
|
41
|
+
"./web-app": "./apps/web/src/App.jsx",
|
|
42
|
+
"./ui/cli": "./src/cli.js"
|
|
32
43
|
},
|
|
33
44
|
"keywords": [
|
|
34
45
|
"ui",
|
|
35
46
|
"nanoweb"
|
|
36
47
|
],
|
|
48
|
+
"nan0web": {
|
|
49
|
+
"workflowDir": "docs/{locale}/workflows",
|
|
50
|
+
"workflows": [
|
|
51
|
+
"olm-ui-architecture.md",
|
|
52
|
+
"olm-ui-architecture-core.md",
|
|
53
|
+
"olm-ui-architecture-adapters.md",
|
|
54
|
+
"forge-component.md",
|
|
55
|
+
"interface-welding.md"
|
|
56
|
+
],
|
|
57
|
+
"inspectors": [
|
|
58
|
+
"./src/domain/app/SnapshotAuditor.js"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
37
61
|
"author": "YaRaSLove (ЯRаСлав) <support@yaro.page>",
|
|
38
62
|
"license": "ISC",
|
|
39
63
|
"devDependencies": {
|
|
40
|
-
"@nan0web/event": "^1.0.1",
|
|
41
|
-
"@nan0web/i18n": "^1.1.0",
|
|
42
|
-
"@nan0web/release": "^1.0.2",
|
|
43
|
-
"@nan0web/test": "^1.1.3",
|
|
44
|
-
"@nan0web/ui-cli": "^2.9.0",
|
|
45
|
-
"@nan0web/ui-lit": "^1.1.0",
|
|
46
64
|
"@playwright/test": "^1.58.2",
|
|
47
65
|
"@rollup/plugin-yaml": "^4.1.2",
|
|
48
66
|
"@vitest/coverage-v8": "^3.2.4",
|
|
@@ -51,40 +69,51 @@
|
|
|
51
69
|
"knip": "^5.86.0",
|
|
52
70
|
"lit": "^3.3.2",
|
|
53
71
|
"vite": "^6.4.1",
|
|
54
|
-
"vitest": "^3.2.4"
|
|
72
|
+
"vitest": "^3.2.4",
|
|
73
|
+
"@nan0web/db-fs": "1.2.1",
|
|
74
|
+
"@nan0web/event": "1.0.1",
|
|
75
|
+
"@nan0web/i18n": "1.5.0",
|
|
76
|
+
"@nan0web/inspect": "1.0.0",
|
|
77
|
+
"@nan0web/ui-cli": "2.12.3",
|
|
78
|
+
"@nan0web/test": "1.1.4",
|
|
79
|
+
"@nan0web/icons": "1.1.0",
|
|
80
|
+
"@nan0web/release": "1.0.3",
|
|
81
|
+
"@nan0web/nan0web.app": "0.1.0",
|
|
82
|
+
"@nan0web/ui-lit": "1.1.0"
|
|
55
83
|
},
|
|
56
84
|
"dependencies": {
|
|
57
|
-
"@nan0web/co": "^2.0.0",
|
|
58
|
-
"@nan0web/log": "^1.1.1",
|
|
59
|
-
"@nan0web/types": "^1.4.0",
|
|
60
85
|
"string-width": "^7.2.0",
|
|
61
|
-
"@nan0web/
|
|
86
|
+
"@nan0web/co": "2.0.1",
|
|
87
|
+
"@nan0web/log": "1.1.1",
|
|
88
|
+
"@nan0web/types": "1.7.2",
|
|
89
|
+
"@nan0web/core": "1.1.3"
|
|
62
90
|
},
|
|
63
91
|
"scripts": {
|
|
92
|
+
"prebuild": "rm -rf types/",
|
|
64
93
|
"build": "tsc",
|
|
65
94
|
"play": "node play/main.js",
|
|
66
|
-
"test:unit": "node --test --test-timeout=3333 \"src/**/*.test.js\"",
|
|
67
|
-
"test": "npm run test:unit && npm run test:
|
|
95
|
+
"test:unit": "node --test --test-timeout=3333 \"src/*.test.js\" \"src/core/**/*.test.js\" \"src/domain/**/*.test.js\" \"src/test/*.test.js\" \"src/testing/**/*.test.js\"",
|
|
96
|
+
"test": "npm run test:unit && npm run test:audit && npm run build",
|
|
68
97
|
"test:nan0test": "node --test --test-timeout=3333 \"src/**/*.test.js\" | nan0test parse --fail",
|
|
69
98
|
"test:coverage": "node --experimental-test-coverage --test-coverage-include=\"src/**/*.js\" --test-coverage-exclude=\"src/**/*.test.js\" --test \"src/**/*.test.js\"",
|
|
70
99
|
"test:coverage:collect": "nan0test coverage",
|
|
71
100
|
"test:docs": "node --test src/README.md.js",
|
|
72
|
-
"release:spec": "node --test \"releases/**/*.
|
|
73
|
-
"test:release": "node --test \"releases/**/*.test.js\"",
|
|
101
|
+
"release:spec": "node --test \"src/test/releases/**/*.test.js\"",
|
|
102
|
+
"test:release": "node --test \"src/test/releases/**/*.test.js\"",
|
|
74
103
|
"test:status": "nan0test status --hide-name",
|
|
75
104
|
"test:play": "node --test --test-timeout=3333 \"play/**/*.test.js\"",
|
|
76
|
-
"test:
|
|
77
|
-
"test:
|
|
78
|
-
"test:
|
|
79
|
-
"test:
|
|
105
|
+
"test:snapshots": "node src/testing/GalleryGenerator.js",
|
|
106
|
+
"test:gallery": "npm run test:snapshots && npx nan0gallery --dir=snapshots/core",
|
|
107
|
+
"test:audit": "nan0cli audit --dir snapshots/core --data .",
|
|
108
|
+
"test:stories": "node --test \"src/**/*.story.js\"",
|
|
109
|
+
"test:all": "npm run test && npm run test:docs && npm run test:play && npm run test:stories && npm run knip",
|
|
80
110
|
"knip": "knip --production",
|
|
81
111
|
"precommit": "npm test",
|
|
82
112
|
"prepush": "npm test",
|
|
83
113
|
"release": "nan0release publish",
|
|
84
114
|
"clean": "rm -rf .cache/ && rm -rf dist/",
|
|
85
115
|
"clean:modules": "rm -rf node_modules",
|
|
86
|
-
"docs:
|
|
87
|
-
"docs:
|
|
88
|
-
"docs:build": "node docs/site/scripts/generate-data.js && node docs/site/scripts/generate-pages.js && vite build docs/site"
|
|
116
|
+
"docs:dev": "nan0web run --data docs --index README --port 4270",
|
|
117
|
+
"docs:build": "nan0web build --data docs --index README --locale uk"
|
|
89
118
|
}
|
|
90
119
|
}
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import UiMessage from '../../core/Message/Message.js'
|
|
2
2
|
|
|
3
3
|
class DepsCommandBody {
|
|
4
|
-
fix = false
|
|
5
4
|
static fix = {
|
|
6
5
|
help: 'Fix dependencies',
|
|
7
6
|
defaultValue: false,
|
|
8
7
|
}
|
|
9
8
|
constructor(input = {}) {
|
|
10
|
-
const { fix =
|
|
9
|
+
const { fix = DepsCommandBody.fix.defaultValue } = input
|
|
10
|
+
this.fix = !!fix
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class DepsCommand extends UiMessage {
|
|
15
15
|
static Body = DepsCommandBody
|
|
16
|
-
/** @type {DepsCommandBody} */
|
|
17
|
-
body
|
|
18
16
|
constructor(input = {}) {
|
|
19
17
|
const { body = new DepsCommandBody() } = UiMessage.parseBody(input, DepsCommandBody)
|
|
20
18
|
super(input)
|
|
19
|
+
/** @type {DepsCommandBody} */
|
|
21
20
|
this.body = body
|
|
22
21
|
}
|
|
23
22
|
}
|
package/src/Frame/Props.js
CHANGED
|
@@ -53,24 +53,6 @@ class FrameProps extends ObjectWithAlias {
|
|
|
53
53
|
s: 'strikethrough',
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/** @type {string} Text color */
|
|
57
|
-
color = ''
|
|
58
|
-
|
|
59
|
-
/** @type {string} Background color */
|
|
60
|
-
bgColor = ''
|
|
61
|
-
|
|
62
|
-
/** @type {boolean} Bold text flag */
|
|
63
|
-
bold = false
|
|
64
|
-
|
|
65
|
-
/** @type {boolean} Italic text flag */
|
|
66
|
-
italic = false
|
|
67
|
-
|
|
68
|
-
/** @type {boolean} Underline text flag */
|
|
69
|
-
underline = false
|
|
70
|
-
|
|
71
|
-
/** @type {boolean} Strikethrough text flag */
|
|
72
|
-
strikethrough = false
|
|
73
|
-
|
|
74
56
|
/**
|
|
75
57
|
* @param {object} props - Frame properties
|
|
76
58
|
* @param {string} [props.color=""] - Text color
|
|
@@ -90,11 +72,23 @@ class FrameProps extends ObjectWithAlias {
|
|
|
90
72
|
underline = false,
|
|
91
73
|
strikethrough = false,
|
|
92
74
|
} = props
|
|
75
|
+
|
|
76
|
+
/** @type {string} Text color */
|
|
93
77
|
this.color = color
|
|
78
|
+
|
|
79
|
+
/** @type {string} Background color */
|
|
94
80
|
this.bgColor = bgColor
|
|
81
|
+
|
|
82
|
+
/** @type {boolean} Bold text flag */
|
|
95
83
|
this.bold = bold
|
|
84
|
+
|
|
85
|
+
/** @type {boolean} Italic text flag */
|
|
96
86
|
this.italic = italic
|
|
87
|
+
|
|
88
|
+
/** @type {boolean} Underline text flag */
|
|
97
89
|
this.underline = underline
|
|
90
|
+
|
|
91
|
+
/** @type {boolean} Strikethrough text flag */
|
|
98
92
|
this.strikethrough = strikethrough
|
|
99
93
|
}
|
|
100
94
|
}
|
|
@@ -22,13 +22,15 @@ export default class InterfaceTemplate {
|
|
|
22
22
|
*/
|
|
23
23
|
static requiredMethods = ['render', 'ask']
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
constructor() {
|
|
26
|
+
/**
|
|
27
|
+
* The name of this interface (e.g. 'cli', 'web', 'mobile').
|
|
28
|
+
* Override in subclass.
|
|
29
|
+
*
|
|
30
|
+
* @type {string}
|
|
31
|
+
*/
|
|
32
|
+
this.name = 'base'
|
|
33
|
+
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Render data to the user through the interface.
|
package/src/Model/index.js
CHANGED
|
@@ -1,7 +1,91 @@
|
|
|
1
1
|
import User from './User/User.js'
|
|
2
|
+
import { HeaderModel } from '../domain/HeaderModel.js'
|
|
3
|
+
import { FooterModel } from '../domain/FooterModel.js'
|
|
4
|
+
import { HeroModel } from '../domain/HeroModel.js'
|
|
5
|
+
import {
|
|
6
|
+
ButtonModel,
|
|
7
|
+
ConfirmModel,
|
|
8
|
+
InputModel,
|
|
9
|
+
SpinnerModel,
|
|
10
|
+
TableModel,
|
|
11
|
+
ToastModel,
|
|
12
|
+
SelectModel,
|
|
13
|
+
AutocompleteModel,
|
|
14
|
+
TreeModel,
|
|
15
|
+
TabsModel,
|
|
16
|
+
AccordionModel,
|
|
17
|
+
GalleryModel,
|
|
18
|
+
PriceModel,
|
|
19
|
+
PricingModel,
|
|
20
|
+
CommentModel,
|
|
21
|
+
TestimonialModel,
|
|
22
|
+
StatsItemModel,
|
|
23
|
+
StatsModel,
|
|
24
|
+
TimelineItemModel,
|
|
25
|
+
TimelineModel,
|
|
26
|
+
EmptyStateModel,
|
|
27
|
+
BannerModel,
|
|
28
|
+
ProfileDropdownModel,
|
|
29
|
+
} from '../domain/components/index.js'
|
|
2
30
|
|
|
3
|
-
export {
|
|
31
|
+
export {
|
|
32
|
+
User,
|
|
33
|
+
HeaderModel,
|
|
34
|
+
FooterModel,
|
|
35
|
+
HeroModel,
|
|
36
|
+
ButtonModel,
|
|
37
|
+
ConfirmModel,
|
|
38
|
+
InputModel,
|
|
39
|
+
SpinnerModel,
|
|
40
|
+
TableModel,
|
|
41
|
+
ToastModel,
|
|
42
|
+
SelectModel,
|
|
43
|
+
AutocompleteModel,
|
|
44
|
+
TreeModel,
|
|
45
|
+
TabsModel,
|
|
46
|
+
AccordionModel,
|
|
47
|
+
GalleryModel,
|
|
48
|
+
PriceModel,
|
|
49
|
+
PricingModel,
|
|
50
|
+
CommentModel,
|
|
51
|
+
TestimonialModel,
|
|
52
|
+
StatsItemModel,
|
|
53
|
+
StatsModel,
|
|
54
|
+
TimelineItemModel,
|
|
55
|
+
TimelineModel,
|
|
56
|
+
EmptyStateModel,
|
|
57
|
+
BannerModel,
|
|
58
|
+
ProfileDropdownModel,
|
|
59
|
+
}
|
|
4
60
|
|
|
5
|
-
|
|
61
|
+
const Model = {
|
|
6
62
|
User,
|
|
63
|
+
HeaderModel,
|
|
64
|
+
FooterModel,
|
|
65
|
+
HeroModel,
|
|
66
|
+
ButtonModel,
|
|
67
|
+
ConfirmModel,
|
|
68
|
+
InputModel,
|
|
69
|
+
SpinnerModel,
|
|
70
|
+
TableModel,
|
|
71
|
+
ToastModel,
|
|
72
|
+
SelectModel,
|
|
73
|
+
AutocompleteModel,
|
|
74
|
+
TreeModel,
|
|
75
|
+
TabsModel,
|
|
76
|
+
AccordionModel,
|
|
77
|
+
GalleryModel,
|
|
78
|
+
PriceModel,
|
|
79
|
+
PricingModel,
|
|
80
|
+
CommentModel,
|
|
81
|
+
TestimonialModel,
|
|
82
|
+
StatsItemModel,
|
|
83
|
+
StatsModel,
|
|
84
|
+
TimelineItemModel,
|
|
85
|
+
TimelineModel,
|
|
86
|
+
EmptyStateModel,
|
|
87
|
+
BannerModel,
|
|
88
|
+
ProfileDropdownModel,
|
|
7
89
|
}
|
|
90
|
+
|
|
91
|
+
export default Model
|
package/src/StdIn.js
CHANGED
|
@@ -14,12 +14,6 @@ export default class StdIn extends EventProcessor {
|
|
|
14
14
|
/** @type {string[]} Messages to ignore */
|
|
15
15
|
static IGNORE_MESSAGES = ['', 'undefined']
|
|
16
16
|
|
|
17
|
-
/** @type {UiMessage[]} Input message buffer */
|
|
18
|
-
stream = []
|
|
19
|
-
|
|
20
|
-
/** @type {Processor} Input processor */
|
|
21
|
-
processor
|
|
22
|
-
|
|
23
17
|
/**
|
|
24
18
|
* Creates a new StdIn instance.
|
|
25
19
|
* @param {object} props - StdIn properties
|
|
@@ -29,7 +23,9 @@ export default class StdIn extends EventProcessor {
|
|
|
29
23
|
constructor(props = {}) {
|
|
30
24
|
super()
|
|
31
25
|
const { processor = new Processor(), stream = [] } = props
|
|
26
|
+
/** @type {Processor} Input processor */
|
|
32
27
|
this.processor = processor
|
|
28
|
+
/** @type {UiMessage[]} Input message buffer */
|
|
33
29
|
this.stream = stream
|
|
34
30
|
this.processor?.on('data', (data) => {
|
|
35
31
|
this.write(data)
|
package/src/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { UIApp as default } from './domain/app/UIApp.js'
|
package/src/core/Form/Form.js
CHANGED
|
@@ -106,10 +106,11 @@ export default class UIForm extends FormMessage {
|
|
|
106
106
|
/**
|
|
107
107
|
* Validates the entire form.
|
|
108
108
|
*
|
|
109
|
-
* @returns {
|
|
109
|
+
* @returns {any}
|
|
110
110
|
*/
|
|
111
111
|
validate() {
|
|
112
|
-
|
|
112
|
+
/** @type {any} */
|
|
113
|
+
const errors = {}
|
|
113
114
|
let isValid = true
|
|
114
115
|
|
|
115
116
|
this.fields.forEach((field) => {
|
|
@@ -120,7 +121,7 @@ export default class UIForm extends FormMessage {
|
|
|
120
121
|
field.required &&
|
|
121
122
|
(fieldValue === '' || fieldValue === null || fieldValue === undefined)
|
|
122
123
|
) {
|
|
123
|
-
errors
|
|
124
|
+
errors[field.name] = 'This field is required'
|
|
124
125
|
isValid = false
|
|
125
126
|
return
|
|
126
127
|
}
|
|
@@ -132,14 +133,12 @@ export default class UIForm extends FormMessage {
|
|
|
132
133
|
)
|
|
133
134
|
|
|
134
135
|
if (!fieldValid) {
|
|
135
|
-
|
|
136
|
-
errors.set(key, err)
|
|
137
|
-
}
|
|
136
|
+
Object.assign(errors, fieldErrors)
|
|
138
137
|
isValid = false
|
|
139
138
|
}
|
|
140
139
|
})
|
|
141
140
|
|
|
142
|
-
return errors
|
|
141
|
+
return { isValid, errors }
|
|
143
142
|
}
|
|
144
143
|
|
|
145
144
|
/**
|
|
@@ -239,6 +238,8 @@ export default class UIForm extends FormMessage {
|
|
|
239
238
|
*/
|
|
240
239
|
toJSON() {
|
|
241
240
|
return {
|
|
241
|
+
id: this.id,
|
|
242
|
+
type: this.type,
|
|
242
243
|
time: new Date(this.time).toISOString(),
|
|
243
244
|
title: this.title,
|
|
244
245
|
fields: this.fields.map((f) => (f.toJSON ? f.toJSON() : f)),
|
package/src/core/Form/Message.js
CHANGED
|
@@ -14,7 +14,7 @@ export default class FormMessage extends UiMessage {
|
|
|
14
14
|
* @param {Object} [input={}] - Message properties.
|
|
15
15
|
*/
|
|
16
16
|
constructor(input = {}) {
|
|
17
|
-
super(input)
|
|
17
|
+
super({ type: 'form', ...input })
|
|
18
18
|
const { data = {}, schema = {} } = input
|
|
19
19
|
|
|
20
20
|
// Store data and schema for easy access
|