neo.mjs 4.0.50 → 4.0.53
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/buildScripts/createClass.mjs +232 -51
- package/examples/form/field/text/MainContainer.mjs +22 -0
- package/package.json +7 -3
- package/resources/scss/src/form/field/Text.scss +21 -9
- package/src/data/RecordFactory.mjs +31 -12
- package/src/form/field/Number.mjs +35 -6
- package/src/form/field/Text.mjs +60 -2
- package/src/main/mixin/DeltaUpdates.mjs +6 -2
- package/src/table/Container.mjs +4 -7
|
@@ -1,24 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { Command } from 'commander/esm.mjs';
|
|
5
|
+
import envinfo from 'envinfo';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import {fileURLToPath} from 'url';
|
|
11
|
+
|
|
12
|
+
const
|
|
13
|
+
__dirname = fileURLToPath(new URL('../', import.meta.url)),
|
|
10
14
|
cwd = process.cwd(),
|
|
11
15
|
requireJson = path => JSON.parse(fs.readFileSync((path))),
|
|
12
16
|
packageJson = requireJson(path.join(__dirname, 'package.json')),
|
|
13
17
|
insideNeo = packageJson.name === 'neo.mjs',
|
|
14
18
|
program = new Command(),
|
|
15
19
|
programName = `${packageJson.name} create-class`,
|
|
16
|
-
questions = []
|
|
20
|
+
questions = [],
|
|
21
|
+
/**
|
|
22
|
+
* Maintain a list of dir-names recognized as source root directories.
|
|
23
|
+
* When not using dot notation with a class-name, the program assumes
|
|
24
|
+
* that we want to create the class inside the cwd. The proper namespace
|
|
25
|
+
* is then looked up by traversing the directory path up to the first
|
|
26
|
+
* folder that matches an entry in "sourceRootDirs". The owning
|
|
27
|
+
* folder (parent of cwd, child of sourceRootDirs[n]) will then be used as the
|
|
28
|
+
* namespace for this created class.
|
|
29
|
+
* Can be overwritten with the -s option.
|
|
30
|
+
* @type {string[]}
|
|
31
|
+
*/
|
|
32
|
+
sourceRootDirs = ['apps'];
|
|
17
33
|
|
|
18
34
|
program
|
|
19
35
|
.name(programName)
|
|
20
36
|
.version(packageJson.version)
|
|
21
37
|
.option('-i, --info', 'print environment debug info')
|
|
38
|
+
.option('-d, --drop', 'drops class in the currently selected folder')
|
|
39
|
+
.option('-s, --source <value>', `name of the folder containing the project. Defaults to any of ${sourceRootDirs.join(',')}`)
|
|
22
40
|
.option('-b, --baseClass <value>')
|
|
23
41
|
.option('-c, --className <value>')
|
|
24
42
|
.allowUnknownOption()
|
|
@@ -33,7 +51,7 @@ const programOpts = program.opts();
|
|
|
33
51
|
if (programOpts.info) {
|
|
34
52
|
console.log(chalk.bold('\nEnvironment Info:'));
|
|
35
53
|
console.log(`\n current version of ${packageJson.name}: ${packageJson.version}`);
|
|
36
|
-
console.log(` running from ${
|
|
54
|
+
console.log(` running from ${cwd}`);
|
|
37
55
|
|
|
38
56
|
envinfo
|
|
39
57
|
.run({
|
|
@@ -49,12 +67,34 @@ if (programOpts.info) {
|
|
|
49
67
|
} else {
|
|
50
68
|
console.log(chalk.green(programName));
|
|
51
69
|
|
|
70
|
+
if (programOpts.drop) {
|
|
71
|
+
// change source folder if the user wants to
|
|
72
|
+
if (programOpts.source) {
|
|
73
|
+
while (sourceRootDirs.length) {
|
|
74
|
+
sourceRootDirs.pop();
|
|
75
|
+
}
|
|
76
|
+
sourceRootDirs.push(programOpts.source);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!programOpts.className || !programOpts.baseClass) {
|
|
80
|
+
console.error(chalk.red('-d is non interactive. Please provide name base class, and optionally the source parent for the class to create'));
|
|
81
|
+
console.info(chalk.bgCyan('Usage: createClass -d -c <className> -b <baseClass> [-s sourceParent]'));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (programOpts.className.indexOf('.') !== -1) {
|
|
86
|
+
console.error(chalk.red('No .dot-notation available when -d option is selected.'));
|
|
87
|
+
console.info(chalk.bgCyan('Usage: createClass -d -c <className> -b <baseClass> [-s sourceParent]'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
52
92
|
if (!programOpts.className) {
|
|
53
93
|
questions.push({
|
|
54
94
|
type : 'input',
|
|
55
95
|
name : 'className',
|
|
56
96
|
message: 'Please choose the namespace for your class:',
|
|
57
|
-
default: 'Covid.view.
|
|
97
|
+
default: 'Covid.view.HeaderContainerController'
|
|
58
98
|
});
|
|
59
99
|
}
|
|
60
100
|
|
|
@@ -63,39 +103,113 @@ if (programOpts.info) {
|
|
|
63
103
|
type : 'list',
|
|
64
104
|
name : 'baseClass',
|
|
65
105
|
message: 'Please pick the base class, which you want to extend:',
|
|
66
|
-
choices: ['component.Base', 'container.Base'],
|
|
67
|
-
default: '
|
|
106
|
+
choices: ['component.Base', 'container.Base', 'controller.Component', 'core.Base'],
|
|
107
|
+
default: 'container.Base'
|
|
68
108
|
});
|
|
69
109
|
}
|
|
70
110
|
|
|
71
111
|
inquirer.prompt(questions).then(answers => {
|
|
72
112
|
let baseClass = programOpts.baseClass || answers.baseClass,
|
|
73
113
|
className = programOpts.className || answers.className,
|
|
114
|
+
isDrop = programOpts.drop,
|
|
74
115
|
startDate = new Date(),
|
|
75
|
-
classFolder, file, folderDelta, ns, root, rootLowerCase;
|
|
116
|
+
classFolder, file, folderDelta, index, ns, root, rootLowerCase, viewFile;
|
|
76
117
|
|
|
77
118
|
if (className.endsWith('.mjs')) {
|
|
78
119
|
className = className.slice(0, -4);
|
|
79
120
|
}
|
|
80
121
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
122
|
+
if (!isDrop) {
|
|
123
|
+
ns = className.split('.');
|
|
124
|
+
file = ns.pop();
|
|
125
|
+
root = ns.shift();
|
|
126
|
+
rootLowerCase = root.toLowerCase();
|
|
127
|
+
}
|
|
85
128
|
|
|
86
129
|
if (root === 'Neo') {
|
|
87
130
|
console.log('todo: create the file inside the src folder');
|
|
88
131
|
} else {
|
|
89
|
-
if (
|
|
90
|
-
|
|
132
|
+
if (isDrop === true) {
|
|
133
|
+
ns = [];
|
|
134
|
+
|
|
135
|
+
let pathInfo = path.parse(cwd),
|
|
136
|
+
sep = path.sep,
|
|
137
|
+
baseName, loc = baseName = '',
|
|
138
|
+
tmpNs;
|
|
139
|
+
|
|
140
|
+
sourceRootDirs.some(dir => {
|
|
141
|
+
loc = cwd;
|
|
142
|
+
tmpNs = [];
|
|
143
|
+
|
|
144
|
+
while (pathInfo.root !== loc) {
|
|
145
|
+
baseName = path.resolve(loc, './').split(sep).pop();
|
|
146
|
+
|
|
147
|
+
if (baseName === dir) {
|
|
148
|
+
ns = tmpNs.reverse();
|
|
149
|
+
classFolder = path.resolve(loc, ns.join(sep));
|
|
150
|
+
file = className;
|
|
151
|
+
className = ns.concat(className).join('.');
|
|
152
|
+
loc = path.resolve(loc, ns.join(sep));
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
tmpNs.push(baseName);
|
|
157
|
+
loc = path.resolve(loc, '../');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (!ns.length) {
|
|
162
|
+
console.error(chalk.red(
|
|
163
|
+
'Could not determine namespace for application. Did you provide the ' +
|
|
164
|
+
`correct source parent with -s? (was: ${sourceRootDirs.join(',')}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.info(
|
|
169
|
+
chalk.yellow(`Creating ${chalk.bgGreen(className)} extending ${chalk.bgGreen(baseClass)} in ${loc}${sep}${file}.mjs`)
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
let delta_l = path.normalize(__dirname),
|
|
173
|
+
delta_r = path.normalize(loc);
|
|
174
|
+
|
|
175
|
+
if (delta_r.indexOf(delta_l) !== 0) {
|
|
176
|
+
console.error(chalk.red(`Could not determine ${loc} being a child of ${__dirname}`));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let delta = delta_r.replace(delta_l, ''),
|
|
181
|
+
parts = delta.split(sep);
|
|
182
|
+
|
|
183
|
+
folderDelta = parts.length;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (isDrop !== true) {
|
|
187
|
+
if (fs.existsSync(path.resolve(__dirname, 'apps', rootLowerCase))) {
|
|
188
|
+
classFolder = path.resolve(__dirname, 'apps', rootLowerCase, ns.join('/'));
|
|
189
|
+
} else {
|
|
190
|
+
console.log('\nNon existing neo app name:', chalk.red(root));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (folderDelta === undefined) {
|
|
91
196
|
folderDelta = ns.length + 2;
|
|
197
|
+
}
|
|
92
198
|
|
|
93
|
-
|
|
199
|
+
fs.mkdirpSync(classFolder);
|
|
94
200
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
201
|
+
fs.writeFileSync(path.join(classFolder, file + '.mjs'), createContent({baseClass, className, file, folderDelta, ns, root}));
|
|
202
|
+
|
|
203
|
+
if (baseClass === 'controller.Component') {
|
|
204
|
+
index = file.indexOf('Controller');
|
|
205
|
+
|
|
206
|
+
if (index > 0) {
|
|
207
|
+
viewFile = path.join(classFolder, file.substr(0, index) + '.mjs');
|
|
208
|
+
|
|
209
|
+
if (fs.existsSync(viewFile)) {
|
|
210
|
+
adjustView({file, viewFile});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
99
213
|
}
|
|
100
214
|
}
|
|
101
215
|
|
|
@@ -108,9 +222,84 @@ if (programOpts.info) {
|
|
|
108
222
|
/**
|
|
109
223
|
* Adds a comma to the last element of the contentArray
|
|
110
224
|
* @param {String[]} contentArray
|
|
225
|
+
* @returns {String[]}
|
|
111
226
|
*/
|
|
112
227
|
function addComma(contentArray) {
|
|
113
228
|
contentArray[contentArray.length - 1] += ',';
|
|
229
|
+
return contentArray;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Adjusts the views related to controller.Component or model.Component
|
|
234
|
+
* @param {Object} opts
|
|
235
|
+
* @param {String} opts.file
|
|
236
|
+
* @param {String} opts.viewFile
|
|
237
|
+
*/
|
|
238
|
+
function adjustView(opts) {
|
|
239
|
+
let file = opts.file,
|
|
240
|
+
viewFile = opts.viewFile,
|
|
241
|
+
content = fs.readFileSync(viewFile).toString().split(os.EOL),
|
|
242
|
+
fromMaxPosition = 0,
|
|
243
|
+
i = 0,
|
|
244
|
+
len = content.length,
|
|
245
|
+
adjustSpaces, codeLine, fromPosition, importLength, importName, j, spaces;
|
|
246
|
+
|
|
247
|
+
// find the index where we want to insert our import statement
|
|
248
|
+
for (; i < len; i++) {
|
|
249
|
+
codeLine = content[i];
|
|
250
|
+
|
|
251
|
+
if (codeLine === '') {
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
importName = codeLine.substr(7);
|
|
256
|
+
importName = importName.substr(0, importName.indexOf(' '));
|
|
257
|
+
importLength = importName.length;
|
|
258
|
+
|
|
259
|
+
if (importName > file) {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
content.splice(i, 0, `import ${file} from './${file}.mjs';`);
|
|
265
|
+
|
|
266
|
+
// find the longest import module name
|
|
267
|
+
for (i=0; i < len; i++) {
|
|
268
|
+
codeLine = content[i];
|
|
269
|
+
|
|
270
|
+
if (codeLine === '') {
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
fromMaxPosition = Math.max(fromMaxPosition, codeLine.indexOf('from'));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// adjust the block-formatting for imports
|
|
278
|
+
for (i=0; i < len; i++) {
|
|
279
|
+
codeLine = content[i];
|
|
280
|
+
|
|
281
|
+
if (codeLine === '') {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
fromPosition = codeLine.indexOf('from');
|
|
286
|
+
adjustSpaces = fromMaxPosition - fromPosition;
|
|
287
|
+
|
|
288
|
+
if (adjustSpaces > 0) {
|
|
289
|
+
spaces = '';
|
|
290
|
+
|
|
291
|
+
for (j=0; j < adjustSpaces; j++) {
|
|
292
|
+
spaces += ' ';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
content[i] = codeLine.substr(0, fromPosition) + spaces + codeLine.substr(fromPosition);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fs.writeFileSync(viewFile, content.join(os.EOL));
|
|
300
|
+
|
|
301
|
+
console.log(i, opts.file);
|
|
302
|
+
console.log(content);
|
|
114
303
|
}
|
|
115
304
|
|
|
116
305
|
/**
|
|
@@ -125,13 +314,13 @@ if (programOpts.info) {
|
|
|
125
314
|
* @returns {String}
|
|
126
315
|
*/
|
|
127
316
|
function createContent(opts) {
|
|
128
|
-
let baseClass
|
|
129
|
-
baseClassNs
|
|
317
|
+
let baseClass = opts.baseClass,
|
|
318
|
+
baseClassNs = baseClass.split('.'),
|
|
130
319
|
baseFileName = baseClassNs.pop(),
|
|
131
|
-
className
|
|
132
|
-
file
|
|
133
|
-
i
|
|
134
|
-
importDelta
|
|
320
|
+
className = opts.className,
|
|
321
|
+
file = opts.file,
|
|
322
|
+
i = 0,
|
|
323
|
+
importDelta = '';
|
|
135
324
|
|
|
136
325
|
for (; i < opts.folderDelta; i++) {
|
|
137
326
|
importDelta += '../';
|
|
@@ -141,8 +330,8 @@ if (programOpts.info) {
|
|
|
141
330
|
`import ${baseFileName} from '${importDelta}${(insideNeo ? '' : 'node_modules/neo.mjs/')}src/${baseClassNs.join('/')}/${baseFileName}.mjs';`,
|
|
142
331
|
"",
|
|
143
332
|
"/**",
|
|
144
|
-
|
|
145
|
-
|
|
333
|
+
` * @class ${className}`,
|
|
334
|
+
` * @extends Neo.${baseClass}`,
|
|
146
335
|
" */",
|
|
147
336
|
`class ${file} extends ${baseFileName} {`,
|
|
148
337
|
" static getConfig() {return {",
|
|
@@ -153,28 +342,20 @@ if (programOpts.info) {
|
|
|
153
342
|
` className: '${className}'`
|
|
154
343
|
];
|
|
155
344
|
|
|
156
|
-
|
|
157
|
-
addComma(classContent);
|
|
158
|
-
|
|
159
|
-
classContent.push(
|
|
345
|
+
baseClass === 'container.Base' && addComma(classContent).push(
|
|
160
346
|
" /*",
|
|
161
|
-
" * @member {Object}
|
|
347
|
+
" * @member {Object[]} items",
|
|
162
348
|
" */",
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (baseClass === 'container.Base') {
|
|
169
|
-
addComma(classContent);
|
|
349
|
+
" items: []"
|
|
350
|
+
);
|
|
170
351
|
|
|
171
|
-
|
|
352
|
+
baseClass === 'component.Base' && addComma(classContent).push(
|
|
172
353
|
" /*",
|
|
173
|
-
" * @member {Object
|
|
354
|
+
" * @member {Object} _vdom",
|
|
174
355
|
" */",
|
|
175
|
-
"
|
|
176
|
-
|
|
177
|
-
|
|
356
|
+
" _vdom:",
|
|
357
|
+
" {}"
|
|
358
|
+
);
|
|
178
359
|
|
|
179
360
|
classContent.push(
|
|
180
361
|
" }}",
|
|
@@ -36,6 +36,12 @@ class MainContainer extends ConfigurationViewport {
|
|
|
36
36
|
labelText: 'clearToOriginalValue',
|
|
37
37
|
listeners: {change: me.onConfigChange.bind(me, 'clearToOriginalValue')},
|
|
38
38
|
style : {marginTop: '10px'}
|
|
39
|
+
}, {
|
|
40
|
+
module : CheckBox,
|
|
41
|
+
checked : me.exampleComponent.disabled,
|
|
42
|
+
labelText: 'disabled',
|
|
43
|
+
listeners: {change: me.onConfigChange.bind(me, 'disabled')},
|
|
44
|
+
style : {marginTop: '10px'}
|
|
39
45
|
}, {
|
|
40
46
|
module : CheckBox,
|
|
41
47
|
checked : me.exampleComponent.hideLabel,
|
|
@@ -97,6 +103,22 @@ class MainContainer extends ConfigurationViewport {
|
|
|
97
103
|
minValue : 50,
|
|
98
104
|
stepSize : 5,
|
|
99
105
|
value : me.exampleComponent.labelWidth
|
|
106
|
+
}, {
|
|
107
|
+
module : NumberField,
|
|
108
|
+
labelText: 'maxLength',
|
|
109
|
+
listeners: {change: me.onConfigChange.bind(me, 'maxLength')},
|
|
110
|
+
maxValue : 50,
|
|
111
|
+
minValue : 1,
|
|
112
|
+
stepSize : 1,
|
|
113
|
+
value : me.exampleComponent.maxLength
|
|
114
|
+
}, {
|
|
115
|
+
module : NumberField,
|
|
116
|
+
labelText: 'minLength',
|
|
117
|
+
listeners: {change: me.onConfigChange.bind(me, 'minLength')},
|
|
118
|
+
maxValue : 50,
|
|
119
|
+
minValue : 1,
|
|
120
|
+
stepSize : 1,
|
|
121
|
+
value : me.exampleComponent.minLength
|
|
100
122
|
}, {
|
|
101
123
|
module : TextField,
|
|
102
124
|
clearable: true,
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neo.mjs",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.53",
|
|
4
4
|
"description": "The webworkers driven UI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/neomjs/neo.git"
|
|
9
9
|
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"neo-cc": "./buildScripts/createClass.mjs"
|
|
12
|
+
},
|
|
10
13
|
"scripts": {
|
|
11
14
|
"build-all": "node ./buildScripts/buildAll.mjs -f -n",
|
|
12
15
|
"build-all-questions": "node ./buildScripts/buildAll.mjs -f",
|
|
@@ -51,7 +54,7 @@
|
|
|
51
54
|
"neo-jsdoc": "^1.0.1",
|
|
52
55
|
"neo-jsdoc-x": "^1.0.4",
|
|
53
56
|
"postcss": "^8.4.14",
|
|
54
|
-
"sass": "^1.
|
|
57
|
+
"sass": "^1.53.0",
|
|
55
58
|
"webpack": "^5.73.0",
|
|
56
59
|
"webpack-cli": "^4.10.0",
|
|
57
60
|
"webpack-dev-server": "4.9.2",
|
|
@@ -59,7 +62,8 @@
|
|
|
59
62
|
"webpack-node-externals": "^3.0.0"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
|
-
"siesta-lite": "^5.5.2"
|
|
65
|
+
"siesta-lite": "^5.5.2",
|
|
66
|
+
"url": "^0.11.0"
|
|
63
67
|
},
|
|
64
68
|
"funding": {
|
|
65
69
|
"type": "GitHub Sponsors",
|
|
@@ -8,7 +8,19 @@
|
|
|
8
8
|
|
|
9
9
|
&.neo-focus {
|
|
10
10
|
.neo-input-wrapper {
|
|
11
|
-
border-color: v(textfield-border-color-active);
|
|
11
|
+
border-color: v(textfield-border-color-active) !important;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&.neo-invalid {
|
|
16
|
+
.neo-input-wrapper {
|
|
17
|
+
border-color: brown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&.neo-disabled {
|
|
21
|
+
.neo-input-wrapper {
|
|
22
|
+
border-color: inherit;
|
|
23
|
+
}
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
26
|
|
|
@@ -156,10 +168,6 @@
|
|
|
156
168
|
margin : 0; // important for Safari => #1125
|
|
157
169
|
min-height : 25px;
|
|
158
170
|
width : 30px;
|
|
159
|
-
|
|
160
|
-
&:invalid {
|
|
161
|
-
border: 1px solid brown;
|
|
162
|
-
}
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
|
|
@@ -179,14 +187,18 @@
|
|
|
179
187
|
outline : none;
|
|
180
188
|
}
|
|
181
189
|
|
|
182
|
-
&:invalid {
|
|
183
|
-
border-color: brown;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
190
|
&::-webkit-input-placeholder {
|
|
187
191
|
color : v(textfield-input-placeholder-color) !important;
|
|
188
192
|
opacity: v(textfield-input-placeholder-opacity) !important;
|
|
189
193
|
}
|
|
194
|
+
|
|
195
|
+
&.neo-invalid {
|
|
196
|
+
border-color: brown;
|
|
197
|
+
|
|
198
|
+
&.neo-disabled {
|
|
199
|
+
border-color: inherit;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
190
202
|
}
|
|
191
203
|
|
|
192
204
|
.neo-textfield-label {
|
|
@@ -28,9 +28,9 @@ class RecordFactory extends Base {
|
|
|
28
28
|
*/
|
|
29
29
|
ovPrefix: 'ov_',
|
|
30
30
|
/**
|
|
31
|
-
* @member {String} recordNamespace='Neo.data.record
|
|
31
|
+
* @member {String} recordNamespace='Neo.data.record'
|
|
32
32
|
*/
|
|
33
|
-
recordNamespace: 'Neo.data.record
|
|
33
|
+
recordNamespace: 'Neo.data.record'
|
|
34
34
|
}}
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -39,7 +39,7 @@ class RecordFactory extends Base {
|
|
|
39
39
|
* @returns {Object}
|
|
40
40
|
*/
|
|
41
41
|
createRecord(model, config) {
|
|
42
|
-
let recordClass = Neo.ns(this.recordNamespace
|
|
42
|
+
let recordClass = Neo.ns(`${this.recordNamespace}.${model.className}.${model.id}`);
|
|
43
43
|
|
|
44
44
|
if (!recordClass) {
|
|
45
45
|
recordClass = this.createRecordClass(model);
|
|
@@ -54,7 +54,7 @@ class RecordFactory extends Base {
|
|
|
54
54
|
*/
|
|
55
55
|
createRecordClass(model) {
|
|
56
56
|
if (model instanceof Model) {
|
|
57
|
-
let className = this.recordNamespace
|
|
57
|
+
let className = `${this.recordNamespace}.${model.className}.${model.id}`,
|
|
58
58
|
ns = Neo.ns(className),
|
|
59
59
|
key, nsArray;
|
|
60
60
|
|
|
@@ -76,7 +76,7 @@ class RecordFactory extends Base {
|
|
|
76
76
|
|
|
77
77
|
if (Array.isArray(model.fields)) {
|
|
78
78
|
model.fields.forEach(field => {
|
|
79
|
-
let parsedValue = instance.parseRecordValue(field, config[field.name], config),
|
|
79
|
+
let parsedValue = instance.parseRecordValue(me, field, config[field.name], config),
|
|
80
80
|
symbol = Symbol.for(field.name);
|
|
81
81
|
|
|
82
82
|
properties = {
|
|
@@ -97,9 +97,9 @@ class RecordFactory extends Base {
|
|
|
97
97
|
let me = this,
|
|
98
98
|
oldValue = me[symbol];
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
value = instance.parseRecordValue(field, value, null);
|
|
100
|
+
value = instance.parseRecordValue(me, field, value);
|
|
102
101
|
|
|
102
|
+
if (!Neo.isEqual(value, oldValue)) {
|
|
103
103
|
me[symbol] = value;
|
|
104
104
|
|
|
105
105
|
me._isModified = true;
|
|
@@ -221,14 +221,18 @@ class RecordFactory extends Base {
|
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
223
|
* todo: parse value for more field types
|
|
224
|
+
* @param {Object} record
|
|
224
225
|
* @param {Object} field
|
|
225
226
|
* @param {*} value
|
|
226
|
-
* @param {Object} recordConfig
|
|
227
|
+
* @param {Object} recordConfig=null
|
|
227
228
|
* @returns {*}
|
|
228
229
|
*/
|
|
229
|
-
parseRecordValue(field, value, recordConfig) {
|
|
230
|
-
let mapping
|
|
231
|
-
|
|
230
|
+
parseRecordValue(record, field, value, recordConfig=null) {
|
|
231
|
+
let mapping = field.mapping,
|
|
232
|
+
maxLength = field.maxLength,
|
|
233
|
+
minLength = field.minLength,
|
|
234
|
+
oldValue = recordConfig?.[field.name] || record[field.name],
|
|
235
|
+
type = field.type?.toLowerCase();
|
|
232
236
|
|
|
233
237
|
// only trigger mappings for initial values
|
|
234
238
|
// dynamic changes of a field will not pass the recordConfig
|
|
@@ -240,7 +244,21 @@ class RecordFactory extends Base {
|
|
|
240
244
|
value = ns[key];
|
|
241
245
|
}
|
|
242
246
|
|
|
243
|
-
if (
|
|
247
|
+
if (Object.hasOwn(field, maxLength)) {
|
|
248
|
+
if (value?.toString() > maxLength) {
|
|
249
|
+
console.warn(`Setting record field: ${field} value: ${value} conflicts with the maxLength: ${maxLength}`);
|
|
250
|
+
return oldValue;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (Object.hasOwn(field, minLength)) {
|
|
255
|
+
if (value?.toString() < minLength) {
|
|
256
|
+
console.warn(`Setting record field: ${field} value: ${value} conflicts with the minLength: ${minLength}`);
|
|
257
|
+
return oldValue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (type === 'date' && Neo.typeOf(value) !== 'Date') {
|
|
244
262
|
return new Date(value);
|
|
245
263
|
}
|
|
246
264
|
|
|
@@ -259,6 +277,7 @@ class RecordFactory extends Base {
|
|
|
259
277
|
|
|
260
278
|
Object.entries(fields).forEach(([key, value]) => {
|
|
261
279
|
oldValue = record[key];
|
|
280
|
+
value = instance.parseRecordValue(record, model.getField(key), value);
|
|
262
281
|
|
|
263
282
|
if (!Neo.isEqual(oldValue, value)) {
|
|
264
283
|
record[Symbol.for(key)] = value; // silent update
|
|
@@ -99,6 +99,7 @@ class Number extends Text {
|
|
|
99
99
|
* @protected
|
|
100
100
|
*/
|
|
101
101
|
afterSetMaxValue(value, oldValue) {
|
|
102
|
+
this.updateValidationIndicators();
|
|
102
103
|
this.changeInputElKey('max', value);
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -109,6 +110,7 @@ class Number extends Text {
|
|
|
109
110
|
* @protected
|
|
110
111
|
*/
|
|
111
112
|
afterSetMinValue(value, oldValue) {
|
|
113
|
+
this.updateValidationIndicators();
|
|
112
114
|
this.changeInputElKey('min', value);
|
|
113
115
|
}
|
|
114
116
|
|
|
@@ -174,6 +176,31 @@ class Number extends Text {
|
|
|
174
176
|
return this.beforeSetEnumValue(value, oldValue, 'triggerPosition');
|
|
175
177
|
}
|
|
176
178
|
|
|
179
|
+
/**
|
|
180
|
+
* @returns {Boolean}
|
|
181
|
+
*/
|
|
182
|
+
isValid() {
|
|
183
|
+
let me = this,
|
|
184
|
+
maxValue = me.maxValue,
|
|
185
|
+
minValue = me.minValue,
|
|
186
|
+
value = me.value,
|
|
187
|
+
isNumber = Neo.isNumber(value);
|
|
188
|
+
|
|
189
|
+
if (Neo.isNumber(maxValue) && isNumber && value > maxValue) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (Neo.isNumber(minValue) && isNumber && value < minValue) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (value % me.stepSize !== 0) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return super.isValid();
|
|
202
|
+
}
|
|
203
|
+
|
|
177
204
|
/**
|
|
178
205
|
*
|
|
179
206
|
*/
|
|
@@ -215,12 +242,13 @@ class Number extends Text {
|
|
|
215
242
|
*/
|
|
216
243
|
onSpinButtonDownClick() {
|
|
217
244
|
let me = this,
|
|
218
|
-
|
|
219
|
-
|
|
245
|
+
stepSize = me.stepSize,
|
|
246
|
+
oldValue = Neo.isNumber(me.value) ? me.value : me.minValue,
|
|
247
|
+
value = (oldValue - stepSize) < me.minValue ? me.maxValue : (oldValue - stepSize);
|
|
220
248
|
|
|
221
249
|
if (me.excludedValues) {
|
|
222
250
|
while(me.excludedValues.includes(value)) {
|
|
223
|
-
value = Math.max(me.minValue, value -
|
|
251
|
+
value = Math.max(me.minValue, value - stepSize);
|
|
224
252
|
}
|
|
225
253
|
}
|
|
226
254
|
|
|
@@ -234,12 +262,13 @@ class Number extends Text {
|
|
|
234
262
|
*/
|
|
235
263
|
onSpinButtonUpClick() {
|
|
236
264
|
let me = this,
|
|
237
|
-
|
|
238
|
-
|
|
265
|
+
stepSize = me.stepSize,
|
|
266
|
+
oldValue = Neo.isNumber(me.value) ? me.value : me.maxValue,
|
|
267
|
+
value = (oldValue + stepSize) > me.maxValue ? me.minValue : (oldValue + stepSize);
|
|
239
268
|
|
|
240
269
|
if (me.excludedValues) {
|
|
241
270
|
while(me.excludedValues.includes(value)) {
|
|
242
|
-
value = Math.min(me.maxValue, value +
|
|
271
|
+
value = Math.min(me.maxValue, value + stepSize);
|
|
243
272
|
}
|
|
244
273
|
}
|
|
245
274
|
|
package/src/form/field/Text.mjs
CHANGED
|
@@ -93,6 +93,16 @@ class Text extends Base {
|
|
|
93
93
|
* @member {Number|String} labelWidth_=150
|
|
94
94
|
*/
|
|
95
95
|
labelWidth_: 150,
|
|
96
|
+
/**
|
|
97
|
+
* The maximum amount of chars which you can enter into this field
|
|
98
|
+
* @member {Number|null} maxLength_=null
|
|
99
|
+
*/
|
|
100
|
+
maxLength_: null,
|
|
101
|
+
/**
|
|
102
|
+
* The minimum amount of chars which you can enter into this field
|
|
103
|
+
* @member {Number|null} minLength_=null
|
|
104
|
+
*/
|
|
105
|
+
minLength_: null,
|
|
96
106
|
/**
|
|
97
107
|
* @member {String|null} placeholderText_=null
|
|
98
108
|
*/
|
|
@@ -358,6 +368,28 @@ class Text extends Base {
|
|
|
358
368
|
}
|
|
359
369
|
}
|
|
360
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Triggered after the maxLength config got changed
|
|
373
|
+
* @param {Number|null} value
|
|
374
|
+
* @param {Number|null} oldValue
|
|
375
|
+
* @protected
|
|
376
|
+
*/
|
|
377
|
+
afterSetMaxLength(value, oldValue) {
|
|
378
|
+
this.updateValidationIndicators();
|
|
379
|
+
this.changeInputElKey('maxlength', value);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Triggered after the minLength config got changed
|
|
384
|
+
* @param {Number|null} value
|
|
385
|
+
* @param {Number|null} oldValue
|
|
386
|
+
* @protected
|
|
387
|
+
*/
|
|
388
|
+
afterSetMinLength(value, oldValue) {
|
|
389
|
+
this.updateValidationIndicators();
|
|
390
|
+
this.changeInputElKey('minlength', value);
|
|
391
|
+
}
|
|
392
|
+
|
|
361
393
|
/**
|
|
362
394
|
* Triggered after the mounted config got changed
|
|
363
395
|
* @param {Boolean} value
|
|
@@ -407,6 +439,7 @@ class Text extends Base {
|
|
|
407
439
|
* @protected
|
|
408
440
|
*/
|
|
409
441
|
afterSetRequired(value, oldValue) {
|
|
442
|
+
this.updateValidationIndicators();
|
|
410
443
|
this.changeInputElKey('required', value ? value : null);
|
|
411
444
|
}
|
|
412
445
|
|
|
@@ -502,6 +535,7 @@ class Text extends Base {
|
|
|
502
535
|
}
|
|
503
536
|
|
|
504
537
|
NeoArray[me.originalConfig.value !== value ? 'add' : 'remove'](me._cls, 'neo-is-dirty');
|
|
538
|
+
me.updateValidationIndicators();
|
|
505
539
|
|
|
506
540
|
me.vdom = vdom;
|
|
507
541
|
|
|
@@ -776,9 +810,19 @@ class Text extends Base {
|
|
|
776
810
|
* @returns {Boolean}
|
|
777
811
|
*/
|
|
778
812
|
isValid() {
|
|
779
|
-
let me
|
|
813
|
+
let me = this,
|
|
814
|
+
value = me.value,
|
|
815
|
+
valueLength = value?.toString().length;
|
|
780
816
|
|
|
781
|
-
if (me.required && (!
|
|
817
|
+
if (me.required && (!value || valueLength < 1)) {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (Neo.isNumber(me.maxLength) && valueLength > me.maxLength) {
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (Neo.isNumber(me.minLength) && valueLength < me.minLength) {
|
|
782
826
|
return false;
|
|
783
827
|
}
|
|
784
828
|
|
|
@@ -971,6 +1015,20 @@ class Text extends Base {
|
|
|
971
1015
|
});
|
|
972
1016
|
});
|
|
973
1017
|
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* @param {Boolean} silent=true
|
|
1021
|
+
*/
|
|
1022
|
+
updateValidationIndicators(silent=true) {
|
|
1023
|
+
let me = this,
|
|
1024
|
+
vdom = me.vdom;
|
|
1025
|
+
|
|
1026
|
+
NeoArray[!me.isValid() ? 'add' : 'remove'](me._cls, 'neo-invalid');
|
|
1027
|
+
|
|
1028
|
+
if (!silent) {
|
|
1029
|
+
me.vdom = vdom;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
974
1032
|
}
|
|
975
1033
|
|
|
976
1034
|
Neo.applyClassConfig(Text);
|
|
@@ -186,11 +186,15 @@ class DeltaUpdates extends Base {
|
|
|
186
186
|
}
|
|
187
187
|
} else if (key === 'id') {
|
|
188
188
|
node[Neo.config.useDomIds ? 'id' : 'data-neo-id'] = val;
|
|
189
|
-
}else if (key === 'spellcheck' && val === 'false') {
|
|
189
|
+
} else if (key === 'spellcheck' && val === 'false') {
|
|
190
190
|
// see https://github.com/neomjs/neo/issues/1922
|
|
191
191
|
node[key] = false;
|
|
192
192
|
} else {
|
|
193
|
-
|
|
193
|
+
if (key === 'value') {
|
|
194
|
+
node[key] = val;
|
|
195
|
+
} else {
|
|
196
|
+
node.setAttribute(key, val);
|
|
197
|
+
}
|
|
194
198
|
}
|
|
195
199
|
});
|
|
196
200
|
break;
|
package/src/table/Container.mjs
CHANGED
|
@@ -247,9 +247,9 @@ class Container extends BaseContainer {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
if (value) {
|
|
250
|
-
let me = this
|
|
250
|
+
let me = this,
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
listeners = {
|
|
253
253
|
filter : me.onStoreFilter,
|
|
254
254
|
load : me.onStoreLoad,
|
|
255
255
|
recordChange: me.onStoreRecordChange,
|
|
@@ -258,13 +258,10 @@ class Container extends BaseContainer {
|
|
|
258
258
|
|
|
259
259
|
if (value instanceof Store) {
|
|
260
260
|
value.on(listeners);
|
|
261
|
-
|
|
262
|
-
if (value.getCount() > 0) {
|
|
263
|
-
me.onStoreLoad(value.items);
|
|
264
|
-
}
|
|
261
|
+
value.getCount() > 0 && me.onStoreLoad(value.items);
|
|
265
262
|
} else {
|
|
266
263
|
value = ClassSystemUtil.beforeSetInstance(value, Store, {
|
|
267
|
-
listeners
|
|
264
|
+
listeners
|
|
268
265
|
});
|
|
269
266
|
}
|
|
270
267
|
|