Compare commits
67 Commits
project-co
...
0.30.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4441ef683 | ||
|
|
3a832e7d6a | ||
|
|
22da42de9f | ||
|
|
fddf286a8e | ||
|
|
e6692b8779 | ||
|
|
4341fdb5db | ||
|
|
d02852b2ed | ||
|
|
25f6593f35 | ||
|
|
76a22121a9 | ||
|
|
d4154fde09 | ||
|
|
d65b619546 | ||
|
|
d15b246db4 | ||
|
|
fc8e9b0ba4 | ||
|
|
b443deca27 | ||
|
|
8a24c3ee92 | ||
|
|
464d137095 | ||
|
|
903a2caa1d | ||
|
|
d68b2ed863 | ||
|
|
f87bc3ea88 | ||
|
|
b7753143fd | ||
|
|
8f239687f3 | ||
|
|
d970d05711 | ||
|
|
192f4a8597 | ||
|
|
6eff5e1fd0 | ||
|
|
ad63832d18 | ||
|
|
28b3aeb676 | ||
|
|
321c71825e | ||
|
|
6433daee95 | ||
|
|
d210ad3908 | ||
|
|
f461493b0f | ||
|
|
4312078951 | ||
|
|
7c53f8811b | ||
|
|
51ea042580 | ||
|
|
f9242dff27 | ||
|
|
50fc4289e0 | ||
|
|
71944b4a9e | ||
|
|
401944e837 | ||
|
|
67b1e97951 | ||
|
|
8079887f10 | ||
|
|
43c5856986 | ||
|
|
ec98314793 | ||
|
|
0953911571 | ||
|
|
0a673631b2 | ||
|
|
b1ade90e8b | ||
|
|
4fa54e3fb2 | ||
|
|
ba89c2108f | ||
|
|
e9a28e4f0b | ||
|
|
c088774e75 | ||
|
|
7d6bd5696a | ||
|
|
dd30f94b34 | ||
|
|
2eef3679c7 | ||
|
|
7518b6c9ce | ||
|
|
11f2bad2bd | ||
|
|
06a28a2ae7 | ||
|
|
5c84aeb8ee | ||
|
|
fe1c3d90e2 | ||
|
|
78a441036c | ||
|
|
7cba160828 | ||
|
|
15be850423 | ||
|
|
6087cb5619 | ||
|
|
2af87e2d42 | ||
|
|
925fcb8824 | ||
|
|
c7ee067b95 | ||
|
|
68bdaa7411 | ||
|
|
9bd45c2f9d | ||
|
|
83326a4c4c | ||
|
|
54ed377394 |
@@ -19,8 +19,13 @@ jobs:
|
||||
name: Install dependencies
|
||||
command: npm install
|
||||
- run:
|
||||
name: Run tests
|
||||
command: npm run test-once
|
||||
name: BlackDuck scan
|
||||
command: curl -s https://detect.synopsys.com/detect.sh | bash -s -- \
|
||||
--blackduck.url="https://qliktech.blackducksoftware.com" \
|
||||
--blackduck.trust.cert=true \
|
||||
--blackduck.username="svc-blackduck" \
|
||||
--blackduck.password=${svc_blackduck} \
|
||||
--detect.project.name="viz-bundle-qlik-network-chart"
|
||||
|
||||
bump-version:
|
||||
<<: *defaults
|
||||
@@ -48,16 +53,18 @@ jobs:
|
||||
command: |
|
||||
export VERSION=$(scripts/get-bumped-version.sh)
|
||||
echo "Version: ${VERSION}"
|
||||
npm run build
|
||||
npm run build:zip
|
||||
sudo chmod +x scripts/verify-files.sh
|
||||
scripts/verify-files.sh
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
- persist_to_workspace:
|
||||
root: ~/qlik-network-chart
|
||||
paths:
|
||||
- build
|
||||
- dist
|
||||
- store_artifacts:
|
||||
path: build
|
||||
destination: build
|
||||
path: dist
|
||||
destination: dist
|
||||
deploy:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@@ -92,4 +99,3 @@ workflows:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- project-configuration
|
||||
|
||||
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
node_modules/
|
||||
build/
|
||||
dist/
|
||||
BUMPED_VERSION
|
||||
|
||||
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018-present QlikTech International AB
|
||||
Copyright (c) 2016 Michael Laenen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
24
LICENSE.md
@@ -1,24 +0,0 @@
|
||||
**2 Dimensional Heatmap** Visualization Extension for Qlik Sense is licensed under the "MIT" license:
|
||||
|
||||
>
|
||||
> The MIT License (MIT)
|
||||
>
|
||||
> Copyright (c) 2016 Michael Laenen
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in all
|
||||
> copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
> SOFTWARE.
|
||||
BIN
assets/network.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
91
gulpfile.js
@@ -1,20 +1,61 @@
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var zip = require('gulp-zip');
|
||||
var del = require('del');
|
||||
var settings = require('./settings');
|
||||
var webpackConfig = require('./webpack.config');
|
||||
var webpack = require('webpack');
|
||||
var WebpackDevServer = require('webpack-dev-server');
|
||||
var jeditor = require("gulp-json-editor");
|
||||
var pkg = require('./package.json');
|
||||
|
||||
gulp.task('remove-build-folder', function(){
|
||||
return del([settings.buildDestination], { force: true });
|
||||
var DIST = './dist';
|
||||
var VERSION = process.env.VERSION || 'local-dev';
|
||||
|
||||
gulp.task('qext', function () {
|
||||
var qext = {
|
||||
name: 'Network chart',
|
||||
type: 'visualization',
|
||||
description: pkg.description + '\nVersion: ' + VERSION,
|
||||
version: VERSION,
|
||||
icon: 'bubbles',
|
||||
preview: 'network.png',
|
||||
keywords: 'qlik-sense, visualization',
|
||||
author: pkg.author,
|
||||
homepage: pkg.homepage,
|
||||
license: pkg.license,
|
||||
repository: pkg.repository,
|
||||
dependencies: {
|
||||
'qlik-sense': '>=5.5.x'
|
||||
}
|
||||
};
|
||||
if (pkg.contributors) {
|
||||
qext.contributors = pkg.contributors;
|
||||
}
|
||||
var src = require('stream').Readable({
|
||||
objectMode: true
|
||||
});
|
||||
src._read = function () {
|
||||
this.push(new gutil.File({
|
||||
cwd: '',
|
||||
base: '',
|
||||
path: pkg.name + '.qext',
|
||||
contents: Buffer.from(JSON.stringify(qext, null, 4))
|
||||
}));
|
||||
this.push(null);
|
||||
};
|
||||
return src.pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('clean', function(){
|
||||
return del([DIST], { force: true });
|
||||
});
|
||||
|
||||
gulp.task('zip-build', function(){
|
||||
return gulp.src(settings.buildDestination + '/**/*')
|
||||
.pipe(zip(settings.name + '_' + settings.version + '.zip'))
|
||||
.pipe(gulp.dest(settings.buildDestination));
|
||||
return gulp.src(DIST + '/**/*')
|
||||
.pipe(zip(`${pkg.name}_${VERSION}.zip`))
|
||||
.pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('add-assets', function(){
|
||||
return gulp.src('./assets/**/*').pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('webpack-build', done => {
|
||||
@@ -33,39 +74,13 @@ gulp.task('webpack-build', done => {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('update-qext-version', function () {
|
||||
return gulp.src("./build/" + settings.name + ".qext")
|
||||
.pipe(jeditor({
|
||||
'version': settings.version
|
||||
}))
|
||||
.pipe(gulp.dest("./build"));
|
||||
})
|
||||
|
||||
gulp.task('build',
|
||||
gulp.series('remove-build-folder', 'webpack-build', 'update-qext-version', 'zip-build')
|
||||
gulp.series('clean', 'webpack-build', 'qext', 'add-assets')
|
||||
);
|
||||
|
||||
gulp.task('watch', () => new Promise((resolve, reject) => {
|
||||
webpackConfig.entry.unshift('webpack-dev-server/client?http://localhost:' + settings.port);
|
||||
const compiler = webpack(webpackConfig);
|
||||
const originalOutputFileSystem = compiler.outputFileSystem;
|
||||
const devServer = new WebpackDevServer(compiler, {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
},
|
||||
}).listen(settings.port, 'localhost', error => {
|
||||
compiler.outputFileSystem = originalOutputFileSystem;
|
||||
if (error) {
|
||||
console.error(error); // eslint-disable-line no-console
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening at localhost:' + settings.port);
|
||||
|
||||
resolve(null, devServer);
|
||||
});
|
||||
}));
|
||||
gulp.task('zip',
|
||||
gulp.series('build', 'zip-build')
|
||||
);
|
||||
|
||||
gulp.task('default',
|
||||
gulp.series('build')
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
const settings = require('./settings');
|
||||
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
browsers: ['SlimChromeHeadless'],
|
||||
customLaunchers: {
|
||||
SlimChromeHeadless: {
|
||||
base: 'ChromeHeadless',
|
||||
flags: ['--headless', '--disable-gpu', '--disable-translate', '--disable-extensions']
|
||||
}
|
||||
},
|
||||
files: [
|
||||
{ pattern: 'src/*.spec.js', watched: false }
|
||||
],
|
||||
frameworks: ['jasmine'],
|
||||
preprocessors: {
|
||||
'src/*.spec.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
webpack: {
|
||||
devtool: 'source-map',
|
||||
mode: settings.mode,
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: [/node_modules/],
|
||||
loaders: ['babel-loader']
|
||||
},
|
||||
{ test: /\.less$/, loader: 'ignore-loader' },
|
||||
{ test: /\.json$/, loader: 'ignore-loader' }
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
81
package-lock.json
generated
@@ -3222,6 +3222,11 @@
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"emitter-component": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
|
||||
"integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY="
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||
@@ -4397,7 +4402,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -4418,12 +4424,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -4438,17 +4446,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -4565,7 +4576,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -4577,6 +4589,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -4591,6 +4604,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -4598,12 +4612,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -4622,6 +4638,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -4702,7 +4719,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -4714,6 +4732,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -4799,7 +4818,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -4835,6 +4855,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -4854,6 +4875,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -4897,12 +4919,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5480,6 +5504,11 @@
|
||||
"glogg": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||
},
|
||||
"handle-thing": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
|
||||
@@ -6684,6 +6713,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"keycharm": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz",
|
||||
"integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk="
|
||||
},
|
||||
"killable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||
@@ -7315,6 +7349,11 @@
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
@@ -8566,6 +8605,14 @@
|
||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||
"dev": true
|
||||
},
|
||||
"propagating-hammerjs": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.4.6.tgz",
|
||||
"integrity": "sha1-/tAOmwB2f/1C0U9bUxvEk+tnLjc=",
|
||||
"requires": {
|
||||
"hammerjs": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
@@ -11197,6 +11244,18 @@
|
||||
"vinyl": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"vis": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz",
|
||||
"integrity": "sha1-3XFji/9/ZJXQC8n0DCU1JhM97Ws=",
|
||||
"requires": {
|
||||
"emitter-component": "^1.1.1",
|
||||
"hammerjs": "^2.0.8",
|
||||
"keycharm": "^0.2.0",
|
||||
"moment": "^2.18.1",
|
||||
"propagating-hammerjs": "^1.4.6"
|
||||
}
|
||||
},
|
||||
"vm-browserify": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
|
||||
|
||||
27
package.json
@@ -1,18 +1,15 @@
|
||||
{
|
||||
"name": "qlik-network-chart",
|
||||
"version": "0.0.1",
|
||||
"description": "Network chart",
|
||||
"keywords": "network chart qliksense extension",
|
||||
"description": "Displays hierarchical or relational dimensions as nodes and edges´, with measures to show the significance of its links.",
|
||||
"homepage": "",
|
||||
"repository": "https://github.com/qlik-oss/network-vis-chart",
|
||||
"author": "Michael Laenen (miclae76) <m.laenen@contactoffice.net>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"eslint": "eslint src",
|
||||
"eslint:fix": "eslint --fix src",
|
||||
"test": "karma start karma.conf.js",
|
||||
"test-once": "karma start karma.conf.js --single-run",
|
||||
"watch": "gulp watch"
|
||||
"build:zip": "gulp zip",
|
||||
"eslint": "eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.1.5",
|
||||
@@ -20,28 +17,22 @@
|
||||
"@babel/preset-env": "7.1.5",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-loader": "8.0.4",
|
||||
"copy-webpack-plugin": "4.6.0",
|
||||
"css-loader": "1.0.1",
|
||||
"del": "3.0.0",
|
||||
"eslint": "5.8.0",
|
||||
"eslint-loader": "2.1.1",
|
||||
"file-loader": "2.0.0",
|
||||
"gulp": "4.0.0",
|
||||
"gulp-json-editor": "2.4.3",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-zip": "4.2.0",
|
||||
"jasmine-core": "3.3.0",
|
||||
"karma": "3.1.1",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-jasmine": "1.1.2",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-webpack": "3.0.5",
|
||||
"less": "3.8.1",
|
||||
"less-loader": "4.1.0",
|
||||
"style-loader": "0.23.1",
|
||||
"stylelint": "9.7.1",
|
||||
"stylelint-webpack-plugin": "0.10.5",
|
||||
"webpack": "4.25.1",
|
||||
"webpack-cli": "3.1.2",
|
||||
"webpack-dev-server": "3.1.10"
|
||||
"webpack": "4.25.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"vis": "4.21.0"
|
||||
}
|
||||
}
|
||||
|
||||
46
readme.md
@@ -1,44 +1,20 @@
|
||||
# Qlik Network Chart
|
||||
This extension is part of the extension bundles for Qlik Sense. The repository is maintained and moderated by Qlik RD.
|
||||
|
||||
[](https://circleci.com/gh/qlik-oss/network-vis-chart)
|
||||
Feel free to fork and suggest pull requests for improvements and bug fixes. Changes will be moderated and reviewed before inclusion in future bundle versions. Please note that emphasis is on backward compatibility, i.e. breaking changes will most likely not be approved.
|
||||
|
||||
<h2>Qlik Sense extension to visualize networks data</h2>
|
||||
<hr>
|
||||
Based on library vis.js (http://visjs.org)
|
||||
<br>Tested with Qlik Sense 2.2.3.
|
||||
<hr>
|
||||
<h3>Dimensions</h3>
|
||||
4 dimensions are mandatory :
|
||||
<ol>
|
||||
<li>node identifier</li>
|
||||
<li>node label</li>
|
||||
<li>node parent identifier</li>
|
||||
<li>node group</li>
|
||||
</ol>
|
||||
Usage documentation for the extension is available at https://help.qlik.com.
|
||||
|
||||
<h3>Measures</h3>
|
||||
The measures are optional
|
||||
<ol>
|
||||
<li>tooltip : expression that will be push in the tooltip when hover on a node</li>
|
||||
<li>node value : used to scale the node size</li>
|
||||
<li>edge value : used to scale the edge width</li>
|
||||
</ol>
|
||||
# Developing the extension
|
||||
If you want to do code changes to the extension follow these simple steps to get going.
|
||||
|
||||
<h3>Additional network settings</h3>
|
||||
<ul>
|
||||
<li>Edge Type : select type of curve between nodes</li>
|
||||
<li>Node Shape : dot, square, diamond, triangle ...</li>
|
||||
<li>Display Edge Value : switch to display the measures on edge curves</li>
|
||||
<li>Position Edge Label : top, bottom, middle, horizontal</li>
|
||||
<li>Display Shadow : switch to enable shadow effects behind edge and nodes</li>
|
||||
</ul>
|
||||
|
||||
<h3>Sample</h3>
|
||||
QVF based on characters from Victor Hugo's novel , Les Misérables.
|
||||

|
||||
1. Get Qlik Sense Desktop
|
||||
1. Create a new app and add Network chart to a sheet.
|
||||
2. Clone the repository
|
||||
3. Run `npm install`
|
||||
4. Run `npm run build` - to build a dev-version to the /dist folder.
|
||||
5. Move the content of the /dist folder to the extension directory. Usually in `C:/Users/<user>/Documents/Qlik/Sense/Extensions/qlik-network-chart`.
|
||||
|
||||
# Original Author
|
||||
|
||||
**Michael Laenen**
|
||||
|
||||
* [github.com/miclae76](https://github.com/miclae76)
|
||||
|
||||
BIN
resources/Network data.xlsx
Normal file
@@ -2,8 +2,8 @@
|
||||
set -o errexit
|
||||
|
||||
echo "Creating release for version: $VERSION"
|
||||
echo "Artifact name: ./build/${3}_${VERSION}.zip"
|
||||
$HOME/bin/ghr -t ${ghoauth} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} "./build/${3}_${4}.zip"
|
||||
echo "Artifact name: ./dist/${3}_${VERSION}.zip"
|
||||
$HOME/bin/ghr -t ${ghoauth} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${VERSION} "./dist/${3}_${4}.zip"
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
25
scripts/verify-files.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# The build script has a known race-condition that sometimes causes it to not include all files
|
||||
# in the built zip. This script verifies the that the zip contains the correct number of files.
|
||||
|
||||
set -o errexit
|
||||
|
||||
echo "Verifying built file count"
|
||||
|
||||
while read line; do
|
||||
if [[ $line =~ ^\"name\": ]]; then
|
||||
name=${line#*: \"}
|
||||
name=${name%\"*}
|
||||
fi
|
||||
done < package.json
|
||||
|
||||
expected_file_count=$(($(find dist -type f | wc -l)-1))
|
||||
zip_file_count=$(zipinfo dist/${name}_${VERSION}.zip | grep ^- | wc -l)
|
||||
|
||||
if [ "${expected_file_count}" -ne "${zip_file_count}" ]; then
|
||||
# File count is incorrect
|
||||
echo "Expected file count ${expected_file_count}, but was ${zip_file_count}"
|
||||
exit 1
|
||||
fi
|
||||
echo "File count OK"
|
||||
exit 0
|
||||
13
settings.js
@@ -1,13 +0,0 @@
|
||||
const path = require('path');
|
||||
const packageJSON = require('./package.json');
|
||||
|
||||
const defaultBuildDestination = path.resolve("./build");
|
||||
|
||||
module.exports = {
|
||||
buildDestination: process.env.BUILD_PATH || defaultBuildDestination,
|
||||
mode: process.env.NODE_ENV || 'development',
|
||||
name: packageJSON.name,
|
||||
version: process.env.VERSION || 'local-dev',
|
||||
url: process.env.BUILD_URL || defaultBuildDestination,
|
||||
port: 8082
|
||||
};
|
||||
|
Before Width: | Height: | Size: 25 KiB |
138
src/index.js
@@ -5,9 +5,36 @@ Tested on Qlik Sense 2.2.3
|
||||
Agilos.com takes no responsibility for any code.
|
||||
Use at your own risk.
|
||||
*/
|
||||
import "@babel/polyfill";
|
||||
if (!window._babelPolyfill) { // eslint-disable-line no-underscore-dangle
|
||||
require('@babel/polyfill'); // eslint-disable-line global-require
|
||||
}
|
||||
|
||||
import paint from './paint';
|
||||
import './styles/vis.min.css';
|
||||
import './styles/main.less';
|
||||
|
||||
const dimDesc = [
|
||||
"Node Identifier",
|
||||
"Node Label",
|
||||
"Node Parent",
|
||||
"Node Group"
|
||||
];
|
||||
|
||||
const dimLongDesc = [
|
||||
"Node Identifier - a field in the dataset which should be presented as a node in the network diagram."
|
||||
+ " these control the actual elements presented in the network diagram.",
|
||||
"Node Label - controls what field holds the data that described the nodes in the network"
|
||||
+ " diagram. The field content will be presented as label.",
|
||||
"Node Parent - is used to determine the ancestor node for the individual nodes."
|
||||
+ " This field will be used for describing the relationships between network elements.",
|
||||
"Node Group - is a field which describes groups of a node in the network."
|
||||
+ " This is used to apply the same color to several nodes."
|
||||
];
|
||||
|
||||
const measureDesc = [
|
||||
"Tooltip",
|
||||
"Node size",
|
||||
"Edge size"
|
||||
];
|
||||
|
||||
const component = {
|
||||
initialProperties: {
|
||||
@@ -22,30 +49,62 @@ const component = {
|
||||
}
|
||||
},
|
||||
//property panel
|
||||
data: {
|
||||
dimensions: {
|
||||
min: 3,
|
||||
max: 4,
|
||||
description(properties, index) {
|
||||
return dimDesc[index];
|
||||
}
|
||||
/*
|
||||
1. Dimension: Node ID, numeric (Event ID or else) or String
|
||||
2. Dimension: Node Label
|
||||
3. Dimension: Node Parent ID, numeric (Event ID or else) or String
|
||||
4. Dimension: Node Cluster
|
||||
*/
|
||||
},
|
||||
measures: {
|
||||
min: 0,
|
||||
max: 3,
|
||||
description(properties, index) {
|
||||
return measureDesc[index];
|
||||
}
|
||||
/*
|
||||
1. Measure: title text for tooltip (optional)
|
||||
2. Measure: node value
|
||||
3. Measure: edge value
|
||||
*/
|
||||
}
|
||||
},
|
||||
definition: {
|
||||
type: "items",
|
||||
component: "accordion",
|
||||
items: {
|
||||
dimensions: {
|
||||
uses: "dimensions",
|
||||
min: 4,
|
||||
max: 4
|
||||
/*
|
||||
1. Dimension: Node ID, numeric (Event ID or else) or String
|
||||
2. Dimension: Node Label
|
||||
3. Dimension: Node Parent ID, numeric (Event ID or else) or String
|
||||
4. Dimension: Node Cluster
|
||||
*/
|
||||
},
|
||||
measures: {
|
||||
uses: "measures",
|
||||
min: 0,
|
||||
max: 3
|
||||
/*
|
||||
1. Measure: title text for tooltip (optional)
|
||||
2. Measure: node value
|
||||
3. Measure: edge value
|
||||
*/
|
||||
data: {
|
||||
uses: "data",
|
||||
items:{
|
||||
dimensions:{
|
||||
disabledRef: "",
|
||||
items: {
|
||||
helpDesc: {
|
||||
component: 'text',
|
||||
style: 'qlik-network-chart-italic-property',
|
||||
label: function(properties, handler) {
|
||||
var index;
|
||||
handler.getDimensions().forEach((element, i) => {
|
||||
if(element.qDef.cId === properties.qDef.cId) {
|
||||
index = i;
|
||||
}
|
||||
});
|
||||
return dimLongDesc[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
measures: {
|
||||
disabledRef: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
sorting: {
|
||||
uses: "sorting"
|
||||
@@ -59,13 +118,14 @@ const component = {
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
uses: "settings",
|
||||
type: "items",
|
||||
label: "Settings",
|
||||
items: {
|
||||
edgeType: {
|
||||
ref: "edgeType",
|
||||
type: "string",
|
||||
component: "dropdown",
|
||||
label: "Egde Type",
|
||||
label: "Edge Type",
|
||||
options: [
|
||||
{ value: 'dynamic' },
|
||||
{ value: 'continuous' },
|
||||
@@ -74,8 +134,8 @@ const component = {
|
||||
{ value: 'straightCross' },
|
||||
{ value: 'horizontal' },
|
||||
{ value: 'vertical' },
|
||||
{ value: 'curveCW' },
|
||||
{ value: 'curveCCW' },
|
||||
{ value: 'curvedCW' },
|
||||
{ value: 'curvedCCW' },
|
||||
{ value: 'cubicBezier' }
|
||||
],
|
||||
defaultValue: "dynamic"
|
||||
@@ -134,14 +194,36 @@ const component = {
|
||||
defaultValue: false
|
||||
}
|
||||
}
|
||||
},
|
||||
about: {
|
||||
component: 'items',
|
||||
label: 'About',
|
||||
items: {
|
||||
header: {
|
||||
label: 'Network chart',
|
||||
style: 'header',
|
||||
component: 'text'
|
||||
},
|
||||
paragraph1: {
|
||||
label: `Network chart is Qlik Sense chart which allows you to draw a network of connected nodes and edges from a data set to a sheet.`,
|
||||
component: 'text'
|
||||
},
|
||||
paragraph2: {
|
||||
label: 'Network chart is based upon an extension created by Michael Laenen.',
|
||||
component: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
support: {
|
||||
export: true,
|
||||
snapshot: true,
|
||||
exportData: true
|
||||
},
|
||||
snapshot: {
|
||||
canTakeSnapshot: true
|
||||
},
|
||||
|
||||
paint: paint
|
||||
};
|
||||
|
||||
|
||||
334
src/paint.js
@@ -1,146 +1,230 @@
|
||||
import vis from './scripts/vis.min';
|
||||
import { Network } from 'vis/index-network';
|
||||
import qlik from 'qlik';
|
||||
import { createTooltipHTML } from './tooltip';
|
||||
import { escapeHTML } from './utilities';
|
||||
|
||||
function isTextCellNotEmpty(c) {
|
||||
return (c.qText && !(c.qIsNull || c.qText.trim() == ''));
|
||||
}
|
||||
|
||||
function paint ( $element, layout ) {
|
||||
var _this = this,
|
||||
qData = layout.qHyperCube.qDataPages[0],
|
||||
id = layout.qInfo.qId,
|
||||
containerId = 'network-container_' + id;
|
||||
function getColor (index, colors) {
|
||||
return colors[index % colors.length];
|
||||
}
|
||||
|
||||
function paint ( $element, layout, qTheme, component ) {
|
||||
|
||||
if(qData && qData.qMatrix) {
|
||||
$element.empty().append($('<div />')
|
||||
.attr({ id: containerId })
|
||||
.css({
|
||||
height: $element.height(),
|
||||
width: $element.width(),
|
||||
overflow: 'auto'
|
||||
}));
|
||||
return new qlik.Promise(function(resolve) {
|
||||
|
||||
var dataSet = qData.qMatrix.map(function(e){
|
||||
var dataItem = {
|
||||
id: e[0].qNum,
|
||||
label: e[1].qText,
|
||||
group: e[3].qText,
|
||||
parentid : e[2].qNum
|
||||
const colorScale = qTheme.properties.palettes.data[0];
|
||||
const numDimensions = layout.qHyperCube.qDimensionInfo.length;
|
||||
const numMeasures = layout.qHyperCube.qMeasureInfo.length;
|
||||
|
||||
var qData = layout.qHyperCube.qDataPages[0],
|
||||
id = layout.qInfo.qId,
|
||||
containerId = 'network-container_' + id;
|
||||
|
||||
if(qData && qData.qMatrix) {
|
||||
$element.empty().append($('<div />')
|
||||
.attr({ id: containerId })
|
||||
.toggleClass('is-edit-mode', component.inEditState())
|
||||
.css({
|
||||
height: $element.height(),
|
||||
width: $element.width(),
|
||||
overflow: 'auto'
|
||||
}));
|
||||
|
||||
var dataSet = qData.qMatrix.map(function(e){
|
||||
const nodeName = e[1].qText;
|
||||
let groupNumber;
|
||||
|
||||
const dataItem = {
|
||||
id: e[0].qText,
|
||||
eNum: e[0].qElemNumber,
|
||||
label: nodeName,
|
||||
parentid : e[2].qText
|
||||
};
|
||||
|
||||
if(numDimensions === 4) {
|
||||
groupNumber = e[3].qText;
|
||||
dataItem.group = groupNumber;
|
||||
}
|
||||
|
||||
// optional measures set
|
||||
if (numMeasures > 0) {
|
||||
const tooltip = e[numDimensions];
|
||||
|
||||
if (isTextCellNotEmpty(tooltip)) {
|
||||
const tooltipText = tooltip.qText;
|
||||
dataItem.title = escapeHTML(tooltipText);
|
||||
} else if(numMeasures > 1) {
|
||||
// This part is a bit fishy and should be tested
|
||||
const nodeMeasure = e[numDimensions+1].qText;
|
||||
dataItem.title = createTooltipHTML({
|
||||
name: nodeName,
|
||||
groupNumber,
|
||||
nodeMeasure
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (numMeasures > 1) {
|
||||
if (e[numDimensions+1].qNum) {
|
||||
// node value - to scale node shape size
|
||||
dataItem.nodeValue = e[5].qNum;
|
||||
}
|
||||
}
|
||||
|
||||
if (numMeasures > 2) {
|
||||
if (e[numDimensions+2].qNum) {
|
||||
// edge value - to scale edge width
|
||||
dataItem.edgeValue = e[numDimensions+2].qNum;
|
||||
}
|
||||
}
|
||||
|
||||
return dataItem;
|
||||
});
|
||||
|
||||
// Require 2 arrays : nodes and edges - nodes array must be unique values of IDs !
|
||||
var uniqueId = [];
|
||||
var nodes = [];
|
||||
var edges = [];
|
||||
const groups = {};
|
||||
|
||||
for(let i = 0; i< dataSet.length; i++){
|
||||
if (layout.displayEdgeLabel && dataSet[i].edgeValue !== undefined) {
|
||||
edges.push({
|
||||
"from":dataSet[i].id,
|
||||
"to":dataSet[i].parentid,
|
||||
"value":dataSet[i].edgeValue,
|
||||
"label": `${dataSet[i].edgeValue}`
|
||||
}); // with labels
|
||||
} else {
|
||||
edges.push({
|
||||
"from":dataSet[i].id,
|
||||
"to":dataSet[i].parentid,
|
||||
"value":dataSet[i].edgeValue
|
||||
}); // create edges
|
||||
}
|
||||
|
||||
// process uniqueness
|
||||
if(uniqueId.indexOf(dataSet[i].id) === -1) {
|
||||
uniqueId.push(dataSet[i].id);
|
||||
|
||||
var nodeItem = {
|
||||
id: dataSet[i].id,
|
||||
eNum: dataSet[i].eNum,
|
||||
label: dataSet[i].label,
|
||||
title: dataSet[i].title,
|
||||
group: dataSet[i].group,
|
||||
value: dataSet[i].nodeValue
|
||||
};
|
||||
nodes.push(nodeItem); // create node
|
||||
groups[nodeItem.group] = {};
|
||||
}
|
||||
}
|
||||
const colors = colorScale.scale[Math.min(Object.keys(groups).length-1, colorScale.scale.length-1)];
|
||||
|
||||
Object.keys(groups).forEach(function(g,i) {
|
||||
groups[g].color = getColor(i, colors);
|
||||
});
|
||||
|
||||
// create dataset for \\
|
||||
var data = {
|
||||
nodes: nodes,
|
||||
edges: edges
|
||||
};
|
||||
|
||||
// optional measures set
|
||||
if (e.length > 4) {
|
||||
// tooltip
|
||||
if (isTextCellNotEmpty(e[4])) {
|
||||
dataItem.title = e[4].qText;
|
||||
} else {
|
||||
dataItem.title = "*** Default Tooltip ***" + "<BR/>" + "Name:" + e[1].qText + "<BR/>" + "Group:" + e[3].qText;
|
||||
// create a network
|
||||
var container = document.getElementById(containerId);
|
||||
|
||||
var options = {
|
||||
groups: groups,
|
||||
layout: {
|
||||
randomSeed: 1
|
||||
},
|
||||
nodes: {
|
||||
shape:layout.nodeShape,
|
||||
shadow:layout.shadowMode
|
||||
},
|
||||
edges: {
|
||||
shadow:layout.shadowMode,
|
||||
font: {
|
||||
align: layout.posEdgeLabel
|
||||
},
|
||||
smooth: {
|
||||
type: layout.edgeType
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
hideEdgesOnDrag: true,
|
||||
tooltipDelay: 100
|
||||
},
|
||||
physics: {
|
||||
forceAtlas2Based: {
|
||||
gravitationalConstant: -100,
|
||||
centralGravity: 0.005,
|
||||
springLength: 230,
|
||||
springConstant: 0.18
|
||||
},
|
||||
maxVelocity: 146,
|
||||
solver: 'forceAtlas2Based',
|
||||
timestep: 0.35,
|
||||
stabilization: { iterations: 150 }
|
||||
}
|
||||
}
|
||||
};
|
||||
var network = new Network(container, data, options);
|
||||
network.fit();
|
||||
|
||||
if (e.length > 5) {
|
||||
if (e[5].qNum) {
|
||||
// node value - to scale node shape size
|
||||
dataItem.nodeValue = e[5].qNum;
|
||||
// Handle Selection on 1-node
|
||||
$("#"+containerId).css('cursor','default');
|
||||
|
||||
network.on('select', function (properties) {
|
||||
if (properties.hasOwnProperty("nodes")) {
|
||||
if (properties.nodes.length > 0) {
|
||||
// find connected nodes to selection
|
||||
var connectedNodes = network.getConnectedNodes(properties.nodes[0]);
|
||||
// append node to the array
|
||||
connectedNodes.push(properties.nodes[0]);
|
||||
const toSelect = [];
|
||||
connectedNodes.forEach(function(node) {
|
||||
var id;
|
||||
data.nodes.forEach(function(dataNode) {
|
||||
// Find match, ignore null
|
||||
if(dataNode.id === node && node !== "-") {
|
||||
id = dataNode.eNum;
|
||||
}
|
||||
});
|
||||
if(id !== undefined) {
|
||||
// Remove duplicates
|
||||
toSelect.indexOf(id) === -1 && toSelect.push(id);
|
||||
}
|
||||
});
|
||||
//Make the selections
|
||||
component.backendApi.selectValues(0,toSelect,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (e.length > 6) {
|
||||
if (e[6].qNum) {
|
||||
// edge value - to scale edge width
|
||||
dataItem.edgeValue = e[6].qNum;
|
||||
}
|
||||
}
|
||||
|
||||
return dataItem;
|
||||
});
|
||||
|
||||
// Require 2 arrays : nodes and edges - nodes array must be unique values of IDs !
|
||||
var uniqueId = [];
|
||||
var nodes = [];
|
||||
var edges = [];
|
||||
|
||||
for(let i = 0; i< dataSet.length; i++){
|
||||
if (layout.displayEdgeLabel) {
|
||||
edges.push( { "from":dataSet[i].id, "to":dataSet[i].parentid, "value":dataSet[i].edgeValue, "label":dataSet[i].edgeValue } ); // with labels
|
||||
} else {
|
||||
edges.push( { "from":dataSet[i].id, "to":dataSet[i].parentid, "value":dataSet[i].edgeValue } ); // create edges
|
||||
}
|
||||
|
||||
// process uniqueness
|
||||
if(uniqueId.indexOf(dataSet[i].id) === -1) {
|
||||
uniqueId.push(dataSet[i].id);
|
||||
|
||||
var nodeItem = {
|
||||
"id": dataSet[i].id,
|
||||
"label": dataSet[i].label,
|
||||
"title": dataSet[i].title,
|
||||
"group": dataSet[i].group,
|
||||
"value": dataSet[i].nodeValue
|
||||
};
|
||||
nodes.push(nodeItem); // create node
|
||||
}
|
||||
network.on('stabilizationIterationsDone', function() {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// create dataset for \\
|
||||
var data = {
|
||||
nodes: nodes,
|
||||
edges: edges
|
||||
};
|
||||
function themePaint ($element, layout) {
|
||||
const component = this;
|
||||
try {
|
||||
const app = qlik.currApp(this);
|
||||
|
||||
// create a network
|
||||
var container = document.getElementById(containerId);
|
||||
|
||||
var options = {
|
||||
nodes: {
|
||||
shape:layout.nodeShape,
|
||||
shadow:layout.shadowMode
|
||||
},
|
||||
edges: {
|
||||
shadow:layout.shadowMode,
|
||||
font: {
|
||||
align: layout.posEdgeLabel
|
||||
},
|
||||
smooth: {
|
||||
type: layout.edgeType
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
hideEdgesOnDrag: true,
|
||||
tooltipDelay: 100
|
||||
},
|
||||
physics: {
|
||||
forceAtlas2Based: {
|
||||
gravitationalConstant: -100,
|
||||
centralGravity: 0.005,
|
||||
springLength: 230,
|
||||
springConstant: 0.18
|
||||
},
|
||||
maxVelocity: 146,
|
||||
solver: 'forceAtlas2Based',
|
||||
timestep: 0.35,
|
||||
stabilization: { iterations: 150 }
|
||||
}
|
||||
};
|
||||
var network = new vis.Network(container, data, options);
|
||||
|
||||
// Handle Selection on 1-node
|
||||
$("#"+containerId).css('cursor','default');
|
||||
|
||||
network.on('select', function (properties) {
|
||||
if (properties.hasOwnProperty("nodes")) {
|
||||
if (properties.nodes.length > 0) {
|
||||
// find connected nodes to selection
|
||||
var connectedNodes = network.getConnectedNodes(properties.nodes[0]);
|
||||
// append node to the array
|
||||
connectedNodes.push(properties.nodes[0]);
|
||||
|
||||
//Make the selections
|
||||
_this.backendApi.selectValues(0,connectedNodes,false);
|
||||
}
|
||||
}
|
||||
return app.theme.getApplied().then( function( qTheme ) {
|
||||
return paint($element, layout, qTheme, component);
|
||||
});
|
||||
} catch (exception) {
|
||||
console.error(exception); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export default paint;
|
||||
export default themePaint;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import paint from './paint';
|
||||
|
||||
describe('paint', () => {
|
||||
it('should be defined', () => {
|
||||
expect(paint).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name" : "Network Chart",
|
||||
"description" : "NetWork Chart",
|
||||
"icon" : "extension",
|
||||
"type" : "visualization",
|
||||
"version": "1.0",
|
||||
"preview" : "network.png",
|
||||
"author": "Michael Laenen , agilos.com"
|
||||
}
|
||||
47
src/scripts/vis.min.js
vendored
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 665 B |
17
src/styles/main.less
Normal file
@@ -0,0 +1,17 @@
|
||||
.is-edit-mode > div {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vis-tooltip {
|
||||
position: absolute;
|
||||
background-color: #333;
|
||||
opacity: 0.95;
|
||||
border-radius: 5px;
|
||||
color: #eee;
|
||||
padding: 10px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.qlik-network-chart-italic-property {
|
||||
font-style: italic;
|
||||
}
|
||||
1
src/styles/vis.min.css
vendored
28
src/tooltip.js
Normal file
@@ -0,0 +1,28 @@
|
||||
function createEntry(header, value) {
|
||||
const entry = document.createElement('div');
|
||||
const nameHeader = document.createElement('span');
|
||||
const nameHeaderValue = document.createTextNode(header);
|
||||
const nameValueContainer = document.createElement('b');
|
||||
const nameValue = document.createTextNode(value);
|
||||
|
||||
nameHeader.appendChild(nameHeaderValue);
|
||||
nameValueContainer.appendChild(nameValue);
|
||||
|
||||
entry.appendChild(nameHeader);
|
||||
entry.appendChild(nameValueContainer);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
export function createTooltipHTML({ name, groupNumber, nodeMeasure }) {
|
||||
const tooltip = document.createElement('div');
|
||||
const nameEntry = createEntry('Name: ', name);
|
||||
const groupNumberEntry = createEntry('Group number: ', groupNumber);
|
||||
const nodeMeasureEntry = createEntry('Node measure: ', nodeMeasure);
|
||||
|
||||
tooltip.appendChild(nameEntry);
|
||||
tooltip.appendChild(groupNumberEntry);
|
||||
tooltip.appendChild(nodeMeasureEntry);
|
||||
|
||||
return tooltip.innerHTML;
|
||||
}
|
||||
5
src/utilities.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export function escapeHTML(str){
|
||||
var span = document.createElement('span');
|
||||
span.appendChild(document.createTextNode(str));
|
||||
return span.innerHTML;
|
||||
}
|
||||
@@ -1,19 +1,31 @@
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const StyleLintPlugin = require('stylelint-webpack-plugin');
|
||||
const settings = require('./settings');
|
||||
const packageJSON = require('./package.json');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
console.log('Webpack mode:', settings.mode); // eslint-disable-line no-console
|
||||
const DIST = path.resolve("./dist");
|
||||
const MODE = process.env.NODE_ENV || 'development';
|
||||
|
||||
console.log('Webpack mode:', MODE); // eslint-disable-line no-console
|
||||
|
||||
const config = {
|
||||
devtool: 'source-map',
|
||||
entry: [
|
||||
'./src/index.js'
|
||||
],
|
||||
mode: settings.mode,
|
||||
mode: MODE,
|
||||
output: {
|
||||
path: settings.buildDestination,
|
||||
filename: settings.name + '.js',
|
||||
libraryTarget: 'umd'
|
||||
filename: `${packageJSON.name}.js`,
|
||||
libraryTarget: 'amd',
|
||||
path: DIST
|
||||
},
|
||||
externals: {
|
||||
qlik: {
|
||||
amd: 'qlik',
|
||||
commonjs: 'qlik',
|
||||
commonjs2: 'qlik',
|
||||
root: '_'
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@@ -26,6 +38,16 @@ const config = {
|
||||
failOnError: true
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /node_modules[\\\/]vis[\\\/].*\.js$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /.js$/,
|
||||
exclude: /node_modules/,
|
||||
@@ -52,10 +74,8 @@ const config = {
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([
|
||||
'src/' + settings.name + '.qext'
|
||||
], {}),
|
||||
new StyleLintPlugin()
|
||||
new StyleLintPlugin(),
|
||||
new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/en$/),
|
||||
]
|
||||
};
|
||||
|
||||
|
||||