@nan0web/ui 1.8.0 → 1.10.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.
Files changed (107) hide show
  1. package/README.md +29 -10
  2. package/package.json +18 -22
  3. package/src/Model/index.js +32 -3
  4. package/src/core/Form/Form.js +8 -7
  5. package/src/core/Form/Message.js +1 -1
  6. package/src/core/GeneratorRunner.js +10 -0
  7. package/src/core/Intent.js +21 -5
  8. package/src/core/IntentErrorModel.js +6 -1
  9. package/src/core/Stream.js +16 -5
  10. package/src/core/index.js +1 -1
  11. package/src/domain/FooterModel.js +57 -0
  12. package/src/domain/HeaderModel.js +50 -0
  13. package/src/domain/HeroModel.js +48 -0
  14. package/src/domain/Navigation.js +11 -10
  15. package/src/domain/SandboxModel.js +66 -115
  16. package/src/domain/ShowcaseAppModel.js +133 -50
  17. package/src/domain/components/AccordionModel.js +38 -0
  18. package/src/domain/components/AutocompleteModel.js +11 -21
  19. package/src/domain/components/BannerModel.js +37 -0
  20. package/src/domain/components/BreadcrumbModel.js +11 -9
  21. package/src/domain/components/ButtonModel.js +31 -58
  22. package/src/domain/components/CommentModel.js +44 -0
  23. package/src/domain/components/ConfirmModel.js +26 -33
  24. package/src/domain/components/EmptyStateModel.js +45 -0
  25. package/src/domain/components/FAQModel.js +32 -0
  26. package/src/domain/components/FooterConfigModel.js +26 -0
  27. package/src/domain/components/FooterVisibilityModel.js +48 -0
  28. package/src/domain/components/GalleryModel.js +36 -0
  29. package/src/domain/components/HeaderConfigModel.js +26 -0
  30. package/src/domain/components/HeaderVisibilityModel.js +54 -0
  31. package/src/domain/components/InputModel.js +21 -41
  32. package/src/domain/components/PriceModel.js +30 -0
  33. package/src/domain/components/PricingModel.js +39 -0
  34. package/src/domain/components/PricingSectionModel.js +32 -0
  35. package/src/domain/components/ProfileDropdownModel.js +45 -0
  36. package/src/domain/components/SelectModel.js +11 -21
  37. package/src/domain/components/SpinnerModel.js +11 -26
  38. package/src/domain/components/StatsItemModel.js +38 -0
  39. package/src/domain/components/StatsModel.js +32 -0
  40. package/src/domain/components/TableModel.js +11 -24
  41. package/src/domain/components/TabsModel.js +30 -0
  42. package/src/domain/components/TestimonialModel.js +24 -0
  43. package/src/domain/components/TimelineItemModel.js +38 -0
  44. package/src/domain/components/TimelineModel.js +32 -0
  45. package/src/domain/components/ToastModel.js +24 -51
  46. package/src/domain/components/TreeModel.js +10 -26
  47. package/src/domain/components/index.js +34 -0
  48. package/src/domain/index.js +24 -0
  49. package/src/index.js +2 -0
  50. package/src/testing/GalleryGenerator.js +85 -0
  51. package/src/testing/LogicInspector.js +55 -0
  52. package/src/testing/SnapshotInspector.js +84 -0
  53. package/src/testing/VisualAdapter.js +41 -0
  54. package/src/testing/index.js +3 -0
  55. package/types/Model/index.d.ts +62 -4
  56. package/types/core/Form/Form.d.ts +2 -2
  57. package/types/core/GeneratorRunner.d.ts +4 -0
  58. package/types/core/Intent.d.ts +31 -3
  59. package/types/core/IntentErrorModel.d.ts +4 -0
  60. package/types/core/index.d.ts +1 -1
  61. package/types/domain/FooterModel.d.ts +52 -0
  62. package/types/domain/HeaderModel.d.ts +45 -0
  63. package/types/domain/HeroModel.d.ts +43 -0
  64. package/types/domain/Navigation.d.ts +10 -9
  65. package/types/domain/SandboxModel.d.ts +16 -40
  66. package/types/domain/ShowcaseAppModel.d.ts +26 -54
  67. package/types/domain/components/AccordionModel.d.ts +33 -0
  68. package/types/domain/components/AutocompleteModel.d.ts +10 -29
  69. package/types/domain/components/BannerModel.d.ts +32 -0
  70. package/types/domain/components/BreadcrumbModel.d.ts +13 -6
  71. package/types/domain/components/ButtonModel.d.ts +18 -54
  72. package/types/domain/components/CommentModel.d.ts +39 -0
  73. package/types/domain/components/ConfirmModel.d.ts +20 -35
  74. package/types/domain/components/EmptyStateModel.d.ts +40 -0
  75. package/types/domain/components/FAQModel.d.ts +27 -0
  76. package/types/domain/components/FooterConfigModel.d.ts +21 -0
  77. package/types/domain/components/FooterVisibilityModel.d.ts +43 -0
  78. package/types/domain/components/GalleryModel.d.ts +35 -0
  79. package/types/domain/components/HeaderConfigModel.d.ts +21 -0
  80. package/types/domain/components/HeaderVisibilityModel.d.ts +49 -0
  81. package/types/domain/components/HeroModel.d.ts +24 -0
  82. package/types/domain/components/InputModel.d.ts +19 -59
  83. package/types/domain/components/PriceModel.d.ts +25 -0
  84. package/types/domain/components/PricingModel.d.ts +34 -0
  85. package/types/domain/components/PricingSectionModel.d.ts +27 -0
  86. package/types/domain/components/ProfileDropdownModel.d.ts +40 -0
  87. package/types/domain/components/SelectModel.d.ts +13 -28
  88. package/types/domain/components/ShowcaseAppModel.d.ts +32 -0
  89. package/types/domain/components/SpinnerModel.d.ts +10 -27
  90. package/types/domain/components/StatsItemModel.d.ts +33 -0
  91. package/types/domain/components/StatsModel.d.ts +27 -0
  92. package/types/domain/components/TableModel.d.ts +10 -26
  93. package/types/domain/components/TabsModel.d.ts +28 -0
  94. package/types/domain/components/TestimonialModel.d.ts +18 -0
  95. package/types/domain/components/TimelineItemModel.d.ts +33 -0
  96. package/types/domain/components/TimelineModel.d.ts +27 -0
  97. package/types/domain/components/ToastModel.d.ts +16 -45
  98. package/types/domain/components/TreeModel.d.ts +13 -36
  99. package/types/domain/components/index.d.ts +20 -0
  100. package/types/domain/index.d.ts +4 -1
  101. package/types/index.d.ts +1 -0
  102. package/types/testing/GalleryGenerator.d.ts +1 -0
  103. package/types/testing/LogicInspector.d.ts +22 -0
  104. package/types/testing/SnapshotInspector.d.ts +17 -0
  105. package/types/testing/VisualAdapter.d.ts +15 -0
  106. package/types/testing/index.d.ts +3 -0
  107. package/src/README.md.js +0 -436
