@nebula.js/cli-create 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/command.js +50 -0
- package/lib/create.js +193 -0
- package/package.json +31 -0
- package/templates/common/_editorconfig +15 -0
- package/templates/common/_gitignore +11 -0
- package/templates/mashup/_package.json +19 -0
- package/templates/mashup/src/configure.js +18 -0
- package/templates/mashup/src/connect.js +44 -0
- package/templates/mashup/src/index.html +36 -0
- package/templates/mashup/src/index.js +19 -0
- package/templates/sn/common/README.md +7 -0
- package/templates/sn/common/_eslintignore +3 -0
- package/templates/sn/common/_eslintrc.json +38 -0
- package/templates/sn/none/_package.json +38 -0
- package/templates/sn/none/src/data.js +3 -0
- package/templates/sn/none/src/ext.js +9 -0
- package/templates/sn/none/src/index.js +16 -0
- package/templates/sn/none/src/meta.json +5 -0
- package/templates/sn/none/src/object-properties.js +8 -0
- package/templates/sn/none/test/integration/hello.int.js +10 -0
- package/templates/sn/none/test/integration/setup.int.js +28 -0
- package/templates/sn/picasso/barchart/src/data.js +15 -0
- package/templates/sn/picasso/barchart/src/ext.js +9 -0
- package/templates/sn/picasso/barchart/src/meta.json +5 -0
- package/templates/sn/picasso/barchart/src/object-properties.js +15 -0
- package/templates/sn/picasso/barchart/src/pic-definition.js +68 -0
- package/templates/sn/picasso/barchart/test/integration/interaction.int.js +19 -0
- package/templates/sn/picasso/common/_package.json +40 -0
- package/templates/sn/picasso/common/src/index.js +81 -0
- package/templates/sn/picasso/common/src/pic-selections.js +132 -0
- package/templates/sn/picasso/common/test/integration/setup.int.js +28 -0
- package/templates/sn/picasso/minimal/src/data.js +13 -0
- package/templates/sn/picasso/minimal/src/ext.js +9 -0
- package/templates/sn/picasso/minimal/src/meta.json +5 -0
- package/templates/sn/picasso/minimal/src/object-properties.js +15 -0
- package/templates/sn/picasso/minimal/src/pic-definition.js +9 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [2.0.0-beta.1](https://github.com/qlik-oss/nebula.js/compare/v1.7.0...v2.0.0-beta.1) (2021-09-21)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
9
|
+
|
|
10
|
+
# [1.7.0](https://github.com/qlik-oss/nebula.js/compare/v1.6.0...v1.7.0) (2021-08-24)
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- adjust generation of ext and meta files on creating template ([#656](https://github.com/qlik-oss/nebula.js/issues/656)) ([c940d8b](https://github.com/qlik-oss/nebula.js/commit/c940d8b4b8a0ff0eda63c53ecfbeb0e7cf87f85e))
|
|
15
|
+
|
|
16
|
+
# [1.6.0](https://github.com/qlik-oss/nebula.js/compare/v1.5.0...v1.6.0) (2021-08-16)
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
- update mashup dependencies ([#652](https://github.com/qlik-oss/nebula.js/issues/652)) ([876f529](https://github.com/qlik-oss/nebula.js/commit/876f529c2ef14c5837e16c9e88b1444f89ad3b06))
|
|
21
|
+
|
|
22
|
+
# [1.4.0](https://github.com/qlik-oss/nebula.js/compare/v1.3.0...v1.4.0) (2021-06-28)
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
- **deps:** update dependency fs-extra to v10 ([#616](https://github.com/qlik-oss/nebula.js/issues/616)) ([73bf35f](https://github.com/qlik-oss/nebula.js/commit/73bf35f53f1882f89207a1215c35fc3c59f9c35a))
|
|
27
|
+
|
|
28
|
+
# [1.2.0](https://github.com/qlik-oss/nebula.js/compare/v1.1.1...v1.2.0) (2021-05-12)
|
|
29
|
+
|
|
30
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
31
|
+
|
|
32
|
+
## [1.1.1](https://github.com/qlik-oss/nebula.js/compare/v1.1.0...v1.1.1) (2021-04-19)
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **deps:** update dependency inquirer to v8 ([#585](https://github.com/qlik-oss/nebula.js/issues/585)) ([153b319](https://github.com/qlik-oss/nebula.js/commit/153b31964d4f3e587f8a6e9e5b09085df8d506a6))
|
|
37
|
+
|
|
38
|
+
# [1.1.0](https://github.com/qlik-oss/nebula.js/compare/v1.1.0-alpha0...v1.1.0) (2021-02-01)
|
|
39
|
+
|
|
40
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
41
|
+
|
|
42
|
+
# [1.1.0-alpha0](https://github.com/qlik-oss/nebula.js/compare/v1.0.2-alpha.1...v1.1.0-alpha0) (2021-01-21)
|
|
43
|
+
|
|
44
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
45
|
+
|
|
46
|
+
## [1.0.2-alpha.1](https://github.com/qlik-oss/nebula.js/compare/v1.0.2-alpha.0...v1.0.2-alpha.1) (2020-12-04)
|
|
47
|
+
|
|
48
|
+
### Bug Fixes
|
|
49
|
+
|
|
50
|
+
- **cli-create:** add missing / in template ([#522](https://github.com/qlik-oss/nebula.js/issues/522)) ([f0575f3](https://github.com/qlik-oss/nebula.js/commit/f0575f3))
|
|
51
|
+
|
|
52
|
+
# [1.0.0](https://github.com/qlik-oss/nebula.js/compare/v1.0.0-alpha.0...v1.0.0) (2020-06-09)
|
|
53
|
+
|
|
54
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
55
|
+
|
|
56
|
+
# [1.0.0-alpha.0](https://github.com/qlik-oss/nebula.js/compare/v0.6.1...v1.0.0-alpha.0) (2020-06-09)
|
|
57
|
+
|
|
58
|
+
### Code Refactoring
|
|
59
|
+
|
|
60
|
+
- rename public classnames ([#446](https://github.com/qlik-oss/nebula.js/issues/446)) ([c02a7ab](https://github.com/qlik-oss/nebula.js/commit/c02a7ab))
|
|
61
|
+
|
|
62
|
+
### BREAKING CHANGES
|
|
63
|
+
|
|
64
|
+
- rename nebulajs-cell to njs-cell and nebulajs-sn to njs-viz
|
|
65
|
+
|
|
66
|
+
# [0.5.0](https://github.com/qlik-oss/nebula.js/compare/v0.4.0...v0.5.0) (2020-04-20)
|
|
67
|
+
|
|
68
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
69
|
+
|
|
70
|
+
# [0.4.0](https://github.com/qlik-oss/nebula.js/compare/v0.3.0...v0.4.0) (2020-04-02)
|
|
71
|
+
|
|
72
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
|
73
|
+
|
|
74
|
+
# [0.3.0](https://github.com/qlik-oss/nebula.js/compare/v0.2.1...v0.3.0) (2020-03-30)
|
|
75
|
+
|
|
76
|
+
**Note:** Version bump only for package @nebula.js/cli-create
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-present QlikTech International AB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @nebula.js/cli-create
|
|
2
|
+
|
|
3
|
+
Scaffold a nebula visualization or mashup.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install -g @nebula.js/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### CLI
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
nebula create <name>
|
|
17
|
+
|
|
18
|
+
Create a visualization
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
nebula create mashup <name> Create a mashup
|
|
22
|
+
|
|
23
|
+
Positionals:
|
|
24
|
+
name name of the project [string] [required]
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--version Show version number [boolean]
|
|
28
|
+
--install Run package installation step [boolean] [default: true]
|
|
29
|
+
--pkgm Package manager [string] [choices: "npm", "yarn"]
|
|
30
|
+
--picasso Picasso template [string] [choices: "none", "minimal", "barchart"]
|
|
31
|
+
--author Package author [string]
|
|
32
|
+
-h, --help Show help [boolean]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
nebula create mashup <name>
|
|
37
|
+
|
|
38
|
+
Create a mashup
|
|
39
|
+
|
|
40
|
+
Positionals:
|
|
41
|
+
name name of the project [string] [required]
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
--version Show version number [boolean]
|
|
45
|
+
--install Run package installation step [boolean] [default: true]
|
|
46
|
+
--pkgm Package manager [string] [choices: "npm", "yarn"]
|
|
47
|
+
--picasso Picasso template
|
|
48
|
+
[string] [choices: "none", "minimal", "barchart"]
|
|
49
|
+
--author Package author [string]
|
|
50
|
+
-h, --help Show help [boolean]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Example
|
|
54
|
+
|
|
55
|
+
#### Create a visualization
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
nebula create hello-sunshine
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Running nebula create without --picasso prompts a selection of the available
|
|
62
|
+
options.
|
|
63
|
+
|
|
64
|
+
- none: without the picasso.js template
|
|
65
|
+
- minimal: a basic setup of picasso.js is ready
|
|
66
|
+
- barchart: a bar chart component created by picasso.js is ready
|
|
67
|
+
|
|
68
|
+
You can find the [tutorial](https://qlik.dev/tutorials/build-a-helloworld-extension-using-nebulajs) to build a basic nebula visualization using nebula.js.
|
|
69
|
+
|
|
70
|
+
#### Create a mashup
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
nebula create mashup hello-mashie
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
You can find the [tutorial](https://qlik.dev/tutorials/build-a-simple-mashup-using-nebulajs) to build a basic mashup using nebula.js.
|
|
77
|
+
|
|
78
|
+
#### Package manager
|
|
79
|
+
|
|
80
|
+
Create a nebula visualization project called `sn-table` with the npm package manager
|
|
81
|
+
instead of yarn
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
nebula create sn-table --pkgm npm
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Create a mashup called `table-mashup` with the npm package manager instead of yarn
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
nebula create mashup table-mashup --pkgm npm
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Package installation step
|
|
94
|
+
|
|
95
|
+
Create a nebula visualization project and do not install any dependencies yet
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
nebula create sn-table --install false
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Create a mashup and do not install any dependencies yet
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
nebula create mashup table-mashup --install false
|
|
105
|
+
```
|
package/command.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const create = require('./lib/create');
|
|
2
|
+
|
|
3
|
+
const mashup = {
|
|
4
|
+
command: 'mashup <name>',
|
|
5
|
+
desc: 'Create a mashup',
|
|
6
|
+
builder(yargs) {
|
|
7
|
+
yargs.positional('name', {
|
|
8
|
+
type: 'string',
|
|
9
|
+
description: 'name of the project',
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
handler(argv) {
|
|
13
|
+
create(argv);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
command: 'create <name>',
|
|
19
|
+
desc: 'Create a visualization',
|
|
20
|
+
builder(yargs) {
|
|
21
|
+
yargs.command(mashup);
|
|
22
|
+
|
|
23
|
+
yargs.positional('name', {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'name of the project',
|
|
26
|
+
});
|
|
27
|
+
yargs.option('install', {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
default: true,
|
|
30
|
+
description: 'Run package installation step',
|
|
31
|
+
});
|
|
32
|
+
yargs.option('pkgm', {
|
|
33
|
+
type: 'string',
|
|
34
|
+
choices: ['npm', 'yarn'],
|
|
35
|
+
description: 'Package manager',
|
|
36
|
+
});
|
|
37
|
+
yargs.option('picasso', {
|
|
38
|
+
type: 'string',
|
|
39
|
+
choices: ['none', 'minimal', 'barchart'],
|
|
40
|
+
description: 'Picasso template',
|
|
41
|
+
});
|
|
42
|
+
yargs.option('author', {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Package author',
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
handler(argv) {
|
|
48
|
+
create(argv);
|
|
49
|
+
},
|
|
50
|
+
};
|
package/lib/create.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const ejs = require('ejs');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const pkg = require('../package.json');
|
|
11
|
+
|
|
12
|
+
const hasYarn = () => {
|
|
13
|
+
try {
|
|
14
|
+
execSync('yarnpkg --version', { stdio: 'ignore' });
|
|
15
|
+
return true;
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const author = async (cwd) => {
|
|
22
|
+
try {
|
|
23
|
+
const email = execSync('git config --get user.email', { cwd }).toString().trim();
|
|
24
|
+
const name = execSync('git config --get user.name', { cwd }).toString().trim();
|
|
25
|
+
return {
|
|
26
|
+
email,
|
|
27
|
+
name,
|
|
28
|
+
};
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return {
|
|
31
|
+
email: '',
|
|
32
|
+
name: '',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const parseAuthor = (str = '') => {
|
|
38
|
+
const m = str.match(/([^<(]+)?(<([^\s]+)>)?/);
|
|
39
|
+
return {
|
|
40
|
+
name: m && m[1] ? m[1].trim() : '',
|
|
41
|
+
email: m && m[3] ? m[3].trim() : '',
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function copyFactory(root, destination) {
|
|
46
|
+
return (source, target, data) => {
|
|
47
|
+
if (data) {
|
|
48
|
+
const content = fs.readFileSync(path.resolve(root, ...source.split('/')), { encoding: 'utf8' });
|
|
49
|
+
const rendered = ejs.render(content, data);
|
|
50
|
+
fs.writeFileSync(path.resolve(destination, ...target.split('/')), rendered);
|
|
51
|
+
} else {
|
|
52
|
+
fse.copyFileSync(path.resolve(root, ...source.split('/')), path.resolve(destination, ...target.split('/')));
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const create = async (argv) => {
|
|
58
|
+
const { name } = argv;
|
|
59
|
+
|
|
60
|
+
const isMashup = argv._.includes('mashup');
|
|
61
|
+
|
|
62
|
+
const projectFolder = name;
|
|
63
|
+
const packageName = name.split('/').slice(-1)[0];
|
|
64
|
+
|
|
65
|
+
const cwd = process.cwd();
|
|
66
|
+
const templatesRoot = path.resolve(__dirname, '..', 'templates');
|
|
67
|
+
const destination = path.resolve(cwd, projectFolder);
|
|
68
|
+
|
|
69
|
+
let options = {
|
|
70
|
+
install: true,
|
|
71
|
+
...argv,
|
|
72
|
+
pkgm: argv.pkgm || ((await hasYarn()) ? 'yarn' : 'npm'),
|
|
73
|
+
author: argv.author ? parseAuthor(argv.author) : await author(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const results = {};
|
|
77
|
+
|
|
78
|
+
if (await fse.exists(destination)) {
|
|
79
|
+
console.error(chalk.red(`Oopsie, looks like '${projectFolder}' already exists. Try a different name.`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const prompt = async () => {
|
|
84
|
+
const answers = await inquirer.prompt([
|
|
85
|
+
{
|
|
86
|
+
type: 'list',
|
|
87
|
+
name: 'picasso',
|
|
88
|
+
message: 'Pick a picasso template',
|
|
89
|
+
default: 'none',
|
|
90
|
+
choices: ['none', 'minimal', 'barchart'],
|
|
91
|
+
when: !isMashup && !argv.picasso,
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
options = { ...options, ...answers };
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const write = async () => {
|
|
99
|
+
console.log('\n');
|
|
100
|
+
console.log('> Generating files...');
|
|
101
|
+
const { picasso } = options;
|
|
102
|
+
fse.ensureDirSync(destination);
|
|
103
|
+
|
|
104
|
+
const copy = copyFactory(templatesRoot, destination);
|
|
105
|
+
|
|
106
|
+
// ==== template files ====
|
|
107
|
+
const folders = ['common'];
|
|
108
|
+
if (isMashup) {
|
|
109
|
+
folders.push('mashup');
|
|
110
|
+
} else {
|
|
111
|
+
folders.push('sn/common');
|
|
112
|
+
if (picasso !== 'none') {
|
|
113
|
+
folders.push('sn/picasso/common');
|
|
114
|
+
folders.push(`sn/picasso/${picasso}`);
|
|
115
|
+
} else {
|
|
116
|
+
folders.push('sn/none');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = {
|
|
121
|
+
name: packageName,
|
|
122
|
+
description: '',
|
|
123
|
+
user: options.author.name,
|
|
124
|
+
email: options.author.email,
|
|
125
|
+
nebulaVersion: pkg.version,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const traverse = (sourceFolder, targetFolder = '') => {
|
|
129
|
+
const files = fs.readdirSync(path.resolve(templatesRoot, sourceFolder));
|
|
130
|
+
|
|
131
|
+
files.forEach((file) => {
|
|
132
|
+
const p = `${sourceFolder}/${file}`;
|
|
133
|
+
const stats = fs.lstatSync(path.resolve(templatesRoot, p));
|
|
134
|
+
const next = `${targetFolder}/${file}`.replace(/^\//, '');
|
|
135
|
+
if (stats.isDirectory()) {
|
|
136
|
+
fse.ensureDirSync(path.resolve(destination, next));
|
|
137
|
+
traverse(p, next);
|
|
138
|
+
} else if (file[0] === '_') {
|
|
139
|
+
const newFileName = file === '_package.json' ? 'package.json' : `.${file.substr(1)}`;
|
|
140
|
+
copy(`${sourceFolder}/${file}`, `${targetFolder}/${newFileName}`.replace(/^\//, ''), data);
|
|
141
|
+
} else {
|
|
142
|
+
copy(`${sourceFolder}/${file}`, next, data);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
folders.forEach((folder) => {
|
|
148
|
+
traverse(folder);
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const install = async () => {
|
|
153
|
+
if (options.install !== false) {
|
|
154
|
+
console.log('> Installing dependencies...');
|
|
155
|
+
console.log('\n');
|
|
156
|
+
const command = `${options.pkgm} install`;
|
|
157
|
+
try {
|
|
158
|
+
execSync(command, {
|
|
159
|
+
cwd: destination,
|
|
160
|
+
stdio: 'inherit',
|
|
161
|
+
});
|
|
162
|
+
console.log('\n');
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.log('\n');
|
|
165
|
+
console.log(`> Something went wrong when running ${chalk.yellow(command)}, try running the command yourself.`);
|
|
166
|
+
results.fail = true;
|
|
167
|
+
results.failedInstall = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const end = async () => {
|
|
173
|
+
const p = options.pkgm;
|
|
174
|
+
if (!results.fail) {
|
|
175
|
+
console.log(`> Successfully created project ${chalk.yellow(options.name)}`);
|
|
176
|
+
}
|
|
177
|
+
console.log('> Get started with the following commands:');
|
|
178
|
+
console.log('\n');
|
|
179
|
+
console.log(chalk.cyan(` cd ${projectFolder}`));
|
|
180
|
+
if (options.install === false || results.failedInstall) {
|
|
181
|
+
console.log(chalk.cyan(` ${p} install`));
|
|
182
|
+
}
|
|
183
|
+
console.log(chalk.cyan(` ${p} run start`));
|
|
184
|
+
console.log('\n');
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await prompt();
|
|
188
|
+
await write();
|
|
189
|
+
await install();
|
|
190
|
+
await end();
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
module.exports = create;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nebula.js/cli-create",
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "QlikTech International AB",
|
|
7
|
+
"keywords": [],
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"main": "lib/create.js",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/qlik-oss/nebula.js.git"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"command.js",
|
|
18
|
+
"templates",
|
|
19
|
+
"lib"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"generate": "yo ./generator/index.js"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"chalk": "4.1.2",
|
|
26
|
+
"ejs": "3.1.6",
|
|
27
|
+
"fs-extra": "10.0.0",
|
|
28
|
+
"inquirer": "8.1.2"
|
|
29
|
+
},
|
|
30
|
+
"gitHead": "851bf623f70a49f07615fe3b7e37b8b44fad534f"
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# http://editorconfig.org
|
|
2
|
+
|
|
3
|
+
root = true
|
|
4
|
+
|
|
5
|
+
[*]
|
|
6
|
+
charset = utf-8
|
|
7
|
+
indent_style = space
|
|
8
|
+
indent_size = 2
|
|
9
|
+
end_of_line = lf
|
|
10
|
+
insert_final_newline = true
|
|
11
|
+
trim_trailing_whitespace = true
|
|
12
|
+
|
|
13
|
+
[*.md]
|
|
14
|
+
insert_final_newline = false
|
|
15
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "<%= user %>",
|
|
6
|
+
"email": "<%= email %>"
|
|
7
|
+
},
|
|
8
|
+
"keywords": ["qlik", "nebula"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "parcel src/index.html --open",
|
|
11
|
+
"build": "parcel build src/index.html --dist-dir ./dist"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@nebula.js/stardust": "<%= nebulaVersion %>",
|
|
15
|
+
"@nebula.js/sn-bar-chart": "^0.8.24",
|
|
16
|
+
"enigma.js": "^2.6.3",
|
|
17
|
+
"parcel": "^2.0.0-rc.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { embed } from '@nebula.js/stardust';
|
|
2
|
+
|
|
3
|
+
import barchart from '@nebula.js/sn-bar-chart';
|
|
4
|
+
|
|
5
|
+
const n = embed.createConfiguration({
|
|
6
|
+
context: {
|
|
7
|
+
theme: 'light',
|
|
8
|
+
language: 'en-US',
|
|
9
|
+
},
|
|
10
|
+
types: [
|
|
11
|
+
{
|
|
12
|
+
name: 'barchart',
|
|
13
|
+
load: () => Promise.resolve(barchart),
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default n;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import enigma from 'enigma.js';
|
|
2
|
+
import schema from 'enigma.js/schemas/12.170.2.json';
|
|
3
|
+
|
|
4
|
+
async function getQCSHeaders({ webIntegrationId, url }) {
|
|
5
|
+
const response = await fetch(`${url}/api/v1/csrf-token`, {
|
|
6
|
+
credentials: 'include',
|
|
7
|
+
headers: { 'qlik-web-integration-id': webIntegrationId },
|
|
8
|
+
});
|
|
9
|
+
if (response.status === 401) {
|
|
10
|
+
const loginUrl = new URL(`${url}/login`);
|
|
11
|
+
loginUrl.searchParams.append('returnto', window.location.href);
|
|
12
|
+
loginUrl.searchParams.append('qlik-web-integration-id', webIntegrationId);
|
|
13
|
+
window.location.href = loginUrl;
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const csrfToken = new Map(response.headers).get('qlik-csrf-token');
|
|
17
|
+
return {
|
|
18
|
+
'qlik-web-integration-id': webIntegrationId,
|
|
19
|
+
'qlik-csrf-token': csrfToken,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function getEnigmaApp({ host, appId, headers }) {
|
|
24
|
+
const params = Object.keys(headers)
|
|
25
|
+
.map((key) => `${key}=${headers[key]}`)
|
|
26
|
+
.join('&');
|
|
27
|
+
|
|
28
|
+
const enigmaGlobal = await enigma
|
|
29
|
+
.create({
|
|
30
|
+
schema,
|
|
31
|
+
url: `wss://${host}/app/${appId}?${params}`,
|
|
32
|
+
})
|
|
33
|
+
.open();
|
|
34
|
+
|
|
35
|
+
return enigmaGlobal.openDoc(appId);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function connect({ url, webIntegrationId, appId }) {
|
|
39
|
+
const host = url.replace(/^https?:\/\//, '').replace(/\/?/, '');
|
|
40
|
+
const headers = await getQCSHeaders({ url, webIntegrationId });
|
|
41
|
+
return getEnigmaApp({ host, headers, appId });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default connect;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Nebula mashup</title>
|
|
6
|
+
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
background: #eee;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.content {
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
width: 80%;
|
|
15
|
+
max-width: 800px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.object {
|
|
19
|
+
position: relative;
|
|
20
|
+
height: 600px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.toolbar {
|
|
24
|
+
margin: 12px 0px;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div class="content">
|
|
30
|
+
<div class="toolbar"></div>
|
|
31
|
+
<div class="object"></div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<script src="./index.js" type="module"></script>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import embed from './configure';
|
|
3
|
+
import connect from './connect';
|
|
4
|
+
|
|
5
|
+
async function run() {
|
|
6
|
+
const app = await connect({
|
|
7
|
+
url: '<URL>',
|
|
8
|
+
webIntegrationId: '<Qlik web integration id>',
|
|
9
|
+
appId: '<App id>',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const n = embed(app);
|
|
13
|
+
|
|
14
|
+
(await n.selections()).mount(document.querySelector('.toolbar'));
|
|
15
|
+
|
|
16
|
+
// n.render({});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
run();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"env": {
|
|
4
|
+
"browser": true
|
|
5
|
+
},
|
|
6
|
+
"parserOptions": {
|
|
7
|
+
"sourceType": "module"
|
|
8
|
+
},
|
|
9
|
+
"extends": ["airbnb-base"],
|
|
10
|
+
"rules": {
|
|
11
|
+
"max-len": 0,
|
|
12
|
+
"no-plusplus": 0,
|
|
13
|
+
"no-bitwise": 0,
|
|
14
|
+
"no-unused-expressions": 0,
|
|
15
|
+
"import/no-extraneous-dependencies": [2, { "devDependencies": true }]
|
|
16
|
+
},
|
|
17
|
+
"overrides": [
|
|
18
|
+
{
|
|
19
|
+
"files": ["**/*.spec.js"],
|
|
20
|
+
"env": {
|
|
21
|
+
"browser": false,
|
|
22
|
+
"node": true,
|
|
23
|
+
"mocha": true
|
|
24
|
+
},
|
|
25
|
+
"globals": {
|
|
26
|
+
"chai": false,
|
|
27
|
+
"expect": false,
|
|
28
|
+
"sinon": false,
|
|
29
|
+
"aw": false,
|
|
30
|
+
"page": false
|
|
31
|
+
},
|
|
32
|
+
"plugins": ["mocha"],
|
|
33
|
+
"rules": {
|
|
34
|
+
"mocha/no-exclusive-tests": "error"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<%= description %>",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "<%= user %>",
|
|
8
|
+
"email": "<%= email %>"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["qlik", "nebula", "stardust"],
|
|
11
|
+
"files": ["dist"],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=8"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/<%= name %>.js",
|
|
16
|
+
"module": "dist/<%= name %>.esm.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "nebula build",
|
|
19
|
+
"lint": "eslint src",
|
|
20
|
+
"start": "nebula serve",
|
|
21
|
+
"sense": "nebula sense --ext src/ext.js --meta src/meta.json",
|
|
22
|
+
"test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js'"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@after-work.js/aw": "6.0.10",
|
|
26
|
+
"@nebula.js/cli": "<%= nebulaVersion %>",
|
|
27
|
+
"@nebula.js/cli-build": "<%= nebulaVersion %>",
|
|
28
|
+
"@nebula.js/cli-serve": "<%= nebulaVersion %>",
|
|
29
|
+
"@nebula.js/cli-sense": "<%= nebulaVersion %>",
|
|
30
|
+
"eslint": "7.27.0",
|
|
31
|
+
"eslint-config-airbnb-base": "14.2.1",
|
|
32
|
+
"eslint-plugin-import": "2.23.4",
|
|
33
|
+
"eslint-plugin-mocha": "9.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@nebula.js/stardust": "^1.x"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useElement } from '@nebula.js/stardust';
|
|
2
|
+
import properties from './object-properties';
|
|
3
|
+
import data from './data';
|
|
4
|
+
|
|
5
|
+
export default function supernova() {
|
|
6
|
+
return {
|
|
7
|
+
qae: {
|
|
8
|
+
properties,
|
|
9
|
+
data,
|
|
10
|
+
},
|
|
11
|
+
component() {
|
|
12
|
+
const element = useElement();
|
|
13
|
+
element.innerHTML = '<div>Hello!</div>'; // eslint-disable-line
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
describe('sn', () => {
|
|
2
|
+
const content = '.njs-viz[data-render-count="1"]';
|
|
3
|
+
it('should say hello', async () => {
|
|
4
|
+
const app = encodeURIComponent(process.env.APP_ID || '/apps/ctrl00.qvf');
|
|
5
|
+
await page.goto(`${process.env.BASE_URL}/render/?app=${app}`);
|
|
6
|
+
await page.waitForSelector(content, { visible: true });
|
|
7
|
+
const text = await page.$eval(content, (el) => el.textContent);
|
|
8
|
+
expect(text).to.equal('Hello!');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const serve = require('@nebula.js/cli-serve'); // eslint-disable-line
|
|
2
|
+
|
|
3
|
+
page.on('pageerror', (e) => {
|
|
4
|
+
console.error('Web: ', e.message);
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
page.on('console', (msg) => {
|
|
8
|
+
for (let i = 0; i < msg.args().length; ++i) {
|
|
9
|
+
console.log(`console ${msg.text()}`);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!process.env.BASE_URL) {
|
|
14
|
+
let s;
|
|
15
|
+
|
|
16
|
+
before(async function setup() {
|
|
17
|
+
this.timeout(15000);
|
|
18
|
+
s = await serve({
|
|
19
|
+
open: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
process.env.BASE_URL = s.url;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
after(() => {
|
|
26
|
+
s.close();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const properties = {
|
|
2
|
+
qHyperCubeDef: {
|
|
3
|
+
qDimensions: [],
|
|
4
|
+
qMeasures: [],
|
|
5
|
+
qInitialDataFetch: [{ qWidth: 2, qHeight: 5000 }],
|
|
6
|
+
qSuppressZero: false,
|
|
7
|
+
qSuppressMissing: true,
|
|
8
|
+
},
|
|
9
|
+
showTitles: true,
|
|
10
|
+
title: '',
|
|
11
|
+
subtitle: '',
|
|
12
|
+
footnote: '',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default properties;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export default function picassoDefinition({ layout, constraints }) {
|
|
2
|
+
if (!layout.qHyperCube) {
|
|
3
|
+
throw new Error('Layout is missing a hypercube');
|
|
4
|
+
}
|
|
5
|
+
return {
|
|
6
|
+
scales: {
|
|
7
|
+
x: { data: { extract: { field: 'qDimensionInfo/0' } } },
|
|
8
|
+
y: {
|
|
9
|
+
data: { field: 'qMeasureInfo/0' },
|
|
10
|
+
expand: 0.2,
|
|
11
|
+
include: [0],
|
|
12
|
+
invert: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
components: [
|
|
16
|
+
{
|
|
17
|
+
type: 'axis',
|
|
18
|
+
dock: 'left',
|
|
19
|
+
scale: 'y',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'axis',
|
|
23
|
+
dock: 'bottom',
|
|
24
|
+
scale: 'x',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'box',
|
|
28
|
+
data: {
|
|
29
|
+
extract: {
|
|
30
|
+
field: 'qDimensionInfo/0',
|
|
31
|
+
props: {
|
|
32
|
+
start: 0,
|
|
33
|
+
end: { field: 'qMeasureInfo/0' },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
settings: {
|
|
38
|
+
major: { scale: 'x' },
|
|
39
|
+
minor: { scale: 'y' },
|
|
40
|
+
box: {
|
|
41
|
+
width: 0.7,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
brush:
|
|
45
|
+
!constraints.active && !constraints.select
|
|
46
|
+
? {
|
|
47
|
+
trigger: [
|
|
48
|
+
{
|
|
49
|
+
contexts: ['selection'],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
consume: [
|
|
53
|
+
{
|
|
54
|
+
context: 'selection',
|
|
55
|
+
data: ['', 'end'],
|
|
56
|
+
style: {
|
|
57
|
+
inactive: {
|
|
58
|
+
opacity: 0.3,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
}
|
|
64
|
+
: {},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
describe('interaction', () => {
|
|
2
|
+
const content = '.njs-viz[data-render-count="1"]';
|
|
3
|
+
it('should select two bars', async () => {
|
|
4
|
+
const app = encodeURIComponent(process.env.APP_ID || '/apps/ctrl00.qvf');
|
|
5
|
+
await page.goto(`${process.env.BASE_URL}/render/?app=${app}&cols=Alpha,=5+avg(Expression1)`);
|
|
6
|
+
await page.waitForSelector(content, { visible: true });
|
|
7
|
+
|
|
8
|
+
await page.click('rect[data-label="K"]');
|
|
9
|
+
await page.click('rect[data-label="S"]');
|
|
10
|
+
|
|
11
|
+
await page.waitForSelector('button[title="Confirm selection"]:not([disabled])', { visible: true });
|
|
12
|
+
await page.click('button[title="Confirm selection"]');
|
|
13
|
+
|
|
14
|
+
await page.waitForFunction((selector) => document.querySelectorAll(selector).length === 2, {}, 'rect[data-label]');
|
|
15
|
+
|
|
16
|
+
const rects = await page.$$eval('rect[data-label]', (sel) => sel.map((r) => r.getAttribute('data-label')));
|
|
17
|
+
expect(rects).to.eql(['K', 'S']);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= name %>",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<%= description %>",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "<%= user %>",
|
|
8
|
+
"email": "<%= email %>"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["qlik", "nebula", "stardust"],
|
|
11
|
+
"files": ["dist"],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=8"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/<%= name %>.js",
|
|
16
|
+
"module": "dist/<%= name %>.esm.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "nebula build",
|
|
19
|
+
"lint": "eslint src",
|
|
20
|
+
"start": "nebula serve",
|
|
21
|
+
"sense": "nebula sense",
|
|
22
|
+
"test:integration": "aw puppet --testExt '*.int.js' --glob 'test/integration/**/*.int.js'"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@after-work.js/aw": "6.0.14",
|
|
26
|
+
"@nebula.js/cli": "<%= nebulaVersion %>",
|
|
27
|
+
"@nebula.js/cli-build": "<%= nebulaVersion %>",
|
|
28
|
+
"@nebula.js/cli-serve": "<%= nebulaVersion %>",
|
|
29
|
+
"@nebula.js/cli-sense": "<%= nebulaVersion %>",
|
|
30
|
+
"eslint": "7.27.0",
|
|
31
|
+
"eslint-config-airbnb-base": "14.2.1",
|
|
32
|
+
"eslint-plugin-import": "2.23.4",
|
|
33
|
+
"eslint-plugin-mocha": "9.0.0",
|
|
34
|
+
"picasso.js": "1.2.1",
|
|
35
|
+
"picasso-plugin-q": "1.2.1"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@nebula.js/stardust": "^1.x"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useElement,
|
|
3
|
+
useState,
|
|
4
|
+
useStaleLayout,
|
|
5
|
+
useSelections,
|
|
6
|
+
useRect,
|
|
7
|
+
useEffect,
|
|
8
|
+
useConstraints,
|
|
9
|
+
} from '@nebula.js/stardust';
|
|
10
|
+
import picassojs from 'picasso.js';
|
|
11
|
+
import picassoQ from 'picasso-plugin-q';
|
|
12
|
+
|
|
13
|
+
import properties from './object-properties';
|
|
14
|
+
import data from './data';
|
|
15
|
+
import picSelections from './pic-selections';
|
|
16
|
+
import definition from './pic-definition';
|
|
17
|
+
|
|
18
|
+
export default function supernova(/* env */) {
|
|
19
|
+
const picasso = picassojs();
|
|
20
|
+
picasso.use(picassoQ);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
qae: {
|
|
24
|
+
properties,
|
|
25
|
+
data,
|
|
26
|
+
},
|
|
27
|
+
component() {
|
|
28
|
+
const element = useElement();
|
|
29
|
+
const selections = useSelections();
|
|
30
|
+
const layout = useStaleLayout();
|
|
31
|
+
const rect = useRect();
|
|
32
|
+
const constraints = useConstraints();
|
|
33
|
+
|
|
34
|
+
const [instance, setInstance] = useState();
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const p = picasso.chart({
|
|
38
|
+
element,
|
|
39
|
+
data: [],
|
|
40
|
+
settings: {},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const s = picSelections({
|
|
44
|
+
selections,
|
|
45
|
+
brush: p.brush('selection'),
|
|
46
|
+
picassoQ,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
setInstance(p);
|
|
50
|
+
|
|
51
|
+
return () => {
|
|
52
|
+
s.release();
|
|
53
|
+
p.destroy();
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!instance) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
instance.update({
|
|
62
|
+
data: [
|
|
63
|
+
{
|
|
64
|
+
type: 'q',
|
|
65
|
+
key: 'qHyperCube',
|
|
66
|
+
data: layout.qHyperCube,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
settings: definition({ layout, constraints }),
|
|
70
|
+
});
|
|
71
|
+
}, [layout, instance]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!instance) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
instance.update();
|
|
78
|
+
}, [rect.width, rect.height, instance]);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* eslint no-param-reassign: 0 */
|
|
2
|
+
|
|
3
|
+
// --- enable keyboard accessibility ---
|
|
4
|
+
// pressing enter (escape) key should confirm (cancel) selections
|
|
5
|
+
const KEYS = {
|
|
6
|
+
ENTER: 'Enter',
|
|
7
|
+
ESCAPE: 'Escape',
|
|
8
|
+
IE11_ESC: 'Esc',
|
|
9
|
+
SHIFT: 'Shift',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const instances = [];
|
|
13
|
+
let expando = 0;
|
|
14
|
+
const confirmOrCancelSelection = (e) => {
|
|
15
|
+
const active = instances.filter((a) => a.selections && a.selections.isActive());
|
|
16
|
+
if (!active.length) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (e.key === KEYS.ENTER) {
|
|
20
|
+
active.forEach((a) => a.selections.confirm());
|
|
21
|
+
} else if (e.key === KEYS.ESCAPE || e.key === KEYS.IE11_ESC) {
|
|
22
|
+
active.forEach((a) => a.selections.cancel());
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const setup = () => {
|
|
27
|
+
document.addEventListener('keyup', confirmOrCancelSelection);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const teardown = () => {
|
|
31
|
+
document.removeEventListener('keyup', confirmOrCancelSelection);
|
|
32
|
+
};
|
|
33
|
+
// ------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
const addListeners = (emitter, listeners) => {
|
|
36
|
+
Object.keys(listeners).forEach((type) => {
|
|
37
|
+
emitter.on(type, listeners[type]);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const removeListeners = (emitter, listeners) => {
|
|
42
|
+
Object.keys(listeners).forEach((type) => {
|
|
43
|
+
emitter.removeListener(type, listeners[type]);
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default function ({ selections, brush, picassoQ } = {}, { path = '/qHyperCubeDef' } = {}) {
|
|
48
|
+
if (!selections) {
|
|
49
|
+
return {
|
|
50
|
+
layout: () => {},
|
|
51
|
+
release: () => {},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const key = ++expando;
|
|
55
|
+
|
|
56
|
+
let layout = null;
|
|
57
|
+
|
|
58
|
+
// interceptors primary job is to ensure selections only occur on either values OR ranges
|
|
59
|
+
const valueInterceptor = (added) => {
|
|
60
|
+
const brushes = brush.brushes();
|
|
61
|
+
brushes.forEach((b) => {
|
|
62
|
+
if (b.type === 'range') {
|
|
63
|
+
// has range selections
|
|
64
|
+
brush.clear([]);
|
|
65
|
+
} else if (added[0] && added[0].key !== b.id) {
|
|
66
|
+
// has selections in another dimension
|
|
67
|
+
brush.clear([]);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return added.filter((t) => t.value !== -2); // do not allow selection on null value
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const rangeInterceptor = (a) => {
|
|
74
|
+
const v = brush.brushes().filter((b) => b.type === 'value');
|
|
75
|
+
if (v.length) {
|
|
76
|
+
// has dimension values selected
|
|
77
|
+
brush.clear([]);
|
|
78
|
+
return a;
|
|
79
|
+
}
|
|
80
|
+
return a;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
brush.intercept('set-ranges', rangeInterceptor);
|
|
84
|
+
brush.intercept('toggle-ranges', rangeInterceptor);
|
|
85
|
+
|
|
86
|
+
brush.intercept('toggle-values', valueInterceptor);
|
|
87
|
+
brush.intercept('set-values', valueInterceptor);
|
|
88
|
+
brush.intercept('add-values', valueInterceptor);
|
|
89
|
+
|
|
90
|
+
brush.on('start', () => selections.begin(path));
|
|
91
|
+
|
|
92
|
+
const selectionListeners = {
|
|
93
|
+
activate: () => {
|
|
94
|
+
// TODO - check if we can select in the current chart,
|
|
95
|
+
},
|
|
96
|
+
deactivated: () => brush.end(),
|
|
97
|
+
cleared: () => brush.clear(),
|
|
98
|
+
canceled: () => brush.end(),
|
|
99
|
+
};
|
|
100
|
+
addListeners(selections, selectionListeners);
|
|
101
|
+
|
|
102
|
+
brush.on('update', () => {
|
|
103
|
+
const generated = picassoQ.selections(brush, {}, layout);
|
|
104
|
+
generated.forEach((s) => selections.select(s));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (instances.length === 0) {
|
|
108
|
+
setup();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
instances.push({
|
|
112
|
+
key,
|
|
113
|
+
selections,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
layout: (lt) => {
|
|
118
|
+
layout = lt;
|
|
119
|
+
},
|
|
120
|
+
release: () => {
|
|
121
|
+
layout = null;
|
|
122
|
+
const idx = instances.indexOf(instances.filter((i) => i.key === key)[0]);
|
|
123
|
+
if (idx !== -1) {
|
|
124
|
+
instances.splice(idx, 1);
|
|
125
|
+
}
|
|
126
|
+
if (!instances.length) {
|
|
127
|
+
teardown();
|
|
128
|
+
}
|
|
129
|
+
removeListeners(selections, selectionListeners);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const serve = require('@nebula.js/cli-serve'); // eslint-disable-line
|
|
2
|
+
|
|
3
|
+
page.on('pageerror', (e) => {
|
|
4
|
+
console.error('Web: ', e.message);
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
page.on('console', (msg) => {
|
|
8
|
+
for (let i = 0; i < msg.args().length; ++i) {
|
|
9
|
+
console.log(`console ${msg.text()}`);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!process.env.BASE_URL) {
|
|
14
|
+
let s;
|
|
15
|
+
|
|
16
|
+
before(async function setup() {
|
|
17
|
+
this.timeout(15000); // to allow time for the server to start
|
|
18
|
+
s = await serve({
|
|
19
|
+
build: false,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
process.env.BASE_URL = s.url;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
after(() => {
|
|
26
|
+
s.close();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const properties = {
|
|
2
|
+
qHyperCubeDef: {
|
|
3
|
+
qDimensions: [],
|
|
4
|
+
qMeasures: [],
|
|
5
|
+
qInitialDataFetch: [{ qWidth: 10, qHeight: 500 }],
|
|
6
|
+
qSuppressZero: false,
|
|
7
|
+
qSuppressMissing: true,
|
|
8
|
+
},
|
|
9
|
+
showTitles: true,
|
|
10
|
+
title: '',
|
|
11
|
+
subtitle: '',
|
|
12
|
+
footnote: '',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default properties;
|