initial commit

This commit is contained in:
Miralem Drek
2019-01-29 17:12:38 +01:00
commit 058fc55d97
53 changed files with 9470 additions and 0 deletions

14
.editorconfig Normal file
View File

@@ -0,0 +1,14 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# 2 space indentation
[*.*]
indent_style = space
indent_size = 2

25
.eslintrc.json Normal file
View File

@@ -0,0 +1,25 @@
{
"root": true,
"env": {
"browser": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": [
"airbnb"
],
"settings": {
"react": {
"pragma": "preact"
}
},
"rules": {
"import/no-extraneous-dependencies": [2, { "devDependencies": true }],
"max-len": 0,
"no-plusplus": 0,
"no-bitwise" : 0,
"react/destructuring-assignment": [0, "always"],
"react/prop-types": 0
}
}

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
*.log
*.rej
*.tmp
*.log
.cache/
.DS_Store
.idea/
.vscode/
.npmrc
node_modules/
coverage/
dist/

1
README.md Normal file
View File

@@ -0,0 +1 @@
# nebula.js

67
aw.config.js Normal file
View File

@@ -0,0 +1,67 @@
/* eslint import/no-extraneous-dependencies: 0, prefer-destructuring: 0, no-param-reassign: 0 */
const yargs = require('yargs');
const path = require('path');
const globby = require('globby');
const { workspaces } = require('./package.json');
const argv = yargs
.options({
scope: {
description: 'Scope to package',
type: 'string',
default: '',
alias: 's',
},
type: {
description: 'Type of tests to run',
type: 'string',
alias: 't',
default: 'unit',
choices: ['unit'],
},
})
.coerce('scope', (scope) => {
const scopes = new Map();
globby.sync(workspaces.map(p => `${p}/package.json`)).forEach((p) => {
const name = require(`./${p}`).name; //eslint-disable-line
const pkgPath = path.dirname(p);
scopes.set(name, pkgPath);
});
const s = scopes.get(scope);
if (s) {
return s;
}
if (scope && !s) {
throw new Error(`Scope ${scope} not found`);
}
return `*(${workspaces.join('|').split('/*').join('')})/*`;
})
.argv;
const TYPES = {
unit: {
glob: `${argv.scope}/__tests__/unit/**/*.spec.js`,
reportDir: 'coverage/unit',
},
};
const type = TYPES[argv.type];
const glob = [type.glob];
const src = [`${argv.scope}/src/**/*.{js,jsx}`];
module.exports = {
glob,
src,
watchGlob: [...src, ...glob],
nyc: {
include: src,
sourceMap: false,
instrumenter: './lib/instrumenters/noop',
reportDir: 'coverage/unit',
},
mocha: Object.assign({
timeout: 30000,
}, type.mocha),
};

21
babel.config.js Normal file
View File

@@ -0,0 +1,21 @@
module.exports = {
env: {
test: {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
],
plugins: [
'@babel/plugin-transform-react-jsx',
[
'istanbul',
{
exclude: [
'**/test/**',
'**/dist/**',
],
},
],
],
},
},
};

View File

@@ -0,0 +1,21 @@
window.connect = function connect() {
const loadSchema = () => fetch('https://raw.githubusercontent.com/qlik-oss/enigma.js/master/schemas/3.2.json').then(response => response.json());
const createConnection = () => loadSchema().then(schema => window.enigma.create({
schema,
url: `ws://${window.location.hostname || 'localhost'}:19076/app/${Date.now()}`,
}).open().then(qix => qix.createSessionApp()));
return createConnection().then(app => app.setScript(`
Characters:
Load Chr(RecNo()+Ord('A')-1) as Alpha, RecNo() as Num autogenerate 26;
ASCII:
Load
if(RecNo()>=65 and RecNo()<=90,RecNo()-64) as Num,
Chr(RecNo()) as AsciiAlpha,
RecNo() as AsciiNum
autogenerate 85
Where (RecNo()>=65 and RecNo()<=126) or RecNo()>=160;
`).then(() => app.doReload().then(() => app)));
};

View File

@@ -0,0 +1,9 @@
version: "2"
services:
engine:
image: qlikcore/engine:12.300.0
restart: always
command: -S AcceptEULA=${ACCEPT_EULA}
ports:
- "19076:19076"

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Nebula mashup</title>
<script src="./node_modules/enigma.js/enigma.js"></script>
<script src="../../packages/nucleus/dist/nucleus.js"></script>
<link rel="stylesheet" href="./node_modules/leonardo-ui/dist/leonardo-ui.css" type="text/css"/>
<style>
body {
background: #eee;
}
@font-face {
font-family: "LUI icons";
src: url(node_modules/leonardo-ui/dist/lui-icons.woff) format('woff'),
url(node_modules/leonardo-ui/dist/lui-icons.ttf) format('truetype');
}
.content {
margin: 0 auto;
width: 80%;
max-width: 800px;
}
.object {
position: relative;
width: 600px;
height: 400px;
background-color: white;
}
</style>
</head>
<body>
<div class="content">
<div class="object" data-type='foo'></div>
</div>
<script src="connect.js"></script>
<script src="index.js"></script>
</html>

22
examples/mashup/index.js Normal file
View File

@@ -0,0 +1,22 @@
connect().then((app) => {
const sn = {
component: {
mounted(element) {
element.textContent = 'Hello';
},
},
};
const nebbie = window.nucleus(app)
.load((type, config) => config.Promise.resolve(sn));
document.querySelectorAll('.object').forEach((el) => {
const type = el.getAttribute('data-type');
nebbie.create({
type,
}, {
element: el,
});
});
});

View File

@@ -0,0 +1,7 @@
{
"private": true,
"dependencies": {
"enigma.js": "^2.4.0",
"leonardo-ui": "^1.6.0"
}
}

11
examples/mashup/yarn.lock Normal file
View File

