@nan0web/ui 1.0.2 → 1.0.4

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 (58) hide show
  1. package/README.md +19 -54
  2. package/package.json +21 -17
  3. package/src/App/Core/CoreApp.js +14 -19
  4. package/src/App/Core/UI.js +4 -50
  5. package/src/App/Core/Widget.js +4 -6
  6. package/src/App/User/Command/Message.js +23 -37
  7. package/src/App/User/Command/index.js +3 -8
  8. package/src/App/User/UserApp.js +32 -27
  9. package/src/App/User/UserUI.js +4 -4
  10. package/src/App/User/index.js +0 -6
  11. package/src/App/index.js +0 -3
  12. package/src/README.md.js +33 -57
  13. package/src/StdIn.js +12 -15
  14. package/src/View/View.js +5 -5
  15. package/src/core/Error/index.js +9 -0
  16. package/src/core/Form/Form.js +43 -23
  17. package/src/core/Form/Input.js +16 -7
  18. package/src/core/Form/Message.js +6 -8
  19. package/src/core/InputAdapter.js +6 -2
  20. package/src/core/Message/Message.js +109 -19
  21. package/src/core/Message/OutputMessage.js +8 -8
  22. package/src/core/Message/index.js +3 -4
  23. package/src/core/Stream.js +10 -10
  24. package/src/core/UiAdapter.js +189 -0
  25. package/src/core/index.js +5 -6
  26. package/src/index.js +5 -4
  27. package/types/App/Core/CoreApp.d.ts +9 -9
  28. package/types/App/Core/UI.d.ts +5 -15
  29. package/types/App/Core/Widget.d.ts +7 -8
  30. package/types/App/User/Command/Message.d.ts +15 -29
  31. package/types/App/User/Command/Options.d.ts +9 -2
  32. package/types/App/User/Command/index.d.ts +3 -7
  33. package/types/App/User/UserApp.d.ts +19 -9
  34. package/types/App/User/UserUI.d.ts +0 -9
  35. package/types/App/User/index.d.ts +0 -4
  36. package/types/App/index.d.ts +1 -3
  37. package/types/Frame/Frame.d.ts +5 -5
  38. package/types/StdIn.d.ts +13 -13
  39. package/types/View/View.d.ts +9 -9
  40. package/types/core/Error/index.d.ts +6 -0
  41. package/types/core/Form/Form.d.ts +14 -11
  42. package/types/core/Form/Input.d.ts +20 -7
  43. package/types/core/Form/Message.d.ts +5 -4
  44. package/types/core/InputAdapter.d.ts +2 -0
  45. package/types/core/Intent.d.ts +91 -0
  46. package/types/core/Message/InputMessage.d.ts +5 -5
  47. package/types/core/Message/Message.d.ts +58 -15
  48. package/types/core/Message/OutputMessage.d.ts +4 -4
  49. package/types/core/Message/index.d.ts +3 -4
  50. package/types/core/Stream.d.ts +5 -4
  51. package/types/core/StreamEntry.d.ts +1 -1
  52. package/types/core/UiAdapter.d.ts +104 -0
  53. package/types/core/index.d.ts +3 -3
  54. package/types/index.d.ts +5 -4
  55. package/src/App/Command/Options.js +0 -78
  56. package/src/App/Command/index.js +0 -9
  57. package/src/App/User/Command/Options.js +0 -48
  58. package/src/core/Message/InputMessage.js +0 -119
package/src/App/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import Command from "./Command/index.js"
2
1
  import Scenario from "./Scenario.js"
3
2
  import UI from "./Core/UI.js"
4
3
 
@@ -8,7 +7,6 @@ import User from "./User/index.js"
8
7
  export {
9
8
  Core,
10
9
  User,
11
- Command,
12
10
  Scenario,
13
11
  UI,
14
12
  }
