docs: rename supernova and nucleus to stardust (#419)

This commit is contained in:
Miralem Drek
2020-05-03 16:35:52 +02:00
committed by GitHub
parent d781cf0894
commit c25c7a6237
45 changed files with 2256 additions and 673 deletions

View File

@@ -33,14 +33,17 @@ If the proposal includes new designs or bigger changes, please be prepared to di
This is a multi-package repository which uses [lerna](https://github.com/lerna/lerna) for package task management and publishing.
- `packages` - all packages are in this folder
- `apis` - JavaScript APIs
- `nucleus`: JavaScript library for mashups
- `supernova`: JavaScript API for consuming and visualizing QIX data
- `stardust`: Public JavaScript API which exposes the nucleus and supernova APIs
- `commands` - CLI commands
- `build`: cli command to build a supernova
- `cli`: entry point for all cli commands
- `create`: cli command for creating a supernova project
- `nucleus`: JavaScript library for mashups
- `sense`: cli command to build a Qlik Sense extension from a supernova
- `serve`: cli command to start a development server for rapid prototyping of a supernova
- `supernova`: JavaScript API for consuming and visualizing QIX data
- `packages`
- `ui`: [private] ui components
- `test/`: contains test configs

View File

@@ -5,19 +5,15 @@
# !!EXPERIMENTAL!!
`nebula.js` is a collection of JavaScript libraries and APIs that helps developers integrate visualizations and mashups on top of Qlik's Associative Engine.
NebulaJS is a collection of JavaScript libraries, charts and CLIs that helps developers build and integrate visualizations on top of Qlik's Associative Engine. The collection is organized under the `@nebula.js` npm scope.
It consists of three parts:
- `nucleus`: A product and framework agnostic JavaScript library for building mashups.
- `supernova`: A JavaScript API for consuming and visualizing QIX data.
- `cli`: Tools to help you create, develop and build a `supernova`.
The primary package is `@nebula.js/stardust` which contains APIs for integrating existing visualizations into mashups, as well as APIs for building custom visualizations.
![nebula-overview](docs/assets/nebula-overview.png)
## Roadmap
Since nebula.js is still in an early stage we are very much open to input and suggestions. If you think something is missing, an API is weird or have general opinions about anything, let us know.
Since `stardust` is still in an early stage we are very much open to input and suggestions. If you think something is missing, an API is weird or have general opinions about anything, let us know.
_Your opinions, requirements and involvement is key to the success of this project._ Click on the linked issues below ([or create you own](https://github.com/qlik-oss/nebula.js/issues/new/choose)), voice your opinion and vote if you would like to see it get implemented.
@@ -29,12 +25,11 @@ This is the primary task right now as there is currently barely any documentatio
**Integration APIs**
- nucleus
- stardust
- theming ([#24](https://github.com/qlik-oss/nebula.js/issues/24))
- translations, localization ([#25](https://github.com/qlik-oss/nebula.js/issues/25))
- error handling
- export ([#26](https://github.com/qlik-oss/nebula.js/issues/26))
- supernova
- consume theme ([#27](https://github.com/qlik-oss/nebula.js/issues/27))
- consume translator ([#28](https://github.com/qlik-oss/nebula.js/issues/28))
- actions api

View File

@@ -50,7 +50,7 @@ describe('nucleus', () => {
some: 'thing',
},
});
expect(typesFn.getCall(0).args[0].corona.public.galaxy).to.eql({
expect(typesFn.getCall(0).args[0].halo.public.galaxy).to.eql({
anything: {
some: 'thing',
},

View File

@@ -48,7 +48,7 @@ describe('viz', () => {
};
api = create({
model,
corona: { public: {} },
halo: { public: {} },
});
});
after(() => {

View File

@@ -158,13 +158,13 @@ const loadType = async ({ dispatch, types, name, version, layout, model, app, se
return undefined;
};
const Cell = forwardRef(({ corona, model, initialSnOptions, initialError, onMount }, ref) => {
const Cell = forwardRef(({ halo, model, initialSnOptions, initialError, onMount }, ref) => {
const {
app,
public: {
nebbie: { types },
},
} = corona;
} = halo;
const { translator, language } = useContext(InstanceContext);
const theme = useTheme();
@@ -295,11 +295,11 @@ const Cell = forwardRef(({ corona, model, initialSnOptions, initialError, onMoun
};
},
async exportImage() {
if (typeof corona.config.snapshot.capture !== 'function') {
if (typeof halo.config.snapshot.capture !== 'function') {
throw new Error('Nebula has not been configured with snapshot.capture callback');
}
const snapshot = await this.takeSnapshot(); // eslint-disable-line
return corona.config.snapshot.capture(snapshot);
return halo.config.snapshot.capture(snapshot);
},
}),
[state.sn, contentRect, cellRect, layout, theme.name, appLayout]
@@ -316,7 +316,7 @@ const Cell = forwardRef(({ corona, model, initialSnOptions, initialError, onMoun
<Supernova
key={layout.visualization}
sn={state.sn}
corona={corona}
halo={halo}
snOptions={snOptions}
layout={layout}
appLayout={appLayout}

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useContext, useRef } from 'rea
import InstanceContext from '../contexts/InstanceContext';
import useRect from '../hooks/useRect';
const Supernova = ({ sn, snOptions: options, layout, appLayout, corona }) => {
const Supernova = ({ sn, snOptions: options, layout, appLayout, halo }) => {
const { component } = sn;
const { theme: themeName, language, constraints } = useContext(InstanceContext);
@@ -58,7 +58,7 @@ const Supernova = ({ sn, snOptions: options, layout, appLayout, corona }) => {
if (!constraints.select) {
permissions.push('select');
}
if (corona.app && corona.app.session) {
if (halo.app && halo.app.session) {
permissions.push('fetch');
}
Promise.resolve(
@@ -67,8 +67,8 @@ const Supernova = ({ sn, snOptions: options, layout, appLayout, corona }) => {
options,
context: {
constraints,
// corona.public.theme is a singleton so themeName is used as dep to make sure this effect is triggered
theme: corona.public.theme,
// halo.public.theme is a singleton so themeName is used as dep to make sure this effect is triggered
theme: halo.public.theme,
appLayout,
// TODO - remove when old component api is removed

View File

@@ -100,7 +100,7 @@ describe('<Cell />', () => {
...defaultModel,
...model,
};
const corona = {
const halo = {
...defaultCorona,
...app,
public: {
@@ -115,7 +115,7 @@ describe('<Cell />', () => {
renderer = create(
<ThemeProvider theme={theme}>
<InstanceContext.Provider value={{ translator: { get: (s) => s, language: () => 'sv' } }}>
<Cell ref={cellRef} corona={corona} model={model} initialSnOptions={initialSnOptions} onMount={onMount} />
<Cell ref={cellRef} halo={halo} model={model} initialSnOptions={initialSnOptions} onMount={onMount} />
</InstanceContext.Provider>
</ThemeProvider>,
rendererOptions || null

View File

@@ -13,7 +13,7 @@ describe('glue', () => {
beforeEach(() => {
sandbox = sinon.createSandbox();
param = {
corona: {
halo: {
root: {
add: sandbox.spy(),
remove: sandbox.spy(),
@@ -34,7 +34,7 @@ describe('glue', () => {
it('should glue outside world with react world', () => {
const [dissolve] = glue(param);
dissolve();
expect(param.corona.root.add.callCount).to.equal(1);
expect(param.corona.root.remove.callCount).to.equal(1);
expect(param.halo.root.add.callCount).to.equal(1);
expect(param.halo.root.remove.callCount).to.equal(1);
});
});

View File

@@ -20,12 +20,12 @@ describe('<Supernova />', () => {
snOptions = {},
layout = {},
appLayout = {},
corona = {},
halo = {},
rendererOptions,
} = {}) => {
await act(async () => {
renderer = create(
<Supernova sn={sn} snOptions={snOptions} layout={layout} appLayout={appLayout} corona={corona} />,
<Supernova sn={sn} snOptions={snOptions} layout={layout} appLayout={appLayout} halo={halo} />,
rendererOptions || null
);
});
@@ -45,7 +45,7 @@ describe('<Supernova />', () => {
snOptions: {},
layout: {},
appLayout: {},
corona: {},
halo: {},
});
});
it('should mount', async () => {
@@ -99,7 +99,7 @@ describe('<Supernova />', () => {
snOptions,
layout: 'layout',
appLayout: { qLocaleInfo: 'loc' },
corona: { public: { theme: 'theme' }, app: { session: {} } },
halo: { public: { theme: 'theme' }, app: { session: {} } },
rendererOptions: {
createNodeMock: () => {
return {
@@ -146,7 +146,7 @@ describe('<Supernova />', () => {
component,
},
layout: {},
corona: { public: {} },
halo: { public: {} },
rendererOptions: {
createNodeMock: () => {
return {

View File

@@ -2,13 +2,13 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Cell from './Cell';
export default function glue({ corona, element, model, initialSnOptions, onMount, initialError }) {
const { root } = corona;
export default function glue({ halo, element, model, initialSnOptions, onMount, initialError }) {
const { root } = halo;
const cellRef = React.createRef();
const portal = ReactDOM.createPortal(
<Cell
ref={cellRef}
corona={corona}
halo={halo}
model={model}
initialSnOptions={initialSnOptions}
initialError={initialError}

View File

@@ -154,7 +154,7 @@ function nuked(configuration = {}) {
nebbie: null, // actual value is set further down
};
const corona = {
const halo = {
app,
root,
config: configuration,
@@ -163,7 +163,7 @@ function nuked(configuration = {}) {
nebbie: null,
};
const types = typesFn({ corona });
const types = typesFn({ halo });
configuration.types.forEach((t) =>
types.register(
@@ -209,9 +209,9 @@ function nuked(configuration = {}) {
render: async (cfg) => {
await currentThemePromise;
if (cfg.id) {
return get(cfg, corona);
return get(cfg, halo);
}
return create(cfg, corona);
return create(cfg, halo);
},
/**
* Updates the current context of this embed instance.
@@ -245,7 +245,7 @@ function nuked(configuration = {}) {
translator: locale.translator,
};
corona.context = currentContext;
halo.context = currentContext;
if (changes.theme) {
currentThemePromise = appTheme.setTheme(changes.theme);
@@ -253,7 +253,7 @@ function nuked(configuration = {}) {
}
if (changes.language) {
corona.public.translator.language(changes.language);
halo.public.translator.language(changes.language);
}
root.context(currentContext);
@@ -306,7 +306,7 @@ function nuked(configuration = {}) {
types,
};
corona.public.nebbie = api;
halo.public.nebbie = api;
return api;
}

View File

@@ -1,5 +1,5 @@
describe('create-session-object', () => {
let corona = {};
let halo = {};
let types;
let sn;
let merged;
@@ -26,7 +26,7 @@ describe('create-session-object', () => {
types = {
get: sandbox.stub(),
};
corona = {
halo = {
app: {
createSessionObject: sandbox.stub().returns(Promise.resolve(objectModel)),
},
@@ -53,7 +53,7 @@ describe('create-session-object', () => {
});
it('should call types.get with name and version', () => {
create({ type: 't', version: 'v', fields: 'f' }, corona);
create({ type: 't', version: 'v', fields: 'f' }, halo);
expect(types.get).to.have.been.calledWithExactly({ name: 't', version: 'v' });
});
@@ -61,46 +61,46 @@ describe('create-session-object', () => {
const t = { initialProperties: sinon.stub() };
t.initialProperties.returns({ then: () => {} });
types.get.returns(t);
create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, corona);
create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, halo);
expect(t.initialProperties).to.have.been.calledWithExactly('props');
});
it('should populate fields', async () => {
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, corona);
expect(populator).to.have.been.calledWithExactly({ sn, properties: merged, fields: 'f' }, corona);
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, halo);
expect(populator).to.have.been.calledWithExactly({ sn, properties: merged, fields: 'f' }, halo);
});
it('should call properties onChange handler when optional props are provided', async () => {
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, corona);
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, halo);
expect(sn.qae.properties.onChange).to.have.been.calledWithExactly(merged);
});
it('should not call onChange handler when optional props are not provided', async () => {
await create({ type: 't', version: 'v', fields: 'f' }, corona);
await create({ type: 't', version: 'v', fields: 'f' }, halo);
expect(sn.qae.properties.onChange.callCount).to.equal(0);
});
it('should create a session object with merged props', async () => {
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, corona);
expect(corona.app.createSessionObject).to.have.been.calledWithExactly(merged);
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, halo);
expect(halo.app.createSessionObject).to.have.been.calledWithExactly(merged);
});
it('should create a dummy session object when error is thrown', async () => {
types.get.throws('oops');
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, corona);
expect(corona.app.createSessionObject).to.have.been.calledWithExactly({
await create({ type: 't', version: 'v', fields: 'f', properties: 'props' }, halo);
expect(halo.app.createSessionObject).to.have.been.calledWithExactly({
qInfo: { qType: 't' },
visualization: 't',
});
});
it('should call init', async () => {
const ret = await create({ type: 't', version: 'v', fields: 'f', properties: 'props', options: 'a' }, corona);
const ret = await create({ type: 't', version: 'v', fields: 'f', properties: 'props', options: 'a' }, halo);
expect(ret).to.equal('api');
expect(init).to.have.been.calledWithExactly(
objectModel,
{ options: 'a', element: undefined },
corona,
halo,
undefined,
sinon.match.func
);
@@ -110,12 +110,12 @@ describe('create-session-object', () => {
const err = new Error('oops');
types.get.throws(err);
const optional = { properties: 'props', element: 'el', options: 'opts' };
const ret = await create({ type: 't', ...optional }, corona);
const ret = await create({ type: 't', ...optional }, halo);
expect(ret).to.equal('api');
expect(init).to.have.been.calledWithExactly(
objectModel,
{ options: 'opts', element: 'el' },
corona,
halo,
err,
sinon.match.func
);

View File

@@ -1,6 +1,6 @@
describe('initiate api', () => {
const optional = 'optional';
const corona = 'corona';
const halo = 'halo';
const model = 'model';
let create;
let sandbox;
@@ -28,18 +28,18 @@ describe('initiate api', () => {
it('should call viz api', async () => {
const initialError = 'err';
const onDestroy = () => {};
const ret = await create(model, optional, corona, initialError, onDestroy);
expect(viz).to.have.been.calledWithExactly({ model, corona, initialError, onDestroy });
const ret = await create(model, optional, halo, initialError, onDestroy);
expect(viz).to.have.been.calledWithExactly({ model, halo, initialError, onDestroy });
expect(ret).to.equal(api);
});
it('should call mount when element is provided ', async () => {
await create(model, { element: 'el' }, corona);
await create(model, { element: 'el' }, halo);
expect(api.mount).to.have.been.calledWithExactly('el');
});
it('should call options when provided ', async () => {
await create(model, { options: 'opts' }, corona);
await create(model, { options: 'opts' }, halo);
expect(api.options).to.have.been.calledWithExactly('opts');
});
});

View File

@@ -10,11 +10,11 @@ import { subscribe } from '../stores/modelStore';
* @property {qae.GenericObjectProperties=} properties
*/
export default async function createSessionObject({ type, version, fields, properties, options, element }, corona) {
export default async function createSessionObject({ type, version, fields, properties, options, element }, halo) {
let mergedProps = {};
let error;
try {
const t = corona.public.nebbie.types.get({ name: type, version });
const t = halo.public.nebbie.types.get({ name: type, version });
mergedProps = await t.initialProperties(properties);
const sn = await t.supernova();
if (fields) {
@@ -24,7 +24,7 @@ export default async function createSessionObject({ type, version, fields, prope
properties: mergedProps,
fields,
},
corona
halo
);
}
if (properties && sn && sn.qae.properties.onChange) {
@@ -42,11 +42,11 @@ export default async function createSessionObject({ type, version, fields, prope
};
// console.error(e); // eslint-disable-line
}
const model = await corona.app.createSessionObject(mergedProps);
const model = await halo.app.createSessionObject(mergedProps);
const unsubscribe = subscribe(model);
const onDestroy = async () => {
await corona.app.destroySessionObject(model.id);
await halo.app.destroySessionObject(model.id);
unsubscribe();
};
return init(model, { options, element }, corona, error, onDestroy);
return init(model, { options, element }, halo, error, onDestroy);
}

View File

@@ -13,14 +13,14 @@ import { modelStore, rpcRequestModelStore } from '../stores/modelStore';
* @property {string} id
*/
export default async function getObject({ id, options, element }, corona) {
export default async function getObject({ id, options, element }, halo) {
const key = `${id}`;
let rpc = rpcRequestModelStore.get(key);
if (!rpc) {
rpc = corona.app.getObject(id);
rpc = halo.app.getObject(id);
rpcRequestModelStore.set(key, rpc);
}
const model = await rpc;
modelStore.set(key, model);
return init(model, { options, element }, corona);
return init(model, { options, element }, halo);
}

View File

@@ -1,9 +1,9 @@
import vizualizationAPI from '../viz';
export default async function (model, optional, corona, initialError, onDestroy = async () => {}) {
export default async function (model, optional, halo, initialError, onDestroy = async () => {}) {
const api = vizualizationAPI({
model,
corona,
halo,
initialError,
onDestroy,
});

View File

@@ -1,9 +1,9 @@
import { load, clearFromCache } from '../load';
describe('load', () => {
let corona = {};
let halo = {};
beforeEach(() => {
corona = {
halo = {
config: {
load: sinon.stub(),
},
@@ -16,7 +16,7 @@ describe('load', () => {
it('should throw when resolving to a falsy value', async () => {
const loader = () => false;
try {
await load('pie', '1.0.0', corona, loader);
await load('pie', '1.0.0', halo, loader);
expect(0).to.equal(1);
} catch (e) {
expect(e.message).to.equal("Failed to load supernova: 'pie v1.0.0'");
@@ -25,14 +25,14 @@ describe('load', () => {
it('should call load() with name and version', async () => {
const loader = sinon.stub();
load('pie', '1.0.0', corona, loader);
load('pie', '1.0.0', halo, loader);
expect(loader).to.have.been.calledWithExactly({ name: 'pie', version: '1.0.0' });
});
it('should load valid sn', async () => {
const sn = { component: {} };
const loader = () => sn;
const s = await load('pie', '1.0.0', corona, loader);
const s = await load('pie', '1.0.0', halo, loader);
expect(s).to.eql(sn);
});
@@ -40,16 +40,16 @@ describe('load', () => {
const sn = { component: {} };
const loader = () => sn;
const spy = sinon.spy(loader);
load('pie', '1.0.0', corona, spy);
load('pie', '1.0.0', corona, spy);
load('pie', '1.0.0', corona, spy);
load('pie', '1.0.0', halo, spy);
load('pie', '1.0.0', halo, spy);
load('pie', '1.0.0', halo, spy);
expect(spy.callCount).to.equal(1);
});
it('should fallback to global load() when custom loader is not provided', async () => {
const sn = { component: {} };
corona.config.load.returns(sn);
const s = await load('pie', '1.0.0', corona);
halo.config.load.returns(sn);
const s = await load('pie', '1.0.0', halo);
expect(s).to.eql(sn);
});
});

View File

@@ -5,7 +5,7 @@ describe('type', () => {
let satisfies;
let create;
let sb;
let corona;
let halo;
before(() => {
sb = sinon.createSandbox();
SNFactory = sb.stub();
@@ -21,8 +21,8 @@ describe('type', () => {
);
});
beforeEach(() => {
corona = { public: { env: 'env' } };
c = create({ name: 'pie', version: '1.1.0' }, corona, { load: 'customLoader' });
halo = { public: { env: 'env' } };
c = create({ name: 'pie', version: '1.1.0' }, halo, { load: 'customLoader' });
});
afterEach(() => {
sb.reset();
@@ -61,7 +61,7 @@ describe('type', () => {
const def = Promise.resolve('def');
const normalized = { qae: { properties: {} } };
load.withArgs('pie', '1.1.0', corona, 'customLoader').returns(def);
load.withArgs('pie', '1.1.0', halo, 'customLoader').returns(def);
SNFactory.withArgs('def').returns(normalized);
const sn = await c.supernova();
@@ -74,7 +74,7 @@ describe('type', () => {
const def = Promise.resolve('def');
const normalized = { qae: { properties: { initial: { a: 'a', b: 'b' } } } };
load.withArgs('pie', '1.1.0', corona, 'customLoader').returns(def);
load.withArgs('pie', '1.1.0', halo, 'customLoader').returns(def);
SNFactory.withArgs('def').returns(normalized);
const props = await c.initialProperties({ c: 'c', b: 'override' });

View File

@@ -24,7 +24,7 @@ describe('types', () => {
});
beforeEach(() => {
c = create({ corona: 'corona' });
c = create({ halo: 'halo' });
type.returns('t');
});
@@ -47,7 +47,7 @@ describe('types', () => {
name: 'pie',
version: '1.0.3',
},
'corona',
'halo',
'opts'
);
});

View File

@@ -10,7 +10,7 @@ import { load } from './load';
* @property {object=} meta
*/
export default function create(info, corona, opts = {}) {
export default function create(info, halo, opts = {}) {
let sn;
let stringified;
const { meta } = opts;
@@ -24,8 +24,8 @@ export default function create(info, corona, opts = {}) {
return true;
},
supernova: () =>
load(type.name, type.version, corona, opts.load).then((SNDefinition) => {
sn = sn || SNFactory(SNDefinition, corona.public.galaxy);
load(type.name, type.version, halo, opts.load).then((SNDefinition) => {
sn = sn || SNFactory(SNDefinition, halo.public.galaxy);
stringified = JSON.stringify(sn.qae.properties.initial);
return sn;
}),

View File

@@ -13,7 +13,7 @@ export function semverSort(arr) {
];
}
export function typeCollection(name, corona) {
export function typeCollection(name, halo) {
const versions = {};
let sortedVersions = null;
@@ -29,7 +29,7 @@ export function typeCollection(name, corona) {
name,
version,
},
corona,
halo,
opts
);
sortedVersions = null;
@@ -50,7 +50,7 @@ export function typeCollection(name, corona) {
};
}
export function create({ corona, parent }) {
export function create({ halo, parent }) {
const tc = {};
const p = parent || {
@@ -60,7 +60,7 @@ export function create({ corona, parent }) {
return {
register: (typeInfo, opts) => {
if (!tc[typeInfo.name]) {
tc[typeInfo.name] = typeCollection(typeInfo.name, corona);
tc[typeInfo.name] = typeCollection(typeInfo.name, halo);
}
tc[typeInfo.name].register(typeInfo.version, opts);
},

View File

@@ -4,7 +4,7 @@ import getPatches from './utils/patcher';
const noopi = () => {};
export default function viz({ model, corona, initialError, onDestroy = async () => {} } = {}) {
export default function viz({ model, halo, initialError, onDestroy = async () => {} } = {}) {
let unmountCell = noopi;
let cellRef = null;
let mountedReference = null;
@@ -39,7 +39,7 @@ export default function viz({ model, corona, initialError, onDestroy = async ()
* @hideconstructor
* @classdesc A controller to further modify a supernova after it has been rendered.
* @example
* const ctl = await nucleus(app).render({
* const ctl = await embed(app).render({
* element,
* type: 'barchart'
* });
@@ -53,7 +53,7 @@ export default function viz({ model, corona, initialError, onDestroy = async ()
}
mountedReference = element;
[unmountCell, cellRef] = glueCell({
corona,
halo,
element,
model,
initialSnOptions,

View File

@@ -0,0 +1,53 @@
module.exports = {
fromJsdoc: {
glob: [
'!*.spec.js',
'../nucleus/src/**/*.js',
'../supernova/src/**/*.js',
'../locale/src/translator.js',
'../theme/src/**/*.js',
],
api: {
stability: 'experimental',
},
output: {
sort: {
alpha: false,
},
file: './api-spec/spec.json',
},
parse: {
types: {
'qae.NxAppLayout': {},
'enigma.GenericObject': {},
'enigma.Global': {},
'enigma.Doc': {
url: 'https://github.com/qlik-oss/enigma.js/blob/master/docs/api.md#generated-api',
},
'qae.GenericObjectLayout': {
url: 'https://core.qlik.com/services/qix-engine/apis/qix/definitions/#genericobjectlayout',
},
'qae.GenericObjectProperties': {
url: 'https://core.qlik.com/services/qix-engine/apis/qix/definitions/#genericobjectproperties',
},
'qae.NxDimension': {
url: 'https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxdimension',
},
'qae.NxMeasure': {
url: 'https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxmeasure',
},
},
},
},
toMd: {
output: '../../docs/stardust-api.md',
type(e) {
if (e.indexOf('qae.') === 0) {
return {
url: `https://core.qlik.com/services/qix-engine/apis/qix/definitions/#${e.substr(4).toLowerCase()}`,
};
}
return undefined;
},
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
{
"scriptappy": "1.0.0",
"info": {
"name": "@nebula.js/stardust",
"description": "Product and framework agnostic integration API for Qlik's Associative Engine",
"version": "0.6.0-alpha.0",
"license": "MIT"
},
"entries": {},
"definitions": {}
}

View File

@@ -7,7 +7,7 @@
"name": "<%= user %>",
"email": "<%= email %>"
},
"keywords": ["qlik", "nebula", "supernova"],
"keywords": ["qlik", "nebula", "stardust"],
"files": ["dist"],
"engines": {
"node": ">=8"

View File

@@ -7,7 +7,7 @@
"name": "<%= user %>",
"email": "<%= email %>"
},
"keywords": ["qlik", "nebula", "supernova"],
"keywords": ["qlik", "nebula", "stardust"],
"files": ["dist"],
"engines": {
"node": ">=8"

View File

@@ -5,11 +5,6 @@ title: Introduction
## What is nebula.js?
`nebula.js` is a collection of JavaScript libraries and APIs that helps developers integrate visualizations and mashups on top of Qlik's Associative Engine.
`nebula.js` is a collection of JavaScript libraries, visualizations and CLIs that helps developers build and integrate visualizations on top of Qlik's Associative Engine.
It consists of :
- `nucleus`: A product and framework agnostic JavaScript library for building mashups.
- `supernova`: A JavaScript API for consuming and visualizing QIX data.
- `snapshooter`: A JavaScript API for rendering a `supernova` in offline mode.
- `cli`: Tools to help you create, develop and build a `supernova`.
The collection is organized under the `@nebula.js` scope in the form of npm packages. The primary package is `@nebula.js/stardust` which contains APIs for integrating existing visualizations with mashups, as well as APIs for building custom visualizations.

View File

@@ -1,10 +1,16 @@
---
id: sn-reference
id: api-reference
title: API Reference
---
# @nebula.js/stardust
> version: 0.6.0-alpha.0
## Table of contents
- [function: embed(app[, instanceConfig])](#function-embedapp-instanceconfig)
- [embed.createConfiguration(configuration)](#embedcreateconfigurationconfiguration)
- [function: useState(initialState)](#function-usestateinitialstate)
- [function: useEffect(effect[, deps])](#function-useeffecteffect-deps)
- [function: useMemo(factory, deps)](#function-usememofactory-deps)
@@ -24,16 +30,27 @@ title: API Reference
- [function: useConstraints()](#function-useconstraints)
- [function: useOptions()](#function-useoptions)
- [function: onTakeSnapshot(snapshotCallback)](#function-ontakesnapshotsnapshotcallback)
- [interface: Supernova([env])](#interface-supernovaenv)
- [interface: SupernovaDefinition](#interface-supernovadefinition)
- [interface: SetStateFn(newState)](#interface-setstatefnnewstate)
- [type EffectCallback = <[Function]>](#type-effectcallback-function)
- [interface: Rect](#interface-rect)
- [interface: ActionDefinition](#interface-actiondefinition)
- [interface: Constraints](#interface-constraints)
- [interface: QAEDefinition](#interface-qaedefinition)
- [interface: DataTarget](#interface-datatarget)
- [interface: FieldTarget](#interface-fieldtarget)
- [interface: Context](#interface-context)
- [interface: Configuration](#interface-configuration)
- [undefined: Galaxy.translator](#undefined-galaxytranslator)
- [undefined: Galaxy.flags](#undefined-galaxyflags)
- [undefined: Galaxy.anything](#undefined-galaxyanything)
- [class: Embed](#class-embed)
- [embed.render(cfg)](#embedrendercfg)
- [embed.context(ctx)](#embedcontextctx)
- [embed.selections()](#embedselections)
- [interface: ThemeInfo](#interface-themeinfo)
- [class: SupernovaController](#class-supernovacontroller)
- [supernovaController.destroy()](#supernovacontrollerdestroy)
- [interface: Flags](#interface-flags)
- [interface: CreateConfig](#interface-createconfig)
- [interface: BaseConfig](#interface-baseconfig)
- [interface: GetConfig](#interface-getconfig)
- [type Field = <[string]|`qae.NxDimension`|`qae.NxMeasure`|[LibraryField]>](#type-field-stringqaenxdimensionqaenxmeasurelibraryfield)
- [interface: LibraryField](#interface-libraryfield)
- [class: AppSelections](#class-appselections)
- [appSelections.mount(element)](#appselectionsmountelement)
- [appSelections.unmount()](#appselectionsunmount)
- [class: ObjectSelections](#class-objectselections)
- [objectSelections.begin(paths)](#objectselectionsbeginpaths)
- [objectSelections.clear()](#objectselectionsclear)
@@ -48,6 +65,18 @@ title: API Reference
- [objectSelections.goModal(paths)](#objectselectionsgomodalpaths)
- [objectSelections.noModal([accept])](#objectselectionsnomodalaccept)
- [objectSelections.abortModal()](#objectselectionsabortmodal)
- [interface: LoadType(type)](#interface-loadtypetype)
- [interface: TypeInfo](#interface-typeinfo)
- [interface: Supernova(galaxy)](#interface-supernovagalaxy)
- [interface: SupernovaDefinition](#interface-supernovadefinition)
- [interface: SetStateFn(newState)](#interface-setstatefnnewstate)
- [type EffectCallback = <[Function]>](#type-effectcallback-function)
- [interface: Rect](#interface-rect)
- [interface: ActionDefinition](#interface-actiondefinition)
- [interface: Constraints](#interface-constraints)
- [interface: QAEDefinition](#interface-qaedefinition)
- [interface: DataTarget](#interface-datatarget)
- [interface: FieldTarget](#interface-fieldtarget)
- [class: Translator](#class-translator)
- [translator.add(item)](#translatoradditem)
- [translator.get(str[, args])](#translatorgetstr-args)
@@ -66,6 +95,53 @@ title: API Reference
## API
### function: embed(app[, instanceConfig])
- `app` <`enigma.Doc`>
- `instanceConfig` <[Configuration]>
- `returns:` <[Embed]>
Initiates a new embed instance using the specified `app`.
```js
import { embed } from '@nebula.js/stardust';
const n = embed(app);
n.render({ id: 'abc' });
```
#### embed.createConfiguration(configuration)
- `configuration` <[Configuration]> The configuration object
- `returns:` <[Embed]>
Creates a new `embed` scope bound to the specified `configuration`.
The configuration is merged with all previous scopes.
```js
import { embed } from '@nebula.js/stardust';
// create a 'master' config which registers all types
const m = embed.createConfiguration({
types: [
{
name: 'mekko',
version: '1.0.0',
load: () => Promise.resolve(mekko),
},
],
});
// create an alternate config with dark theme
// and inherit the config from the previous
const d = m.createConfiguration({
theme: 'dark',
});
m(app).render({ type: 'mekko' }); // will render the object with default theme
d(app).render({ type: 'mekko' }); // will render the object with 'dark' theme
embed(app).render({ type: 'mekko' }); // will throw error since 'mekko' is not a register type on the default instance
```
### function: useState(initialState)
- `initialState` <`S`|[Function]> The initial state.
@@ -74,7 +150,7 @@ title: API Reference
Creates a stateful value.
```js
import { useState } from '@nebula.js/supernova';
import { useState } from '@nebula.js/stardust';
// ...
// initiate with simple primitive value
const [zoomed, setZoomed] = useState(false);
@@ -94,7 +170,7 @@ const [value, setValue] = useState(() => heavy());
Triggers a callback function when a dependant value changes.
```js
import { useEffect } from '@nebula.js/supernova';
import { useEffect } from '@nebula.js/stardust';
// ...
useEffect(() => {
console.log('mounted');
@@ -113,7 +189,7 @@ useEffect(() => {
Creates a stateful value when a dependant changes.
```js
import { useMemo } from '@nebula.js/supernova';
import { useMemo } from '@nebula.js/stardust';
// ...
const v = useMemo(() => {
return doSomeHeavyCalculation();
@@ -129,8 +205,8 @@ const v = useMemo(() => {
Runs a callback function when a dependant changes.
```js
import { usePromise } from '@nebula.js/supernova';
import { useModel } from '@nebula.js/supernova';
import { usePromise } from '@nebula.js/stardust';
import { useModel } from '@nebula.js/stardust';
// ...
const model = useModel();
const [resolved, rejected] = usePromise(() => model.getLayout(), []);
@@ -143,7 +219,7 @@ const [resolved, rejected] = usePromise(() => model.getLayout(), []);
Gets the HTMLElement this supernova is rendered into.
```js
import { useElement } from '@nebula.js/supernova';
import { useElement } from '@nebula.js/stardust';
// ...
const el = useElement();
el.innerHTML = 'Hello!';
@@ -156,7 +232,7 @@ el.innerHTML = 'Hello!';
Gets the size of the HTMLElement the supernova is rendered into.
```js
import { useRect } from '@nebula.js/supernova';
import { useRect } from '@nebula.js/stardust';
// ...
const rect = useRect();
useEffect(() => {
@@ -166,12 +242,12 @@ useEffect(() => {
### function: useLayout()
- `returns:` <[qae.GenericObjectLayout]>
- `returns:` <`qae.GenericObjectLayout`>
Gets the layout of the generic object associated with this supernova.
```js
import { useLayout } from '@nebula.js/supernova';
import { useLayout } from '@nebula.js/stardust';
// ...
const layout = useLayout();
console.log(layout);
@@ -179,7 +255,7 @@ console.log(layout);
### function: useStaleLayout()
- `returns:` <[qae.GenericObjectLayout]>
- `returns:` <`qae.GenericObjectLayout`>
Gets the layout of the generic object associated with this supernova.
@@ -190,7 +266,7 @@ The returned value from `useStaleLayout()` and `useLayout()` are identical when
is not in a modal state.
```js
import { useStaleLayout } from '@nebula.js/supernova';
import { useStaleLayout } from '@nebula.js/stardust';
// ...
const staleLayout = useStaleLayout();
console.log(staleLayout);
@@ -198,12 +274,12 @@ console.log(staleLayout);
### function: useAppLayout()
- `returns:` <[qae.NxAppLayout]> The app layout
- `returns:` <`qae.NxAppLayout`> The app layout
Gets the layout of the app associated with this supernova.
```js
import { useAppLayout } from '@nebula.js/supernova';
import { useAppLayout } from '@nebula.js/stardust';
// ...
const appLayout = useAppLayout();
console.log(appLayout.qLocaleInfo);
@@ -211,12 +287,12 @@ console.log(appLayout.qLocaleInfo);
### function: useModel()
- `returns:` <`enigma.GenericObject`|[undefined]>
- `returns:` <`enigma.GenericObject`|`undefined`>
Gets the generic object API of the generic object connected to this supernova.
```js
import { useModel } from '@nebula.js/supernova';
import { useModel } from '@nebula.js/stardust';
// ...
const model = useModel();
useEffect(() => {
@@ -228,12 +304,12 @@ useEffect(() => {
### function: useApp()
- `returns:` <`enigma.Doc`|[undefined]> The doc API.
- `returns:` <`enigma.Doc`|`undefined`> The doc API.
Gets the doc API.
```js
import { useApp } from '@nebula.js/supernova';
import { useApp } from '@nebula.js/stardust';
// ...
const app = useApp();
useEffect(() => {
@@ -245,12 +321,12 @@ useEffect(() => {
### function: useGlobal()
- `returns:` <`enigma.Global`|[undefined]> The global API.
- `returns:` <`enigma.Global`|`undefined`> The global API.
Gets the global API.
```js
import { useGlobal } from '@nebula.js/supernova';
import { useGlobal } from '@nebula.js/stardust';
// ...
const g = useGlobal();
@@ -268,9 +344,9 @@ useEffect(() => {
Gets the object selections.
```js
import { useSelections } from '@nebula.js/supernova';
import { useElement } from '@nebula.js/supernova';
import { useEffect } from '@nebula.js/supernova';
import { useSelections } from '@nebula.js/stardust';
import { useElement } from '@nebula.js/stardust';
import { useEffect } from '@nebula.js/stardust';
// ...
const selections = useSelections();
const element = useElement();
@@ -292,7 +368,7 @@ useEffect(() => {
Gets the theme.
```js
import { useTheme } from '@nebula.js/supernova';
import { useTheme } from '@nebula.js/stardust';
const theme = useTheme();
console.log(theme.getContrastinColorTo('#ff0000'));
@@ -305,7 +381,7 @@ console.log(theme.getContrastinColorTo('#ff0000'));
Gets the translator.
```js
import { useTranslator } from '@nebula.js/supernova';
import { useTranslator } from '@nebula.js/stardust';
// ...
const translator = useTranslator();
console.log(translator.get('SomeString'));
@@ -320,7 +396,7 @@ console.log(translator.get('SomeString'));
Registers a custom action.
```js
import { useAction } from '@nebula.js/supernova';
import { useAction } from '@nebula.js/stardust';
// ...
const [zoomed, setZoomed] = useState(false);
const act = useAction(
@@ -346,8 +422,8 @@ The constraints are set on the nuclues configuration before the supernova is ren
and should respected by you when implementing the supernova.
```js
// configure nucleus to disallow active interactions when rendering
nucleus(app, {
// configure embed to disallow active interactions when rendering
embed(app, {
constraints: {
active: true, // do not allow interactions
},
@@ -355,7 +431,7 @@ nucleus(app, {
```
```js
import { useConstraints } from '@nebula.js/supernova';
import { useConstraints } from '@nebula.js/stardust';
// ...
const constraints = useConstraints();
useEffect(() => {
@@ -384,8 +460,8 @@ are only temporary settings applied to the supernova when rendered.
You have the responsibility to provide documentation of the options you support, if any.
```js
// when rendering the supernova with nucleus, anything can be set in options
nucleus(app).render({
// when embedding the supernova, anything can be set in options
embed(app).render({
element,
type: 'my-chart',
options: {
@@ -396,8 +472,8 @@ nucleus(app).render({
```js
// it is up to you use and implement the provided options
import { useOptions } from '@nebula.js/supernova';
import { useEffect } from '@nebula.js/supernova';
import { useOptions } from '@nebula.js/stardust';
import { useEffect } from '@nebula.js/stardust';
// ...
const options = useOptions();
useEffect(() => {
@@ -416,9 +492,9 @@ useEffect(() => {
Registers a callback that is called when a snapshot is taken.
```js
import { onTakeSnapshot } from '@nebula.js/supernova';
import { useState } from '@nebula.js/supernova';
import { useLayout } from '@nebula.js/supernova';
import { onTakeSnapshot } from '@nebula.js/stardust';
import { useState } from '@nebula.js/stardust';
import { useLayout } from '@nebula.js/stardust';
const layout = useLayout();
const [zoomed] = useState(layout.isZoomed || false);
@@ -429,15 +505,241 @@ onTakeSnapshot((copyOfLayout) => {
});
```
### interface: Supernova([env])
### interface: Context
- `env` <[Object]>
- `constraints` <[Object]>
- `active` <[boolean]>
- `passive` <[boolean]>
- `select` <[boolean]>
- `theme` <[string]> Defaults to `light`
- `language` <[string]> Defaults to `en-US`
### interface: Configuration
- `context` <[Context]>
- `types` <[Array]<[TypeInfo]>>
- `themes` <[Array]<[ThemeInfo]>>
- `anything` <[Object]>
### undefined: Galaxy.translator
### undefined: Galaxy.flags
### undefined: Galaxy.anything
### class: Embed
#### embed.render(cfg)
- `cfg` <[CreateConfig]|[GetConfig]> The render configuration.
- `returns:` <[Promise]<[SupernovaController]>> A controller to the rendered visualization
Renders a visualization into an HTMLElement.
```js
// render from existing object
n.render({
element: el,
id: 'abcdef',
});
```
```js
// render on the fly
n.render({
type: 'barchart',
fields: ['Product', { qLibraryId: 'u378hn', type: 'measure' }],
});
```
#### embed.context(ctx)
- `ctx` <[Context]> The context to update.
- `returns:` <[Promise]<`undefined`>>
Updates the current context of this embed instance.
Use this when you want to change some part of the current context, like theme.
```js
// change theme
n.context({ theme: 'dark' });
```
```js
// limit constraints
n.context({ constraints: { active: true } });
```
#### embed.selections()
- `returns:` <[Promise]<[AppSelections]>>
Gets the app selections of this instance.
```js
const selections = await n.selections();
selections.mount(element);
```
### interface: ThemeInfo
- `id` <[string]> Theme identifier
- `load` <[Function]> A function that should return a Promise that resolve to a raw JSON theme
### class: SupernovaController
A controller to further modify a supernova after it has been rendered.
```js
const ctl = await embed(app).render({
element,
type: 'barchart',
});
ctl.destroy();
```
#### supernovaController.destroy()
Destroys the supernova and removes if from the the DOM.
```js
const ctl = ctl.destroy();
```
### interface: Flags
- `isEnabled` <[Function]> Checks whether the specified flag is enabled.
### interface: CreateConfig
- extends: <[BaseConfig]>
* `type` <[string]>
* `version` <[string]>
* `fields` <[Array]>
* `properties` <`qae.GenericObjectProperties`>
### interface: BaseConfig
- `element` <`HTMLElement`>
- `options` <[Object]>
### interface: GetConfig
- extends: <[BaseConfig]>
* `id` <[string]>
### type Field = <[string]|`qae.NxDimension`|`qae.NxMeasure`|[LibraryField]>
### interface: LibraryField
- `qLibraryId` <[string]>
- `type` <`'dimension'`|`'measure'`>
### class: AppSelections
#### appSelections.mount(element)
- `element` <`HTMLElement`>
Mounts the app selection UI into the provided HTMLElement
```js
selections.mount(element);
```
#### appSelections.unmount()
Unmounts the app selection UI from the DOM
```js
selections.unmount();
```
### class: ObjectSelections
#### objectSelections.begin(paths)
- `paths` <[Array]<[string]>>
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.clear()
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.confirm()
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.cancel()
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.select(s)
- `s` <[Object]>
- `method` <[string]>
- `params` <[Array]<`any`>>
- `returns:` <[Promise]<[boolean]>>
#### objectSelections.canClear()
- `returns:` <[boolean]>
#### objectSelections.canConfirm()
- `returns:` <[boolean]>
#### objectSelections.canCancel()
- `returns:` <[boolean]>
#### objectSelections.isActive()
- `returns:` <[boolean]>
#### objectSelections.isModal()
- `returns:` <[boolean]>
#### objectSelections.goModal(paths)
- `paths` <[Array]<[string]>>
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.noModal([accept])
- `accept` <[boolean]>
- `returns:` <[Promise]<`undefined`>>
#### objectSelections.abortModal()
- `returns:` <[Promise]<`undefined`>>
### interface: LoadType(type)
- `type` <[Object]>
- `name` <[string]>
- `version` <[string]>
- `returns:` <[Promise]<[Supernova]>>
### interface: TypeInfo
- `name` <[string]>
- `version` <[string]>
- `load` <[LoadType]>
- `meta` <[Object]>
### interface: Supernova(galaxy)
- `galaxy` <`Galaxy`>
- `returns:` <[SupernovaDefinition]>
The entry point for defining a supernova.
```js
import { useElement, useLayout } from '@nebula.js/supernova';
import { useElement, useLayout } from '@nebula.js/stardust';
export default function () {
return {
@@ -490,15 +792,15 @@ export default function () {
### interface: QAEDefinition
- `properties` <[qae.GenericObjectProperties]>
- `properties` <`qae.GenericObjectProperties`>
- `data` <[Object]>
- `targets` <[Array]<[DataTarget]>>
### interface: DataTarget
- `path` <[string]>
- `dimensions` <[FieldTarget]<[qae.NxDimension]>>
- `measures` <[FieldTarget]<[qae.NxMeasure]>>
- `dimensions` <[FieldTarget]<`qae.NxDimension`>>
- `measures` <[FieldTarget]<`qae.NxMeasure`>>
### interface: FieldTarget
@@ -507,66 +809,6 @@ export default function () {
- `added` <[Function]>
- `removed` <[Function]>
### class: ObjectSelections
#### objectSelections.begin(paths)
- `paths` <[Array]<[string]>>
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.clear()
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.confirm()
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.cancel()
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.select(s)
- `s` <[Object]>
- `method` <[string]>
- `params` <[Array]<`any`>>
- `returns:` <[Promise]<[boolean]>>
#### objectSelections.canClear()
- `returns:` <[boolean]>
#### objectSelections.canConfirm()
- `returns:` <[boolean]>
#### objectSelections.canCancel()
- `returns:` <[boolean]>
#### objectSelections.isActive()
- `returns:` <[boolean]>
#### objectSelections.isModal()
- `returns:` <[boolean]>
#### objectSelections.goModal(paths)
- `paths` <[Array]<[string]>>
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.noModal([accept])
- `accept` <[boolean]>
- `returns:` <[Promise]<[undefined]>>
#### objectSelections.abortModal()
- `returns:` <[Promise]<[undefined]>>
### class: Translator
#### translator.add(item)
@@ -661,13 +903,13 @@ theme.getStyle('', '', 'fontSize'));
#### interface: ScalePalette
- `key` <[string]>
- `type` <`gradient`|`class`>
- `type` <`'gradient'`|`'class'`>
- `colors` <[Array]<[string]>>
#### interface: DataPalette
- `key` <[string]>
- `type` <`pyramid`|`row`>
- `type` <`'pyramid'`|`'row'`>
- `colors` <[Array]|[Array]>
#### interface: ColorPickerPalette
@@ -681,27 +923,42 @@ theme.getStyle('', '', 'fontSize'));
- `nil` <[string]>
- `others` <[string]>
[enigma.doc]: undefined
[s]: undefined
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
[any]: undefined
[t]: undefined
[htmlelement]: undefined
[qae.genericobjectlayout]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#genericobjectlayout
[qae.nxapplayout]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxapplayout
[qae.genericobjectlayout]: undefined
[qae.nxapplayout]: undefined
[enigma.genericobject]: undefined
[undefined]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined
[enigma.doc]: undefined
[undefined]: undefined
[enigma.global]: undefined
[a]: undefined
[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type
[qae.genericobjectproperties]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#genericobjectproperties
[qae.nxdimension]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxdimension
[qae.nxmeasure]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxmeasure
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[qae.genericobjectproperties]: undefined
[qae.nxdimension]: undefined
[qae.nxmeasure]: undefined
[galaxy]: undefined
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type
[context]: #interface-context
[configuration]: #interface-configuration
[embed]: #class-embed
[themeinfo]: #interface-themeinfo
[supernovacontroller]: #class-supernovacontroller
[createconfig]: #interface-createconfig
[baseconfig]: #interface-baseconfig
[getconfig]: #interface-getconfig
[libraryfield]: #interface-libraryfield
[appselections]: #class-appselections
[objectselections]: #class-objectselections
[loadtype]: #interface-loadtypetype
[typeinfo]: #interface-typeinfo
[supernova]: #interface-supernovagalaxy
[supernovadefinition]: #interface-supernovadefinition
[effectcallback]: #type-effectcallback-function
[rect]: #interface-rect
@@ -709,7 +966,6 @@ theme.getStyle('', '', 'fontSize'));
[qaedefinition]: #interface-qaedefinition
[datatarget]: #interface-datatarget
[fieldtarget]: #interface-fieldtarget
[objectselections]: #class-objectselections
[translator]: #class-translator
[theme]: #class-theme
[scalepalette]: #interface-scalepalette

View File

@@ -7,7 +7,7 @@ An essential part of the Qlik experience is filtering data through selections. M
## Current app selections bar
The current app selections bar shows you the currently active selections in the specified app. To render this bar you need an `HTMLElement` first:
The current app selections bar shows you the currently active selections in the specified app. To render this bar you first need an `HTMLElement`:
```html
<div class="curr-selections"></div>
@@ -16,7 +16,7 @@ The current app selections bar shows you the currently active selections in the
You can then `mount` the selections UI into that element:
```js
const n = nucleus(enigmaApp);
const n = embed(enigmaApp);
(await n.selections()).mount(document.querySelector('.curr-selections'));
```
@@ -34,9 +34,9 @@ As you start applying selections in the various charts, the UI will update to re
If you are connected to multiple apps, you can show the current selections in each one by mounting the bar into different elements:
```js
(await nucleus(enigmaApp).selections()).mount(document.querySelector('.curr-selections'));
(await embed(enigmaApp).selections()).mount(document.querySelector('.curr-selections'));
(await nucleus(anotherApp, { context: { theme: 'dark' } }).selections()).mount(
(await embed(anotherApp, { context: { theme: 'dark' } }).selections()).mount(
document.querySelector('.another-curr-selections')
);
```

View File

@@ -1,18 +1,20 @@
---
id: nucleus-configuration
id: embed-configuration
title: Configuration
---
When you are building a website, whether it's a company wide site or a small personal project, you most likely have your own design guidelines, UX patterns and requirements, and would want to apply those guidelines on the things you integrate with it; charts should use your color schemes, fonts, locale and respect any restrictions you may have on interactivty. You may also want to control how charts and themes are loaded based on whether your solution is online or offline.
You can control most of these through the `Configuration` object for `nucleus`.
You can control most of these through the `Configuration` object.
## Temporary config
The `nucleus` module is a function that requires an `enigmaApp` and an optional `config` object, which you then can use to render charts:
The `Configuration` object is an optional argument that you can provide when instantiating the `embed` instance:
```js
const n = nucleus(enigmaApp, {
import { embed } from '@nebula.js/stardust';
const n = embed(enigmaApp, {
context: {
theme: 'dark',
},
@@ -32,7 +34,7 @@ If you are working with multiple apps, or want to have multiple different config
Create a `baseConfig` which does the heavy lifting of registering types and themes:
```js
const baseConfig = nucleus.createConfiguration({
const baseConfig = embed.createConfiguration({
types: [
/* register type once*/
],
@@ -72,19 +74,19 @@ swedishPink.render(/* chart config */);
## Registering types
Before rendering a supernova type, its module needs to be loaded and registered. You can load the modules you know you will need from npm:
Before rendering a visualization, its module needs to be loaded and registered. You can load the modules you know you will need from npm:
```bash
$ npm install @nebula.js/sn-bar-chart @nebula.js/sn-pie-chart
```
And then register each one individually:
And then register each `type` individually:
```js
import barchart from '@nebula.js/sn-bar-chart';
import piechart from '@nebula.js/sn-pie-chart';
nucles.createConfiguration({
embed.createConfiguration({
types: [
{
type: 'bar',
@@ -108,25 +110,25 @@ Start by installing the module:
npm install d3-require
```
and then configure it to load modules from a CDN like `https://unpkg.com`, as well as specify an alias to use the local version of `@nebula.js/supernova`:
and then configure it to load modules from a CDN like `https://unpkg.com`, as well as specify an alias to use the local version of `@nebula.js/stardust`:
```js
import { requireFrom } from 'd3-require';
import * as supernova from '@nebula.js/supernova';
import * as stardust from '@nebula.js/stardust';
const loadSnType = requireFrom(name => `https://unpkg.com/@nebula.js/sn-${name}-chart`).alias({
'@nebula.js/supernova': supernova,
const loadSnType = requireFrom((name) => `https://unpkg.com/@nebula.js/sn-${name}-chart`).alias({
'@nebula.js/stardust': stardust,
});
```
You can then configure all types you expect might be used:
```js
const types = ['bar', 'line', 'pie', 'sankey'].map(t => ({
const types = ['bar', 'line', 'pie', 'sankey'].map((t) => ({
type: t,
load: () => loadSnType(t),
}));
const baseConfig = nucleus.createConfiguration({ types });
const baseConfig = stardust.embed.createConfiguration({ types });
```
The type will be loaded from the remote url the first time you render it:
@@ -140,23 +142,23 @@ baseConfig(enigmaApp).render({
## Context
When setting up the configuration you can apply a `context` which controls the language, theme and constraints in the supernova types you render:
When setting up the configuration you can apply a `context` which controls the language, theme and constraints in each visualization you render:
```js
nucleus.createConfiguration({
embed.createConfiguration({
context: {},
});
```
### Constraints
Constraints enables you to instruct supernova types to disable certain types of interactions and behaviour.
Constraints enables you to instruct a visualization to disable certain types of interactions and behaviour.
There are three different constraints you can apply:
- `passive`: disable interactions like tooltips.
- `active`: disable interactions that affect the state of the visual representation like zoom, scroll, etc.
- `select`: disable selections
- `select`: disable selections.
```js
{
@@ -170,11 +172,11 @@ There are three different constraints you can apply:
};
```
`nucleus` does not enforce these constraints in any way, instead it is up to the supernova developer to respect and implement them.
`stardust` does not enforce these constraints in any way, instead it is up to the visualization developer to respect and implement them.
### Language
`nucleus` supports 15 languages:
`stardust` supports 15 languages:
- `'en-US'` - American English
- `'sv-SE'` - Swedish
@@ -242,12 +244,3 @@ You can also register custom themes and apply one of those on the context:
```
![Pinkish theme](assets/theme-pinkish.png)
## Snapshots
TODO
Read more:
- [API reference](./nucleus-reference#interface-configuration)
- [Theme API](#TODO)

View File

@@ -0,0 +1,72 @@
---
id: embedding-visualizations
title: Embedding visualizations
---
You can embed a visualization in two ways:
1. On the fly
1. From an existing object
Rendering is done using the `render()` method on the instance returned by the `embed` function, which at minimum requires the `HTMLElement` you want to render into:
```js
import { embed } from '@nebula.js/stardust';
const n = embed(enigmaApp);
n.render({
element,
// rest of the config
});
```
## Render on the fly
When rendering a visualization on the fly you need to specify the `type` you want to render:
```js
n.render({
element,
type: 'barchart',
});
```
Some visualizations have minimum requirements on the various properties and/or data it needs in order to render, in which case you might see something like this:
![Incomplete visualization](assets/supernova-incomplete.png)
To provide initial data to the visualization, add the data dimensions and measures into the `fields` property:
```js
n.render({
element,
type: 'barchart',
fields: ['Region', '=sum(Sales)'],
});
```
You can also modify the initial properties:
```js
n.render({
element,
type: 'barchart',
fields: ['Product', '=sum(Sales)'],
properties: {
title: 'Sales by region',
},
});
```
![Bar chart](assets/supernova-barchart.png)
## Render from existing objects
If you already have created a generic object in your app and want to render it, you can do so by providing the object's `id`:
```js
n.render({
element,
id: '<ObjectID>',
});
```

View File

@@ -5,51 +5,47 @@ title: Installation
All `nebula.js` modules are available on the public npm registry as npm packages and can be installed through either npm or as a script import.
`@nebula.js/supernova` and `@nebula.js/nucleus` are the two core modules that you will be using and are required when integrating `nebula.js` on the web.
`@nebula.js/stardust` is the primary module that you will be using and is required when integrating `nebula.js` on the web.
## Script import
The easiest way to load the modules is from a CDN like `jsdelivr`:
The easiest way to load the module is from a CDN like `jsdelivr`:
```html
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/supernova" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/nucleus" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/stardust" crossorigin></script>
```
Both are UMD packages and will add the variables `supernova` and `nucleus` to the global namespace.
When imported using the script tag, it will add the variable `stardust` to the global namespace.
For production, it is recommended to use a specific version of each module to avoid surprises from newer or breaking versions of the APIs:
For production, it is recommended to use a specific version of the module to avoid surprises from newer or breaking versions of the APIs:
```html
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/supernova@0.1.1" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/nucleus@0.1.1" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/stardust@0.6.0" crossorigin></script>
```
## Npm or yarn
If you are building your own web project using Webpack, Rollup, Parcel or similar you can install the packages with npm:
If you are building your own web project using Webpack, Rollup, Parcel or similar you can install the package with npm:
```bash
$ npm install @nebula.js/supernova @nebula.js/nucleus
$ npm install @nebula.js/stardust
```
or yarn:
```bash
$ yarn add @nebula.js/supernova @nebula.js/nucleus
$ yarn add @nebula.js/stardust
```
and then import `nucleus` wherever you're using it:
and then import `{ embed }` wherever you intend to embed a visualization:
```js
import nucleus from '@nebula.js/nucleus';
import { embed } from '@nebula.js/stardust';
```
You should not need to import `@nebula.js/supernova` yourself, it is a dependency to most charts and will be resolved automatically by the bundling tool when needed.
## CLI
`nebula.js` provides a CLI for quickly getting started with a supernova project and provides a development server to help you during the
`nebula.js` provides a CLI for quickly getting started with a project and provides a development server to help you during the
development phase.
```bash
@@ -62,6 +58,5 @@ Some modules are available as a development build which provide more errors and
You should only use these during the development phase of your project, never in production.
```html
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/supernova@0.1.1/dist/supernova.dev.js" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/nucleus@0.1.1/dist/nucleus.dev.js" crossorigin></script>
<script src="https://cdn.jsdelivr.net/npm/@nebula.js/stardust@0.6.0/dist/stardust.dev.js" crossorigin></script>
```

View File

@@ -1,230 +0,0 @@
---
id: nucleus-reference
title: API Reference
---
## Table of contents
- [interface: nucleus(app[, instanceConfig])](#interface-nucleusapp-instanceconfig)
- [interface: Context](#interface-context)
- [interface: Configuration](#interface-configuration)
- [class: Nucleus](#class-nucleus)
- [nucleus.render(cfg)](#nucleusrendercfg)
- [nucleus.context(ctx)](#nucleuscontextctx)
- [nucleus.selections()](#nucleusselections)
- [interface: ThemeInfo](#interface-themeinfo)
- [class: SupernovaController](#class-supernovacontroller)
- [supernovaController.destroy()](#supernovacontrollerdestroy)
- [class: AppSelections](#class-appselections)
- [appSelections.mount(element)](#appselectionsmountelement)
- [appSelections.unmount()](#appselectionsunmount)
- [interface: CreateConfig](#interface-createconfig)
- [interface: BaseConfig](#interface-baseconfig)
- [interface: GetConfig](#interface-getconfig)
- [type Field = <[string]|[qae.NxDimension]|[qae.NxMeasure]|[LibraryField]>](#type-field-stringqaenxdimensionqaenxmeasurelibraryfield)
- [interface: LibraryField](#interface-libraryfield)
- [interface: LoadType(type, env)](#interface-loadtypetype-env)
- [interface: TypeInfo](#interface-typeinfo)
## API
### interface: nucleus(app[, instanceConfig])
- `app` <`enigma.Doc`>
- `instanceConfig` <[Configuration]>
- `returns:` <[Nucleus]>
Initiates a new `Nucleus` instance using the specified `app`.
```js
import nucleus from '@nebula.js/nucleus';
const n = nucleus(app);
n.render({ id: 'abc' });
```
- `createConfiguration` <[Function]> Creates a new `nucleus` scope bound to the specified `configuration`.
The configuration is merged with all previous scopes.
### interface: Context
- `constraints` <[Object]>
- `active` <[boolean]>
- `passive` <[boolean]>
- `select` <[boolean]>
- `theme` <[string]> Defaults to `light`
- `language` <[string]> Defaults to `en-US`
### interface: Configuration
- `context` <[Context]>
- `types` <[Array]<[TypeInfo]>>
- `themes` <[Array]<[ThemeInfo]>>
- `env` <[Object]>
### class: Nucleus
#### nucleus.render(cfg)
- `cfg` <[CreateConfig]|[GetConfig]> The render configuration.
- `returns:` <[Promise]<[SupernovaController]>> A controller to the rendered supernova
Renders a supernova into an HTMLElement.
```js
// render from existing object
n.render({
element: el,
id: 'abcdef',
});
```
```js
// render on the fly
n.render({
type: 'barchart',
fields: ['Product', { qLibraryId: 'u378hn', type: 'measure' }],
});
```
#### nucleus.context(ctx)
- `ctx` <[Context]> The context to update.
- `returns:` <[Promise]<[undefined]>>
Updates the current context of this nucleus instance.
Use this when you want to change some part of the current context, like theme.
```js
// change theme
n.context({ theme: 'dark' });
```
```js
// limit constraints
n.context({ constraints: { active: true } });
```
#### nucleus.selections()
- `returns:` <[Promise]<[AppSelections]>>
Gets the app selections of this instance.
```js
const selections = await n.selections();
selections.mount(element);
```
### interface: ThemeInfo
- `id` <[string]> Theme identifier
- `load` <[Function]> A function that should return a Promise that resolve to a raw JSON theme
### class: SupernovaController
A controller to further modify a supernova after it has been rendered.
```js
const ctl = await nucleus(app).render({
element,
type: 'barchart',
});
ctl.destroy();
```
#### supernovaController.destroy()
Destroys the supernova and removes if from the the DOM.
```js
const ctl = ctl.destroy();
```
### class: AppSelections
#### appSelections.mount(element)
- `element` <`HTMLElement`>
Mounts the app selection UI into the provided HTMLElement
```js
selections.mount(element);
```
#### appSelections.unmount()
Unmounts the app selection UI from the DOM
```js
selections.unmount();
```
### interface: CreateConfig
- extends: <[BaseConfig]>
* `type` <[string]>
* `version` <[string]>
* `fields` <[Array]>
* `properties` <[qae.GenericObjectProperties]>
### interface: BaseConfig
- `element` <`HTMLElement`>
- `options` <[Object]>
### interface: GetConfig
- extends: <[BaseConfig]>
* `id` <[string]>
### type Field = <[string]|[qae.NxDimension]|[qae.NxMeasure]|[LibraryField]>
### interface: LibraryField
- `qLibraryId` <[string]>
- `type` <`dimension`|`measure`>
### interface: LoadType(type, env)
- `type` <[Object]>
- `name` <[string]>
- `version` <[string]>
- `env` <[Object]>
- `returns:` <[Promise]<`Supernova`>>
### interface: TypeInfo
- `name` <[string]>
- `version` <[string]>
- `load` <[LoadType]>
- `meta` <[Object]>
[enigma.doc]: undefined
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[undefined]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined
[htmlelement]: undefined
[qae.genericobjectproperties]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#genericobjectproperties
[qae.nxdimension]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxdimension
[qae.nxmeasure]: https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxmeasure
[supernova]: undefined
[context]: #interface-context
[configuration]: #interface-configuration
[nucleus]: #class-nucleus
[themeinfo]: #interface-themeinfo
[supernovacontroller]: #class-supernovacontroller
[appselections]: #class-appselections
[createconfig]: #interface-createconfig
[baseconfig]: #interface-baseconfig
[getconfig]: #interface-getconfig
[libraryfield]: #interface-libraryfield
[loadtype]: #interface-loadtypetype-env
[typeinfo]: #interface-typeinfo

View File

@@ -1,75 +0,0 @@
---
id: render-charts
title: Rendering supernovae
---
You can render a supernova in two ways:
1. On the fly
1. From existing object
Rendering is done using the `render()` method on the `nucleus` instance, which at minimum requires the `HTMLElement` you want to render into:
```js
const n = nucleus(enigmaApp);
n.render({
element,
// rest of the config
});
```
## Render on the fly
When rendering a supernova on the fly you need to specify the `type` of supernova to render:
```js
n.render({
element,
type: 'barchart',
});
```
Some supernovae have minimum requirements on the various properties and/or data it needs in order to render, in which case you might see something like this:
![Incomplete supernova](assets/supernova-incomplete.png)
To provide initial data to the supernova, add the data dimensions and measures into the `fields` property:
```js
n.render({
element,
type: 'barchart',
fields: ['Region', '=sum(Sales)'],
});
```
You can also modify the initial properties of the supernova:
```js
n.render({
element,
type: 'barchart',
fields: ['Product', '=sum(Sales)'],
properties: {
title: 'Sales by region',
},
});
```
![Supernova bar chart](assets/supernova-barchart.png)
Read more
- [API reference](./nucleus-reference#nucleusrendercfg)
- [API reference per supernova](#TODO)
## Render from existing objects
If you already have created a generic object in your app and want to render it, you can do so by providing the object's `id`:
```js
n.render({
element,
id: '<ObjectID>',
});
```

View File

@@ -15,10 +15,10 @@ export default function () {
}
```
In order to render something you need to access the DOM element the supernova is assigned to, you can do so by importing the `useElement` function:
In order to render something you need to access the DOM element the visualization is assigned to, you can do so by importing the `useElement` function:
```js
import { useElement } from '@nebula.js/supernova';
import { useElement } from '@nebula.js/stardust';
```
This function returns a simple HTMLElement which is your entry point to the visual world:
@@ -30,11 +30,11 @@ component() {
}
```
`useElement` is one of many functions that provide you with the most common requirements when developing a chart, they allow you to _hook_ into the resources provided by both `nebula.js` and Qlik's Associative Engine.
`useElement` is one of many functions that provide you with the most common requirements when developing a visualization, they allow you to _hook_ into the resources provided by both `stardust` and Qlik's Associative Engine.
## Hooks
If you have been working with [React](https://reactjs.org/) you might recognize this as _hooks_. Hooks is a concept which emphasizes reusable composable functions rather than classical object oriented classes and inheritance. While our implementation is completely custom with our own hooks, the concept and rules are very similar, so much so that you can read the [React hooks documentation](https://reactjs.org/docs/hooks-intro.html) to understand how to use nebula's own hooks.
If you have been working with [React](https://reactjs.org/) you might recognize this as _hooks_. Hooks is a concept which emphasizes reusable composable functions rather than classical object oriented classes and inheritance. While our implementation is completely custom with our own hooks, the concept and rules are very similar, so much so that you can read the [React hooks documentation](https://reactjs.org/docs/hooks-intro.html) to understand how to use stardust's own hooks.
### useElement
@@ -58,7 +58,7 @@ The `component()` function is executed every time something that might be connec
`useEffect` is a hook that accepts a callback function which will be run only when the value you specify changes. This enables you to not only batch updates but to also implement your own form of lifecycle management in your component.
```js
import { useEffect } from '@nebula.js/supernova';
import { useEffect } from '@nebula.js/stardust';
// ...
component() {
const element = useElement();
@@ -98,7 +98,7 @@ In the example above, `element` is provided as an observable value as the second
Since `component()` is a function and not a class or object instance, you can not use `this` to store instance values as you would otherwise. The way to store state is through `useState`:
```js
import { useState } from '@nebula.js/supernova';
import { useState } from '@nebula.js/stardust';
export default function () {
return {

View File

@@ -3,7 +3,7 @@ id: sn-configure-data
title: Configuring data
---
The `qae` section of the supernova definition is where you define the properties of the Generic Object and the shape of the data you expect to consume.
The `qae` section of the definition is where you define the properties of the Generic Object and the shape of the data you expect to consume.
```json
{
@@ -15,9 +15,9 @@ The `qae` section of the supernova definition is where you define the properties
## Generic Object
Every supernova is connected to the entire data model and Qlik's Associative Engine through a Generic Object. This is a JSON object containing _properties_ which result in a _layout_ that describe the state of the backend portion of your supernova.
Every visualization is connected to the entire data model and Qlik's Associative Engine through a _Generic Object_. This is a JSON object containing _properties_ which result in a _layout_ that describe the state of the backend portion of the visualization.
Every time someone wants to render your supernova, an instance of the generic object will be created in the data model. If the creator has the right permissions, they can choose to store and persist this object in their data model.
Every time someone wants to render your visualization, an instance of the generic object will be created in the data model. If the creator has the right permissions, they can choose to store and persist this object in their data model.
### Properties
@@ -30,9 +30,9 @@ What properties you set is entirely up to you, it must however be a valid JSON o
}
```
These are static properties only, what goes in comes out exactly the same. The true power of the Generic Object are the dynamic properties you can set that enables you to leverage Qlik's Associative Engine and access the data inside it.
These properties are _static_, what goes in comes out exactly the same. The true power of the Generic Object are the _dynamic_ properties you can set that enables you to leverage Qlik's Associative Engine and access the data inside it.
Dynamic properties have a specific structure that enables the backend to differentiate between dynamic properties and static. They also have a naming convention where they all begin with a `q` followed by a capital letter, this makes it easy for both humans and machines to distinguish between the two property types.
Dynamic properties have a specific structure that enables the backend to differentiate between dynamic properties and static. They also have a naming convention: they all begin with a `q` followed by a capital letter, this makes it easy for both humans and machines to distinguish between the two property types.
There are a lot of different [predefined](https://core.qlik.com/services/qix-engine/apis/qix/definitions/) dynamic properties for various purposes, you can for example use [ValueExpression](https://core.qlik.com/services/qix-engine/apis/qix/definitions/#valueexpression) to do simple calculations:
@@ -93,7 +93,7 @@ In this case the dimensions and measures are hardcoded to a predefined value tha
### Data targets
A data target is a way for you to define where the dynamic HyperCubeDefs are located in the Generic Object's properties. While `nebula.js` could traverse the properties and locate all usages of `qHyperCubeDef`, you may not want all of those to be dynamic, or you may generate them for internal use only.
A data target is a way for you to define where the dynamic HyperCubeDefs are located in the Generic Object's properties. While `stardust` could traverse the properties and locate all usages of `qHyperCubeDef`, you may not want all of those to be dynamic, or you may generate them for internal use only.
You specify data targets with the `data.targets` key in `qae`, each target must have a `path` key which indicates the JSON path of the HyperCubeDef from the root of the properties object:
@@ -118,7 +118,7 @@ qae: {
You can for each data target specify additional details like the maximum/minimum amount of dimensions and measures, and make modifications when they are added.
This is useful when you now the limitations of what a chart can render, a pie chart for example is mostly usable when it has exactly one dimension and one measure, but you might also be implementing support for a second measure. This also saves you some code since `nebula.js` won't attempt to render a chart whose limitations have not been fulfilled, and will instead show that some fields are missing.
This is useful when you know the limitations of what a chart can render, a pie chart for example is mostly usable when it has exactly one dimension and one measure, but you might also be implementing support for a second measure. This also saves you some code logic since `stardust` won't attempt to render a chart whose limitations have not been fulfilled, and will instead show that some fields are missing.
### Field limitations

View File

@@ -3,7 +3,7 @@ id: sn-create
title: Quick start
---
This guide will walk you through creating a simple supernova project that renders a table.
This guide will walk you through creating a simple project that renders a table.
It will include the following steps:
@@ -29,13 +29,13 @@ $ npx @nebula.js/cli create hello --picasso none
The command will scaffold a project into the `hello` folder with the following structure:
- `/src`
- `index.js` - Main entry point of this supernova
- `index.js` - Main entry point of this visualization
- `object-properties.js` - Object properties stored in the app
- `data.js` - Data configuration
- `/test` - Integration tests
- `package.json`
The folder contains some additional dotfiles files that provides linting and formatting of code.
The folder contains some additional dotfiles that provides linting and formatting of code.
### Start the development server
@@ -50,17 +50,17 @@ The command will start a local development server and open up http://localhost:8
![Connect to engine](./assets/hub-connect.png)
The development server needs to connect to a Qlik Associative Engine running in any of the Qlik's deployments. Enter the WebSocket URL that corresponds to the Qlik product you are using.
The development server needs to connect to a Qlik Associative Engine running in any Qlik deployment. Enter the WebSocket URL that corresponds to the Qlik product you are using.
Next, select an app to connect to.
![Connect to app](./assets/hub-app.png)
You will then be redirected to the main developer UI where you should see your supernova rendered:
You will then be redirected to the main developer UI where you should see your visualization rendered:
![Dev](./assets/hub-dev.png)
Any updates in `/src/index.js` that affects the output will automatically cause a refresh of the supernova and you will see the changes immediately.
Any updates in `/src/index.js` that affects the output will automatically cause a refresh of the visualization and you will see the changes immediately.
## Configure data structure
@@ -100,7 +100,7 @@ Add a dimension by clicking on **Add dimension** and selecting a value in the me
In order to render the data you first need to access it through the `useLayout` hook:
```js
import { useElement, useLayout } from `@nebula.js/nucleus`;
import { useElement, useLayout } from `@nebula.js/stardust`;
// ...
component() {
@@ -190,7 +190,7 @@ useEffect(() => {
}, [element]);
```
Next, we update the styling of the selected rows in the table whenever they change:
Next, update the styling of the selected rows in the table whenever they change:
```js
useEffect(() => {
@@ -226,7 +226,3 @@ useEffect(() => {
}
}, [selections.isActive(), selectedRows]);
```
## Next steps
- [Supernova introduction](./sn-component.md)

View File

@@ -7,7 +7,7 @@ The `HyperCubeDef` is the fundamental structure which you configure before you a
## HyperCubeDef configuration
Not all properties are equally important, but there are a few key ones that you need to keep in mind when configuring the `HyperCubeDef`.
Not all properties are equally important, and there are a few key ones that you need to keep in mind when configuring the `HyperCubeDef`.
### qMode
@@ -123,7 +123,7 @@ Due to the limitation of the amount of data you can get in the initial layout, t
When paging data in straight mode, you should begin by looking at the `qHyperCubeDef.qSize` property which contains information on the width and height of the full hypercube. You can from that calculate the number of pages you need to fetch:
```js
import { useModel, useLayout, useEffect } from '@nebula.js/supernova';
import { useModel, useLayout, useEffect } from '@nebula.js/stardust';
const NUM_CELLS_PER_PAGE = 10000;
const MAX_PAGES = 10;
@@ -132,18 +132,18 @@ component() {
const model = useModel();
const layout = useLayout();
const Y = layout.qHyperCube.qSize.qcy;
const X = layout.qHyperCube.qSize.qcx;
const HEIGHT_PER_PAGE = Math.ceil(NUM_CELLS_PER_PAGE / X);
const NUM_PAGES = Math.floor(MAX_PAGES, Math.ceil(Y / HEIGHT_PER_PAGE));
const pagesToFetch = [];
for (let i = 0; i < NUM_PAGES; i++) {
pagesToFetch.push({ qLeft: 0, qTop: i * HEIGHT_PER_PAGE, qHeight: HEIGHT_PER_PAGE, qWidth: X });
}
useEffect(() => {
const Y = layout.qHyperCube.qSize.qcy;
const X = layout.qHyperCube.qSize.qcx;
const HEIGHT_PER_PAGE = Math.ceil(NUM_CELLS_PER_PAGE / X);
const NUM_PAGES = Math.floor(MAX_PAGES, Math.ceil(Y / HEIGHT_PER_PAGE));
const pagesToFetch = [];
for (let i = 0; i < NUM_PAGES; i++) {
pagesToFetch.push({ qLeft: 0, qTop: i * HEIGHT_PER_PAGE, qHeight: HEIGHT_PER_PAGE, qWidth: X });
}
Promise.all(pagesToFetch.map((page) => model.getHyperCubeData('/qHyperCubeDef', [page]))).then((pages) => {
console.log(pages);
});

View File

@@ -3,17 +3,17 @@ id: sn-introduction
title: Introduction
---
## What is a supernova?
## What is a visualization?
A supernova in the context of this API represents the visual output of some underlying data stored in Qlik's Associative Data Model. It could be almost anything you want it to be and is traditionally developed to show the data in the shape of a chart, table, kpi etc.
A visualization in the context of this API represents the visual output of some underlying data stored in Qlik's Associative Data Model. It could be almost anything you want it to be and is traditionally developed to show the data in the shape of a chart, table, kpi etc.
## Composition
A supernova has two main parts: a backend _Generic Object_ that describes the _properties_ of the supernova and is persisted in the data model, and a frontend visual part that renders the _layout_ of the _Generic Object_.
A visualization has two main parts: a backend _Generic Object_ that describes the _properties_ of the visualization and is persisted in the data model, and a frontend visual part that renders the _layout_ of the _Generic Object_.
## Definition
The minimal supernova that doesn't contain any data nor renders anything looks like this:
The minimal visualization that doesn't contain any data nor renders anything looks like this:
```js
export default function () {

View File

@@ -12,7 +12,7 @@ Selections can be applied with methods exposed on the model returned from the `u
Since a generic object can hold multiple hypercubes, you always need to specify which hypercube you want to select in by providing the JSON path of it as the first argument:
```js
import { useModel } from '@nebula.js/supernova';
import { useModel } from '@nebula.js/stardust';
// ...
component() {
const model = useModel();
@@ -51,7 +51,7 @@ To implement this type of pattern you need to `useSelections` in combination wit
1. Keep track of when the modal state has been exited in order to reset the visual feedback.
```js
import { useModel, useSelections, useElement } from '@nebula.js/supernova';
import { useModel, useSelections, useElement } from '@nebula.js/stardust';
// ...
component() {
const element = useElement();

View File

@@ -12,7 +12,7 @@ You can access the _layout_ of the Generic Object through a set of predefined ho
`useLayout` returns the evalutated layout of the Generic Object's properties:
```js
import { useLayout } from '@nebula.js/supernova';
import { useLayout } from '@nebula.js/stardust';
export default function () {
return {
@@ -49,7 +49,7 @@ useEffect(() => {
`useAppLayout` returns the [NxAppLayout](https://core.qlik.com/services/qix-engine/apis/qix/definitions/#nxapplayout) you are currently connected to:
```js
import { useAppLayout } from '@nebula.js/supernova';
import { useAppLayout } from '@nebula.js/stardust';
export default function () {
return {
@@ -69,10 +69,10 @@ In addition to the layouts of the app and generic object, you have full access t
### useModel
`useModel` returns the generated API of the [Generic Object](https://core.qlik.com/services/qix-engine/apis/qix/genericobject/) of your supernova:
`useModel` returns the generated API of the [Generic Object](https://core.qlik.com/services/qix-engine/apis/qix/genericobject/):
```js
import { useModel } from '@nebula.js/supernova';
import { useModel } from '@nebula.js/stardust';
export default function () {
return {
@@ -93,10 +93,10 @@ Common operations in this API is to:
### useApp
`useApp` returns the generated API of the [Doc](https://core.qlik.com/services/qix-engine/apis/qix/doc/) your supernova belongs to:
`useApp` returns the generated API of the [Doc](https://core.qlik.com/services/qix-engine/apis/qix/doc/):
```js
import { useApp } from '@nebula.js/supernova';
import { useApp } from '@nebula.js/stardust';
export default function () {
return {
@@ -115,10 +115,10 @@ Common operations in this API is to:
### useGlobal
`useGlobal` returns the generated API of the [Global](https://core.qlik.com/services/qix-engine/apis/qix/global/) your supernova belongs to:
`useGlobal` returns the generated API of the [Global](https://core.qlik.com/services/qix-engine/apis/qix/global/):
```js
import { useGlobal } from '@nebula.js/supernova';
import { useGlobal } from '@nebula.js/stardust';
export default function () {
return {

View File

@@ -1,6 +1,6 @@
---
id: web-integration
title: Web integration
title: Quick start
---
This guide will walk you through creating a simple mashup that connects to Qlik Sense and visualizes some data.
@@ -17,7 +17,7 @@ and requires that you have:
- `node.js` `v10.0.0+` installed on your machine
- A decent IDE, we recommend [VSCode](https://code.visualstudio.com/).
## Setup up a Qlik Cloud Services account
## Setup a Qlik Cloud Services account
Though `nebula.js` works and can be integrated with all Qlik Sense products, this will guide you on how to integrate with Qlik Cloud Services.
@@ -31,7 +31,7 @@ Perform the following steps:
1. [Upload an app](https://help.qlik.com/en-US/cloud-services/Subsystems/Hub/Content/Sense_Hub/Apps/create-app-cloud-hub.htm)
1. If you don't have an app or data already prepared, you can [download the movies dataset](https://github.com/qlik-oss/nebula.js/raw/master/data/apps/the_movies.qvf) from the github repository and upload it to the cloud.
Each app in QCS has a global unique identifier (GUID) which you will need later on.
Each app on QCS has a global unique identifier (GUID) which you will need later on.
The GUID can be extracted from the URL and is the part that follows `/app`. For example, if the URL is `https://qcs.us.qlikcloud.com/sense/app/7fc4d85c-0e07-4160-90a9-c2ca4a45fd81`, then the GUID of the app is `7fc4d85c-0e07-4160-90a9-c2ca4a45fd81`.
## Create a simple web project
@@ -54,7 +54,11 @@ The command will scaffold a web project into the `hello-qcs` folder with the fol
## Configure connection
To configure the connection you will need your QCS tenant url, the `qlik-web-integration-id` and the GUID of the app you want to open.
To configure the connection you will need:
- The QCS tenant url.
- The qlik-web-integration-id.
- the GUID of the app you want to open.
Open up `src/index.js` and update the `<>` placeholders with the correct values, for example:
@@ -107,9 +111,9 @@ n.render({
### More types
The core modules of `nebula.js`do not contain any visualizations by themselves, each visualization is its own separate module and needs to be loaded and registered before it can be used.
The core `stardust` module does not contain any visualizations, each visualization is its own separate module and needs to be loaded and registered before it can be used.
Official supernova modules from Qlik are published under the `@nebula.js` scope and are prefixed with `sn-`.
Official visualizations from Qlik are published under the `@nebula.js` scope and are prefixed with `sn-`.
The available visualizations are as follows:
@@ -132,7 +136,7 @@ Then modify `/src/configure.js` to include the new modules:
import piechart from '@nebula.js/sn-pie-chart';
//...
nucles.createConfiguration({
embed.createConfiguration({
// ...
types: [
// ...

View File

@@ -5,27 +5,24 @@
"previous": "Previous",
"tagline": "A new star on the rise",
"docs": {
"api-reference": {
"title": "API Reference"
},
"app-selections": {
"title": "Current app selections"
},
"embed-configuration": {
"title": "Configuration"
},
"embedding-visualizations": {
"title": "Embedding visualizations"
},
"installation": {
"title": "Installation"
},
"nucleus-configuration": {
"title": "Configuration"
},
"nucleus-reference": {
"title": "API Reference"
},
"introduction": {
"title": "Introduction"
},
"render-charts": {
"title": "Rendering supernovae"
},
"sn-associations": {
"title": "sn-associations"
},
"sn-component": {
"title": "Rendering"
},
@@ -44,9 +41,6 @@
"sn-introduction": {
"title": "Introduction"
},
"sn-reference": {
"title": "API Reference"
},
"sn-selections": {
"title": "Selecting data"
},
@@ -54,14 +48,14 @@
"title": "Consuming data"
},
"web-integration": {
"title": "Web integration"
"title": "Quick start"
}
},
"links": {},
"categories": {
"Getting started": "Getting started",
"Nucleus guide": "Nucleus guide",
"Supernova guide": "Supernova guide"
"Introduction": "Introduction",
"Embedding visualizations": "Embedding visualizations",
"Developing visualizations": "Developing visualizations"
}
},
"pages-strings": {

View File

@@ -1,15 +1,20 @@
{
"docs": {
"Getting started": ["introduction", "installation", "web-integration", "sn-create"],
"Nucleus guide": ["nucleus-configuration", "render-charts", "app-selections", "nucleus-reference"],
"Supernova guide": [
"Introduction": ["introduction", "installation", "api-reference"],
"Embedding visualizations": [
"web-integration",
"embed-configuration",
"embedding-visualizations",
"app-selections"
],
"Developing visualizations": [
"sn-create",
"sn-introduction",
"sn-component",
"sn-configure-data",
"sn-using-data",
"sn-hypercube",
"sn-selections",
"sn-reference"
"sn-selections"
]
}
}