@nan0web/ui 1.0.0 → 1.0.1
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/package.json +6 -1
- package/.datasets/README.dataset.jsonl +0 -12
- package/.editorconfig +0 -20
- package/CONTRIBUTING.md +0 -42
- package/docs/uk/README.md +0 -240
- package/playground/User.js +0 -52
- package/playground/currency.exchange.js +0 -48
- package/playground/i18n/index.js +0 -21
- package/playground/i18n/uk.js +0 -53
- package/playground/language.form.js +0 -25
- package/playground/main.js +0 -72
- package/playground/registration.form.js +0 -58
- package/playground/topup.telephone.js +0 -62
- package/src/App/User/UserApp.test.js +0 -56
- package/src/App/User/UserUI.test.js +0 -51
- package/src/Frame/Frame.test.js +0 -429
- package/src/View/View.test.js +0 -77
- package/src/core/Form/Form.test.js +0 -116
- package/src/core/Form/Input.test.js +0 -58
- package/src/core/Form/Message.test.js +0 -54
- package/src/core/InputAdapter.test.js +0 -35
- package/src/core/Message/InputMessage.test.js +0 -45
- package/src/core/Message/Message.test.js +0 -58
- package/src/core/Message/OutputMessage.test.js +0 -61
- package/src/core/OutputAdapter.test.js +0 -35
- package/src/core/Stream.test.js +0 -78
- package/src/index.test.js +0 -14
- package/stories/App/AppView.js +0 -15
- package/stories/App/AppView.test.js +0 -22
- package/stories/App/RenderOptions.js +0 -14
- package/stories/nodejs/interface.test.js +0 -27
- package/system.md +0 -187
- package/system1.md +0 -137
- package/task.md +0 -181
- package/tsconfig.json +0 -23
- package/vitest.config.js +0 -26
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nan0web/ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"src/**/*.js",
|
|
10
|
+
"!src/**/*.test.js",
|
|
11
|
+
"types/**/*.d.ts"
|
|
12
|
+
],
|
|
8
13
|
"scripts": {
|
|
9
14
|
"build": "tsc",
|
|
10
15
|
"playground": "node playground/main.js",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{"instruction":"How to install with npm?","output":"```bash\nnpm install @nan0web/ui\n```\n","context":["h1:@nan0web/ui","h2:Installation"],"input":"## Installation\n\n","tags":["@nan0web/ui","Installation"],"proven":"assert-in-@nan0web/ui"}
|
|
2
|
-
{"instruction":"How to install with pnpm?","output":"```bash\npnpm add @nan0web/ui\n```\n","context":["h1:@nan0web/ui","h2:Installation"],"input":"## Installation\n\n","tags":["@nan0web/ui","Installation"],"proven":"assert-in-@nan0web/ui"}
|
|
3
|
-
{"instruction":"How to install with yarn?","output":"```bash\nyarn add @nan0web/ui\n```\n","context":["h1:@nan0web/ui","h2:Installation"],"input":"## Installation\n\n","tags":["@nan0web/ui","Installation"],"proven":"assert-in-@nan0web/ui"}
|
|
4
|
-
{"instruction":"How to create input and output messages?","output":"```js\nimport { InputMessage, OutputMessage } from '@nan0web/ui'\n\nconst input = InputMessage.from({ value: 'Hello User' })\nconst output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })\nconsole.info(input.value) // ← Hello User\nconsole.info(output.content[0]) // ← Welcome to @nan0web/ui\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Message Flow"],"input":"### Message Flow\n\nUI communication is built around messages:\n\n- **`UIMessage`** – abstract message base class\n- **`InputMessage`** – user input message (value, options)\n- **`OutputMessage`** – system output (content, error, priority)\n\nMessages are simple, serializable data containers. They help build\ndecoupled communication systems between UI components.\n\n","tags":["@nan0web/ui","Concepts & Architecture","Message Flow"],"proven":"assert-in-@nan0web/ui"}
|
|
5
|
-
{"instruction":"How to define and validate a UIForm?","output":"```js\nimport { UIForm } from '@nan0web/ui'\n\nconst form = new UIForm({\n\ttitle: \"Contact Form\",\n\tfields: [\n\t\tFormInput.from({ name: \"email\", label: \"Email Address\", type: \"email\", required: true }),\n\t\tFormInput.from({ name: \"message\", label: \"Your Message\", type: \"textarea\", required: true })\n\t],\n\tstate: {\n\t\temail: \"invalid-email\",\n\t\tmessage: \"Hello!\"\n\t}\n})\n\nconst result = form.validate()\nconsole.info(result.isValid) // ← false\nconsole.info(result.errors.email) // ← Invalid email format\n\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Forms"],"input":"### Forms\n\n`UIForm` supports field definitions, data management, and schema validation.\nEvery form includes a title, fields, and current state.\n\nField types include:\n\n- `text`\n- `email`\n- `number`\n- `select`\n- `checkbox`\n- `textarea`\n\n","tags":["@nan0web/ui","Concepts & Architecture","Forms"],"proven":"assert-in-@nan0web/ui"}
|
|
6
|
-
{"instruction":"How to render the Welcome component?","output":"```js\nimport { Welcome } from '@nan0web/ui'\n\nconst frame = Welcome({ user: { name: \"Alice\" } })\nconst firstLine = frame[0].join(\"\")\nconsole.info(firstLine) // ← Welcome Alice!\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Components"],"input":"### Components\n\nComponents render data as frame-ready output.\n\n- `Welcome` – greets user by name\n- `Process` – shows progress bar and time\n\n","tags":["@nan0web/ui","Concepts & Architecture","Components"],"proven":"assert-in-@nan0web/ui"}
|
|
7
|
-
{"instruction":"How to render frame with View?","output":"```js\nimport { View } from '@nan0web/ui'\n\nconst view = new View()\nview.render(1)([\"Hello, world\"])\nconsole.info(String(view.frame)) // ← \"\\rHello, world\"\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:View Manager"],"input":"### View Manager\n\n`View` combines components and renders frames.\n\nEvery view has:\n\n- Locale – formatted text, numbers, currency\n- StdIn / StdOut – input/output streams\n- Frame – output buffer with visual properties\n\n","tags":["@nan0web/ui","Concepts & Architecture","View Manager"],"proven":"assert-in-@nan0web/ui"}
|
|
8
|
-
{"instruction":"How to create a Frame with fixed size?","output":"```js\nimport { Frame } from '@nan0web/ui'\n\nconst frame = new Frame({\n\tvalue: [[\"Frame content\"]],\n\twidth: 20,\n\theight: 5,\n\trenderMethod: Frame.RenderMethod.APPEND,\n})\n\nconst rendered = frame.render()\nconsole.info(rendered.includes(\"Frame content\")) // ← true\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Frame Rendering"],"input":"### Frame Rendering\n\n`Frame` manages visual rendering with width and height limits.\nUseful for fixed-size terminals or UI blocks.\n\n","tags":["@nan0web/ui","Concepts & Architecture","Frame Rendering"],"proven":"assert-in-@nan0web/ui"}
|
|
9
|
-
{"instruction":"How to create a basic user app that greets?","output":"```js\nimport { App, View } from '@nan0web/ui'\n\nconst app = new App.User.App({ name: \"GreetApp\" })\nconst view = new View()\nview.register(\"Welcome\", Welcome)\n\nconst cmd = App.Command.Message.parse(\"welcome --user Bob\")\nconst result = await app.processCommand(cmd, new App.User.UI(app, view))\nconsole.info(String(result)) // ← Welcome Bob!\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:App Architecture"],"input":"### App Architecture\n\n`App` provides the main application logic.\n\n- Core – minimal UI layer\n- User – user-specific UI commands\n\nEach app registers commands and binds them to UI actions.\n\n","tags":["@nan0web/ui","Concepts & Architecture","App Architecture"],"proven":"assert-in-@nan0web/ui"}
|
|
10
|
-
{"instruction":"How to use a User model?","output":"```js\nimport { Model } from '@nan0web/ui'\n\nconst user = new Model.User({ name: \"Charlie\", email: \"charlie@example.com\" })\nconsole.info(user.name) // ← Charlie\nconsole.info(user.email) // ← charlie@example.com\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Models"],"input":"### Models\n\nUI models are plain data objects managed by `Model` classes.\n\n- `User` – user data\n\n","tags":["@nan0web/ui","Concepts & Architecture","Models"],"proven":"assert-in-@nan0web/ui"}
|
|
11
|
-
{"instruction":"How to test UI components with assertions?","output":"```js\nimport { Welcome, InputMessage } from '@nan0web/ui'\n\nconst output = Welcome({ user: { name: \"Test\" } })\nconst input = InputMessage.from({ value: \"test\" })\nconsole.log(output[0].join(\"\")) // ← Welcome Test!\n```\n","context":["h1:@nan0web/ui","h2:Concepts & Architecture","h3:Testing UI"],"input":"### Testing UI\n\nCore unit-tested to ensure stability in different environments.\n\nAll components, adapters, and models are designed to be testable\nwith minimal setup.\n\n","tags":["@nan0web/ui","Concepts & Architecture","Testing UI"],"proven":"assert-in-@nan0web/ui"}
|
|
12
|
-
{"instruction":"How to run the playground?","output":"```bash\n# Clone repository and run playground\ngit clone https://github.com/nan0web/ui.git\ncd ui\nnpm install\nnpm run playground\n```\n","context":["h1:@nan0web/ui","h2:Playground Demos"],"input":"## Playground Demos\n\nThe library includes rich playground demos:\n\n- [Registration Form](./playground/registration.form.js)\n- [Currency Exchange](./playground/currency.exchange.js)\n- [Mobile Top-up](./playground/topup.telephone.js)\n- [Language Selector](./playground/language.form.js)\n\nRun to explore live functionality:\n\n","tags":["@nan0web/ui","Playground Demos"],"proven":"assert-in-@nan0web/ui"}
|
package/.editorconfig
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
root = true
|
|
2
|
-
|
|
3
|
-
[*]
|
|
4
|
-
indent_style = tab
|
|
5
|
-
indent_size = 2
|
|
6
|
-
charset = utf-8
|
|
7
|
-
end_of_line = lf
|
|
8
|
-
trim_trailing_whitespace = true
|
|
9
|
-
insert_final_newline = true
|
|
10
|
-
|
|
11
|
-
[*.md]
|
|
12
|
-
trim_trailing_whitespace = false
|
|
13
|
-
|
|
14
|
-
[*.jsx]
|
|
15
|
-
indent_style = tab
|
|
16
|
-
indent_size = 2
|
|
17
|
-
|
|
18
|
-
[*.{md,nano,yaml}]
|
|
19
|
-
indent_style = space
|
|
20
|
-
indent_size = 2
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
## Developer Certificate of Origin and License
|
|
4
|
-
By contributing, You accept and agree to the following terms and conditions for your present and future contributions submitted.
|
|
5
|
-
Except for the license granted herein, You reserve all right, title, and interest in and to your Contributions.
|
|
6
|
-
All contributions are subject to the Developer Certificate of Origin and license as set out in the LICENSE file.
|
|
7
|
-
|
|
8
|
-
## Code of Conduct
|
|
9
|
-
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
|
10
|
-
|
|
11
|
-
We are committed to making participation in this project a harassment-free experience for everyone, regardless of:
|
|
12
|
-
- Level of experience,
|
|
13
|
-
- Gender,
|
|
14
|
-
- Gender identity and expression,
|
|
15
|
-
- Sexual orientation,
|
|
16
|
-
- Disability,
|
|
17
|
-
- Personal appearance,
|
|
18
|
-
- Body size,
|
|
19
|
-
- Race,
|
|
20
|
-
- Ethnicity,
|
|
21
|
-
- Age,
|
|
22
|
-
- Religion.
|
|
23
|
-
|
|
24
|
-
> We recognize all people as the foundation of humanity, and our actions contribute to the continued existence and growth of our shared humanity.
|
|
25
|
-
|
|
26
|
-
### Release versioning
|
|
27
|
-
- Keep releases close to the minor versions and publish patches only if required.
|
|
28
|
-
|
|
29
|
-
### Examples of unacceptable behavior by participants:
|
|
30
|
-
- Use of sexual language or imagery.
|
|
31
|
-
- Derogatory comments or personal attacks.
|
|
32
|
-
- Trolling, public or private harassment.
|
|
33
|
-
- Insults or other unprofessional conduct.
|
|
34
|
-
|
|
35
|
-
### Enforcement
|
|
36
|
-
Project maintainers have the right and responsibility to:
|
|
37
|
-
- Remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions not aligned with this Code of Conduct.
|
|
38
|
-
- Remove maintainers who violate this Code of Conduct.
|
|
39
|
-
|
|
40
|
-
Instances of unacceptable behavior can be reported by contacting the project team.
|
|
41
|
-
|
|
42
|
-
This Code of Conduct is adapted from the [Contributor Covenant, version 1.1.0](https://contributor-covenant.org/version/1/1/0/).
|
package/docs/uk/README.md
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
# @nan0web/ui
|
|
2
|
-
|
|
3
|
-
|[Статус](https://github.com/nan0web/monorepo/blob/main/system.md#написання-сценаріїв)|Документація|Тестове покриття|Фічі|Версія npm|
|
|
4
|
-
|---|---|---|---|---|
|
|
5
|
-
|🟢 `96.6%` |🧪 [English 🏴](https://github.com/nan0web/ui/blob/main/README.md)<br />[Українською 🇺🇦](https://github.com/nan0web/ui/blob/main/docs/uk/README.md) |🟡 `80.9%` |✅ d.ts 📜 system.md 🕹️ playground |— |
|
|
6
|
-
|
|
7
|
-
Легкий, незалежний UI-фреймворк, створений за філософією **nan0web** —
|
|
8
|
-
одна логіка програми, багато UI-реалізацій.
|
|
9
|
-
|
|
10
|
-
Бібліотека надає базові класи та утиліти для побудови структурованих інтерфейсів.
|
|
11
|
-
Вона підтримує:
|
|
12
|
-
|
|
13
|
-
- Обмін повідомленнями (Input/Output)
|
|
14
|
-
- Форми з валідацією
|
|
15
|
-
- Відстеження прогресу
|
|
16
|
-
- Рендеринг компонентів
|
|
17
|
-
- Управління представленням за допомогою Frame-рендерингу
|
|
18
|
-
- Архітектуру додатку з core- та user-частинами
|
|
19
|
-
|
|
20
|
-
Працює синхронно та асинхронно, у терміналі та у веб-програмах,
|
|
21
|
-
зосереджуючись на типобезпеці, мінімалізмі та чистому JavaScript.
|
|
22
|
-
|
|
23
|
-
## Встановлення
|
|
24
|
-
|
|
25
|
-
Як встановити через npm?
|
|
26
|
-
```bash
|
|
27
|
-
npm install @nan0web/ui
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Як встановити через pnpm?
|
|
31
|
-
```bash
|
|
32
|
-
pnpm add @nan0web/ui
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Як встановити через yarn?
|
|
36
|
-
```bash
|
|
37
|
-
yarn add @nan0web/ui
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Концепції та архітектура
|
|
41
|
-
|
|
42
|
-
### Потік повідомлень
|
|
43
|
-
|
|
44
|
-
UI-комунікація будована навколо повідомлень:
|
|
45
|
-
|
|
46
|
-
- **`UIMessage`** – базовий абстрактний клас повідомлення
|
|
47
|
-
- **`InputMessage`** – повідомлення користувача (значення, опції)
|
|
48
|
-
- **`OutputMessage`** – системне повідомлення (вміст, помилка, пріоритет)
|
|
49
|
-
|
|
50
|
-
Повідомлення — прості, серіалізовані контейнери даних. Вони допомагають будувати
|
|
51
|
-
розслаблену комунікацію між UI-компонентами.
|
|
52
|
-
|
|
53
|
-
Як створити вхідне та вихідне повідомлення?
|
|
54
|
-
```js
|
|
55
|
-
import { InputMessage, OutputMessage } from '@nan0web/ui'
|
|
56
|
-
|
|
57
|
-
const input = InputMessage.from({ value: 'Hello User' })
|
|
58
|
-
const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
|
|
59
|
-
console.info(input.value) // ← Hello User
|
|
60
|
-
console.info(output.content[0]) // ← Welcome to @nan0web/ui
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Форми
|
|
64
|
-
|
|
65
|
-
`UIForm` підтримує визначення полів, управління даними та схему валідації.
|
|
66
|
-
Кожна форма має заголовок, список полів та поточний стан.
|
|
67
|
-
|
|
68
|
-
Типи полів:
|
|
69
|
-
|
|
70
|
-
- `text`
|
|
71
|
-
- `email`
|
|
72
|
-
- `number`
|
|
73
|
-
- `select`
|
|
74
|
-
- `checkbox`
|
|
75
|
-
- `textarea`
|
|
76
|
-
|
|
77
|
-
Як визначити та валідати форму?
|
|
78
|
-
```js
|
|
79
|
-
import { UIForm } from '@nan0web/ui'
|
|
80
|
-
|
|
81
|
-
const form = new UIForm({
|
|
82
|
-
title: "Контактна форма",
|
|
83
|
-
fields: [
|
|
84
|
-
FormInput.from({ name: "email", label: "Email-адреса", type: "email", required: true }),
|
|
85
|
-
FormInput.from({ name: "message", label: "Ваше повідомлення", type: "textarea", required: true })
|
|
86
|
-
],
|
|
87
|
-
state: {
|
|
88
|
-
email: "invalid-email",
|
|
89
|
-
message: "Привіт!"
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const result = form.validate()
|
|
94
|
-
console.info(result.isValid) // ← false
|
|
95
|
-
console.info(result.errors.email) // ← Неправильний формат email
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Компоненти
|
|
99
|
-
|
|
100
|
-
Компоненти рендерять дані у вигляді готових до виведення кадрів.
|
|
101
|
-
|
|
102
|
-
- `Welcome` – привітання користувача за ім’ям
|
|
103
|
-
- `Process` – індикатор прогресу
|
|
104
|
-
|
|
105
|
-
Як відрендерити компонент Welcome?
|
|
106
|
-
```js
|
|
107
|
-
import { Welcome } from '@nan0web/ui'
|
|
108
|
-
|
|
109
|
-
const frame = Welcome({ user: { name: "Аліса" } })
|
|
110
|
-
const firstLine = frame[0].join("")
|
|
111
|
-
console.info(firstLine) // ← Вітаємо, Аліса!
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Менеджер представлення (View)
|
|
115
|
-
|
|
116
|
-
`View` поєднує компоненти та рендерить кадри.
|
|
117
|
-
|
|
118
|
-
Кожне представлення має:
|
|
119
|
-
|
|
120
|
-
- Локаль – форматування тексту, чисел, валют
|
|
121
|
-
- StdIn / StdOut – потоки вводу/виводу
|
|
122
|
-
- Frame – буфер виводу з візуальними властивостями
|
|
123
|
-
|
|
124
|
-
Як вивести кадр за допомогою View?
|
|
125
|
-
```js
|
|
126
|
-
import { View } from '@nan0web/ui'
|
|
127
|
-
|
|
128
|
-
const view = new View()
|
|
129
|
-
view.render(1)(["Привіт, світе!"])
|
|
130
|
-
console.info(String(view.frame)) // ← "\rПривіт, світе!"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Рендеринг кадрів (Frame)
|
|
134
|
-
|
|
135
|
-
`Frame` керує візуальним рендерингом з обмеженнями ширини та висоти.
|
|
136
|
-
Корисно для терміналів фіксованого розміру або блокових UI.
|
|
137
|
-
|
|
138
|
-
Як створити кадр фіксованого розміру?
|
|
139
|
-
```js
|
|
140
|
-
import { Frame } from '@nan0web/ui'
|
|
141
|
-
|
|
142
|
-
const frame = new Frame({
|
|
143
|
-
value: [["Вміст кадру"]],
|
|
144
|
-
width: 20,
|
|
145
|
-
height: 5,
|
|
146
|
-
renderMethod: Frame.RenderMethod.APPEND,
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
const rendered = frame.render()
|
|
150
|
-
console.info(rendered.includes("Вміст кадру")) // ← true
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Архітектура додатку (App)
|
|
154
|
-
|
|
155
|
-
`App` забезпечує основну логіку програми.
|
|
156
|
-
|
|
157
|
-
- Core – мінімальний UI-шар
|
|
158
|
-
- User – користувацькі UI-команди
|
|
159
|
-
|
|
160
|
-
Кожен додаток реєструє команди та прив’язує їх до UI-дій.
|
|
161
|
-
|
|
162
|
-
Як створити простий користувацький додаток, який вітає?
|
|
163
|
-
```js
|
|
164
|
-
import { App, View } from '@nan0web/ui'
|
|
165
|
-
|
|
166
|
-
const app = new App.User.App({ name: "GreetApp" })
|
|
167
|
-
const view = new View()
|
|
168
|
-
view.register("Welcome", Welcome)
|
|
169
|
-
|
|
170
|
-
const cmd = App.Command.Message.parse("welcome --user Боб")
|
|
171
|
-
const result = await app.processCommand(cmd, new App.User.UI(app, view))
|
|
172
|
-
console.info(String(result)) // ← Вітаємо, Боб!
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Моделі
|
|
176
|
-
|
|
177
|
-
UI-моделі – це прості об’єкти даних, що керуються класами `Model`.
|
|
178
|
-
|
|
179
|
-
- `User` – дані користувача
|
|
180
|
-
|
|
181
|
-
Як використовувати модель User?
|
|
182
|
-
```js
|
|
183
|
-
import { Model } from '@nan0web/ui'
|
|
184
|
-
|
|
185
|
-
const user = new Model.User({ name: "Чарлі", email: "charlie@example.com" })
|
|
186
|
-
console.info(user.name) // ← Чарлі
|
|
187
|
-
console.info(user.email) // ← charlie@example.com
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Тестування UI
|
|
191
|
-
|
|
192
|
-
Основні модулі покриті юніт-тестами для забезпечення стабільності у різних середовищах.
|
|
193
|
-
|
|
194
|
-
Як тестувати UI-компоненти за допомогою асерцій?
|
|
195
|
-
```js
|
|
196
|
-
import { Welcome, InputMessage } from '@nan0web/ui'
|
|
197
|
-
|
|
198
|
-
const output = Welcome({ user: { name: "Тест" } })
|
|
199
|
-
const input = InputMessage.from({ value: "тест" })
|
|
200
|
-
console.log(output[0].join("")) // ← Вітаємо, Тест!
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Демо-плейграунди
|
|
204
|
-
|
|
205
|
-
Бібліотека містить багаті демо-плейграунди:
|
|
206
|
-
|
|
207
|
-
- [Форма реєстрації](./playground/registration.form.js)
|
|
208
|
-
- [Обмін валют](./playground/currency.exchange.js)
|
|
209
|
-
- [Поповнення мобільного телефону](./playground/topup.telephone.js)
|
|
210
|
-
- [Вибір мови](./playground/language.form.js)
|
|
211
|
-
|
|
212
|
-
Як запустити плейграунд?
|
|
213
|
-
```bash
|
|
214
|
-
# Клонувати репозиторій та запустити плейграунд
|
|
215
|
-
git clone https://github.com/nan0web/ui.git
|
|
216
|
-
cd ui
|
|
217
|
-
npm install
|
|
218
|
-
npm run playground
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## API Документація
|
|
222
|
-
|
|
223
|
-
Детальна API-документація доступна у JSDoc кожного класу.
|
|
224
|
-
Перегляньте:
|
|
225
|
-
|
|
226
|
-
- [Повідомлення](./src/core/Message/)
|
|
227
|
-
- [Форми](./src/core/Form/)
|
|
228
|
-
- [Поток (Stream)](./src/core/Stream.js)
|
|
229
|
-
- [Компоненти](./src/Component/)
|
|
230
|
-
- [View](./src/View/)
|
|
231
|
-
- [App](./src/App/)
|
|
232
|
-
- [Моделі](./src/Model/)
|
|
233
|
-
|
|
234
|
-
## Внесок у проєкт
|
|
235
|
-
|
|
236
|
-
Як зробити внесок? – [докладніше тут](./CONTRIBUTING.md)
|
|
237
|
-
|
|
238
|
-
## Ліцензія
|
|
239
|
-
|
|
240
|
-
Як ознайомитися з ліцензією ISC? – [докладніше тут](./LICENSE)
|
package/playground/User.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import FormInput from "../src/core/Form/Input.js"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Simple domain model representing a user.
|
|
5
|
-
*
|
|
6
|
-
* The static `formFields` property defines the UI fields that should be
|
|
7
|
-
* displayed when generating a form for this model.
|
|
8
|
-
*
|
|
9
|
-
* Each field is described using `FormInput`, which the UI core library
|
|
10
|
-
* understands. The `required` flag, `type`, and other properties are
|
|
11
|
-
* respected by the validation routine inside `UIForm`.
|
|
12
|
-
*/
|
|
13
|
-
export default class User {
|
|
14
|
-
/**
|
|
15
|
-
* Constructs a new User instance.
|
|
16
|
-
*
|
|
17
|
-
* @param {Object} data - Initial data.
|
|
18
|
-
* @param {string} data.name
|
|
19
|
-
* @param {string} data.email
|
|
20
|
-
* @param {number} data.age
|
|
21
|
-
*/
|
|
22
|
-
constructor({ name = "", email = "", age = null } = {}) {
|
|
23
|
-
this.name = String(name)
|
|
24
|
-
this.email = String(email)
|
|
25
|
-
this.age = age !== null ? Number(age) : null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** @type {FormInput[]} UI fields for the User model */
|
|
29
|
-
static formFields = [
|
|
30
|
-
new FormInput({
|
|
31
|
-
name: "name",
|
|
32
|
-
label: "Name",
|
|
33
|
-
type: FormInput.TYPES.TEXT,
|
|
34
|
-
required: true,
|
|
35
|
-
placeholder: "Enter full name",
|
|
36
|
-
}),
|
|
37
|
-
new FormInput({
|
|
38
|
-
name: "email",
|
|
39
|
-
label: "Email",
|
|
40
|
-
type: FormInput.TYPES.EMAIL,
|
|
41
|
-
required: true,
|
|
42
|
-
placeholder: "example@domain.com",
|
|
43
|
-
}),
|
|
44
|
-
new FormInput({
|
|
45
|
-
name: "age",
|
|
46
|
-
label: "Age",
|
|
47
|
-
type: FormInput.TYPES.NUMBER,
|
|
48
|
-
required: false,
|
|
49
|
-
placeholder: "Optional",
|
|
50
|
-
}),
|
|
51
|
-
]
|
|
52
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple currency exchange console tool.
|
|
3
|
-
*
|
|
4
|
-
* Prompts for:
|
|
5
|
-
* - From currency (options)
|
|
6
|
-
* - To currency (options)
|
|
7
|
-
* - Amount (number)
|
|
8
|
-
*
|
|
9
|
-
* Controls:
|
|
10
|
-
* - Input "0" on any prompt to cancel.
|
|
11
|
-
* - Empty input on final confirmation submits.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { CancelError, select } from "@nan0web/ui-cli"
|
|
15
|
-
|
|
16
|
-
const rates = {
|
|
17
|
-
USD: 1,
|
|
18
|
-
EUR: 0.9,
|
|
19
|
-
UAH: 27,
|
|
20
|
-
GBP: 0.8,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Main exchange flow */
|
|
24
|
-
export async function runExchange(t, ask, console, prompt, invalidPrompt) {
|
|
25
|
-
/** Choose a currency from available list */
|
|
26
|
-
async function chooseCurrency(title, selected = []) {
|
|
27
|
-
const input = await select({
|
|
28
|
-
title,
|
|
29
|
-
prompt, invalidPrompt, console,
|
|
30
|
-
options: Object.keys(rates).filter(c => !selected.includes(c))
|
|
31
|
-
})
|
|
32
|
-
return input.value
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.info("\n=== " + t("Currency Exchange") + " ===")
|
|
36
|
-
const from = await chooseCurrency(t("From Currency"))
|
|
37
|
-
if (!from) throw new CancelError()
|
|
38
|
-
const to = await chooseCurrency(t("To Currency"), [from])
|
|
39
|
-
if (!to) throw new CancelError()
|
|
40
|
-
const input = await ask(
|
|
41
|
-
t("Amount") + ": ",
|
|
42
|
-
input => isNaN(input) || Number(input) <= 0,
|
|
43
|
-
t("Invalid amount.") + ": ",
|
|
44
|
-
)
|
|
45
|
-
const amount = Number(input)
|
|
46
|
-
const result = (amount / rates[from]) * rates[to]
|
|
47
|
-
console.success(`\n${amount} ${from} = ${result.toFixed(2)} ${to}\n`)
|
|
48
|
-
}
|
package/playground/i18n/index.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import i18n, { createT } from "@nan0web/i18n"
|
|
2
|
-
import uk from "./uk.js"
|
|
3
|
-
|
|
4
|
-
const getVocab = i18n({ uk })
|
|
5
|
-
|
|
6
|
-
export function detectLocale(argv = []) {
|
|
7
|
-
// Detect language from CLI argument or environment variable
|
|
8
|
-
const langArg = argv.find(a => a.startsWith("--lang="))
|
|
9
|
-
let locale = "en"
|
|
10
|
-
if (langArg) {
|
|
11
|
-
locale = langArg.split("=")[1]
|
|
12
|
-
}
|
|
13
|
-
return locale
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const localesMap = new Map([
|
|
17
|
-
["en", "English"],
|
|
18
|
-
["uk", "Українська"],
|
|
19
|
-
])
|
|
20
|
-
|
|
21
|
-
export default (locale) => createT(getVocab(locale))
|
package/playground/i18n/uk.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ukrainian translations for the playground.
|
|
3
|
-
*
|
|
4
|
-
* The object keys are English identifiers, the values are Ukrainian strings.
|
|
5
|
-
*
|
|
6
|
-
* Example usage:
|
|
7
|
-
* import vocabMap from "./i18n/index.js"
|
|
8
|
-
* const vocab = vocabMap("uk")
|
|
9
|
-
* const t = createT(vocab)
|
|
10
|
-
* console.log(t("Registration Form")) // → "Форма реєстрації"
|
|
11
|
-
* console.log(vocab["Registration Form"]) // → "Форма реєстрації"
|
|
12
|
-
*/
|
|
13
|
-
export default {
|
|
14
|
-
"Select demo:": "Оберіть демонстрацію:",
|
|
15
|
-
"Registration Form": "Форма реєстрації",
|
|
16
|
-
"Currency Exchange": "Обмін валют",
|
|
17
|
-
"Top‑up Telephone": "Поповнення телефону",
|
|
18
|
-
"Enter number (or 0 to quit):": "Введіть номер (або 0 для виходу):",
|
|
19
|
-
"Good‑bye.": "До побачення.",
|
|
20
|
-
"Invalid choice.": "Невірний вибір.",
|
|
21
|
-
"Username": "Ім'я користувача",
|
|
22
|
-
"Password": "Пароль",
|
|
23
|
-
"Confirm Password": "Підтвердження пароля",
|
|
24
|
-
"Email or Telephone": "Електронна пошта або телефон",
|
|
25
|
-
"Press ENTER to submit, type 0 to cancel": "Натисніть ENTER для відправки, введіть 0 для скасування",
|
|
26
|
-
"Cancelled.": "Скасовано.",
|
|
27
|
-
"Form submitted successfully!": "Форма успішно надіслана!",
|
|
28
|
-
"Phone Number": "Номер телефону",
|
|
29
|
-
"Top‑up Amount": "Сума поповнення",
|
|
30
|
-
"Select currency": "Виберіть валюту",
|
|
31
|
-
"Currency options:": "Валютні опції:",
|
|
32
|
-
"Invalid choice.": "Неправильний вибір.",
|
|
33
|
-
"Phone number is required.": "Необхідно вказати номер телефону.",
|
|
34
|
-
"Invalid phone number. Use digits only, 7‑15 characters.": "Неправильний номер телефону. Використовуйте лише цифри, 7-15 символів.",
|
|
35
|
-
"Invalid amount.": "Неправильна сума.",
|
|
36
|
-
"Top‑up of": "Поповнення на",
|
|
37
|
-
"to": "до",
|
|
38
|
-
"scheduled.": "заплановано.",
|
|
39
|
-
"options:": "опції:",
|
|
40
|
-
"Select": "Зробіть вибір",
|
|
41
|
-
"From Currency": "З якої валюти міняємо",
|
|
42
|
-
"To Currency": "У яку валюту",
|
|
43
|
-
"Amount": "Сума",
|
|
44
|
-
"Invalid choice, try again.": "Неправильний вибір, спробуйте знову.",
|
|
45
|
-
"Password (min 4 chars)": "Пароль (мінімум 4 символи)",
|
|
46
|
-
"Passwords do not match. Try again.": "Паролі не збігаються. Спробуйте знову.",
|
|
47
|
-
"[me]": "[Я]",
|
|
48
|
-
"[me invalid]": "[Я помилився]",
|
|
49
|
-
"! Invalid choice.": "! Неправильний вибір.",
|
|
50
|
-
"⨉ Cancelled.": "⨉ Скасовано.",
|
|
51
|
-
"Select a language": "Виберіть мову",
|
|
52
|
-
"Language Selector": "Вибір мови",
|
|
53
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple currency exchange console tool.
|
|
3
|
-
*
|
|
4
|
-
* Prompts for:
|
|
5
|
-
* - From currency (options)
|
|
6
|
-
* - To currency (options)
|
|
7
|
-
* - Amount (number)
|
|
8
|
-
*
|
|
9
|
-
* Controls:
|
|
10
|
-
* - Input "0" on any prompt to cancel.
|
|
11
|
-
* - Empty input on final confirmation submits.
|
|
12
|
-
*/
|
|
13
|
-
import { localesMap } from "./i18n/index.js"
|
|
14
|
-
import { select } from "@nan0web/ui-cli"
|
|
15
|
-
|
|
16
|
-
/** Main exchange flow */
|
|
17
|
-
export async function runLanguage(t, ask, console, prompt, invalidPrompt) {
|
|
18
|
-
const lang = await select({
|
|
19
|
-
title: "\n=== " + t("Language Selector") + " ===",
|
|
20
|
-
prompt, invalidPrompt, console,
|
|
21
|
-
options: Array.from(localesMap.keys()),
|
|
22
|
-
})
|
|
23
|
-
console.success(`\n${lang.value}\n`)
|
|
24
|
-
return lang.value
|
|
25
|
-
}
|
package/playground/main.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Playground entry point.
|
|
3
|
-
*
|
|
4
|
-
* Choose which demo to run:
|
|
5
|
-
* 1) Select a language
|
|
6
|
-
* 2) Registration Form
|
|
7
|
-
* 3) Currency Exchange
|
|
8
|
-
* 4) Top‑up Telephone
|
|
9
|
-
*
|
|
10
|
-
* Type 0 to exit.
|
|
11
|
-
*/
|
|
12
|
-
import { argv } from "node:process"
|
|
13
|
-
import Logger from "@nan0web/log"
|
|
14
|
-
import { runLanguage } from "./language.form.js"
|
|
15
|
-
import { runRegistration } from "./registration.form.js"
|
|
16
|
-
import { runExchange } from "./currency.exchange.js"
|
|
17
|
-
import { runTopup } from "./topup.telephone.js"
|
|
18
|
-
import createT, { detectLocale } from "./i18n/index.js"
|
|
19
|
-
import createInput, { CancelError, select } from "./ui/index.js"
|
|
20
|
-
|
|
21
|
-
const console = new Logger(Logger.detectLevel(argv))
|
|
22
|
-
let t = createT(detectLocale(argv))
|
|
23
|
-
const ask = createInput(["0"])
|
|
24
|
-
const menuOptions = [
|
|
25
|
-
"Select a language", // t("Select a language")
|
|
26
|
-
"Registration Form", // t("Registration Form")
|
|
27
|
-
"Currency Exchange", // t("Currency Exchange")
|
|
28
|
-
"Top‑up Telephone", // t("Top‑up Telephone")
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
async function main() {
|
|
32
|
-
console.info(Logger.style(Logger.LOGO, { color: "magenta" }))
|
|
33
|
-
|
|
34
|
-
while (true) {
|
|
35
|
-
try {
|
|
36
|
-
const prompt = t("[me]") + ": "
|
|
37
|
-
const invalidPrompt = Logger.style(t("[me invalid]", t("[me]")), { bgColor: "yellow" }) + ": "
|
|
38
|
-
const choice = await select({
|
|
39
|
-
title: t("Select demo:"),
|
|
40
|
-
prompt: t("[me]") + ": ",
|
|
41
|
-
invalidPrompt: Logger.style(t("[me]"), { bgColor: "yellow" }) + ": ",
|
|
42
|
-
options: menuOptions.map(t),
|
|
43
|
-
console,
|
|
44
|
-
})
|
|
45
|
-
switch (choice.index) {
|
|
46
|
-
case 0:
|
|
47
|
-
const lang = await runLanguage(t, ask, console, prompt, invalidPrompt)
|
|
48
|
-
t = createT(lang)
|
|
49
|
-
break
|
|
50
|
-
case 1:
|
|
51
|
-
await runRegistration(t, ask, console, prompt, invalidPrompt)
|
|
52
|
-
break
|
|
53
|
-
case 2:
|
|
54
|
-
await runExchange(t, ask, console, prompt, invalidPrompt)
|
|
55
|
-
break
|
|
56
|
-
case 3:
|
|
57
|
-
await runTopup(t, ask, console, prompt, invalidPrompt)
|
|
58
|
-
break
|
|
59
|
-
default:
|
|
60
|
-
console.warn(t("! Invalid choice."))
|
|
61
|
-
}
|
|
62
|
-
} catch (err) {
|
|
63
|
-
if (err instanceof CancelError) {
|
|
64
|
-
console.warn("\n" + t("⨉ Cancelled."))
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
throw err
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
main()
|