@@ -0,0 +1,11 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
enigma.js@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/enigma.js/-/enigma.js-2.4.0.tgz#e5263116e7168e27f11cfe326ac83ed738bb7cc4"
leonardo-ui@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/leonardo-ui/-/leonardo-ui-1.6.0.tgz#65eab0a4fd9a54f87a75d08e4440900555f56c24"

15
lerna.json Normal file
View File

@@ -0,0 +1,15 @@
{
"command": {
"publish": {
"registry": "https://qliktech.jfrog.io/qliktech/api/npm/npm-local/"
},
"version": {
"allowBranch": "master",
"conventionalCommits": true,
"message": "chore(release): %s 🚀"
}
},
"version": "0.1.0",
"npmClient": "yarn",
"useWorkspaces": true
}

41
package.json Normal file
View File

@@ -0,0 +1,41 @@
{
"private": true,
"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",
"test": "yarn run test:unit",
"test:unit": "aw -c aw.config.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/qlik-trial/nebula.js.git"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"after-work.js": "^4.3.2",
"cross-env": "^5.2.0",
"eslint": "^5.12.1",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.15.0",
"eslint-plugin-jsx-a11y": "^6.2.0",
"eslint-plugin-react": "^7.12.4",
"lerna": "^3.10.7",
"rollup": "^1.1.2",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-dependency-flow": "^0.3.0",
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-sass": "^1.1.0",
"rollup-plugin-terser": "^4.0.3",
"yargs": "^12.0.5"
},
"workspaces": [
"packages/*"
]
}

View File

@@ -0,0 +1,72 @@
.nucleus-selection-toolbar {
position: absolute;
left: 0;
right: 0;
top: -48px;
padding: 8px;
line-height: 24px;
box-sizing: border-box;
background: white;
display: flex;
justify-content: flex-end;
}
.nucleus-selection-toolbar > * {
margin-left: 8px;
}
.nucleus-cell {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
display: flex;
flex-direction: column;
font-size: 14px;
line-height: 16px;
color: #404040;
font-family: "Source Sans Pro", Arial, "sans-serif";
}
.nucleus-cell .nucleus-cell__header {
flex: 0 0 auto;
padding: 8px;
box-sizing: border-box;
}
.nucleus-cell .nucleus-cell__footnote {
flex: 0 0 auto;
padding: 8px;
box-sizing: border-box;
}
.nucleus-cell .nucleus-content {
flex: 1 0 auto;
}
.nucleus-cell__error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAM0lEQVQYV2P8z8BgzMjAcJYBCUDFkIUYGECCIBFGdJVQwbNwCSSVYGPBEuiCYAlsgiAJANbwDGtj7MYdAAAAAElFTkSuQmCC);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
.nucleus-content {
position: relative;
}
.nucleus-content .nucleus-content__body {
position: absolute;
top: 8px;
left: 8px;
bottom: 8px;
right: 8px;
}
.nucleus-type--s {
font-size: 12px;
line-height: 16px;
}
.nucleus-type--m {
font-size: 16px;
line-height: 20px;
}

View File

@@ -0,0 +1,22 @@
{
"private": true,
"name": "@nebula.js/nucleus",
"version": "0.1.0",
"description": "",
"license": "MIT",
"author": "QlikTech International AB",
"keywords": [],
"main": "dist/nucleus.js",
"module": "dist/nucleus.esm.js",
"scripts": {
"build": "cross-env NODE_ENV=production rollup --config ../../rollup.config.js",
"build:watch": "rollup --config ../../rollup.config.js -w",
"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"
}
}

View File

@@ -0,0 +1,71 @@
import preact from 'preact';
import { createAppSelectionAPI } from '@nebula.js/selections';
import Cell from './components/Cell';
import populateData from './populator';
import './components/Style.scss';
// import viz from './viz';
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 = () => {
preact.render('', visual.element, reference);
};
const init = (model) => {
reference = preact.render(
<Cell
nebbie={nebbie}
model={model}
app={app}
prom={resolve}
config={config}
visual={visual}
unmount={unmount}
/>,
visual.element,
);
model.once('closed', unmount);
};
app.getObject(id).then(init);
});
}
export function create({
type,
version,
}, visual, nebbie, app) {
const t = nebbie.types.fetch(type, version);
return t.initialProperties(visual.props)
.then(properties => t.supernova().then((sn) => {
if (visual.fields) {
populateData({
sn,
properties,
fields: visual.fields,
});
}
return app.createSessionObject(properties)
.then(model => nebbie.get({
id: model.id,
}, visual));
}));
}

View File

@@ -0,0 +1,42 @@
// https://github.com/ricsv/react-leonardo-ui/blob/master/src/button/button.js
import preact from 'preact';
import { luiClassName } from '../lui/util';
export default class Button extends preact.Component {
render() {
const {
children,
className,
variant,
size,
block,
rounded,
active,
...extraProps
} = this.props;
const finalClassName = luiClassName('button', {
className,
modifiers: {
variant,
size,
block,
rounded,
},
states: { active },
});
return (
<button
type="button"
ref={(element) => { this.element = element; }}
className={finalClassName}
{...extraProps}
>
{children}
</button>
);
}
}

View File