@@ -16,7 +14,6 @@ export {
16
14
  export default {
17
15
  Core,
18
16
  User,
19
- Command,
20
17
  Scenario,
21
18
  UI,
22
19
  }
package/src/README.md.js CHANGED
@@ -8,17 +8,13 @@ import {
8
8
  runSpawn,
9
9
  } from "@nan0web/test"
10
10
  import {
11
- App,
12
- Component,
13
11
  Frame,
14
- InputMessage,
15
12
  Model,
16
13
  OutputMessage,
17
- UIMessage,
18
- UIForm,
19
- UIStream,
20
14
  View,
21
15
  FormInput,
16
+ UiMessage,
17
+ UiForm,
22
18
  } from "./index.js"
23
19
  import { Welcome } from "./Component/index.js"
24
20
 
@@ -108,8 +104,7 @@ function testRender() {
108
104
  *
109
105
  * UI communication is built around messages:
110
106
  *
111
- * - **`UIMessage`** – abstract message base class
112
- * - **`InputMessage`** – user input message (value, options)
107
+ * - **`UiMessage`** – abstract message base class
113
108
  * - **`OutputMessage`** – system output (content, error, priority)
114
109
  *
115
110
  * Messages are simple, serializable data containers. They help build
@@ -118,19 +113,22 @@ function testRender() {
118
113
  it("How to create input and output messages?", () => {
119
114
  //import { InputMessage, OutputMessage } from '@nan0web/ui'
120
115
 
121
- const input = InputMessage.from({ value: 'Hello User' })
116
+ const input = UiMessage.from({ body: 'Hello User' })
122
117
  const output = OutputMessage.from({ content: ['Welcome to @nan0web/ui'] })
123
- console.info(input.value) // ← Hello User
124
- console.info(output.content[0]) // ← Welcome to @nan0web/ui
125
- assert.equal(input.value, 'Hello User')
126
- assert.equal(output.content[0], 'Welcome to @nan0web/ui')
118
+ console.info(input) // ← Message { body: "Hello User", head: {}, id: "....", type: "" }
119
+ console.info(String(output)) // ← Welcome to @nan0web/ui
120
+ assert.deepStrictEqual(console.output()[0][1].body, "Hello User")
121
+ assert.deepStrictEqual(console.output()[0][1].head, {})
122
+ assert.deepStrictEqual(console.output()[0][1].type, "")
123
+ assert.ok(console.output()[0][1].id)
124
+ assert.ok(console.output()[1][1].endsWith('Welcome to @nan0web/ui'))
127
125
  })
128
126
 
129
127
  /**
130
128
  * @docs
131
129
  * ### Forms
132
130
  *
133
- * `UIForm` supports field definitions, data management, and schema validation.
131
+ * `UiForm` supports field definitions, data management, and schema validation.
134
132
  * Every form includes a title, fields, and current state.
135
133
  *
136
134
  * Field types include:
@@ -142,10 +140,10 @@ function testRender() {
142
140
  * - `checkbox`
143
141
  * - `textarea`
144
142
  */
145
- it("How to define and validate a UIForm?", () => {
146
- //import { UIForm } from '@nan0web/ui'
143
+ it("How to define and validate a UiForm?", () => {
144
+ //import { UiForm } from '@nan0web/ui'
147
145
 
148
- const form = new UIForm({
146
+ const form = new UiForm({
149
147
  title: "Contact Form",
150
148
  fields: [
151
149
  FormInput.from({ name: "email", label: "Email Address", type: "email", required: true }),
@@ -157,12 +155,12 @@ function testRender() {
157
155
  }
158
156
  })
159
157
 
160
- const result = form.validate()
161
- console.info(result.isValid) // ← false
162
- console.info(result.errors.email) // ← Invalid email format
158
+ const errors = form.validate()
159
+ console.info(errors.size) // ← 1
160
+ console.info(errors.get("email")) // ← Invalid email format
163
161
 
164
- assert.equal(result.isValid, false)
165
- assert.equal(result.errors.email, "Invalid email format")
162
+ assert.equal(console.output()[0][1], 1)
163
+ assert.equal(console.output()[1][1], "Invalid email format")
166
164
  })
167
165
 
168
166
  /**
@@ -249,30 +247,6 @@ function testRender() {
249
247
  assert.ok(renderedVisible.includes("Frame content"))
250
248
  })
251
249
 
252
- /**
253
- * @docs
254
- * ### App Architecture
255
- *
256
- * `App` provides the main application logic.
257
- *
258
- * - Core – minimal UI layer
259
- * - User – user-specific UI commands
260
- *
261
- * Each app registers commands and binds them to UI actions.
262
- */
263
- it("How to create a basic user app that greets?", async () => {
264
- //import { App, View } from '@nan0web/ui'
265
-
266
- const app = new App.User.App({ name: "GreetApp" })
267
- const view = new View()
268
- view.register("Welcome", Welcome)
269
-
270
- const cmd = App.Command.Message.parse("welcome --user Bob")
271
- const result = await app.processCommand(cmd, new App.User.UI(app, view))
272
- console.info(String(result)) // ← Welcome Bob!
273
- assert.ok(console.output()[0][1].includes("Welcome Bob!"))
274
- })
275
-
276
250
  /**
277
251
  * @docs
278
252
  * ### Models
@@ -301,13 +275,15 @@ function testRender() {
301
275
  * with minimal setup.
302
276
  */
303
277
  it("How to test UI components with assertions?", () => {
304
- //import { Welcome, InputMessage } from '@nan0web/ui'
278
+ //import { Welcome } from '@nan0web/ui'
305
279
 
306
280
  const output = Welcome({ user: { name: "Test" } })
307
- const input = InputMessage.from({ value: "test" })
308
- console.log(output[0].join("")) // ← Welcome Test!
309
- assert.equal(console.output()[0][1], "Welcome Test!")
310
- assert.ok(input instanceof InputMessage)
281
+ console.info(output) // Welcome Test!
282
+ assert.deepStrictEqual(console.output()[0][1], [
283
+ ["Welcome", " ", "Test", "!"],
284
+ ["What can we do today great?"],
285
+ [""],
286
+ ])
311
287
  })
312
288
 
313
289
  /**
@@ -316,10 +292,10 @@ function testRender() {
316
292
  *
317
293
  * The library includes rich playground demos:
318
294
  *
319
- * - [Registration Form](./playground/registration.form.js)
320
- * - [Currency Exchange](./playground/currency.exchange.js)
321
- * - [Mobile Top-up](./playground/topup.telephone.js)
322
- * - [Language Selector](./playground/language.form.js)
295
+ * - [Registration Form](./play/registration.form.js)
296
+ * - [Currency Exchange](./play/currency.exchange.js)
297
+ * - [Mobile Top-up](./play/topup.telephone.js)
298
+ * - [Language Selector](./play/language.form.js)
323
299
  *
324
300
  * Run to explore live functionality:
325
301
  */
@@ -330,10 +306,10 @@ function testRender() {
330
306
  * git clone https://github.com/nan0web/ui.git
331
307
  * cd ui
332
308
  * npm install
333
- * npm run playground
309
+ * npm run play
334
310
  * ```
335
311
  */
336
- assert.ok(String(pkg.scripts?.playground).includes("node playground"))
312
+ assert.ok(String(pkg.scripts?.play).includes("node play"))
337
313
  const response = await runSpawn("git", ["remote", "get-url", "origin"])
338
314
  assert.ok(response.code === 0, "git command fails (e.g., not in a git repo)")
339
315
  assert.ok(response.text.trim().endsWith(":nan0web/ui.git"))
package/src/StdIn.js CHANGED
@@ -1,21 +1,20 @@
1
1
  import EventProcessor from "@nan0web/event/oop"
2
- import { CommandMessage } from "@nan0web/co"
3
2
  import { typeOf } from "@nan0web/types"
4
- import InputMessage from "./core/Message/InputMessage.js"
3
+ import { UiMessage } from "./core/index.js"
5
4
 
6
5
  class Processor extends EventProcessor { }
7
6
 
8
7
  /**
9
8
  * Handles standard input stream with message buffering.
10
9
  */
11
- class StdIn extends EventProcessor {
10
+ export default class StdIn extends EventProcessor {
12
11
  /** @type {number} Read interval in milliseconds */
13
12
  static READ_INTERVAL = 99
14
13
 
15
14
  /** @type {string[]} Messages to ignore */
16
15
  static IGNORE_MESSAGES = ["", "undefined"]
17
16
 
18
- /** @type {InputMessage[]} Input message buffer */
17
+ /** @type {UiMessage[]} Input message buffer */
19
18
  stream = []
20
19
 
21
20
  /** @type {Processor} Input processor */
@@ -25,7 +24,7 @@ class StdIn extends EventProcessor {
25
24
  * Creates a new StdIn instance.
26
25
  * @param {object} props - StdIn properties
27
26
  * @param {Processor} [props.processor] - Input processor
28
- * @param {InputMessage[]} [props.stream=[]] - Initial input stream
27
+ * @param {UiMessage[]} [props.stream=[]] - Initial input stream
29
28
  */
30
29
  constructor(props = {}) {
31
30
  super()
@@ -59,13 +58,13 @@ class StdIn extends EventProcessor {
59
58
  /**
60
59
  * Reads a message from the input stream.
61
60
  * Waits until messages are available if stream is empty.
62
- * @returns {Promise<InputMessage>} Next input message
61
+ * @returns {Promise<UiMessage>} Next input message
63
62
  */
64
63
  async read() {
65
64
  while (this.ended) {
66
65
  await new Promise(resolve => setTimeout(resolve, StdIn.READ_INTERVAL))
67
66
  }
68
- return this.stream.shift() ?? new InputMessage()
67
+ return this.stream.shift() ?? new UiMessage()
69
68
  }
70
69
 
71
70
  /**
@@ -84,17 +83,16 @@ class StdIn extends EventProcessor {
84
83
  }
85
84
 
86
85
  /**
87
- * Decodes a message into an InputMessage instance.
88
- * @param {InputMessage | string[] | any} message - Message to decode
89
- * @returns {InputMessage} Decoded input message
86
+ * Decodes a message into an UiMessage instance.
87
+ * @param {UiMessage | string[] | any} message - Message to decode
88
+ * @returns {UiMessage} Decoded input message
90
89
  */
91
90
  decode(message) {
92
- if (message instanceof InputMessage) return message
91
+ if (message instanceof UiMessage) return message
93
92
  if (Array.isArray(message) && message.every(typeOf(String))) {
94
- const parsed = CommandMessage.parse(message)
95
- return new InputMessage({ value: parsed })
93
+ return new UiMessage({ value: message })
96
94
  }
97
- return new InputMessage(message)
95
+ return new UiMessage(message)
98
96
  }
99
97
 
100
98
  /**
@@ -108,4 +106,3 @@ class StdIn extends EventProcessor {
108
106
  }
109
107
  }
110
108
 
111
- export default StdIn
package/src/View/View.js CHANGED
@@ -3,13 +3,13 @@ import Frame, { FrameRenderMethod } from "../Frame/Frame.js"
3
3
  import Locale from "../Locale.js"
4
4
  import StdOut from "../StdOut.js"
5
5
  import StdIn from "../StdIn.js"
6
- import InputMessage from "../core/Message/InputMessage.js"
7
6
  import RenderOptions from "./RenderOptions.js"
7
+ import UiMessage from "../core/Message/Message.js"
8
8
 
9
9
  /**
10
10
  * @typedef {Object} ComponentFn
11
11
  * @property {string} name
12
- * @property {(input: InputMessage) => Promise<any>} ask
12
+ * @property {(input: UiMessage) => Promise<any>} ask
13
13
  * @property {Function} bind
14
14
  */
15
15
 
@@ -256,8 +256,8 @@ export default class View {
256
256
  }
257
257
 
258
258
  /**
259
- * @param {InputMessage} input
260
- * @returns {Promise<InputMessage | null>}
259
+ * @param {UiMessage} input
260
+ * @returns {Promise<UiMessage | null>}
261
261
  */
262
262
  async ask(input) {
263
263
  const name = input.constructor.name.replace(/Input$/, "")
@@ -268,7 +268,7 @@ export default class View {
268
268
  let result = null
269
269
  do {
270
270
  const answer = await this.stdin.read()
271
- result = /** @type {typeof InputMessage} */ (input.constructor).from(answer)
271
+ result = /** @type {typeof UiMessage} */ (input.constructor).from(answer)
272
272
  } while (!result.isValid && !result.escaped)
273
273
  return result.escaped ? null : result
274
274
  }
@@ -0,0 +1,9 @@
1
+ import CancelError from "./CancelError.js"
2
+
3
+ export {
4
+ CancelError
5
+ }
6
+
7
+ export default {
8
+ CancelError
9
+ }
@@ -1,3 +1,4 @@
1
+ import Message from "@nan0web/co"
1
2
  import FormMessage from "./Message.js"
2
3
  import FormInput from "./Input.js"
3
4
 
@@ -18,24 +19,24 @@ export default class UIForm extends FormMessage {
18
19
  /** @type {Object} */ schema = {}
19
20
 
20
21
  /* ------------------------------------------------------------------ */
21
- /* static validator registry */
22
+ /* static validation registry */
22
23
  /* ------------------------------------------------------------------ */
23
24
 
24
25
  /** @type {Object<string,Function>} */
25
- static _validators = {}
26
+ static _validations = {}
26
27
 
27
28
  /**
28
- * Register a custom validator that can be referenced by name in a schema.
29
+ * Register a custom validation that can be referenced by name in a schema.
29
30
  *
30
- * @param {string} name - Identifier used in schema.validator.
31
+ * @param {string} name - Identifier used in schema.validation.
31
32
  * @param {(value:any)=>true|string} fn - Function returns true if valid,
32
33
  * otherwise returns an error message.
33
34
  */
34
- static addValidator(name, fn) {
35
+ static addValidation(name, fn) {
35
36
  if (typeof name !== "string" || typeof fn !== "function") {
36
- throw new Error("Validator name must be a string and fn must be a function")
37
+ throw new Error("validation name must be a string and fn must be a function")
37
38
  }
38
- UIForm._validators[name] = fn
39
+ UIForm._validations[name] = fn
39
40
  }
40
41
 
41
42
  /**
@@ -111,10 +112,10 @@ export default class UIForm extends FormMessage {
111
112
  /**
112
113
  * Validates the entire form.
113
114
  *
114
- * @returns {{isValid: boolean, errors: Object}} Validation result.
115
+ * @returns {Map<string, string>} Map of validation errors, empty if valid.
115
116
  */
116
117
  validate() {
117
- const errors = {}
118
+ const errors = new Map()
118
119
  let isValid = true
119
120
 
120
121
  this.fields.forEach((field) => {
@@ -122,7 +123,7 @@ export default class UIForm extends FormMessage {
122
123
 
123
124
  // Required validation based on field definition or schema
124
125
  if (field.required && (fieldValue === '' || fieldValue === null || fieldValue === undefined)) {
125
- errors[field.name] = 'This field is required'
126
+ errors.set(field.name, 'This field is required')
126
127
  isValid = false
127
128
  return
128
129
  }
@@ -131,12 +132,14 @@ export default class UIForm extends FormMessage {
131
132
  const { isValid: fieldValid, errors: fieldErrors } = this.validateField(field.name, fieldValue)
132
133
 
133
134
  if (!fieldValid) {
134
- Object.assign(errors, fieldErrors)
135
+ for (const [key, err] of Object.entries(fieldErrors)) {
136
+ errors.set(key, err)
137
+ }
135
138
  isValid = false
136
139
  }
137
140
  })
138
141
 
139
- return { isValid, errors }
142
+ return errors
140
143
  }
141
144
 
142
145
  /**
@@ -208,15 +211,15 @@ export default class UIForm extends FormMessage {
208
211
  }
209
212
  }
210
213
 
211
- // Custom validator – can be a function or a string referencing a static validator
212
- if (schema.validator) {
214
+ // Custom validation – can be a function or a string referencing a static validation
215
+ if (schema.validation) {
213
216
  let result
214
- if (typeof schema.validator === 'function') {
215
- result = schema.validator(value)
216
- } else if (typeof schema.validator === 'string') {
217
- const fn = UIForm._validators[schema.validator]
217
+ if (typeof schema.validation === 'function') {
218
+ result = schema.validation(value)
219
+ } else if (typeof schema.validation === 'string') {
220
+ const fn = UIForm._validations[schema.validation]
218
221
  if (!fn) {
219
- throw new Error(`Validator "${schema.validator}" not registered`)
222
+ throw new Error(`validation "${schema.validation}" not registered`)
220
223
  }
221
224
  result = fn(value)
222
225
  }
@@ -236,9 +239,7 @@ export default class UIForm extends FormMessage {
236
239
  */
237
240
  toJSON() {
238
241
  return {
239
- id: this.id,
240
- type: this.type,
241
- time: this.time.toISOString(),
242
+ time: new Date(this.time).toISOString(),
242
243
  title: this.title,
243
244
  fields: this.fields.map(f => f.toJSON ? f.toJSON() : f),
244
245
  state: this.state,
@@ -252,6 +253,25 @@ export default class UIForm extends FormMessage {
252
253
  */
253
254
  static from(input) {
254
255
  if (input instanceof UIForm) return input
256
+ if (input instanceof Message) {
257
+ const Class = input.constructor
258
+ const fields = []
259
+ for (const [name, value] of Object.entries(input)) {
260
+ fields.push(new FormInput({
261
+ name,
262
+ label: Class[name]?.label ?? Class[`${name}Label`] ?? name,
263
+ type: Class[name]?.type ?? Class[`${name}Type`] ?? typeof value,
264
+ required: Class[name]?.required ?? Class[`${name}Required`] ?? false,
265
+ placeholder: Class[name]?.placeholder ?? Class[`${name}Placeholder`] ?? "",
266
+ defaultValue: Class[name]?.defaultValue ?? Class[`${name}Default`] ?? "",
267
+ validation: Class[name]?.validation ?? Class[`${name}Validation`] ?? (() => true),
268
+ }))
269
+ }
270
+ return new UIForm({
271
+ title: Class.name,
272
+ fields
273
+ })
274
+ }
255
275
  return new UIForm(input)
256
276
  }
257
277
 
@@ -280,7 +300,7 @@ export default class UIForm extends FormMessage {
280
300
  required: !!custom.required,
281
301
  placeholder: custom.placeholder ?? "",
282
302
  options: custom.options ?? [],
283
- validator: custom.validator ?? undefined,
303
+ validation: custom.validation ?? undefined,
284
304
  defaultValue: custom.defaultValue ?? "",
285
305
  })
286
306
  })
@@ -1,3 +1,12 @@
1
+ /**
2
+ * @typedef {Object} Filter
3
+ * @property {string} [q=""]
4
+ * @property {number} [offset=0]
5
+ * @property {number} [limit=36]
6
+ */
7
+
8
+ /** @typedef {Array<string> | ((filter: Filter) => Promise<string[]>)} InputOptions */
9
+
1
10
  /**
2
11
  * Form input field descriptor.
3
12
  *
@@ -8,7 +17,7 @@
8
17
  * @property {boolean} required - Whether the field is required.
9
18
  * @property {string} placeholder - Placeholder text.
10
19
  * @property {Array<string>} options - Select options (if type is 'select').
11
- * @property {Function|null} validator - Custom validation function.
20
+ * @property {Function|null} validation - Custom validation function.
12
21
  * @property {*} defaultValue - Default value.
13
22
  */
14
23
  export default class FormInput {
@@ -17,8 +26,8 @@ export default class FormInput {
17
26
  /** @type {string} */ type = 'text'
18
27
  /** @type {boolean} */ required = false
19
28
  /** @type {string} */ placeholder = ''
20
- /** @type {Array<string>} */ options = []
21
- /** @type {Function|null} */ validator = null
29
+ /** @type {InputOptions} */ options = []
30
+ /** @type {import("@nan0web/co").ValidateFn|null} */ validation = null
22
31
  /** @type {*} */ defaultValue = null
23
32
 
24
33
  /**
@@ -42,8 +51,8 @@ export default class FormInput {
42
51
  * @param {string} [props.type='text'] - Input type.
43
52
  * @param {boolean} [props.required=false] - Is required.
44
53
  * @param {string} [props.placeholder=''] - Placeholder.
45
- * @param {Array<string>} [props.options=[]] - Select options.
46
- * @param {Function} [props.validator=null] - Custom validator.
54
+ * @param {InputOptions} [props.options=[]] - Select options or async function to retrieve data with the search and page.
55
+ * @param {Function} [props.validation=null] - Custom validation.
47
56
  * @param {*} [props.defaultValue=null] - Default value.
48
57
  */
49
58
  constructor(props) {
@@ -54,7 +63,7 @@ export default class FormInput {
54
63
  required = this.required,
55
64
  placeholder = this.placeholder,
56
65
  options = [],
57
- validator = this.validator,
66
+ validation = this.validation,
58
67
  defaultValue = this.defaultValue
59
68
  } = props
60
69
 
@@ -68,7 +77,7 @@ export default class FormInput {
68
77
  this.required = Boolean(required)
69
78
  this.placeholder = String(placeholder)
70
79
  this.options = options
71
- this.validator = validator
80
+ this.validation = validation
72
81
  this.defaultValue = defaultValue
73
82
 
74
83
  this.requireValidType()
@@ -1,22 +1,20 @@
1
- import OutputMessage from "../Message/OutputMessage.js"
1
+ import UiMessage from "../Message/Message.js"
2
2
 
3
3
  /**
4
- * FormMessage – specialized OutputMessage for forms.
4
+ * FormMessage – specialized UiMessage for forms.
5
+ * It carries form-specific data and schema for validation.
5
6
  *
6
7
  * @class FormMessage
7
- * @extends OutputMessage
8
+ * @extends UiMessage
8
9
  */
9
- export default class FormMessage extends OutputMessage {
10
+ export default class FormMessage extends UiMessage {
10
11
  /**
11
12
  * Creates a FormMessage.
12
13
  *
13
14
  * @param {Object} [input={}] - Message properties.
14
15
  */
15
16
  constructor(input = {}) {
16
- super({
17
- ...input,
18
- type: OutputMessage.TYPES.FORM,
19
- })
17
+ super(input)
20
18
  const {
21
19
  data = {},
22
20
  schema = {},
@@ -1,6 +1,6 @@
1
1
  import Event from "@nan0web/event/oop"
2
- import InputMessage from "./Message/InputMessage.js"
3
2
  import CancelError from "./Error/CancelError.js"
3
+ import UiMessage from "./Message/Message.js"
4
4
 
5
5
  /**
6
6
  * Abstract input adapter for UI implementations.
@@ -10,6 +10,10 @@ import CancelError from "./Error/CancelError.js"
10
10
  */
11
11
  export default class InputAdapter extends Event {
12
12
  static CancelError = CancelError
13
+ /** @returns {typeof CancelError} */
14
+ get CancelError() {
15
+ return /** @type {typeof InputAdapter} */ (this.constructor).CancelError
16
+ }
13
17
  /**
14
18
  * Starts listening for input and emits an `input` event.
15
19
  *
@@ -17,7 +21,7 @@ export default class InputAdapter extends Event {
17
21
  */
18
22
  start() {
19
23
  this.emit('input',
20
- InputMessage.from({ value: "Adapter started" })
24
+ UiMessage.from({ body: "Adapter started" })
21
25
  )
22
26
  }
23
27