@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.
- package/README.md +29 -10
- package/package.json +18 -22
- package/src/Model/index.js +32 -3
- package/src/core/Form/Form.js +8 -7
- package/src/core/Form/Message.js +1 -1
- package/src/core/GeneratorRunner.js +10 -0
- package/src/core/Intent.js +21 -5
- package/src/core/IntentErrorModel.js +6 -1
- package/src/core/Stream.js +16 -5
- package/src/core/index.js +1 -1
- package/src/domain/FooterModel.js +57 -0
- package/src/domain/HeaderModel.js +50 -0
- package/src/domain/HeroModel.js +48 -0
- package/src/domain/Navigation.js +11 -10
- package/src/domain/SandboxModel.js +66 -115
- package/src/domain/ShowcaseAppModel.js +133 -50
- package/src/domain/components/AccordionModel.js +38 -0
- package/src/domain/components/AutocompleteModel.js +11 -21
- package/src/domain/components/BannerModel.js +37 -0
- package/src/domain/components/BreadcrumbModel.js +11 -9
- package/src/domain/components/ButtonModel.js +31 -58
- package/src/domain/components/CommentModel.js +44 -0
- package/src/domain/components/ConfirmModel.js +26 -33
- package/src/domain/components/EmptyStateModel.js +45 -0
- package/src/domain/components/FAQModel.js +32 -0
- package/src/domain/components/FooterConfigModel.js +26 -0
- package/src/domain/components/FooterVisibilityModel.js +48 -0
- package/src/domain/components/GalleryModel.js +36 -0
- package/src/domain/components/HeaderConfigModel.js +26 -0
- package/src/domain/components/HeaderVisibilityModel.js +54 -0
- package/src/domain/components/InputModel.js +21 -41
- package/src/domain/components/PriceModel.js +30 -0
- package/src/domain/components/PricingModel.js +39 -0
- package/src/domain/components/PricingSectionModel.js +32 -0
- package/src/domain/components/ProfileDropdownModel.js +45 -0
- package/src/domain/components/SelectModel.js +11 -21
- package/src/domain/components/SpinnerModel.js +11 -26
- package/src/domain/components/StatsItemModel.js +38 -0
- package/src/domain/components/StatsModel.js +32 -0
- package/src/domain/components/TableModel.js +11 -24
- package/src/domain/components/TabsModel.js +30 -0
- package/src/domain/components/TestimonialModel.js +24 -0
- package/src/domain/components/TimelineItemModel.js +38 -0
- package/src/domain/components/TimelineModel.js +32 -0
- package/src/domain/components/ToastModel.js +24 -51
- package/src/domain/components/TreeModel.js +10 -26
- package/src/domain/components/index.js +34 -0
- package/src/domain/index.js +24 -0
- package/src/index.js +2 -0
- package/src/testing/GalleryGenerator.js +85 -0
- package/src/testing/LogicInspector.js +55 -0
- package/src/testing/SnapshotInspector.js +84 -0
- package/src/testing/VisualAdapter.js +41 -0
- package/src/testing/index.js +3 -0
- package/types/Model/index.d.ts +62 -4
- package/types/core/Form/Form.d.ts +2 -2
- package/types/core/GeneratorRunner.d.ts +4 -0
- package/types/core/Intent.d.ts +31 -3
- package/types/core/IntentErrorModel.d.ts +4 -0
- package/types/core/index.d.ts +1 -1
- package/types/domain/FooterModel.d.ts +52 -0
- package/types/domain/HeaderModel.d.ts +45 -0
- package/types/domain/HeroModel.d.ts +43 -0
- package/types/domain/Navigation.d.ts +10 -9
- package/types/domain/SandboxModel.d.ts +16 -40
- package/types/domain/ShowcaseAppModel.d.ts +26 -54
- package/types/domain/components/AccordionModel.d.ts +33 -0
- package/types/domain/components/AutocompleteModel.d.ts +10 -29
- package/types/domain/components/BannerModel.d.ts +32 -0
- package/types/domain/components/BreadcrumbModel.d.ts +13 -6
- package/types/domain/components/ButtonModel.d.ts +18 -54
- package/types/domain/components/CommentModel.d.ts +39 -0
- package/types/domain/components/ConfirmModel.d.ts +20 -35
- package/types/domain/components/EmptyStateModel.d.ts +40 -0
- package/types/domain/components/FAQModel.d.ts +27 -0
- package/types/domain/components/FooterConfigModel.d.ts +21 -0
- package/types/domain/components/FooterVisibilityModel.d.ts +43 -0
- package/types/domain/components/GalleryModel.d.ts +35 -0
- package/types/domain/components/HeaderConfigModel.d.ts +21 -0
- package/types/domain/components/HeaderVisibilityModel.d.ts +49 -0
- package/types/domain/components/HeroModel.d.ts +24 -0
- package/types/domain/components/InputModel.d.ts +19 -59
- package/types/domain/components/PriceModel.d.ts +25 -0
- package/types/domain/components/PricingModel.d.ts +34 -0
- package/types/domain/components/PricingSectionModel.d.ts +27 -0
- package/types/domain/components/ProfileDropdownModel.d.ts +40 -0
- package/types/domain/components/SelectModel.d.ts +13 -28
- package/types/domain/components/ShowcaseAppModel.d.ts +32 -0
- package/types/domain/components/SpinnerModel.d.ts +10 -27
- package/types/domain/components/StatsItemModel.d.ts +33 -0
- package/types/domain/components/StatsModel.d.ts +27 -0
- package/types/domain/components/TableModel.d.ts +10 -26
- package/types/domain/components/TabsModel.d.ts +28 -0
- package/types/domain/components/TestimonialModel.d.ts +18 -0
- package/types/domain/components/TimelineItemModel.d.ts +33 -0
- package/types/domain/components/TimelineModel.d.ts +27 -0
- package/types/domain/components/ToastModel.d.ts +16 -45
- package/types/domain/components/TreeModel.d.ts +13 -36
- package/types/domain/components/index.d.ts +20 -0
- package/types/domain/index.d.ts +4 -1
- package/types/index.d.ts +1 -0
- package/types/testing/GalleryGenerator.d.ts +1 -0
- package/types/testing/LogicInspector.d.ts +22 -0
- package/types/testing/SnapshotInspector.d.ts +17 -0
- package/types/testing/VisualAdapter.d.ts +15 -0
- package/types/testing/index.d.ts +3 -0
- package/src/README.md.js +0 -436
|
@@ -1,192 +1,143 @@
|
|
|
1
|
-
import { Model } from '@nan0web/
|
|
2
|
-
import * as ComponentModels from './components/index.js'
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
3
2
|
import { BreadcrumbModel } from './components/BreadcrumbModel.js'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @property {string} [selectedComponent]
|
|
9
|
-
* @property {string} [themeFormat]
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Model-as-Schema for the UI Sandbox environment.
|
|
14
|
-
* Represents a tool wrapping standard OLMUI components, allowing
|
|
15
|
-
* users to inspect their models, tweak variables interactively,
|
|
16
|
-
* and export the configuration as themes for the Marketplace.
|
|
17
|
-
*
|
|
18
|
-
* Navigation uses BreadcrumbModel:
|
|
19
|
-
* ESC = pop one level (if stack has no parent → exit app)
|
|
20
|
-
* Ctrl+C = always exit (handled by prompts.js wrapper)
|
|
21
|
-
*
|
|
22
|
-
* URL mapping:
|
|
23
|
-
* /sandbox → Select Component
|
|
24
|
-
* /sandbox/button → Edit Button properties
|
|
25
|
-
* /sandbox/button/export → Choose export format
|
|
5
|
+
* SandboxModel — OLMUI Model-as-Schema
|
|
6
|
+
* Environment for testing and previewing UI components with dynamic property editing.
|
|
26
7
|
*/
|
|
27
8
|
export class SandboxModel extends Model {
|
|
28
|
-
|
|
29
|
-
// 1. MODEL AS SCHEMA (Static Definition)
|
|
30
|
-
// ==========================================
|
|
9
|
+
static $id = '@nan0web/ui/SandboxModel'
|
|
31
10
|
|
|
32
11
|
static components = {
|
|
33
12
|
help: 'List of registered UI components available for inspection',
|
|
34
13
|
type: 'string[]',
|
|
35
|
-
default: []
|
|
14
|
+
default: [],
|
|
36
15
|
}
|
|
37
16
|
|
|
38
17
|
static selectedComponent = {
|
|
39
|
-
help: 'The
|
|
40
|
-
|
|
18
|
+
help: 'The component currently being inspected in the sandbox',
|
|
19
|
+
placeholder: 'Button',
|
|
20
|
+
default: '',
|
|
41
21
|
}
|
|
42
22
|
|
|
43
23
|
static themeFormat = {
|
|
44
24
|
help: 'The file format chosen to export the custom theme configuration',
|
|
45
25
|
options: ['yaml', 'css', 'json'],
|
|
46
|
-
default: 'yaml'
|
|
26
|
+
default: 'yaml',
|
|
47
27
|
}
|
|
48
28
|
|
|
49
29
|
/**
|
|
50
|
-
* @param {
|
|
30
|
+
* @param {Partial<SandboxModel> | Record<string, any>} data Model input data.
|
|
31
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
51
32
|
*/
|
|
52
|
-
constructor(data = {}) {
|
|
53
|
-
super(data)
|
|
54
|
-
/** @type {string[]
|
|
55
|
-
|
|
56
|
-
/** @type {string
|
|
33
|
+
constructor(data = {}, options = {}) {
|
|
34
|
+
super(data, options)
|
|
35
|
+
/** @type {string[]} List of registered UI components available for inspection */ this
|
|
36
|
+
.components
|
|
37
|
+
/** @type {string} The component currently being inspected in the sandbox */ this
|
|
38
|
+
.selectedComponent
|
|
39
|
+
/** @type {'yaml'|'css'|'json'} The file format chosen to export the custom theme configuration */ this
|
|
40
|
+
.themeFormat
|
|
57
41
|
}
|
|
58
42
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
/**
|
|
44
|
+
* @returns {AsyncGenerator<any, any, any>}
|
|
45
|
+
*/
|
|
63
46
|
async *run() {
|
|
64
47
|
/** @type {any} */
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
// ── BreadcrumbModel as navigation stack ──
|
|
68
|
-
const nav = new BreadcrumbModel()
|
|
69
|
-
nav.push('🏖 Sandbox', 'sandbox')
|
|
48
|
+
let lastResponse = null
|
|
70
49
|
|
|
71
50
|
while (true) {
|
|
72
|
-
|
|
51
|
+
const nav = new BreadcrumbModel({ items: ['Sandbox'] })
|
|
52
|
+
|
|
73
53
|
if (!this.selectedComponent) {
|
|
74
54
|
// Show breadcrumb
|
|
75
|
-
yield
|
|
76
|
-
type: 'log',
|
|
55
|
+
yield {
|
|
56
|
+
type: 'log',
|
|
57
|
+
level: 'info',
|
|
77
58
|
message: `\n${nav}`,
|
|
78
59
|
component: 'Breadcrumbs',
|
|
79
|
-
model:
|
|
80
|
-
}
|
|
60
|
+
model: nav,
|
|
61
|
+
}
|
|
81
62
|
|
|
82
|
-
|
|
83
|
-
const listResponse = yield {
|
|
63
|
+
const response = yield {
|
|
84
64
|
type: 'ask',
|
|
85
65
|
field: 'selectedComponent',
|
|
86
66
|
schema: {
|
|
87
|
-
help: 'Select a component to inspect
|
|
67
|
+
help: 'Select a component to inspect',
|
|
88
68
|
options: this.components || [],
|
|
89
|
-
validate: (/** @type {string} */ val) =>
|
|
90
|
-
(this.components || []).includes(val) || 'Component not found in sandbox registry',
|
|
91
69
|
},
|
|
92
70
|
component: 'Select',
|
|
93
|
-
model:
|
|
71
|
+
model: this,
|
|
94
72
|
}
|
|
95
|
-
this.selectedComponent =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// Instantiate the selected model class
|
|
99
|
-
const Ctor = ComponentModels[`${this.selectedComponent}Model`]
|
|
100
|
-
targetInstance = Ctor ? new Ctor() : this
|
|
73
|
+
this.selectedComponent = response.value
|
|
74
|
+
if (!this.selectedComponent) break
|
|
101
75
|
}
|
|
102
76
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
77
|
+
nav.push(this.selectedComponent, this.selectedComponent)
|
|
78
|
+
|
|
79
|
+
// 2. Component Configuration Mode
|
|
106
80
|
let configResponse
|
|
107
81
|
try {
|
|
108
|
-
yield
|
|
109
|
-
type: 'log',
|
|
82
|
+
yield {
|
|
83
|
+
type: 'log',
|
|
84
|
+
level: 'info',
|
|
110
85
|
message: `\n${nav}`,
|
|
111
86
|
component: 'Breadcrumbs',
|
|
112
|
-
model:
|
|
113
|
-
}
|
|
87
|
+
model: nav,
|
|
88
|
+
}
|
|
114
89
|
|
|
115
90
|
configResponse = yield {
|
|
116
91
|
type: 'ask',
|
|
117
|
-
field: '
|
|
118
|
-
schema:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
component: 'SandboxWrapper',
|
|
122
|
-
model: true,
|
|
123
|
-
instance: /** @type {any} */ (targetInstance),
|
|
92
|
+
field: 'config',
|
|
93
|
+
schema: { help: `Configure ${this.selectedComponent} properties` },
|
|
94
|
+
component: 'PropertyEditor',
|
|
95
|
+
model: this, // Passes the whole Sandbox context
|
|
124
96
|
}
|
|
125
97
|
} catch (e) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Pop: Level 2 → Level 1
|
|
129
|
-
nav.pop()
|
|
130
|
-
this.selectedComponent = undefined
|
|
131
|
-
continue
|
|
132
|
-
}
|
|
133
|
-
throw e
|
|
98
|
+
this.selectedComponent = ''
|
|
99
|
+
continue
|
|
134
100
|
}
|
|
135
101
|
|
|
136
|
-
|
|
137
|
-
|
|
102
|
+
if (configResponse.cancelled) {
|
|
103
|
+
this.selectedComponent = ''
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
138
106
|
|
|
139
|
-
//
|
|
140
|
-
// URL: /sandbox/button/export Data: data/sandbox/button/export/index.yaml
|
|
141
|
-
/** @type {any} */
|
|
142
|
-
let formatResponse
|
|
107
|
+
// 3. Theme Export Mode
|
|
143
108
|
try {
|
|
144
109
|
nav.push('Export', 'export')
|
|
145
|
-
yield
|
|
146
|
-
type: 'log',
|
|
110
|
+
yield {
|
|
111
|
+
type: 'log',
|
|
112
|
+
level: 'info',
|
|
147
113
|
message: `\n${nav}`,
|
|
148
114
|
component: 'Breadcrumbs',
|
|
149
|
-
model:
|
|
150
|
-
}
|
|
115
|
+
model: nav,
|
|
116
|
+
}
|
|
151
117
|
|
|
152
|
-
|
|
118
|
+
const themeResponse = yield {
|
|
153
119
|
type: 'ask',
|
|
154
120
|
field: 'themeFormat',
|
|
155
121
|
schema: {
|
|
156
|
-
help: 'Choose
|
|
122
|
+
help: 'Choose export format',
|
|
157
123
|
options: SandboxModel.themeFormat.options,
|
|
158
124
|
},
|
|
159
125
|
component: 'Select',
|
|
160
|
-
model:
|
|
126
|
+
model: this,
|
|
161
127
|
}
|
|
128
|
+
this.themeFormat = themeResponse.value
|
|
162
129
|
} catch (e) {
|
|
163
|
-
|
|
164
|
-
if (err.name === 'CancelError') {
|
|
165
|
-
// Pop: Level 3 → Level 2 (same targetInstance preserved)
|
|
166
|
-
nav.pop()
|
|
167
|
-
continue
|
|
168
|
-
}
|
|
169
|
-
throw e
|
|
130
|
+
// stay on results
|
|
170
131
|
}
|
|
171
132
|
|
|
172
|
-
this.themeFormat = formatResponse.value
|
|
173
|
-
|
|
174
|
-
// 4. Success notification
|
|
175
|
-
yield /** @type {any} */ ({
|
|
176
|
-
type: 'log',
|
|
177
|
-
level: 'success',
|
|
178
|
-
message: `Theme exported as ${(this.themeFormat || 'json').toUpperCase()}! Path: ${nav.path}`,
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
// 5. Return result with navigation context
|
|
182
133
|
return {
|
|
183
134
|
type: 'result',
|
|
184
135
|
data: {
|
|
185
|
-
|
|
136
|
+
component: this.selectedComponent,
|
|
186
137
|
themeConfig: configResponse.value,
|
|
187
138
|
exportFormat: this.themeFormat,
|
|
188
139
|
breadcrumb: nav.path,
|
|
189
|
-
}
|
|
140
|
+
},
|
|
190
141
|
}
|
|
191
142
|
}
|
|
192
143
|
}
|
|
@@ -1,90 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
import {
|
|
3
|
+
ConfirmModel,
|
|
4
|
+
SpinnerModel,
|
|
5
|
+
ToastModel,
|
|
6
|
+
TableModel,
|
|
7
|
+
ButtonModel,
|
|
8
|
+
} from './components/index.js'
|
|
9
|
+
import { resolveDefaults } from '@nan0web/types'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sub-model representing the user's profile for the showcase journey.
|
|
13
|
+
* Following strictly Model-as-Schema (0HCnAI) pattern.
|
|
14
|
+
*/
|
|
15
|
+
class ProfileModel extends Model {
|
|
16
|
+
static $id = '@nan0web/ui/ProfileModel'
|
|
17
|
+
|
|
18
|
+
static UI = {
|
|
19
|
+
title: 'Profile',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Use alias 'name' to avoid conflict with JS static name property
|
|
23
|
+
static profileName = {
|
|
24
|
+
alias: 'name',
|
|
25
|
+
help: 'Your name',
|
|
26
|
+
placeholder: 'John Doe',
|
|
27
|
+
required: true,
|
|
28
|
+
pattern: '.{3,}',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static role = {
|
|
32
|
+
help: 'Your role',
|
|
33
|
+
options: ['Developer', 'Designer', 'Manager'],
|
|
34
|
+
default: 'Developer',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static tool = {
|
|
38
|
+
help: 'Your tool',
|
|
39
|
+
options: ['React', 'Lit', 'Node.js', 'Playwright', 'Vitest', 'Vite'],
|
|
40
|
+
hint: 'autocomplete',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {Partial<ProfileModel> | Record<string, any>} data Model input data.
|
|
45
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
46
|
+
*/
|
|
47
|
+
constructor(data = {}, options = {}) {
|
|
48
|
+
super(data, options)
|
|
49
|
+
/** @type {string} Your name */ this.profileName
|
|
50
|
+
/** @type {'Developer'|'Designer'|'Manager'} Your role */ this.role
|
|
51
|
+
/** @type {'React'|'Lit'|'Node.js'|'Playwright'|'Vitest'|'Vite'} Your tool */ this.tool
|
|
52
|
+
}
|
|
53
|
+
}
|
|
3
54
|
|
|
4
55
|
/**
|
|
5
56
|
* Model-as-Schema for the entire UI Sandbox Showcase.
|
|
6
57
|
* Represents a complete User Journey demonstrating all components.
|
|
7
|
-
* Showcases OLMUI Scenario Testing capabilities.
|
|
8
58
|
*/
|
|
9
59
|
export class ShowcaseAppModel extends Model {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
60
|
+
static $id = '@nan0web/ui/ShowcaseAppModel'
|
|
61
|
+
|
|
62
|
+
// Canonical UI Texts and Messages
|
|
63
|
+
static UI = {
|
|
64
|
+
appName: 'showcase.app_name',
|
|
65
|
+
startBtn: 'showcase.start_btn',
|
|
66
|
+
generateConfirm: 'showcase.msg_generate_confirm',
|
|
67
|
+
aborted: 'showcase.msg_aborted',
|
|
68
|
+
success: 'showcase.msg_success',
|
|
69
|
+
cancelled: 'showcase.msg_cancelled',
|
|
70
|
+
tableProperty: 'table.property',
|
|
71
|
+
tableValue: 'table.value',
|
|
72
|
+
tableStatus: 'table.status',
|
|
73
|
+
tableStatusActive: 'table.status_active',
|
|
74
|
+
}
|
|
13
75
|
|
|
14
|
-
static
|
|
15
|
-
|
|
16
|
-
|
|
76
|
+
static appTitle = {
|
|
77
|
+
alias: 'appName',
|
|
78
|
+
help: 'showcase.app_name_help',
|
|
79
|
+
default: ShowcaseAppModel.UI.appName,
|
|
17
80
|
type: 'string',
|
|
18
81
|
}
|
|
19
82
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
83
|
+
/**
|
|
84
|
+
* @param {Partial<ShowcaseAppModel> | Record<string, any>} data Model input data.
|
|
85
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
86
|
+
*/
|
|
87
|
+
constructor(data = {}, options = {}) {
|
|
88
|
+
super(data, options)
|
|
89
|
+
/** @type {string} App name help */ this.appTitle
|
|
23
90
|
}
|
|
24
91
|
|
|
25
92
|
// ==========================================
|
|
26
93
|
// 2. AGNOSTIC LOGIC (Async Generator)
|
|
27
94
|
// ==========================================
|
|
28
95
|
|
|
96
|
+
/**
|
|
97
|
+
* @returns {AsyncGenerator<any, any, any>}
|
|
98
|
+
*/
|
|
29
99
|
async *run() {
|
|
30
|
-
|
|
31
|
-
|
|
100
|
+
const { UI } = ShowcaseAppModel
|
|
101
|
+
|
|
102
|
+
// 1. Initial interaction: Start Button
|
|
103
|
+
const startBtn = new ButtonModel({
|
|
104
|
+
content: UI.startBtn,
|
|
105
|
+
variant: 'primary',
|
|
106
|
+
size: 'lg',
|
|
107
|
+
})
|
|
108
|
+
const btnIntent = yield* startBtn.run()
|
|
32
109
|
|
|
33
110
|
if (!btnIntent.data.clicked) {
|
|
34
111
|
return { type: 'result', data: { success: false, reason: 'start_cancelled' } }
|
|
35
112
|
}
|
|
36
113
|
|
|
37
|
-
// 2.
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
114
|
+
// 2. Journey Form: Pure Model-as-Schema
|
|
115
|
+
const profile = new ProfileModel()
|
|
116
|
+
const profileResult = yield {
|
|
117
|
+
type: 'ask',
|
|
118
|
+
model: profile,
|
|
119
|
+
schema: ProfileModel,
|
|
120
|
+
field: 'profile_form',
|
|
121
|
+
}
|
|
44
122
|
|
|
45
|
-
|
|
123
|
+
if (profileResult.cancelled) {
|
|
124
|
+
yield* new ToastModel({
|
|
125
|
+
message: UI.aborted,
|
|
126
|
+
variant: 'warn',
|
|
127
|
+
duration: 0,
|
|
128
|
+
}).run()
|
|
129
|
+
return { type: 'result', data: { success: false, reason: 'user_aborted' } }
|
|
130
|
+
}
|
|
46
131
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
options: ['Developer', 'Designer', 'Manager']
|
|
50
|
-
}).run()
|
|
51
|
-
|
|
52
|
-
const role = /** @type {string} */ (roleData.selected)
|
|
132
|
+
// Update model data with alias resolution
|
|
133
|
+
profile.setData(profileResult.value)
|
|
53
134
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
|
|
135
|
+
// 3. Confirmation - key is used, adapter should handle translation/interpolation
|
|
136
|
+
const confirmIntent = yield* new ConfirmModel({
|
|
137
|
+
message: UI.generateConfirm,
|
|
138
|
+
// Pass interpolation data
|
|
139
|
+
data: { name: profile.profileName, role: profile.role },
|
|
57
140
|
}).run()
|
|
58
141
|
|
|
59
|
-
const tool = /** @type {string} */ (toolData.selected)
|
|
60
|
-
|
|
61
|
-
// 5. Ask for confirmation before proceeding to heavy calculation
|
|
62
|
-
const confirmIntent = yield* new ConfirmModel({ message: `Ready to generate profile for ${userName} (${role})?` }).run()
|
|
63
|
-
|
|
64
142
|
if (!confirmIntent.data.confirmed) {
|
|
65
|
-
yield* new ToastModel({
|
|
143
|
+
yield* new ToastModel({
|
|
144
|
+
message: UI.cancelled,
|
|
145
|
+
variant: 'info',
|
|
146
|
+
duration: 0,
|
|
147
|
+
}).run()
|
|
66
148
|
return { type: 'result', data: { success: false, reason: 'user_aborted' } }
|
|
67
149
|
}
|
|
68
150
|
|
|
69
|
-
//
|
|
151
|
+
// 4. Progress Feedback
|
|
70
152
|
yield* new SpinnerModel({ size: 'md' }).run()
|
|
71
153
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
154
|
+
yield* new ToastModel({
|
|
155
|
+
message: UI.success,
|
|
156
|
+
variant: 'success',
|
|
157
|
+
duration: 0,
|
|
158
|
+
}).run()
|
|
76
159
|
|
|
77
|
-
//
|
|
160
|
+
// 5. Result Visualization
|
|
78
161
|
const { data: tableData } = yield* new TableModel({
|
|
79
|
-
columns: [
|
|
162
|
+
columns: [UI.tableProperty, UI.tableValue],
|
|
80
163
|
rows: [
|
|
81
|
-
['
|
|
82
|
-
['
|
|
83
|
-
['
|
|
84
|
-
[
|
|
85
|
-
]
|
|
164
|
+
['profile.name', profile.profileName],
|
|
165
|
+
['profile.role', profile.role],
|
|
166
|
+
['profile.tool', profile.tool],
|
|
167
|
+
[UI.tableStatus, UI.tableStatusActive],
|
|
168
|
+
],
|
|
86
169
|
}).run()
|
|
87
170
|
|
|
88
|
-
return { type: 'result', data: { success: true, profile
|
|
171
|
+
return { type: 'result', data: { success: true, profile, rowsDisplayed: tableData.rowsCount } }
|
|
89
172
|
}
|
|
90
173
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AccordionModel — OLMUI Model-as-Schema
|
|
5
|
+
* Collapsible FAQ / accordion item with title + content.
|
|
6
|
+
*/
|
|
7
|
+
export class AccordionModel extends Model {
|
|
8
|
+
static $id = '@nan0web/ui/AccordionModel'
|
|
9
|
+
|
|
10
|
+
static title = {
|
|
11
|
+
help: 'Accordion item header / question',
|
|
12
|
+
placeholder: 'How does it work?',
|
|
13
|
+
default: '',
|
|
14
|
+
required: true,
|
|
15
|
+
}
|
|
16
|
+
static content = {
|
|
17
|
+
help: 'Accordion item body / answer (supports markdown)',
|
|
18
|
+
placeholder: 'It works by...',
|
|
19
|
+
default: '',
|
|
20
|
+
required: true,
|
|
21
|
+
}
|
|
22
|
+
static open = {
|
|
23
|
+
help: 'Whether the item is expanded by default',
|
|
24
|
+
default: false,
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {Partial<AccordionModel> | Record<string, any>} data Model input data.
|
|
30
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
31
|
+
*/
|
|
32
|
+
constructor(data = {}, options = {}) {
|
|
33
|
+
super(data, options)
|
|
34
|
+
/** @type {string} Accordion item header / question */ this.title
|
|
35
|
+
/** @type {string} Accordion item body / answer (supports markdown) */ this.content
|
|
36
|
+
/** @type {boolean} Whether the item is expanded by default */ this.open
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import { Model } from '@nan0web/
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {Object} AutocompleteData
|
|
5
|
-
* @property {string} [content]
|
|
6
|
-
* @property {string[]} [options]
|
|
7
|
-
*/
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
4
|
* Model-as-Schema for Autocomplete component.
|
|
11
5
|
* Represents a text input with search suggestions.
|
|
12
6
|
*/
|
|
13
7
|
export class AutocompleteModel extends Model {
|
|
14
|
-
// ==========================================
|
|
15
|
-
// 1. MODEL AS SCHEMA (Static Definition)
|
|
16
|
-
// ==========================================
|
|
17
|
-
|
|
18
8
|
static content = {
|
|
19
9
|
help: 'Current search text',
|
|
20
10
|
default: '',
|
|
@@ -28,18 +18,18 @@ export class AutocompleteModel extends Model {
|
|
|
28
18
|
}
|
|
29
19
|
|
|
30
20
|
/**
|
|
31
|
-
* @param {
|
|
21
|
+
* @param {Partial<AutocompleteModel> | Record<string, any>} data Model input data.
|
|
22
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
32
23
|
*/
|
|
33
|
-
constructor(data = {}) {
|
|
34
|
-
super(data)
|
|
35
|
-
/** @type {string
|
|
36
|
-
/** @type {string[]
|
|
24
|
+
constructor(data = {}, options = {}) {
|
|
25
|
+
super(data, options)
|
|
26
|
+
/** @type {string} Current search text */ this.content
|
|
27
|
+
/** @type {string[]} List of suggestions based on input */ this.options
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @returns {AsyncGenerator<any, any, any>}
|
|
32
|
+
*/
|
|
43
33
|
async *run() {
|
|
44
34
|
const response = yield {
|
|
45
35
|
type: 'ask',
|
|
@@ -49,7 +39,7 @@ export class AutocompleteModel extends Model {
|
|
|
49
39
|
options: this.options,
|
|
50
40
|
},
|
|
51
41
|
component: 'Autocomplete',
|
|
52
|
-
model:
|
|
42
|
+
model: this,
|
|
53
43
|
}
|
|
54
44
|
|
|
55
45
|
this.content = response.value
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BannerModel — OLMUI Model-as-Schema
|
|
5
|
+
* Global notification bar (cookies, maintenance, announcements).
|
|
6
|
+
*/
|
|
7
|
+
export class BannerModel extends Model {
|
|
8
|
+
static $id = '@nan0web/ui/BannerModel'
|
|
9
|
+
|
|
10
|
+
static text = {
|
|
11
|
+
help: 'Banner message text',
|
|
12
|
+
placeholder: 'We use cookies to improve your experience',
|
|
13
|
+
default: '',
|
|
14
|
+
required: true,
|
|
15
|
+
}
|
|
16
|
+
static href = {
|
|
17
|
+
help: 'Optional link for "Learn more" or action URL',
|
|
18
|
+
placeholder: 'https://...',
|
|
19
|
+
default: '',
|
|
20
|
+
}
|
|
21
|
+
static closable = {
|
|
22
|
+
help: 'Whether the user can dismiss the banner',
|
|
23
|
+
default: true,
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Partial<BannerModel> | Record<string, any>} data Model input data.
|
|
29
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
30
|
+
*/
|
|
31
|
+
constructor(data = {}, options = {}) {
|
|
32
|
+
super(data, options)
|
|
33
|
+
/** @type {string} Banner message text */ this.text
|
|
34
|
+
/** @type {string} Optional link for "Learn more" or action URL */ this.href
|
|
35
|
+
/** @type {boolean} Whether the user can dismiss the banner */ this.closable
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Model } from '@nan0web/
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {Object} BreadcrumbItem
|
|
@@ -45,12 +45,14 @@ export class BreadcrumbModel extends Model {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* @param {BreadcrumbData | any}
|
|
48
|
+
* @param {BreadcrumbData | Record<string, any>} data Model input data.
|
|
49
|
+
* @param {object} [options] Extended options (db, etc.)
|
|
49
50
|
*/
|
|
50
|
-
constructor(data = {}) {
|
|
51
|
-
super(data)
|
|
52
|
-
/** @type {BreadcrumbItem[]} */ this.items
|
|
53
|
-
/** @type {string} */ this.separator
|
|
51
|
+
constructor(data = {}, options = {}) {
|
|
52
|
+
super(data, options)
|
|
53
|
+
/** @type {BreadcrumbItem[]} Navigation stack */ this.items
|
|
54
|
+
/** @type {string} Visual separator between breadcrumb segments */ this.separator
|
|
55
|
+
|
|
54
56
|
// Normalize: if items were passed as plain strings, convert to {label, path}
|
|
55
57
|
if (Array.isArray(this.items)) {
|
|
56
58
|
this.items = this.items.map((item) =>
|
|
@@ -245,13 +247,13 @@ export class BreadcrumbModel extends Model {
|
|
|
245
247
|
* This is a "display-only" run — it shows the navigation state.
|
|
246
248
|
*/
|
|
247
249
|
async *run() {
|
|
248
|
-
yield
|
|
250
|
+
yield {
|
|
249
251
|
type: 'log',
|
|
250
252
|
level: 'info',
|
|
251
253
|
message: this.toString(),
|
|
252
254
|
component: 'Breadcrumbs',
|
|
253
|
-
model: /** @type {
|
|
254
|
-
}
|
|
255
|
+
model: /** @type {BreadcrumbModel} */ (this),
|
|
256
|
+
}
|
|
255
257
|
|
|
256
258
|
return {
|
|
257
259
|
type: 'result',
|