@@ -0,0 +1,225 @@
import preact from 'preact';
import { prefixer } from '../utils';
import Requirements from './Requirements';
import CError from './Error';
import Header from './Header';
import Footer from './Footer';
import Supernova from './Supernova';
import Placeholder from './Placeholder';
import SelectionToolbar from './SelectionToolbar';
// import './Cell.scss';
import viz from '../viz';
const showRequirements = (sn, layout) => {
if (!sn || !sn.definition || !sn.definition.qae || !layout || !layout.qHyperCube) {
return false;
}
const def = sn.definition.qae.data.targets[0];
if (!def) {
return false;
}
const minD = def.dimensions.min();
const minM = def.measures.min();
return (layout.qHyperCube.qDimensionInfo.length < minD
|| layout.qHyperCube.qMeasureInfo.length < minM);
};
const Content = ({ children }) => (
<div className={prefixer(['content'])}>
<div className={prefixer(['content__body'])}>
{children}
</div>
</div>
);
class Cell extends preact.Component {
constructor(props) {
super(props);
this.state = {
context: {
...props.visual.context,
},
options: {
...props.visual.options,
},
layout: {},
sn: null,
};
this.contentRef = preact.createRef();
let type = '__placeholder__';
let version = '__placeholder__';
const { Promise } = props.config.env;
const { model, app } = props;
const externalAPI = viz({
app,
model,
cell: this,
});
this.unmount = () => {
props.unmount();
};
this.takeSnapshot = () => {
const rect = this.contentRef.current.base.children[0].getBoundingClientRect();
if (this.state.sn) {
const snapshot = {
...this.state.layout,
snapshotData: {
object: {
size: {
w: rect.width,
h: rect.height,
},
},
},
};
if (typeof this.state.sn.component.setSnapshotData === 'function') {
return this.state.sn.component.setSnapshotData(snapshot);
}
return Promise.resolve(snapshot);
}
return Promise.reject();
};
const onDone = new Promise((resolve) => { // eslint-disable-line
this.onDone = resolve;
}).then(() => {
this.props.prom(externalAPI);
});
const setType = (t, v) => {
type = t;
version = v;
if (!type) {
return;
}
props.nebbie.types.supernova(t, v).then((SN) => {
const sn = SN.create({
model,
app: props.app,
});
sn.component.on('rendered', (...args) => {
externalAPI.emit('rendered', ...args);
});
this.setState({
sn,
error: null,
});
}).catch((e) => {
console.error(e);
this.setState({
error: {
message: `${e.message}`,
},
});
});
};
const onChanged = () => model.getLayout().then((layout) => {
if (model.selections) {
model.selections.setLayout(layout);
if (layout.qSelectionInfo && layout.qSelectionInfo.qInSelections
&& !model.selections.isModal()) {
model.selections.goModal('/qHyperCubeDef');
}
if (!layout.qSelectionInfo || !layout.qSelectionInfo.qInSelections) {
if (model.selections.isModal()) {
model.selections.noModal();
}
}
}
if (layout.visualization !== type || layout.version !== version) {
setType(layout.visualization, layout.version);
this.setState({
layout,
sn: null,
error: null,
});
} else {
this.setState({
layout,
error: null,
});
}
});
model.on('changed', onChanged);
model.once('closed', () => {
model.removeListener('changed', onChanged);
});
onChanged();
this.cleanUp = () => {
model.removeListener('changed', onChanged);
};
}
// componentDidMount() {
// // console.log('mounted');
// }
// componentDidUpdate() {
// }
componentWillUnmount() {
this.cleanUp();
}
componentDidCatch() {
this.setState({
error: {
message: 'Failed to render',
},
});
}
render() {
const s = this.state;
const SN = (showRequirements(this.state.sn, this.state.layout) ? Requirements : Supernova);
const Comp = !this.state.sn ? Placeholder : SN;
return (
<div className={prefixer(['cell-wrapper'])}>
{
this.state.sn
&& this.state.layout.qSelectionInfo
&& this.state.layout.qSelectionInfo.qInSelections
&& <SelectionToolbar model={this.props.model} sn={this.state.sn} />
}
<div className={prefixer(['cell'])}>
<Header layout={s.layout} />
<Content ref={this.contentRef}>
{this.state.error
? (<CError {...this.state.error} />)
: (
<Comp
key={this.state.layout.visualization}
sn={this.state.sn}
snContext={this.state.context}
snOptions={this.state.options}
prom={this.onDone}
model={this.props.model}
layout={this.state.layout}
/>
)
}
</Content>
<Footer layout={s.layout} />
</div>
</div>
);
}
}
export default Cell;

View File

@@ -0,0 +1,62 @@
.#{$ns}-cell {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
display: flex;
flex-direction: column;
font-size: 14px;
line-height: 16px;
color: #404040;
font-family: 'Source Sans Pro', Arial, 'sans-serif';
.#{$ns}-cell__header {
flex: 0 0 auto;
padding: $spacing;
box-sizing: border-box;
}
.#{$ns}-cell__footnote {
flex: 0 0 auto;
padding: $spacing;
box-sizing: border-box;
}
.#{$ns}-content {
flex: 1 0 auto;
}
}
.#{$ns}-cell__error {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAM0lEQVQYV2P8z8BgzMjAcJYBCUDFkIUYGECCIBFGdJVQwbNwCSSVYGPBEuiCYAlsgiAJANbwDGtj7MYdAAAAAElFTkSuQmCC);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}
.#{$ns}-content {
position: relative;
.#{$ns}-content__body {
position: absolute;
top: $spacing;
left: $spacing;
bottom: $spacing;
right: $spacing;
}
}
.#{$ns}-type--s {
font-size: 12px;
line-height: 16px;
}
.#{$ns}-type--m {
font-size: 16px;
line-height: 20px;
}

View File

@@ -0,0 +1,12 @@
import preact from 'preact';
import { prefixer } from '../utils';
const Component = props => (
<div className={prefixer('cell__error')}>
<div>Error</div>
<div>{props.message}</div>
</div>
);
export default Component;

View File

@@ -0,0 +1,41 @@
// https://github.com/ricsv/react-leonardo-ui/blob/master/src/fade-button/fade-button.js
import preact from 'preact';
import { luiClassName } from '../lui/util';
const FadeButton = ({
className,
children,
variant,
size,
block,
rounded,
active,
...extraProps
}) => {
const finalClassName = luiClassName('fade-button', {
className,
modifiers: {
variant,
size,
block,
rounded,
},
states: { active },
});
return (
<button
type="button"
className={finalClassName}
{
...extraProps
}
>
{children}
</button>
);
};
export default FadeButton;

