neo.mjs 4.1.1 → 4.2.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.
Files changed (30) hide show
  1. package/apps/realworld/view/FooterComponent.mjs +5 -5
  2. package/apps/realworld/view/HeaderComponent.mjs +30 -30
  3. package/apps/realworld/view/HomeComponent.mjs +4 -4
  4. package/apps/realworld/view/MainContainerController.mjs +4 -4
  5. package/apps/realworld/view/article/Component.mjs +2 -2
  6. package/apps/realworld/view/article/PreviewComponent.mjs +1 -1
  7. package/apps/realworld/view/article/TagListComponent.mjs +2 -2
  8. package/apps/realworld2/view/MainContainer.mjs +1 -1
  9. package/apps/realworld2/view/MainContainerController.mjs +2 -2
  10. package/buildScripts/addConfig.mjs +400 -0
  11. package/buildScripts/createApp.mjs +3 -3
  12. package/buildScripts/createClass.mjs +15 -7
  13. package/examples/grid/container/MainContainer.mjs +102 -0
  14. package/examples/grid/container/MainModel.mjs +29 -0
  15. package/examples/grid/container/MainStore.mjs +55 -0
  16. package/examples/grid/container/app.mjs +6 -0
  17. package/examples/grid/container/index.html +11 -0
  18. package/examples/grid/container/neo-config.json +7 -0
  19. package/examples/table/container/MainModel.mjs +4 -4
  20. package/package.json +4 -3
  21. package/resources/scss/src/grid/Container.scss +6 -5
  22. package/resources/scss/src/grid/header/Button.scss +6 -0
  23. package/resources/scss/src/grid/header/Toolbar.scss +2 -0
  24. package/src/data/connection/WebSocket.mjs +1 -0
  25. package/src/grid/View.mjs +2 -2
  26. package/src/grid/header/Button.mjs +25 -0
  27. package/src/selection/grid/CellColumnModel.mjs +7 -7
  28. package/src/selection/grid/CellColumnRowModel.mjs +5 -5
  29. package/src/selection/grid/CellModel.mjs +17 -23
  30. package/src/selection/grid/ColumnModel.mjs +30 -34
@@ -15,12 +15,12 @@ class FooterComponent extends Component {
15
15
  * @member {Object} _vdom
16
16
  */
17
17
  _vdom:
18
- {tag: 'footer', cn: [
19
- {cls: ['container'], cn: [
20
- {tag: 'a', cls: ['logo-font'], href: '#/', html: 'conduit'},
21
- {tag: 'span', cls: 'attribution', html: 'An interactive learning project from <a href="https://thinkster.io">Thinkster</a>. Code &amp; design licensed under MIT.'}
22
- ]}
18
+ {tag: 'footer', cn: [
19
+ {cls: ['container'], cn: [
20
+ {tag: 'a', cls: ['logo-font'], href: '#/', html: 'conduit'},
21
+ {tag: 'span', cls: 'attribution', html: 'An interactive learning project from <a href="https://thinkster.io">Thinkster</a>. Code &amp; design licensed under MIT.'}
23
22
  ]}
23
+ ]}
24
24
  }}
25
25
  }
26
26
 
