mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 09:48:18 -05:00
feat: add selection api (#3)
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
version: "2"
|
||||
version: "3.1"
|
||||
|
||||
services:
|
||||
engine:
|
||||
image: qlikcore/engine:12.300.0
|
||||
image: qlikcore/engine:${ENGINE_VERSION:-latest}
|
||||
restart: always
|
||||
command: -S AcceptEULA=${ACCEPT_EULA}
|
||||
command: -S AcceptEULA="${ACCEPT_EULA:-no}"
|
||||
ports:
|
||||
- "19076:9076"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production FORCE_COLOR=1 lerna run build --stream",
|
||||
"build:watch": "FORCE_COLOR=1 lerna run build:watch --stream",
|
||||
"build:watch": "FORCE_COLOR=1 lerna run build:watch --stream --concurrency 99 --no-sort",
|
||||
"lint": "eslint packages --ext .js --ext .jsx",
|
||||
"test": "yarn run test:unit",
|
||||
"test:integration": "aw puppet -c aw.config.js --type integration",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"lint": "eslint --ext .js,.jsx src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nebula.js/selections": "^0.1.0",
|
||||
"@nebula.js/supernova": "^0.1.0",
|
||||
"node-event-emitter": "^0.0.1",
|
||||
"preact": "^8.4.2"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import preact from 'preact';
|
||||
import { createAppSelectionAPI } from '@nebula.js/selections';
|
||||
import Cell from './components/Cell';
|
||||
|
||||
import populateData from './populator';
|
||||
@@ -11,16 +10,6 @@ import './components/Style.scss';
|
||||
export function boot({
|
||||
id,
|
||||
}, visual, config, nebbie, app) {
|
||||
if (!app.selections) {
|
||||
let selections = null;
|
||||
Object.defineProperty(app, 'selections', {
|
||||
get() {
|
||||
selections = selections || createAppSelectionAPI(app);
|
||||
return selections;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return new config.env.Promise((resolve) => {
|
||||
let reference;
|
||||
const unmount = () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import preact from 'preact';
|
||||
import { createObjectSelectionAPI } from '../selections';
|
||||
|
||||
import { prefixer } from '../utils';
|
||||
|
||||
@@ -107,6 +108,7 @@ class Cell extends preact.Component {
|
||||
const sn = SN.create({
|
||||
model,
|
||||
app: props.app,
|
||||
selections: createObjectSelectionAPI(model, props.app),
|
||||
});
|
||||
sn.component.on('rendered', (...args) => {
|
||||
externalAPI.emit('rendered', ...args);
|
||||
@@ -127,15 +129,16 @@ class Cell extends preact.Component {
|
||||
};
|
||||
|
||||
const onChanged = () => model.getLayout().then((layout) => {
|
||||
if (model.selections) {
|
||||
model.selections.setLayout(layout);
|
||||
const selections = this.state.sn ? this.state.sn.component.selections : null;
|
||||
if (selections && selections.id === model.id) {
|
||||
selections.setLayout(layout);
|
||||
if (layout.qSelectionInfo && layout.qSelectionInfo.qInSelections
|
||||
&& !model.selections.isModal()) {
|
||||
model.selections.goModal('/qHyperCubeDef');
|
||||
&& !selections.isModal()) {
|
||||
selections.goModal('/qHyperCubeDef');
|
||||
}
|
||||
if (!layout.qSelectionInfo || !layout.qSelectionInfo.qInSelections) {
|
||||
if (model.selections.isModal()) {
|
||||
model.selections.noModal();
|
||||
if (selections.isModal()) {
|
||||
selections.noModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class Item extends preact.Component {
|
||||
class Component extends preact.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const api = props.model.selections;
|
||||
const api = props.sn.component.selections;
|
||||
|
||||
this.state = {
|
||||
confirmable: api.canConfirm(),
|
||||
@@ -113,7 +113,7 @@ class Component extends preact.Component {
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps) {
|
||||
const api = nextProps.model.selections;
|
||||
const api = nextProps.sn.component.selections;
|
||||
return {
|
||||
confirmable: api.canConfirm(),
|
||||
cancelable: api.canCancel(),
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createAppSelectionAPI } from './selections';
|
||||
|
||||
import { create, boot } from './booter';
|
||||
import types from './sn/types';
|
||||
|
||||
|
||||
function apiGenerator(app) {
|
||||
createAppSelectionAPI(app);
|
||||
|
||||
const config = {
|
||||
env: {
|
||||
Promise,
|
||||
@@ -24,6 +29,7 @@ function apiGenerator(app) {
|
||||
config.load = $;
|
||||
return api;
|
||||
},
|
||||
selections: () => app._selections, // eslint-disable-line no-underscore-dangle
|
||||
types: context.types,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
import eventmixin from './event-mixin';
|
||||
|
||||
export default function (app) {
|
||||
const cache = {};
|
||||
|
||||
const create = (app) => {
|
||||
let canGoForward = false;
|
||||
let canGoBack = false;
|
||||
let canClear = false;
|
||||
@@ -14,11 +17,11 @@ export default function (app) {
|
||||
if (modalObject) {
|
||||
modalObject.endSelections(accept);
|
||||
api.emit('modal-unset');
|
||||
modalObject.selections.emit('deactivated');
|
||||
modalObject._selections.emit('deactivated');
|
||||
}
|
||||
if (object && object !== null) { // TODO check model state
|
||||
modalObject = object;
|
||||
api.emit('modal', modalObject.selections);
|
||||
api.emit('modal', modalObject._selections);
|
||||
return modalObject.beginSelections(Array.isArray(path) ? path : [path]);
|
||||
}
|
||||
modalObject = null;
|
||||
@@ -33,7 +36,7 @@ export default function (app) {
|
||||
if (!modalObject) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// modalObject.selections.
|
||||
// modalObject._selections.
|
||||
modalObject = null;
|
||||
api.emit('modal-unset');
|
||||
return app.abortModal(accept);
|
||||
@@ -89,9 +92,25 @@ export default function (app) {
|
||||
model.on('changed', onChanged);
|
||||
model.once('closed', () => {
|
||||
model.removeListener('changed', onChanged);
|
||||
app._selections = null; // eslint-disable-line no-param-reassign
|
||||
cache[app.id] = null;
|
||||
});
|
||||
onChanged();
|
||||
});
|
||||
|
||||
return api;
|
||||
};
|
||||
|
||||
export default function (app) {
|
||||
if (!cache[app.id]) {
|
||||
cache[app.id] = {
|
||||
selections: null,
|
||||
};
|
||||
Object.defineProperty(app, '_selections', {
|
||||
get() {
|
||||
cache[app.id].selections = cache[app.id].selections || create(app);
|
||||
return cache[app.id].selections;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
4
packages/nucleus/src/selections/index.js
Normal file
4
packages/nucleus/src/selections/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import createAppSelectionAPI from './app-selections';
|
||||
import createObjectSelectionAPI from './object-selections';
|
||||
|
||||
export { createObjectSelectionAPI, createAppSelectionAPI };
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
import eventmixin from './event-mixin';
|
||||
|
||||
const event = () => {
|
||||
@@ -11,11 +12,15 @@ const event = () => {
|
||||
};
|
||||
|
||||
export default function (model, app) {
|
||||
const appAPI = app.selections;
|
||||
if (model._selections) {
|
||||
return model._selections;
|
||||
}
|
||||
const appAPI = () => app._selections;
|
||||
let hasSelected = false;
|
||||
let isActive = false;
|
||||
let layout = {};
|
||||
const api = {
|
||||
// model,
|
||||
id: model.id,
|
||||
setLayout(lyt) {
|
||||
layout = lyt;
|
||||
@@ -28,7 +33,7 @@ export default function (model, app) {
|
||||
}
|
||||
isActive = true;
|
||||
this.emit('activated');
|
||||
return appAPI.switchModal(model, paths, true);
|
||||
return appAPI().switchModal(model, paths, true);
|
||||
},
|
||||
clear() {
|
||||
hasSelected = false;
|
||||
@@ -40,18 +45,18 @@ export default function (model, app) {
|
||||
isActive = false;
|
||||
this.emit('confirmed');
|
||||
this.emit('deactivated');
|
||||
return appAPI.switchModal(null, null, true);
|
||||
return appAPI().switchModal(null, null, true);
|
||||
},
|
||||
cancel() {
|
||||
hasSelected = false;
|
||||
isActive = false;
|
||||
this.emit('canceled');
|
||||
this.emit('deactivated');
|
||||
return appAPI.switchModal(null, null, false, false);
|
||||
return appAPI().switchModal(null, null, false, false);
|
||||
},
|
||||
select(s) {
|
||||
this.begin([s.params[0]]);
|
||||
if (!appAPI.isModal()) {
|
||||
if (!appAPI().isModal()) {
|
||||
return;
|
||||
}
|
||||
hasSelected = true;
|
||||
@@ -71,12 +76,14 @@ export default function (model, app) {
|
||||
return true;
|
||||
},
|
||||
isActive: () => isActive,
|
||||
isModal: () => appAPI.isModal(model),
|
||||
goModal: paths => appAPI.switchModal(model, paths, false),
|
||||
noModal: () => appAPI.switchModal(null, null, false),
|
||||
isModal: () => appAPI().isModal(model),
|
||||
goModal: paths => appAPI().switchModal(model, paths, false),
|
||||
noModal: () => appAPI().switchModal(null, null, false),
|
||||
};
|
||||
|
||||
eventmixin(api);
|
||||
|
||||
model._selections = api; // eslint-disable-line no-param-reassign
|
||||
|
||||
return api;
|
||||
}
|
||||
@@ -12,7 +12,12 @@ const load = ({ name, version }, config) => {
|
||||
if (typeof p === 'string') {
|
||||
throw new Error('Return value must be a Promise');
|
||||
}
|
||||
LOADED[key] = p.catch((e) => {
|
||||
LOADED[key] = p.then((sn) => {
|
||||
if (!sn) {
|
||||
throw new Error('undefined supernova');
|
||||
}
|
||||
return sn;
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
throw new Error(`Failed to load supernova: ${name}`);
|
||||
});
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "@nebula.js/selections",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"license": "MIT",
|
||||
"author": "QlikTech International AB",
|
||||
"keywords": [],
|
||||
"main": "dist/selections.js",
|
||||
"module": "dist/selections.esm.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production rollup --config ../../rollup.config.js --exports named",
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-event-emitter": "0.0.1"
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
import createAppSelectionAPI from './app-selections';
|
||||
import createObjectSelectionAPI from './object-selections';
|
||||
|
||||
// MIXINS
|
||||
// const objectSelectionAPI = {
|
||||
// types: ['GenericObject'],
|
||||
// init(args) {
|
||||
// let selections = null;
|
||||
// Object.defineProperty(args.api, 'selections', {
|
||||
// get() {
|
||||
// selections = selections || createObjectSelectionAPI(args.api);
|
||||
// return selections;
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// };
|
||||
|
||||
// const appSelectionAPI = {
|
||||
// types: ['Doc'],
|
||||
// init(args) {
|
||||
// let selections = null;
|
||||
// Object.defineProperty(args.api, 'selections', {
|
||||
// get() {
|
||||
// selections = selections || createAppSelectionAPI(args.api);
|
||||
// return selections;
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// };
|
||||
|
||||
// const all = {
|
||||
// types: ['Doc', 'GenericObject'],
|
||||
// init({ api }) {
|
||||
// if (api.getAppLayout) {
|
||||
// api.session.app = api;
|
||||
// }
|
||||
// api.app = api.session.app;
|
||||
// },
|
||||
// };
|
||||
|
||||
// export default [all, objectSelectionAPI, appSelectionAPI];
|
||||
|
||||
export { createObjectSelectionAPI, createAppSelectionAPI };
|
||||
@@ -11,10 +11,9 @@
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production rollup --config ../../rollup.config.js",
|
||||
"build:dev": "rollup --config ../../rollup.config.js",
|
||||
"lint": "eslint src"
|
||||
"build:watch": "rollup --config ../../rollup.config.js -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nebula.js/selections": "^0.1.0",
|
||||
"extend": "^3.0.2",
|
||||
"node-event-emitter": "^0.0.1"
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ export default function ({
|
||||
const actions = {};
|
||||
const selectionToolbarItems = [];
|
||||
const w = actionWrapper(component);
|
||||
((sn.selectionToolbar || {}).items || []).forEach((item) => {
|
||||
((sn.definition.selectionToolbar || {}).items || []).forEach((item) => {
|
||||
const wrapped = w(item);
|
||||
// TODO - check if key exists
|
||||
actions[item.key] = wrapped;
|
||||
selectionToolbarItems.push(wrapped);
|
||||
});
|
||||
|
||||
(sn.actions || []).forEach((item) => {
|
||||
(sn.definition.actions || []).forEach((item) => {
|
||||
const wrapped = w(item);
|
||||
// TODO - check if key exists
|
||||
actions[item.key] = wrapped;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import extend from 'extend';
|
||||
import EventEmitter from 'node-event-emitter';
|
||||
|
||||
import translator from './translator';
|
||||
import actionhero from './action-hero';
|
||||
|
||||
const defaultComponent = {
|
||||
app: null,
|
||||
model: null,
|
||||
actions: null,
|
||||
selections: null,
|
||||
created: () => {},
|
||||
mounted: () => {},
|
||||
render: () => {},
|
||||
@@ -33,7 +33,7 @@ const mixin = (obj) => {
|
||||
return obj;
|
||||
};
|
||||
|
||||
export default function create(sn, opts, env) {
|
||||
export default function create(sn, opts) {
|
||||
const componentInstance = {
|
||||
...defaultComponent,
|
||||
};
|
||||
@@ -64,16 +64,13 @@ export default function create(sn, opts, env) {
|
||||
app: opts.app,
|
||||
selections: opts.selections,
|
||||
actions: hero.actions,
|
||||
resources: {
|
||||
translator: env.translator || translator,
|
||||
Promise: env.Promise || Promise,
|
||||
},
|
||||
});
|
||||
|
||||
extend(componentInstance, {
|
||||
actions: hero.actions,
|
||||
model: opts.model,
|
||||
app: opts.app,
|
||||
selections: opts.selections,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -65,10 +65,6 @@ const config = (isEsm) => {
|
||||
commonjs(),
|
||||
babel({
|
||||
babelrc: false,
|
||||
// include: [
|
||||
// 'src/**',
|
||||
// // /react-leonardo-ui/
|
||||
// ],
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
modules: false,
|
||||
|
||||
@@ -5656,7 +5656,7 @@ nise@^1.4.6:
|
||||
path-to-regexp "^1.7.0"
|
||||
text-encoding "^0.6.4"
|
||||
|
||||
node-event-emitter@0.0.1, node-event-emitter@^0.0.1:
|
||||
node-event-emitter@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/node-event-emitter/-/node-event-emitter-0.0.1.tgz#8b597377d79a1d976f7d45f916d3d3643d73f8ce"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user