View File

@@ -0,0 +1,12 @@
import preact from 'preact';
import { prefixer } from '../utils';
const Footer = ({ layout }) => (
layout && layout.showTitles && layout.footnote ? (
<footer className={prefixer(['cell__footnote'])}>
<div className={prefixer(['type', 'type--s'])}>{layout.footnote}</div>
</footer>
) : null
);
export default Footer;

View File

@@ -0,0 +1,15 @@
import preact from 'preact';
import { prefixer } from '../utils';
const Header = ({ layout }) => (
layout && layout.showTitles && (layout.title || layout.subtitle) ? (
<header className={prefixer(['cell__header'])}>
<div className={prefixer(['type--m'])}>
{layout.title}
</div>
<div className={prefixer(['type--s'])}>{layout.subtitle}</div>
</header>
) : null
);
export default Header;

View File

@@ -0,0 +1,22 @@
// https://github.com/ricsv/react-leonardo-ui/blob/master/src/icon/icon.js
import preact from 'preact';
import { luiClassName } from '../lui/util';
const Icon = ({
className,
name,
size,
...extraProps
}) => {
const finalClassName = luiClassName('icon', {
className,
modifiers: { name, size },
});
return (
<span className={finalClassName} aria-hidden="true" {...extraProps} />
);
};
export default Icon;

View File

@@ -0,0 +1,7 @@
import preact from 'preact';
const Component = () => (
<div>&nbsp;</div>
);
export default Component;

View File

@@ -0,0 +1,7 @@
import preact from 'preact';
const Component = () => (
<div>More stuff required</div>
);
export default Component;

View File

@@ -0,0 +1,133 @@
import preact from 'preact';
import Button from './Button';
import FadeButton from './FadeButton';
import Icon from './Icon';
import { prefixer } from '../utils';
/* eslint react/no-multi-comp: 0 */
class Item extends preact.Component {
constructor(props) {
super(props);
this.state = {
active: props.item.active && props.item.active(),
disabled: props.item.enabled && !props.item.enabled(),
};
}
static getDerivedStateFromProps(nextProps) {
return {
disabled: nextProps.item.enabled && !nextProps.item.enabled(),
};
}
componentDidMount() {
if (this.props.item.active && this.props.item.action && this.props.item.on) {
this.onChange = () => {
this.setState({
active: this.props.item.active && this.props.item.active(),
disabled: this.props.item.enabled && !this.props.item.enabled(),
});
};
this.props.item.on('changed', this.onChange);
}
}
componentWillUnmount() {
if (this.onChange && this.props.item.removeListener) {
this.props.item.removeListener('changed', this.onChange);
}
this.onChange = null;
}
render() {
const props = this.props.item;
const Btn = props.type === 'fade-button' || this.props.isCustom ? FadeButton : Button;
return (
<Btn
onClick={() => props.action()}
variant={props.variant}
active={this.state.active}
disabled={this.state.disabled}
>
{props.icon ? <Icon name={props.icon} /> : props.label}
</Btn>
);
}
}
class Component extends preact.Component {
constructor(props) {
super(props);
const api = props.model.selections;
this.state = {
confirmable: api.canConfirm(),
cancelable: api.canCancel(),
clearable: api.canClear(),
};
const items = [];
items.push({
key: 'confirm',
type: 'fade-button',
label: 'Confirm',
// variant: 'success',
icon: 'tick',
enabled: () => this.state.confirmable,
action: () => api.confirm(props.sn.component),
});
items.push({
key: 'cancel',
type: 'fade-button',
label: 'Cancel',
// variant: 'danger',
icon: 'close',
enabled: () => this.state.cancelable,
action: () => api.cancel(props.sn.component),
});
items.push({
key: 'clear',
type: 'fade-button',
label: 'Clear',
icon: 'clear-selections',
enabled: () => this.state.clearable,
action: () => api.clear(props.sn.component),
});
this.listeners = [];
this.custom = {};
(props.sn.selectionToolbar.items || []).forEach((item) => {
this.custom[item.key] = true;
items.push(item);
});
items.reverse();
this.state.items = items;
}
static getDerivedStateFromProps(nextProps) {
const api = nextProps.model.selections;
return {
confirmable: api.canConfirm(),
cancelable: api.canCancel(),
clearable: api.canClear(),
};
}
render() {
return (
<div className={prefixer('selection-toolbar')}>
{this.state.items.map(itm => <Item key={itm.key} item={itm} isCustom={!!this.custom[itm.key]} />)}
</div>
);
}
}
export default Component;

View File

@@ -0,0 +1,16 @@
.#{$ns}-selection-toolbar {
position: absolute;
left: 0;
right: 0;
top: -48px;
padding: $spacing;
line-height: 24px;
box-sizing: border-box;
background: white;
display: flex;
justify-content: flex-end;
> * {
margin-left: 8px;
}
}

View File

@@ -0,0 +1,5 @@
$ns: nucleus;
$spacing: 8px;
@import 'SelectionToolbar';
@import 'Cell';

View File