@@ -36,40 +36,40 @@ class HeaderComponent extends Component {
36
36
  * @member {Object} _vdom
37
37
  */
38
38
  _vdom:
39
- {tag: 'nav', cls: ['navbar navbar-light'], cn: [
40
- {cls: ['container'], cn: [
41
- {tag: 'a', cls: ['navbar-brand'], href: '#/', html: 'conduit'},
42
- {tag: 'ul', cls: ['nav navbar-nav', 'pull-xs-right'], cn: [
43
- {tag: 'li', cls: ['nav-item'], cn: [
44
- {tag: 'a', cls: ['nav-link'], href: '#/', html: 'Home'}
45
- ]},
46
- {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
47
- {tag: 'a', cls: ['nav-link'], href: '#/editor', cn: [
48
- {tag: 'i', cls: 'ion-compose'},
49
- {vtype: 'text', html: '&nbsp;New Article'}
50
- ]}
51
- ]},
52
- {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
53
- {tag: 'a', cls: ['nav-link'], href: '#/settings', cn: [
54
- {tag: 'i', cls: 'ion-gear-a'},
55
- {vtype: 'text', html: '&nbsp;Settings'}
56
- ]}
57
- ]},
58
- {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
59
- {tag: 'a', cls : ['nav-link'], href: '#/profile', cn: [
60
- {tag: 'img', cls: ['user-pic']},
61
- {vtype: 'text', html: '&nbsp;Profile'}
62
- ]}
63
- ]},
64
- {tag: 'li', cls: ['nav-item'], cn: [
65
- {tag : 'a', cls : ['nav-link'], href: '#/login', html: 'Sign in'}
66
- ]},
67
- {tag: 'li', cls: ['nav-item'], cn: [
68
- {tag: 'a', cls : ['nav-link'], href: '#/register', html: 'Sign up'}
39
+ {tag: 'nav', cls: ['navbar navbar-light'], cn: [
40
+ {cls: ['container'], cn: [
41
+ {tag: 'a', cls: ['navbar-brand'], href: '#/', html: 'conduit'},
42
+ {tag: 'ul', cls: ['nav navbar-nav', 'pull-xs-right'], cn: [
43
+ {tag: 'li', cls: ['nav-item'], cn: [
44
+ {tag: 'a', cls: ['nav-link'], href: '#/', html: 'Home'}
45
+ ]},
46
+ {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
47
+ {tag: 'a', cls: ['nav-link'], href: '#/editor', cn: [
48
+ {tag: 'i', cls: 'ion-compose'},
49
+ {vtype: 'text', html: '&nbsp;New Article'}
69
50
  ]}
51
+ ]},
52
+ {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
53
+ {tag: 'a', cls: ['nav-link'], href: '#/settings', cn: [
54
+ {tag: 'i', cls: 'ion-gear-a'},
55
+ {vtype: 'text', html: '&nbsp;Settings'}
56
+ ]}
57
+ ]},
58
+ {tag: 'li', cls: ['nav-item'], removeDom: true, cn: [
59
+ {tag: 'a', cls : ['nav-link'], href: '#/profile', cn: [
60
+ {tag: 'img', cls: ['user-pic']},
61
+ {vtype: 'text', html: '&nbsp;Profile'}
62
+ ]}
63
+ ]},
64
+ {tag: 'li', cls: ['nav-item'], cn: [
65
+ {tag : 'a', cls : ['nav-link'], href: '#/login', html: 'Sign in'}
66
+ ]},
67
+ {tag: 'li', cls: ['nav-item'], cn: [
68
+ {tag: 'a', cls : ['nav-link'], href: '#/register', html: 'Sign up'}
70
69
  ]}
71
70
  ]}
72
71
  ]}
72
+ ]}
73
73
  }}
74
74
 
75
75
  /**
@@ -280,8 +280,8 @@ class HomeComponent extends Component {
280
280
  cls: ['nav-item'],
281
281
  id : me.id + '__nav-item_' + index,
282
282
  cn : [{
283
- tag: 'a',
284
- cls: cls,
283
+ tag : 'a',
284
+ cls : cls,
285
285
  href: '',
286
286
  html: item.name,
287
287
  id : me.id + '__nav-item-link_' + index,
@@ -447,12 +447,12 @@ class HomeComponent extends Component {
447
447
  if (feeds.length < 3) {
448
448
  feeds.push({
449
449
  active: true,
450
- name : name
450
+ name
451
451
  });
452
452
  } else {
453
453
  Object.assign(feeds[2], {
454
454
  active: true,
455
- name : name
455
+ name
456
456
  });
457
457
  }
458
458
 
@@ -144,7 +144,7 @@ class MainContainerController extends ComponentController {
144
144
  */
145
145
  getArticle(slug) {
146
146
  return ArticleApi.get({
147
- slug: slug
147
+ slug
148
148
  });
149
149
  }
150
150
 
@@ -406,9 +406,9 @@ class MainContainerController extends ComponentController {
406
406
  module = await module();
407
407
 
408
408
  me[key] = Neo.create({
409
- module : module.default,
410
- parentId : me.component.id,
411
- reference: reference
409
+ module : module.default,
410
+ parentId: me.component.id,
411
+ reference
412
412
  });
413
413
  }
414
414
 
@@ -233,8 +233,8 @@ class Component extends BaseComponent {
233
233
 
234
234
  VDomUtil.getByFlag(vdom, 'body').cn[0] = {
235
235
  cn: [{
236
- tag : 'p',
237
- html: html
236
+ tag: 'p',
237
+ html
238
238
  }]
239
239
  };
240
240
 
@@ -267,7 +267,7 @@ class PreviewComponent extends Component {
267
267
  favorited = !me.favorited;
268
268
 
269
269
  me.set({
270
- favorited : favorited,
270
+ favorited,
271
271
  favoritesCount: favorited ? (me.favoritesCount + 1) : (me.favoritesCount - 1)
272
272
  });
273
273
  }
@@ -78,8 +78,8 @@ class TagListComponent extends Component {
78
78
  afterSetActiveTag(value, oldValue) {
79
79
  if (oldValue !== undefined) {
80
80
  this.fire('tagChange', {
81
- oldValue: oldValue,
82
- value : value
81
+ oldValue,
82
+ value
83
83
  });
84
84
  }
85
85
  }
@@ -46,7 +46,7 @@ class MainContainer extends Viewport {
46
46
  ntype : 'container',
47
47
  flex : 1,
48
48
  items : [],
49
- layout : {ntype: 'card'},
49
+ layout : {ntype: 'card', activeIndex: null},
50
50
  reference: 'cards'
51
51
  }, {
52
52
  module: FooterComponent
@@ -209,8 +209,8 @@ class MainContainerController extends ComponentController {
209
209
 
210
210
  if (!card) {
211
211
  card = me.getReference('cards').add({
212
- module : module,
213
- reference: reference
212
+ module,
213
+ reference
214
214
  });
215
215
  }
216
216
 
@@ -0,0 +1,400 @@
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
+
11
+ const
12
+ __dirname = path.resolve(),
13
+ cwd = process.cwd(),
14
+ requireJson = path => JSON.parse(fs.readFileSync((path))),
15
+ packageJson = requireJson(path.join(__dirname, 'package.json')),
16
+ program = new Command(),
17
+ programName = `${packageJson.name} add-config`;
18
+
19
+ /**
20
+ * Adds a comma to the last element of the contentArray
21
+ * @param {String[]} contentArray
22
+ * @param {Number} index=contentArray.length - 1
23
+ * @returns {String[]}
24
+ */
25
+ function addComma(contentArray, index=contentArray.length - 1) {
26
+ contentArray[index] += ',';
27
+ return contentArray;
28
+ }
29
+
30
+ /**
31
+ * Adds a config to the given index of the contentArray
32
+ * @param {Object} opts
33
+ * @param {String} opts.configName
34
+ * @param {String} opts.defaultValue
35
+ * @param {String[]} opts.contentArray
36
+ * @param {Boolean} opts.isLastConfig
37
+ * @param {Number} opts.index
38
+ * @param {String} opts.type
39
+ * @returns {String[]}
40
+ */
41
+ function addConfig(opts) {
42
+ if (opts.type === 'String' && opts.defaultValue !== 'null') {
43
+ opts.defaultValue = `'${opts.defaultValue}'`;
44
+ }
45
+
46
+ const config = [
47
+ ' /**',
48
+ ` * @member {${opts.type}} ${opts.configName}_=${opts.defaultValue}`,
49
+ ' */',
50
+ ` ${opts.configName}_: ${opts.defaultValue}`
51
+ ];
52
+
53
+ !opts.isLastConfig && addComma(config);
54
+
55
+ opts.contentArray.splice(opts.index, 0, config.join(os.EOL));
56
+ return opts.contentArray;
57
+ }
58
+
59
+ /**
60
+ * Adds a config hook at the matching index
61
+ * @param {Object} opts
62
+ * @param {String} opts.comment
63
+ * @param {String[]} opts.contentArray
64
+ * @param {String} opts.name
65
+ * @param {Boolean} opts.oldValueParam
66
+ * @param {Boolean} opts.returnValue
67
+ * @param {String} opts.type
68
+ * @returns {String[]}
69
+ */
70
+ function addHook(opts) {
71
+ let contentArray = opts.contentArray,
72
+ i = 0,
73
+ inserted = false,
74
+ name = opts.name,
75
+ len = contentArray.length,
76
+ type = opts.type,
77
+ j, methodName, nextLine,
78
+
79
+ method = [
80
+ '',
81
+ ' /**',
82
+ ` * ${opts.comment}`,
83
+ ` * @param {${type}} value`
84
+ ];
85
+
86
+ if (opts.oldValueParam) {
87
+ method.push(
88
+ ` * @param {${type}} oldValue`
89
+ );
90
+ }
91
+
92
+ if (opts.returnValue) {
93
+ method.push(
94
+ ` * @returns {${type}}`
95
+ );
96
+ }
97
+
98
+ method.push(
99
+ ' * @protected',
100
+ ' */'
101
+ );
102
+
103
+ if (opts.oldValueParam) {
104
+ method.push(
105
+ ` ${name}(value, oldValue) {`
106
+ );
107
+ } else {
108
+ method.push(
109
+ ` ${name}(value) {`
110
+ );
111
+ }
112
+
113
+ if (opts.returnValue) {
114
+ method.push(
115
+ ' return value;'
116
+ );
117
+ } else {
118
+ method.push(
119
+ ' '
120
+ );
121
+ }
122
+
123
+ method.push(
124
+ ' }'
125
+ );
126
+
127
+ for (; i < len; i++) {
128
+ if (contentArray[i].includes('}}')) {
129
+ break;
130
+ }
131
+ }
132
+
133
+ for (; i < len; i++) {
134
+ if (contentArray[i].includes('*/')) {
135
+ nextLine = contentArray[i + 1]
136
+ methodName = nextLine.substring(0, nextLine.indexOf('(')).trim();
137
+
138
+ if (methodName === 'construct') {
139
+ continue;
140
+ }
141
+
142
+ if (methodName > name) {
143
+ for (j=i; j > 0; j--) {
144
+ if (contentArray[j].includes('/**')) {
145
+ contentArray.splice(j - 1, 0, method.join(os.EOL));
146
+ inserted = true;
147
+ break;
148
+ }
149
+ }
150
+ break;
151
+ }
152
+ }
153
+ }
154
+
155
+ if (!inserted) {
156
+ for (i=contentArray.length - 1; i > 0; i--) {
157
+ if (contentArray[i].includes('}')) {
158
+ contentArray.splice(i, 0, method.join(os.EOL));
159
+ break;
160
+ }
161
+ }
162
+ }
163
+
164
+ return contentArray;
165
+ }
166
+
167
+
168
+ /**
169
+ * Makes the first character of a string uppercase
170
+ * @param {String} value
171
+ * @returns {Boolean|String} Returns false for non string inputs
172
+ */
173
+ function capitalize(value) {
174
+ return value[0].toUpperCase() + value.slice(1);
175
+ }
176
+
177
+ program
178
+ .name(programName)
179
+ .version(packageJson.version)
180
+ .option('-i, --info', 'print environment debug info')
181
+ .option('-c, --className <value>')
182
+ .option('-d, --defaultValue <value>')
183
+ .option('-h, --hooks <value>')
184
+ .option('-n, --configName <value>')
185
+ .option('-t, --type <value>')
186
+ .allowUnknownOption()
187
+ .on('--help', () => {
188
+ console.log('\nIn case you have any issues, please create a ticket here:');
189
+ console.log(chalk.cyan(process.env.npm_package_bugs_url));
190
+ })
191
+ .parse(process.argv);
192
+
193
+ const programOpts = program.opts();
194
+
195
+ if (programOpts.info) {
196
+ console.log(chalk.bold('\nEnvironment Info:'));
197
+ console.log(`\n current version of ${packageJson.name}: ${packageJson.version}`);
198
+ console.log(` running from ${cwd}`);
199
+
200
+ envinfo
201
+ .run({
202
+ System : ['OS', 'CPU'],
203
+ Binaries : ['Node', 'npm', 'Yarn'],
204
+ Browsers : ['Chrome', 'Edge', 'Firefox', 'Safari'],
205
+ npmPackages: ['neo.mjs']
206
+ }, {
207
+ duplicates : true,
208
+ showNotFound: true
209
+ })
210
+ .then(console.log);
211
+ } else {
212
+ console.log(chalk.green(programName));
213
+
214
+ let answers = {},
215
+ answer;
216
+
217
+ if (!programOpts.className) {
218
+ answer = await inquirer.prompt({
219
+ type : 'input',
220
+ name : 'className',
221
+ message: 'Please choose the namespace of your class:',
222
+ default: 'Covid.view.MainContainer'
223
+ });
224
+
225
+ Object.assign(answers, answer);
226
+ }
227
+
228
+ let className = programOpts.className || answers.className,
229
+ ns = className.split('.'),
230
+ root = ns.shift().toLowerCase(),
231
+ classPath = path.resolve(cwd, root === 'neo' ? 'src' : `apps/${root}`, `${ns.join('/')}.mjs`);
232
+
233
+ if (!fs.existsSync(path.resolve(classPath))) {
234
+ console.log(chalk.red(`File not found for ${className} => ${classPath}`));
235
+ process.exit(1);
236
+ }
237
+
238
+ if (!programOpts.configName) {
239
+ answer = await inquirer.prompt({
240
+ type : 'input',
241
+ name : 'configName',
242
+ message: 'Please enter a name for your class config:'
243
+ });
244
+
245
+ Object.assign(answers, answer);
246
+ }
247
+
248
+ let configName = programOpts.configName || answers.configName;
249
+
250
+ if (configName.endsWith('_')) {
251
+ configName = configName.slice(0, -1);
252
+ }
253
+
254
+ let uConfigName = capitalize(configName);
255
+
256
+ if (!programOpts.type) {
257
+ answer = await inquirer.prompt({
258
+ type : 'list',
259
+ name : 'type',
260
+ message: 'Please choose a type for your class config:',
261
+ default: 'Custom',
262
+ choices: [
263
+ 'Custom',
264
+ 'Object',
265
+ 'Object[]',
266
+ 'Number',
267
+ 'Number[]',
268
+ 'String',
269
+ 'String[]'
270
+ ]
271
+ });
272
+
273
+ Object.assign(answers, answer);
274
+ }
275
+
276
+ if (answers.type === 'Custom') {
277
+ answer = await inquirer.prompt({
278
+ type : 'input',
279
+ name : 'type',
280
+ message: 'Please enter the type for your class config:'
281
+ });
282
+
283
+ Object.assign(answers, answer);
284
+ }
285
+
286
+ if (!programOpts.defaultValue) {
287
+ answer = await inquirer.prompt({
288
+ type : 'input',
289
+ name : 'defaultValue',
290
+ message: 'Please enter a defaultValue:',
291
+ default: 'null'
292
+ });
293
+
294
+ Object.assign(answers, answer);
295
+ }
296
+
297
+ if (!programOpts.hooks) {
298
+ answer = await inquirer.prompt({
299
+ type : 'checkbox',
300
+ name : 'hooks',
301
+ message: 'Please choose the hooks for your class config:',
302
+ choices: [`afterSet${uConfigName}()`, `beforeGet${uConfigName}()`, `beforeSet${uConfigName}()`],
303
+ default: [`afterSet${uConfigName}()`]
304
+ });
305
+
306
+ Object.assign(answers, answer);
307
+ }
308
+
309
+ let defaultValue = programOpts.defaultValue || answers.defaultValue,
310
+ hooks = programOpts.hooks || answers.hooks,
311
+ type = programOpts.type || answers.type,
312
+ contentArray = fs.readFileSync(classPath).toString().split(os.EOL),
313
+ i = 0,
314
+ len = contentArray.length,
315
+ codeLine, existingConfigName, j, nextLine;
316
+
317
+ for (; i < len; i++) {
318
+ if (contentArray[i].includes('static getConfig')) {
319
+ break;
320
+ }
321
+ }
322
+
323
+ for (; i < len; i++) {
324
+ codeLine = contentArray[i];
325
+
326
+ if (codeLine.includes('}}')) {
327
+ addComma(contentArray, i - 1);
328
+ addConfig({
329
+ configName,
330
+ defaultValue,
331
+ contentArray,
332
+ index : i,
333
+ isLastConfig: true,
334
+ type
335
+ });
336
+ break;
337
+ }
338
+
339
+ if (codeLine.includes('*/')) {
340
+ nextLine = contentArray[i + 1]
341
+ existingConfigName = nextLine.substring(0, nextLine.indexOf(':')).trim();
342
+
343
+ if (existingConfigName === 'className' || existingConfigName === 'ntype') {
344
+ continue;
345
+ }
346
+
347
+ if (existingConfigName > configName) {
348
+ for (j=i; j > 0; j--) {
349
+ if (contentArray[j].includes('/**')) {
350
+ addConfig({
351
+ configName,
352
+ contentArray,
353
+ defaultValue,
354
+ index : j,
355
+ isLastConfig: false,
356
+ type
357
+ });
358
+ break;
359
+ }
360
+ }
361
+ break;
362
+ }
363
+ }
364
+ }
365
+
366
+ if (hooks.includes(`afterSet${uConfigName}()`)) {
367
+ addHook({
368
+ comment : `Triggered after the ${configName} config got changed`,
369
+ contentArray,
370
+ name : `afterSet${uConfigName}`,
371
+ oldValueParam: true,
372
+ returnValue : false,
373
+ type
374
+ });
375
+ }
376
+
377
+ if (hooks.includes(`beforeGet${uConfigName}()`)) {
378
+ addHook({
379
+ comment : `Gets triggered when accessing the value of the ${configName} config`,
380
+ contentArray,
381
+ name : `beforeGet${uConfigName}`,
382
+ oldValueParam: false,
383
+ returnValue : true,
384
+ type
385
+ });
386
+ }
387
+
388
+ if (hooks.includes(`beforeSet${uConfigName}()`)) {
389
+ addHook({
390
+ comment : `Triggered before the ${configName} config gets changed`,
391
+ contentArray,
392
+ name : `beforeSet${uConfigName}`,
393
+ oldValueParam: true,
394
+ returnValue : true,
395
+ type
396
+ });
397
+ }
398
+
399
+ fs.writeFileSync(classPath, contentArray.join(os.EOL));
400
+ }
@@ -221,16 +221,16 @@ if (programOpts.info) {
221
221
  " */",
222
222
  "class MainContainer extends Viewport {",
223
223
  " static getConfig() {return {",
224
- " /*",
224
+ " /**",
225
225
  " * @member {String} className='" + appName + ".view.MainContainer'",
226
226
  " * @protected",
227
227
  " */",
228
228
  " className: '" + appName + ".view.MainContainer',",
229
- " /*",
229
+ " /**",
230
230
  " * @member {Boolean} autoMount=true",
231
231
  " */",
232
232
  " autoMount: true,",
233
- " /*",
233
+ " /**",
234
234
  " * @member {Object[]} items",
235
235
  " */",
236
236
  " items: [{",