package/src/README.md.js DELETED
@@ -1,436 +0,0 @@
1
- import { describe, it, before, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import FS from '@nan0web/db-fs'
4
- import { NoConsole } from '@nan0web/log'
5
- import { DatasetParser, DocsParser, runSpawn } from '@nan0web/test'
6
- import { Frame, Model, OutputMessage, View, FormInput, UiMessage, UiForm } from './index.js'
7
- import { Welcome } from './Component/index.js'
8
-
9
- const fs = new FS()
10
- let pkg
11
-
12
- // Load package.json once before tests
13
- before(async () => {
14
- const doc = await fs.loadDocument('package.json', {})
15
- pkg = doc || {}
16
- })
17
-
18
- let console = new NoConsole()
19
-
20
- beforeEach((info) => {
21
- console = new NoConsole()
22
- })
23
-
24
- // Core test suite that also serves as the source for README generation.
25
- // The block comments inside each `it` block are extracted to build
26
- // the final `README.md`. Keeping the comments here ensures the
27
- // documentation stays close to the code.
28
- function testRender() {
29
- /**
30
- * @docs
31
- * # @nan0web/ui
32
- *
33
- * 🏴󠁧󠁢󠁥󠁮󠁧󠁿 [English](./README.md) | 🇺🇦 [Українською](./docs/uk/README.md)
34
- *
35
- * <!-- %PACKAGE_STATUS% -->
36
- *
37
- * A lightweight, agnostic UI framework designed with the **nan0web philosophy**
38
- * — one application logic, many UI implementations.
39
- *
40
- * This library provides core classes and utilities for building structured user interfaces.
41
- * It supports:
42
- *
43
- * - Messaging (Input/Output)
44
- * - Forms with validation
45
- * - Progress tracking
46
- * - Component rendering
47
- * - View management with Frame rendering
48
- * - App structure with core and user apps
49
- *
50
- * Built to work in sync or async, terminal-based or web-based apps,
51
- * focusing on type safety, minimalism, and pure JavaScript design.
52
- *
53
- * ## Installation
54
- */
55
- it('How to install with npm?', () => {
56
- /**
57
- * ```bash
58
- * npm install @nan0web/ui
59
- * ```
60
- */
61
- assert.equal(pkg.name, '@nan0web/ui')
62
- })
63
- /**
64
- * @docs
65
- */
66
- it('How to install with pnpm?', () => {
67
- /**
68
- * ```bash
69
- * pnpm add @nan0web/ui
70
- * ```
71
- */
72
- assert.equal(pkg.name, '@nan0web/ui')
73
- })
74
- /**
75
- * @docs
76
- */
77
- it('How to install with yarn?', () => {
78
- /**
79
- * ```bash
80
- * yarn add @nan0web/ui
81
- * ```
82
- */
83
- assert.equal(pkg.name, '@nan0web/ui')
84
- })
85
-
86
- /**
87
- * @docs
88
- * ## Concepts & Architecture
89
- *
90
- * ### Message Flow
91
- *
92
- * UI communication is built around messages:
93
- *
94
- * - **`UiMessage`** – abstract message base class
95
- * - **`OutputMessage`** – system output (content, error, priority)
96
- *
97
- * Messages are simple, serializable data containers. They help build
98
- * decoupled communication systems between UI components.
99
- */
100
- it('How to create input and output messages?', () => {
101
- //import { InputMessage, OutputMessage } from '@nan0web/ui'
102
-
103
- const input = UiMessage.from({ body: 'Hello User' })
104
- const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
105
- console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
106
- console.info(String(output)) // ← Welcome to @nan0web/ui
107
- assert.deepStrictEqual(console.output()[0][1].body, 'Hello User')
108
- assert.deepStrictEqual(console.output()[0][1].head, {})
109
- assert.deepStrictEqual(console.output()[0][1].type, '')
110
- assert.ok(console.output()[0][1].id)
111
- assert.ok(console.output()[1][1].endsWith('Welcome to @nan0web/ui'))
112
- })
113
-
114
- /**
115
- * @docs
116
- * ### Forms
117
- *
118
- * `UiForm` supports field definitions, data management, and schema validation.
119
- * Every form includes a title, fields, and current state.
120
- *
121
- * Field types include:
122
- *
123
- * - `text`
124
- * - `email`
125
- * - `number`
126
- * - `select`
127
- * - `checkbox`
128
- * - `textarea`
129
- */
130
- it('How to define and validate a UiForm?', () => {
131
- //import { UiForm } from '@nan0web/ui'
132
-
133
- const form = new UiForm({
134
- title: 'Contact Form',
135
- fields: [
136
- FormInput.from({ name: 'email', label: 'Email Address', type: 'email', required: true }),
137
- FormInput.from({
138
- name: 'message',
139
- label: 'Your Message',
140
- type: 'textarea',
141
- required: true,
142
- }),
143
- ],
144
- state: {
145
- email: 'invalid-email',
146
- message: 'Hello!',
147
- },
148
- })
149
-
150
- const errors = form.validate()
151
- console.info(errors.size) // ← 1
152
- console.info(errors.get('email')) // ← Invalid email format
153
-
154
- assert.equal(console.output()[0][1], 1)
155
- assert.equal(console.output()[1][1], 'Invalid email format')
156
- })
157
-
158
- /**
159
- * @docs
160
- * ### Components
161
- *
162
- * Components render data as frame-ready output.
163
- *
164
- * - `Welcome` – greets user by name
165
- * - `Process` – shows progress bar and time
166
- */
167
- it('How to render the Welcome component?', () => {
168
- //import { Welcome } from '@nan0web/ui'
169
-
170
- const frame = Welcome({ user: { name: 'Alice' } })
171
- const firstLine = frame[0].join('')
172
- console.info(firstLine) // ← Welcome Alice!
173
- assert.equal(console.output()[0][1], 'Welcome Alice!')
174
- })
175
-
176
- /**
177
- * @docs
178
- * ### View Manager
179
- *
180
- * `View` combines components and renders frames.
181
- *
182
- * Every view has:
183
- *
184
- * - Locale – formatted text, numbers, currency
185
- * - StdIn / StdOut – input/output streams
186
- * - Frame – output buffer with visual properties
187
- */
188
- it('How to render frame with View?', () => {
189
- //import { View } from '@nan0web/ui'
190
-
191
- const view = new View()
192
- view.render(1)(['Hello, world'])
193
- console.info(String(view.frame)) // ← "\rHello, world"
194
- assert.ok(String(view.frame).includes('Hello, world'))
195
- })
196
-
197
- /**
198
- * @docs
199
- * ### Frame Rendering
200
- *
201
- * `Frame` manages visual rendering with width and height limits.
202
- * Useful for fixed-size terminals or UI blocks.
203
- *
204
- * Render methods:
205
- *
206
- * - `APPEND` – adds content after previous frame
207
- * - `REPLACE` – erases and replaces full frame area
208
- * - `VISIBLE` – renders only visible part of frame
209
- */
210
- it('How to create a Frame with fixed size?', () => {
211
- //import { Frame } from '@nan0web/ui'
212
-
213
- const frame = new Frame({
214
- value: [['Frame content']],
215
- width: 20,
216
- height: 5,
217
- renderMethod: Frame.RenderMethod.APPEND,
218
- })
219
-
220
- const rendered = frame.render()
221
- console.info(rendered.includes('Frame content')) // ← true
222
- assert.ok(rendered.includes('Frame content'))
223
- })
224
- it('How to create a Frame with different render methods?', () => {
225
- //import { Frame } from '@nan0web/ui'
226
-
227
- const frame = new Frame({
228
- value: [['Frame content']],
229
- width: 20,
230
- height: 5,
231
- })
232
-
233
- frame.renderMethod = Frame.RenderMethod.REPLACE
234
- const renderedReplace = frame.render()
235
- assert.ok(renderedReplace.includes('Frame content'))
236
-
237
- frame.renderMethod = Frame.RenderMethod.VISIBLE
238
- const renderedVisible = frame.render()
239
- assert.ok(renderedVisible.includes('Frame content'))
240
- })
241
-
242
- /**
243
- * @docs
244
- * ### Models
245
- *
246
- * UI models are plain data objects managed by `Model` classes.
247
- *
248
- * - `User` – user data
249
- */
250
- it('How to use a User model?', () => {
251
- //import { Model } from '@nan0web/ui'
252
-
253
- const user = new Model.User({ name: 'Charlie', email: 'charlie@example.com' })
254
- console.info(user.name) // ← Charlie
255
- console.info(user.email) // ← charlie@example.com
256
- assert.equal(user.name, 'Charlie')
257
- assert.equal(user.email, 'charlie@example.com')
258
- })
259
-
260
- /**
261
- * @docs
262
- * ### Testing UI
263
- *
264
- * Core unit-tested to ensure stability in different environments.
265
- *
266
- * All components, adapters, and models are designed to be testable
267
- * with minimal setup.
268
- */
269
- it('How to test UI components with assertions?', () => {
270
- //import { Welcome } from '@nan0web/ui'
271
-
272
- const output = Welcome({ user: { name: 'Test' } })
273
- console.info(output) // ← Welcome Test!
274
- assert.deepStrictEqual(console.output()[0][1], [
275
- ['Welcome', ' ', 'Test', '!'],
276
- ['What can we do today great?'],
277
- [''],
278
- ])
279
- })
280
-
281
- /**
282
- * @docs
283
- * ### Master IDE (Component Sandbox)
284
- *
285
- * The Master IDE (OlmuiInspector) provides a unified environment for testing and documenting
286
- * web components across platforms. It supports:
287
- *
288
- * - **NaN0 Spec** — a concise YAML-based shorthand for declaring component variations.
289
- * - **OlmuiInspector** — unified UI for exploring component models and props.
290
- * - **Live Preview** — real-time rendering of component states.
291
- * - **i18n UI** — fully localized interface (UK/EN) for global developers.
292
- * - **Theme Editor** — Bootstrap-like CSS variable system with live preview.
293
- *
294
- * It follows the **Olmui** core pattern: *One Logic — Many UI* (same manifest powers both CLI and Web).
295
- *
296
- * #### Theme Editor (CSS Variables)
297
- *
298
- * Professional-grade theming with live preview. Supports:
299
- *
300
- * - **Palette**: primary, secondary, success, warning, danger, info
301
- * - **Geometry**: border-radius (sm/md/lg/pill/circle), spacing (sm/md/lg)
302
- * - **Type-safe inputs**: `type="color"` for colors, number inputs for dimensions
303
- *
304
- * #### Component Rendering Architecture
305
- *
306
- * The IDE handles data transformation between YAML models and web components:
307
- *
308
- * - **Table**: `rows[][] + columns[]` → `data[]` (array of objects)
309
- * - **Tree**: `data` → `items` mapping with 4-level taxonomy
310
- * - **Markdown**: Raw markdown → HTML via `_md2html()` converter
311
- * - **ProgressBar**: Tag alias (`ui-progress-bar` → `ui-progress`), variant colors
312
- * - **LangSelect**: `string[]` → `{code,title}[]` conversion
313
- * - **Hyphenated props**: Auto `camelCase` conversion (`show-label` → `showLabel`)
314
- *
315
- * #### NaN0 Spec (YAML)
316
- *
317
- * Concise format for defining variations:
318
- */
319
- it('How to define a component variation using NaN0 Spec?', () => {
320
- /**
321
- * ```yaml
322
- * - Button: Primary
323
- * $variant: brand
324
- * $outline: true
325
- * ```
326
- */
327
- assert.ok(pkg.name === '@nan0web/ui')
328
- })
329
-
330
- /**
331
- * @docs
332
- * #### Documentation Site
333
- *
334
- * The IDE includes an auto-generated documentation site.
335
- * HTML pages are generated from `ide.html` template via `generate-pages.js`:
336
- *
337
- * - Per-language pages (`/uk/Data/Table.html`, `/en/Feedback/Alert.html`)
338
- * - SEO-optimized with `<title>` and `<meta>` per component
339
- * - Category-based URL routing (`/Data/`, `/Feedback/`, `/Forms/`, `/Actions/`, `/System/`)
340
- * - i18n navbar with `data-i18n` attributes
341
- */
342
- it('How to run the documentation site?', () => {
343
- /**
344
- * ```bash
345
- * npm run docs:dev
346
- * ```
347
- */
348
- assert.ok(pkg.scripts?.['docs:dev'])
349
- })
350
-
351
- /**
352
- * @docs
353
- * ## Playground Demos
354
- *
355
- * The library includes rich playground demos:
356
- *
357
- * - [Registration Form](./play/registration.form.js)
358
- * - [Currency Exchange](./play/currency.exchange.js)
359
- * - [Mobile Top-up](./play/topup.telephone.js)
360
- * - [Language Selector](./play/language.form.js)
361
- *
362
- * Run to explore live functionality:
363
- */
364
- it('How to run the playground?', async () => {
365
- /**
366
- * ```bash
367
- * # Clone repository and run playground
368
- * git clone https://github.com/nan0web/ui.git
369
- * cd ui
370
- * npm install
371
- * npm run play
372
- * ```
373
- */
374
- assert.ok(String(pkg.scripts?.play).includes('node play'))
375
- const response = await runSpawn('git', ['remote', 'get-url', 'origin'])
376
- assert.ok(response.code === 0, 'git command fails (e.g., not in a git repo)')
377
- assert.ok(response.text.trim().endsWith(':nan0web/ui.git'))
378
- })
379
-
380
- /**
381
- * @docs
382
- * ## API Documentation
383
- *
384
- * Detailed API docs are available in each class JSDoc.
385
- * Explore:
386
- *
387
- * - [Messages](./src/core/Message/)
388
- * - [Forms](./src/core/Form/)
389
- * - [Stream](./src/core/Stream.js)
390
- * - [Components](./src/Component/)
391
- * - [View](./src/View/)
392
- * - [App](./src/App/)
393
- * - [Models](./src/Model/)
394
- *
395
- * ## Project Architecture & Specs
396
- *
397
- * How the universal block spec is designed? - [check Universal Blocks Spec (`project.md`)](./project.md)
398
- *
399
- * ## Contributing
400
- */
401
- it('How to contribute? - [check here](./CONTRIBUTING.md)', async () => {
402
- assert.equal(pkg.scripts?.precommit, 'npm test')
403
- assert.equal(pkg.scripts?.prepush, 'npm test')
404
- assert.equal(pkg.scripts?.prepare, 'husky')
405
- const str = await fs.loadDocumentAs('.txt', 'CONTRIBUTING.md')
406
- assert.ok(str.includes('# Contributing'))
407
- })
408
-
409
- /**
410
- * @docs
411
- * ## License
412
- */
413
- it('How to license ISC? - [check here](./LICENSE)', async () => {
414
- /** @docs */
415
- const text = await fs.loadDocumentAs('.txt', 'LICENSE')
416
- assert.ok(text.includes('ISC'))
417
- })
418
- }
419
-
420
- describe('README.md testing', testRender)
421
-
422
- describe('Rendering README.md', async () => {
423
- let text = ''
424
- const format = new Intl.NumberFormat('en-US').format
425
- const parser = new DocsParser()
426
- const source = await fs.loadDocument('src/README.md.js')
427
- text = String(parser.decode(source))
428
- await fs.saveDocument('README.md', { content: text })
429
- const dataset = DatasetParser.parse(text, pkg?.name ?? '@nan0web/ui')
430
- await fs.saveDocument('.datasets/README.dataset.jsonl', dataset)
431
-
432
- it(`document is rendered in README.md [${format(Buffer.byteLength(text))}b]`, async () => {
433
- const text = await fs.loadDocumentAs('.txt', 'README.md')
434
- assert.ok(text.includes('## License'))
435
- })
436
- })