@@ -0,0 +1,201 @@
import preact from 'preact';
const constrainElement = (el, d) => {
/* eslint-disable no-param-reassign */
if (d) {
el.style.width = `${d.width}px`;
el.style.height = `${d.height}px`;
el.style.top = `${d.top}px`;
el.style.left = `${d.left}px`;
} else {
el.style.width = undefined;
el.style.height = undefined;
el.style.top = 0;
el.style.left = 0;
el.style.right = 0;
el.style.bottom = 0;
}
/* eslint-enable no-param-reassign */
};
const scheduleRender = (props, prev, initial, contentElement) => {
if (prev) {
prev.reject();
}
const prom = {};
const p = new Promise((resolve) => {
const timeout = setTimeout(() => {
const parentRect = contentElement.parentElement.parentElement.getBoundingClientRect();
const r = typeof props.snContext.logicalSize === 'function' ? props.snContext.logicalSize(props.layout, props.sn) : props.sn.logicalSize({ layout: props.layout });
const logicalSize = r || undefined;
if (r) {
// const rect = that.element.getBoundingClientRect();
const parentRatio = parentRect.width / parentRect.height;
const rRatio = r.width / r.height;
let width;
let height;
let left = 0;
let top = 0;
if (parentRatio > rRatio) { // parent is wider -> limit height
({ height } = parentRect);
width = height * rRatio;
left = (parentRect.width - width) / 2;
top = 0;
} else {
({ width } = parentRect);
height = width / rRatio;
left = 0;
top = (parentRect.height - height) / 2;
}
constrainElement(contentElement, {
top, left, width, height,
});
} else {
constrainElement(contentElement);
}
initial.mount();
Promise.resolve(props.sn.component.render({
layout: props.layout,
layoutChanges: props.layoutChanges,
options: props.snOptions || {},
context: {
permissions: (props.snContext || {}).permissions,
theme: (props.snContext || {}).theme,
rtl: (props.snContext || {}).rtl,
localeInfo: (props.snContext || {}).localeInfo,
logicalSize,
},
})).then(() => {
initial.rendered();
// props.sn.component.didUpdate(); // TODO - should check if component is in update stage
}).then(resolve);
}, 0);
prom.reject = () => {
clearTimeout(timeout);
resolve();
};
});
prom.then = p.then;
return prom;
};
class Supernova extends preact.Component {
constructor(props) {
super(props);
this.initial = {
mount: () => {
this.props.sn.component.created({
options: this.props.snOptions,
context: this.props.snContext,
});
this.props.sn.component.mounted(this.contentElement);
this.initial.mount = () => {};
},
rendered: () => {
this.props.prom();
this.initial.rendered = () => {};
},
};
}
componentDidMount() {
let resizeObserver;
this.dimensions = this.element.getBoundingClientRect();
if (typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
const dims = this.element.getBoundingClientRect();
if (dims.width !== this.dimensions.width || dims.height !== this.dimensions.height) {
this.dimensions = dims;
this.setState({});
}
});
resizeObserver.observe(this.element);
}
this.onUnmount = () => {
this.onUnmount = null;
if (resizeObserver) {
resizeObserver.unobserve(this.element);
resizeObserver.disconnect();
resizeObserver = null;
}
this.props.sn.component.willUnmount();
if (this.next) {
this.next.reject();
}
};
this.next = scheduleRender({
snOptions: this.props.snOptions,
snContext: this.props.snContext,
sn: this.props.sn,
layout: this.props.layout,
}, this.next, this.initial, this.contentElement);
}
shouldComponentUpdate(nextProps) {
const update = nextProps.sn
&& !(nextProps.layout
&& nextProps.layout.qSelectionInfo
&& nextProps.layout.qSelectionInfo.qInSelections);
if (!update) {
return false;
}
return true;
// const should = nextProps.sn.component.shouldUpdate({
// layout: nextProps.layout,
// options: {},
// });
// return should;
}
componentDidUpdate() {
this.next = scheduleRender(this.props, this.next, this.initial, this.contentElement);
}
componentWillUnmount() {
this.onUnmount();
}
render() {
const style = {
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
};
return (
<div
style={style}
ref={(element) => { this.element = element; }}
>
<div
style={{
position: 'absolute',
}}
ref={(element) => { this.contentElement = element; }}
/>
</div>
);
}
}
export default Supernova;

View File

@@ -0,0 +1,33 @@
import { create, boot } from './booter';
import types from './sn/types';
function apiGenerator(app) {
const config = {
env: {
Promise,
},
load: () => undefined,
};
const context = {
types: types(config),
};
const api = {
get: (common, viz) => boot(common, viz, config, api, app),
create: (common, viz) => create(common, viz, api, app),
env: (e) => {
Object.assign(config.env, e);
return api;
},
load: ($) => {
config.load = $;
return api;
},
types: context.types,
};
return api;
}
export default apiGenerator;

View File

@@ -0,0 +1,38 @@
export function camelToKebabCase(value) {
return value.replace(/([A-Z])/g, '-$1').toLowerCase();
}
export function luiClassName(name, opts = {}) {
const {
className,
modifiers = {},
states = {},
} = opts;
const baseClass = `lui-${name}`;
let resClassName = baseClass;
Object.keys(modifiers).forEach((key) => {
// Modifiers can be booleans or key-value pair of strings
if (typeof modifiers[key] === 'boolean') {
if (modifiers[key]) {
resClassName += ` ${baseClass}--${key}`;
}
} else if (modifiers[key]) {
resClassName += ` ${baseClass}--${modifiers[key]}`;
}
});
Object.keys(states).forEach((key) => {
// States are always booleans
if (states[key]) {
resClassName += ` lui-${key}`;
}
});
if (className) {
resClassName += ` ${className}`;
}
return resClassName;
}

View File

@@ -0,0 +1,66 @@
const nxDimension = f => ({
qDef: {
qFieldDefs: [f],
qSortCriterias: [{
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByAscii: 1,
}],
},
qOtherTotalSpec: {},
});
const nxMeasure = f => ({
qDef: {
qDef: f,
},
qSortBy: {
qSortByLoadOrder: 1,
qSortByNumeric: -1,
},
});
export default function populateData({
sn,
properties,
fields,
}) {
const target = sn.qae.data.targets[0];
if (!target) {
return;
}
const { path } = target;
const parts = path.split('/');
let p = properties;
for (let i = 0; i < parts.length; i++) {
const s = parts[i];
p = s ? p[s] : p;
}
const hc = p;
hc.qInterColumnSortOrder = hc.qInterColumnSortOrder || [];
fields.forEach((f, i) => {
let type = 'dimension';
if ((typeof f === 'string' && f[0] === '=')
|| (typeof f === 'object' && f.qDef.qDef)
|| (typeof f === 'object' && f.qLibraryId && f.qType === 'measure')) {
type = 'measure';
}
hc.qInterColumnSortOrder.push(i);
if (type === 'measure') {
hc.qMeasures = hc.qMeasures || [];
const def = nxMeasure(f);
hc.qMeasures.push(def);
target.measures.add.call(null, def, properties);
} else {
hc.qDimensions = hc.qDimensions || [];
const def = nxDimension(f);
hc.qDimensions.push(def);
target.dimensions.add.call(null, def, properties);
}
});
}

View File

@@ -0,0 +1,64 @@
import SNFactory from '@nebula.js/supernova';
const LOADED = {};
const load = ({ name, version }, config) => {
const key = `${name}__${version}`;
if (!LOADED[key]) {
const p = config.load({
name,
version,
}, config.env);
if (typeof p === 'string') {
throw new Error('Return value must be a Promise');
}
LOADED[key] = p.catch((e) => {
console.error(e);
throw new Error(`Failed to load supernova: ${name}`);
});
}
return LOADED[key];
};
export function clearFromCache(name) {
Object.keys(LOADED).forEach((key) => {
if (key.split('__')[0] === name) {
LOADED[key] = undefined;
}
});
}
export default function (opts, meta, config) {
let sn;
const type = {
name: opts.name,
version: opts.version,
/**
* Initiate load of supernova
* @returns {Promise<function|SNDefinition>}
*/
load: () => load(type, config),
supernova: () => load(type, config)
.then((SNDefinition) => {
sn = sn || SNFactory(SNDefinition, config.env);
return sn;
}),
initialProperties(initial) {
return this.supernova().then((s) => {
const props = {
qInfo: {
qType: 'foobar',
},
visualization: type.name,
version: type.version,
...s.qae.properties,
...initial,
};
return props;
});
},
};
return type;
}

View File

@@ -0,0 +1,50 @@
import type, { clearFromCache } from './type';
const collection = (name, config) => {
const versions = {};
const get = (version) => {
// TODO determine latest version based on semver
const v = version || Object.keys(versions).sort().slice(-1);
return versions[v];
};
const add = (version, meta) => {
if (!versions[version]) {
versions[version] = type({
name,
version,
}, meta, config);
}
return get(version);
};
return {
fetch: version => add(version),
register: (version, meta) => {
if (versions[version]) {
throw new Error('Already registered!');
}
return add(version, meta);
},
};
};
export default function (config) {
const types = {};
const fetch = (name, version) => {
if (!types[name]) {
types[name] = collection(name, config);
}
return types[name].fetch(version);
};
return {
fetch,
clearFromCache,
load: (name, version) => fetch(name, version).load(),
supernova: (name, version) => fetch(name, version).supernova(),
// instance: (name, version) => fetch(name, version).instance(),
};
}

View File

@@ -0,0 +1,11 @@
/* eslint import/prefer-default-export: 0 */
export const prefixer = (list) => {
const arr = Array.isArray(list) ? list : list.split(/\s+/);
return arr.map((s) => {
if (s[0] === '-' || s[0] === '_') {
return `nucleus${s}`;
}
return `nucleus-${s}`;
}).join(' ');
};

View File

@@ -0,0 +1,53 @@
import EventEmitter from 'node-event-emitter';
const mixin = (obj) => {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
return obj;
};
export default function ({
app,
model,
cell,
} = {}) {
const api = {
model,
app,
close() {
cell.unmount();
},
// properties(props) {
// },
// options(options) {
// cell.setState({
// options,
// });
// return api;
// },
// context(context) {
// cell.setState({
// context,
// });
// return api;
// },
set(o) {
cell.setState(o);
return api;
},
takeSnapshot() {
return cell.takeSnapshot();
},
show() {
return api;
},
};
mixin(api);
return api;
}

View File

@@ -0,0 +1,21 @@
{
"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"
}
}

View File

@@ -0,0 +1,97 @@
import eventmixin from './event-mixin';
export default function (app) {
let canGoForward = false;
let canGoBack = false;
let canClear = false;
let modalObject;
const api = {
switchModal(object, path, accept = true) {
if (object === modalObject) {
return Promise.resolve();
}
if (modalObject) {
modalObject.endSelections(accept);
api.emit('modal-unset');
modalObject.selections.emit('deactivated');
}
if (object && object !== null) { // TODO check model state
modalObject = object;
api.emit('modal', modalObject.selections);
return modalObject.beginSelections(Array.isArray(path) ? path : [path]);
}
modalObject = null;
api.emit('modal-unset');
return Promise.resolve();
},
isModal(objectModel) {
// TODO check model state
return objectModel ? modalObject === objectModel : modalObject !== null;
},
abortModal(accept = true) {
if (!modalObject) {
return Promise.resolve();
}
// modalObject.selections.
modalObject = null;
api.emit('modal-unset');
return app.abortModal(accept);
},
canGoForward() {
return canGoForward;
},
canGoBack() {
return canGoBack;
},
canClear() {
return canClear;
},
forward() {
this.switchModal();
return app.forward();
},
back() {
this.switchModal();
return app.back();
},
clear() {
this.switchModal();
return app.clearAll();
},
};
eventmixin(api);
const prom = app.getObject('CurrentSelection');
const obj = new Promise((resolve) => {
prom.then((sel) => {
resolve(sel);
}).catch(() => {
app.createSessionObject({
qInfo: {
qId: 'CurrentSelection',
qType: 'CurrentSelection',
},
qSelectionObjectDef: {},
}).then((sel) => {
resolve(sel);
});
});
});
obj.then((model) => {
const onChanged = () => model.getLayout().then((layout) => {
canGoBack = layout.qSelectionObject && layout.qSelectionObject.qBackCount > 0;
canGoForward = layout.qSelectionObject && layout.qSelectionObject.qForwardCount > 0;
canClear = layout.qSelectionObject && layout.qSelectionObject.qSelections.length > 0;
api.emit('changed');
});
model.on('changed', onChanged);
model.once('closed', () => {
model.removeListener('changed', onChanged);
});
onChanged();
});
return api;
}

View File

@@ -0,0 +1,10 @@
import EventEmitter from 'node-event-emitter';
export default function (obj) {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
return obj;
}

View File

@@ -0,0 +1,44 @@
/* 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 };

View File

@@ -0,0 +1,82 @@
import eventmixin from './event-mixin';
const event = () => {
let prevented = false;
return {
isPrevented: () => prevented,
preventDefault: () => {
prevented = true;
},
};
};
export default function (model, app) {
const appAPI = app.selections;
let hasSelected = false;
let isActive = false;
let layout = {};
const api = {
id: model.id,
setLayout(lyt) {
layout = lyt;
},
begin(paths) {
const e = event();
this.emit('activate', e);
if (e.isPrevented()) {
return Promise.resolve();
}
isActive = true;
this.emit('activated');
return appAPI.switchModal(model, paths, true);
},
clear() {
hasSelected = false;
this.emit('cleared');
return model.resetMadeSelections();
},
confirm() {
hasSelected = false;
isActive = false;
this.emit('confirmed');
this.emit('deactivated');
return appAPI.switchModal(null, null, true);
},
cancel() {
hasSelected = false;
isActive = false;
this.emit('canceled');
this.emit('deactivated');
return appAPI.switchModal(null, null, false, false);
},
select(s) {
this.begin([s.params[0]]);
if (!appAPI.isModal()) {
return;
}
hasSelected = true;
model[s.method](...s.params).then((qSuccess) => {
if (!qSuccess) {
this.clear();
}
});
},
canClear() {
return hasSelected && layout.qSelectionInfo.qMadeSelections;
},
canConfirm() {
return hasSelected && layout.qSelectionInfo.qMadeSelections;
},
canCancel() {
return true;
},
isActive: () => isActive,
isModal: () => appAPI.isModal(model),
goModal: paths => appAPI.switchModal(model, paths, false),
noModal: () => appAPI.switchModal(null, null, false),
};
eventmixin(api);
return api;
}

View File

@@ -0,0 +1,40 @@
import supernova from '../../src/index';
describe('supernova', () => {
it('should pass down resources', () => {
let res;
const mySn = {
component: {
created() {
res = this.resources;
},
},
};
const generator = supernova(mySn, {
translator: 't',
flags: 'f',
Promise: 'P',
});
const sn = generator.create({
model: {},
app: {},
});
sn.component.created();
expect(res).to.eql({
translator: 't',
flags: 'f',
Promise: 'P',
PERMISSIONS: {
PASSIVE: 1,
INTERACT: 2,
SELECT: 4,
FETCH: 8,
},
});
});
});

View File

@@ -0,0 +1,21 @@
{
"private": true,
"name": "@nebula.js/supernova",
"version": "0.1.0",
"description": "",
"license": "MIT",
"author": "QlikTech International AB",
"keywords": [],
"main": "dist/supernova.js",
"module": "dist/supernova.esm.js",
"scripts": {
"build": "cross-env NODE_ENV=production rollup --config ../../rollup.config.js",
"build:dev": "rollup --config ../../rollup.config.js",
"lint": "eslint src"
},
"devDependencies": {
"@nebula.js/selections": "^0.1.0",
"extend": "^3.0.2",
"node-event-emitter": "^0.0.1"
}
}

View File

@@ -0,0 +1,62 @@
import EventEmitter from 'node-event-emitter';
import extend from 'extend';
const mixin = (obj) => {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
return obj;
};
const actionWrapper = component => (item) => {
const wrapped = mixin(extend(true, {}, item, {
action() {
if (typeof item.action === 'function') {
item.action.call(wrapped, component);
}
wrapped.emit('changed');
},
enabled() {
if (typeof item.enabled === 'function') {
return item.enabled.call(wrapped, component);
}
return true;
},
active: typeof item.active === 'function' ? function active() {
return item.active.call(wrapped, component);
} : undefined,
}));
return wrapped;
};
export default function ({
sn,
component,
}) {
const actions = {};
const selectionToolbarItems = [];
const w = actionWrapper(component);
((sn.selectionToolbar || {}).items || []).forEach((item) => {
const wrapped = w(item);
// TODO - check if key exists
actions[item.key] = wrapped;
selectionToolbarItems.push(wrapped);
});
(sn.actions || []).forEach((item) => {
const wrapped = w(item);
// TODO - check if key exists
actions[item.key] = wrapped;
});
return {
actions,
selectionToolbarItems,
destroy() {
selectionToolbarItems.length = 0;
},
};
}

View File

@@ -0,0 +1,15 @@
// dummy capability flags until exposed as require alias in sense-client
const FLAGS = {
PIE_RADIUS: true,
};
export default function flags() {
return {
getFlag(f) {
if (!(f in FLAGS)) {
throw new Error('Oopsie');
}
return FLAGS[f];
},
};
}

View File

@@ -0,0 +1,158 @@
import extend from 'extend';
import EventEmitter from 'node-event-emitter';
import { createObjectSelectionAPI } from '@nebula.js/selections';
import translator from './translator';
import flags from './flags';
import actionhero from './action-hero';
import qae from './qae';
const PERMISSIONS = {
PASSIVE: 1, // allowed actions that don't effect the state of the chart (e.g. click on links, mousehover etc)
INTERACT: 2, // allowed to click, zoom, scroll, pan etc, (anything that does not affect selections)
SELECT: 4, // allowed to select (should never be on if ENGINE is not available)
FETCH: 8, // engine is available (might be better to proxy enigma calls in snapshot mode)
};
const defaultComponent = {
app: null,
model: null,
actions: null,
created: () => {},
mounted: () => {},
render: () => {},
resize: () => {},
willUnmount: () => {},
destroy: () => {},
emit: () => {},
getViewState: () => {},
// temporary
setSnapshotData: snapshot => Promise.resolve(snapshot),
};
const reservedKeys = Object.keys(defaultComponent);
const mixin = (obj) => {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
return obj;
};
function create(sn, opts, env) {
const componentInstance = {
...defaultComponent,
};
mixin(componentInstance);
const userInstance = {
emit(...args) {
componentInstance.emit(...args);
},
};
Object.keys(sn.component || {}).forEach((key) => {
if (reservedKeys.indexOf(key) !== -1) {
componentInstance[key] = sn.component[key].bind(userInstance);
} else {
userInstance[key] = sn.component[key];
}
});
const hero = actionhero({
sn,
component: userInstance,
});
let selections = null;
if (!opts.selections && !opts.model.selections) {
Object.defineProperty(opts.model, 'selections', {
get() {
selections = selections || createObjectSelectionAPI(opts.model, opts.app);
return selections;
},
});
}
extend(userInstance, {
model: opts.model,
app: opts.app,
selections: opts.selections || opts.model.selections,
actions: hero.actions,
resources: {
translator: env.translator || translator,
Promise: env.Promise || Promise,
flags: env.flags || flags(),
PERMISSIONS,
},
});
extend(componentInstance, {
actions: hero.actions,
});
// componentInstance.created({
// options: snOptions,
// });
return {
definition: sn,
component: componentInstance,
selectionToolbar: {
items: hero.selectionToolbarItems,
},
logicalSize: sn.logicalSize || (() => false),
};
}
/**
* @returns
*/
export default function generator(UserSN, env) {
let sn;
const localEnv = extend({
translator,
Promise,
flags: flags(),
PERMISSIONS,
}, env);
if (typeof UserSN === 'function') {
sn = UserSN(localEnv);
} else {
sn = UserSN;
}
const gen = {
qae: qae(sn.qae),
component: sn.component || {},
create({
model,
app,
selections,
}) {
const ss = create(gen, {
model,
app,
selections,
}, localEnv);
return ss;
},
};
Object.keys(sn).forEach((key) => {
if (!gen[key]) {
gen[key] = sn[key];
}
});
return gen;
}

View File

@@ -0,0 +1,37 @@
const noop = () => {};
function fallback(x, value) {
if (typeof x === 'undefined') {
return () => value;
}
return () => x;
}
function defFn(def = {}) {
return {
min: typeof def.min === 'function' ? def.min : fallback(def.min, 0),
max: typeof def.max === 'function' ? def.max : fallback(def.max, 1000),
add: def.add || noop,
};
}
function target(def) {
return {
path: def.path || '/qHyperCubeDef',
dimensions: defFn(def.dimensions),
measures: defFn(def.measures),
};
}
export default function qae(def = {}) {
const q = {
properties: {
...def.properties,
},
data: {
targets: ((def.data || {}).targets || []).map(target),
},
};
return q;
}

View File

@@ -0,0 +1,5 @@
export default {
get(str/* , args */) {
return `$$$${str}$$$`;
},
};

102
rollup.config.js Normal file
View File

@@ -0,0 +1,102 @@
const path = require('path');
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const babel = require('rollup-plugin-babel');
const replace = require('rollup-plugin-replace');
const sass = require('rollup-plugin-sass');
const { terser } = require('rollup-plugin-terser');
const cwd = process.cwd();
const pkg = require(path.join(cwd, 'package.json')); // eslint-disable-line
const {
name,
version,
license,
} = pkg;
const banner = `/*
* ${name} v${version}
* Copyright (c) ${new Date().getFullYear()} QlikTech International AB
* Released under the ${license} license.
*/
`;
const GLOBALS = {
};
// const watch = process.argv.indexOf('-w') > 2;
const config = (isEsm) => {
const outputFile = isEsm ? pkg.module : pkg.main;
const basename = path.basename(outputFile);
const dir = path.dirname(outputFile);
const umdName = basename.replace(/-([a-z])/g, (m, p1) => p1.toUpperCase()).split('.js').join('');
const external = isEsm ? Object.keys(pkg.dependencies || {}) : [];
const globals = {};
external.forEach((e) => {
if ([GLOBALS[e]]) {
globals[e] = GLOBALS[e];
}
});
const cfg = {
input: path.resolve(cwd, 'src', 'index'),
output: {
file: path.resolve(dir, basename),
format: isEsm ? 'esm' : 'umd',
exports: 'default',
name: umdName,
sourcemap: true,
banner,
globals,
},
external,
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify(isEsm ? 'development' : 'production'),
}),
sass({
insert: true,
}),
nodeResolve({
extensions: ['.js', '.jsx'],
}),
commonjs(),
babel({
babelrc: false,
// include: [
// 'src/**',
// // /react-leonardo-ui/
// ],
presets: [
['@babel/preset-env', {
modules: false,
targets: {
browsers: ['ie 11', 'chrome 47'],
},
}],
],
plugins: [
['@babel/plugin-transform-react-jsx', { pragma: 'preact.h' }],
],
}),
// deps(),
],
};
if (process.env.NODE_ENV === 'production' && !isEsm) {
cfg.plugins.push(terser({
output: {
preamble: banner,
},
}));
}
return cfg;
};
module.exports = [
config(),
config(true),
].filter(Boolean);

7184
yarn.lock Normal file

File diff suppressed because it is too large Load Diff