chore: enable prettier (#88)

This commit is contained in:
Miralem Drek
2019-08-20 17:02:21 +02:00
committed by GitHub
parent 61c03a97f1
commit a9b4a9ee18
102 changed files with 1230 additions and 1143 deletions

View File

@@ -1,4 +1,3 @@
dist
node_modules
**/__test__/unit/coverage/*
**/__test__/component/coverage/*
dist/
coverage/
node_modules/

View File

@@ -6,14 +6,14 @@
"parserOptions": {
"sourceType": "module"
},
"extends": [
"airbnb"
],
"extends": ["airbnb", "prettier", "prettier/react"],
"plugins": ["prettier"],
"rules": {
"max-len": 0,
"no-plusplus": 0,
"no-bitwise" : 0,
"no-bitwise": 0,
"no-unused-expressions": 0,
"prettier/prettier": 2,
"react/destructuring-assignment": [0, "always"],
"react/prop-types": 0,
"react/no-deprecated": 0,
@@ -35,9 +35,7 @@
"aw": false,
"page": false
},
"plugins": [
"mocha"
],
"plugins": ["mocha"],
"rules": {
"mocha/no-exclusive-tests": "error"
}

6
.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"endOfLine": "lf",
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5"
}

View File

@@ -4,7 +4,8 @@
"scripts": {
"build": "cross-env NODE_ENV=production FORCE_COLOR=1 lerna run build --stream",
"build:watch": "FORCE_COLOR=1 lerna run build:watch --stream --concurrency 99 --no-sort",
"lint": "eslint packages --ext .js --ext .jsx",
"lint": "eslint packages --ext .js,.jsx",
"lint:check": "eslint --print-config ./aw.config.js | eslint-config-prettier-check",
"start": "MONO=true ./packages/cli/lib/index.js serve --entry ./test/integration/sn.js",
"test": "yarn run test:unit",
"test:integration": "aw puppet -c aw.config.js --type integration",
@@ -39,13 +40,16 @@
"enigma.js": "^2.4.0",
"eslint": "^6.1.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-mocha": "^6.0.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.14.3",
"husky": "^3.0.3",
"lerna": "^3.16.4",
"lint-staged": "^9.2.1",
"prettier": "^1.18.2",
"rollup": "^1.19.4",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.0.2",

View File

@@ -7,18 +7,9 @@ const replace = require('rollup-plugin-replace');
const node = require('rollup-plugin-node-resolve');
const { terser } = require('rollup-plugin-terser');
const config = ({
mode = 'production',
format = 'umd',
cwd = process.cwd(),
} = {}) => {
const config = ({ mode = 'production', format = 'umd', cwd = process.cwd() } = {}) => {
const pkg = require(path.resolve(cwd, 'package.json')); // eslint-disable-line
const {
name,
version,
license,
author,
} = pkg;
const { name, version, license, author } = pkg;
const auth = typeof author === 'object' ? `${author.name} <${author.email}>` : author || '';
const moduleName = name.split('/').reverse()[0];
@@ -49,20 +40,27 @@ const config = ({
babel({
babelrc: false,
presets: [
['@babel/preset-env', {
modules: false,
targets: {
browsers: ['ie 11', 'chrome 47'],
[
'@babel/preset-env',
{
modules: false,
targets: {
browsers: ['ie 11', 'chrome 47'],
},
},
}],
],
],
}),
postcss({}),
...[mode === 'production' ? terser({
output: {
preamble: banner,
},
}) : false],
...[
mode === 'production'
? terser({
output: {
preamble: banner,
},
})
: false,
],
].filter(Boolean),
},
output: {
@@ -108,7 +106,7 @@ const watch = async () => {
});
return new Promise((resolve, reject) => {
watcher.on('event', (event) => {
watcher.on('event', event => {
if (event.code === 'FATAL') {
console.error(event);
reject();

View File

@@ -6,12 +6,12 @@ const create = require('@nebula.js/cli-create/command');
const serve = require('@nebula.js/cli-serve/command');
const sense = require('@nebula.js/cli-sense/command');
yargs.usage('nebula <command> [options]')
yargs
.usage('nebula <command> [options]')
.command(build)
.command(create)
.command(serve)
.command(sense)
.demandCommand()
.alias('h', 'help')
.wrap(Math.min(80, yargs.terminalWidth()))
.argv;
.wrap(Math.min(80, yargs.terminalWidth())).argv;

View File

@@ -18,10 +18,14 @@ const hasYarn = () => {
}
};
const author = async (cwd) => {
const author = async cwd => {
try {
const email = execSync('git config --get user.email', { cwd }).toString().trim();
const name = execSync('git config --get user.name', { cwd }).toString().trim();
const email = execSync('git config --get user.email', { cwd })
.toString()
.trim();
const name = execSync('git config --get user.name', { cwd })
.toString()
.trim();
return {
email,
name,
@@ -54,7 +58,7 @@ function cpy(root, destination) {
};
}
const create = async (argv) => {
const create = async argv => {
const { name } = argv;
const projectFolder = name;
@@ -67,8 +71,8 @@ const create = async (argv) => {
let options = {
install: true,
...argv,
pkgm: argv.pkgm || (await hasYarn() ? 'yarn' : 'npm'),
author: argv.author ? parseAuthor(argv.author) : (await author()),
pkgm: argv.pkgm || ((await hasYarn()) ? 'yarn' : 'npm'),
author: argv.author ? parseAuthor(argv.author) : await author(),
};
const results = {};
@@ -79,14 +83,16 @@ const create = async (argv) => {
}
const prompt = async () => {
const answers = await inquirer.prompt([{
type: 'list',
name: 'picasso',
message: 'Pick a picasso template',
default: 'none',
choices: ['none', 'minimal', 'barchart'],
when: !argv.picasso,
}]);
const answers = await inquirer.prompt([
{
type: 'list',
name: 'picasso',
message: 'Pick a picasso template',
default: 'none',
choices: ['none', 'minimal', 'barchart'],
when: !argv.picasso,
},
]);
options = { ...options, ...answers };
};
@@ -99,15 +105,12 @@ const create = async (argv) => {
// ==== common files ====
// copy raw files
[
'editorconfig',
'eslintignore',
'gitignore',
'eslintrc.json',
].forEach((filename) => fs.copyFileSync(
path.resolve(templatesRoot, 'common', `_${filename}`), // copying dotfiles may not always work, so they are prefixed with an underline
path.resolve(destination, `.${filename}`),
));
['editorconfig', 'eslintignore', 'gitignore', 'eslintrc.json'].forEach(filename =>
fs.copyFileSync(
path.resolve(templatesRoot, 'common', `_${filename}`), // copying dotfiles may not always work, so they are prefixed with an underline
path.resolve(destination, `.${filename}`)
)
);
const copy = cpy(templatesRoot, destination);
@@ -125,7 +128,7 @@ const create = async (argv) => {
const traverse = (sourceFolder, targetFolder = '') => {
const files = fs.readdirSync(path.resolve(templatesRoot, sourceFolder));
files.forEach((file) => {
files.forEach(file => {
const p = `${sourceFolder}/${file}`;
const stats = fs.lstatSync(path.resolve(templatesRoot, p));
const next = `${targetFolder}/${file}`.replace(/^\//, '');
@@ -146,7 +149,7 @@ const create = async (argv) => {
});
};
folders.forEach((folder) => {
folders.forEach(folder => {
traverse(folder);
});
};

View File

@@ -14,10 +14,7 @@ export default function supernova(env) {
mounted(element) {
element.innerHTML = '<div>Hello!</div>'; // eslint-disable-line
},
render({
layout,
context,
}) {
render({ layout, context }) {
console.log('render', layout, context);
},
resize() {},

View File

@@ -4,7 +4,7 @@ describe('sn', () => {
const app = encodeURIComponent(process.env.APP_ID || '/apps/ctrl00.qvf');
await page.goto(`${process.testServer.url}/render/app/${app}`);
await page.waitForFunction(`!!document.querySelector('${content}')`);
const text = await page.$eval(content, (el) => el.textContent);
const text = await page.$eval(content, el => el.textContent);
expect(text).to.equal('Hello!');
});
});

View File

@@ -9,7 +9,7 @@ before(async () => {
process.testServer = s;
page.on('pageerror', (e) => {
page.on('pageerror', e => {
console.log('Error:', e.message, e.stack);
});
});

View File

@@ -1,13 +1,15 @@
export default {
targets: [{
path: 'qHyperCubeDef',
dimensions: {
min: 1,
max: 1,
targets: [
{
path: 'qHyperCubeDef',
dimensions: {
min: 1,
max: 1,
},
measures: {
min: 1,
max: 1,
},
},
measures: {
min: 1,
max: 1,
},
}],
],
};

View File

@@ -2,9 +2,7 @@ const properties = {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [
{ qWidth: 2, qHeight: 5000 },
],
qInitialDataFetch: [{ qWidth: 2, qHeight: 5000 }],
qSuppressZero: false,
qSuppressMissing: true,
},

View File

@@ -1,7 +1,4 @@
export default function picassoDefinition({
layout,
context,
}) {
export default function picassoDefinition({ layout, context }) {
if (!layout.qHyperCube) {
throw new Error('Layout is missing a hypercube');
}
@@ -15,46 +12,57 @@ export default function picassoDefinition({
invert: true,
},
},
components: [{
type: 'axis',
dock: 'left',
scale: 'y',
}, {
type: 'axis',
dock: 'bottom',
scale: 'x',
}, {
type: 'box',
data: {
extract: {
field: 'qDimensionInfo/0',
props: {
start: 0,
end: { field: 'qMeasureInfo/0' },
},
},
components: [
{
type: 'axis',
dock: 'left',
scale: 'y',
},
settings: {
major: { scale: 'x' },
minor: { scale: 'y' },
box: {
width: 0.7,
},
{
type: 'axis',
dock: 'bottom',
scale: 'x',
},
brush: context.permissions.indexOf('interact') !== -1 && context.permissions.indexOf('select') !== -1 ? {
trigger: [{
contexts: ['selection'],
}],
consume: [{
context: 'selection',
data: ['', 'end'],
style: {
inactive: {
opacity: 0.3,
{
type: 'box',
data: {
extract: {
field: 'qDimensionInfo/0',
props: {
start: 0,
end: { field: 'qMeasureInfo/0' },
},
},
}],
} : {},
}],
},
settings: {
major: { scale: 'x' },
minor: { scale: 'y' },
box: {
width: 0.7,
},
},
brush:
context.permissions.indexOf('interact') !== -1 && context.permissions.indexOf('select') !== -1
? {
trigger: [
{
contexts: ['selection'],
},
],
consume: [
{
context: 'selection',
data: ['', 'end'],
style: {
inactive: {
opacity: 0.3,
},
},
},
],
}
: {},
},
],
};
}

View File

@@ -2,14 +2,16 @@ describe('interaction', () => {
const content = '.nebulajs-sn';
it('should select two bars', async () => {
const app = encodeURIComponent(process.env.APP_ID || '/apps/ctrl00.qvf');
await page.goto(`${process.testServer.url}/render/app/${app}?cols=Alpha,=5+avg(Expression1)&&permissions=interact,select`);
await page.goto(
`${process.testServer.url}/render/app/${app}?cols=Alpha,=5+avg(Expression1)&&permissions=interact,select`
);
await page.waitForFunction(`!!document.querySelector('${content}')`);
await page.click('rect[data-label="K"]');
await page.click('rect[data-label="S"]');
await page.click('button[title="Confirm selection"]');
const rects = await page.$$eval('rect[data-label]', (sel) => sel.map((r) => r.getAttribute('data-label')));
const rects = await page.$$eval('rect[data-label]', sel => sel.map(r => r.getAttribute('data-label')));
expect(rects).to.eql(['K', 'S']);
});
});

View File

@@ -29,16 +29,15 @@ export default function supernova(/* env */) {
picassoQ,
});
},
render({
layout,
context,
}) {
render({ layout, context }) {
this.pic.update({
data: [{
type: 'q',
key: 'qHyperCube',
data: layout.qHyperCube,
}],
data: [
{
type: 'q',
key: 'qHyperCube',
data: layout.qHyperCube,
},
],
settings: definition({ layout, context }),
});
},

View File

@@ -11,15 +11,15 @@ const KEYS = {
const instances = [];
let expando = 0;
const confirmOrCancelSelection = (e) => {
const active = instances.filter((a) => a.selections && a.selections.isActive());
const confirmOrCancelSelection = e => {
const active = instances.filter(a => a.selections && a.selections.isActive());
if (!active.length) {
return;
}
if (e.key === KEYS.ENTER) {
active.forEach((a) => a.selections.confirm());
active.forEach(a => a.selections.confirm());
} else if (e.key === KEYS.ESCAPE || e.key === KEYS.IE11_ESC) {
active.forEach((a) => a.selections.cancel());
active.forEach(a => a.selections.cancel());
}
};
@@ -33,24 +33,18 @@ const teardown = () => {
// ------------------------------------------------------
const addListeners = (emitter, listeners) => {
Object.keys(listeners).forEach((type) => {
Object.keys(listeners).forEach(type => {
emitter.on(type, listeners[type]);
});
};
const removeListeners = (emitter, listeners) => {
Object.keys(listeners).forEach((type) => {
Object.keys(listeners).forEach(type => {
emitter.removeListener(type, listeners[type]);
});
};
export default function ({
selections,
brush,
picassoQ,
} = {}, {
path = '/qHyperCubeDef',
} = {}) {
export default function({ selections, brush, picassoQ } = {}, { path = '/qHyperCubeDef' } = {}) {
if (!selections) {
return {
release: () => {},
@@ -61,21 +55,24 @@ export default function ({
let layout = null;
// interceptors primary job is to ensure selections only occur on either values OR ranges
const valueInterceptor = (added) => {
const valueInterceptor = added => {
const brushes = brush.brushes();
brushes.forEach((b) => {
if (b.type === 'range') { // has range selections
brushes.forEach(b => {
if (b.type === 'range') {
// has range selections
brush.clear([]);
} else if (added[0] && added[0].key !== b.id) { // has selections in another dimension
} else if (added[0] && added[0].key !== b.id) {
// has selections in another dimension
brush.clear([]);
}
});
return added.filter((t) => t.value !== -2); // do not allow selection on null value
return added.filter(t => t.value !== -2); // do not allow selection on null value
};
const rangeInterceptor = (a) => {
const v = brush.brushes().filter((b) => b.type === 'value');
if (v.length) { // has dimension values selected
const rangeInterceptor = a => {
const v = brush.brushes().filter(b => b.type === 'value');
if (v.length) {
// has dimension values selected
brush.clear([]);
return a;
}
@@ -103,7 +100,7 @@ export default function ({
brush.on('update', () => {
const generated = picassoQ.selections(brush, {}, layout);
generated.forEach((s) => selections.select(s));
generated.forEach(s => selections.select(s));
});
if (instances.length === 0) {
@@ -116,10 +113,12 @@ export default function ({
});
return {
layout: (lt) => { layout = lt; },
layout: lt => {
layout = lt;
},
release: () => {
layout = null;
const idx = instances.indexOf(instances.filter((i) => i.key === key)[0]);
const idx = instances.indexOf(instances.filter(i => i.key === key)[0]);
if (idx !== -1) {
instances.splice(idx, 1);
}

View File

@@ -9,7 +9,7 @@ before(async () => {
process.testServer = s;
page.on('pageerror', (e) => {
page.on('pageerror', e => {
console.log('Error:', e.message, e.stack);
});
});

View File

@@ -1,11 +1,13 @@
export default {
targets: [{
path: 'qHyperCubeDef',
dimensions: {
min: 1,
targets: [
{
path: 'qHyperCubeDef',
dimensions: {
min: 1,
},
measures: {
min: 1,
},
},
measures: {
min: 1,
},
}],
],
};

View File

@@ -2,9 +2,7 @@ const properties = {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [
{ qWidth: 10, qHeight: 500 },
],
qInitialDataFetch: [{ qWidth: 10, qHeight: 500 }],
qSuppressZero: false,
qSuppressMissing: true,
},

View File

@@ -1,4 +1,4 @@
export default function ({
export default function({
layout, // eslint-disable-line no-unused-vars
context, // eslint-disable-line no-unused-vars
}) {

View File

@@ -10,10 +10,11 @@ const mock = ({
Typography: ({ children }) => <t>{children}</t>,
},
STB = () => <stb />,
} = {}) => aw.mock([
['**/ui/components/index.js', () => components],
['**/SelectionToolbar.jsx', () => STB],
], ['../../src/components/Header']);
} = {}) =>
aw.mock(
[['**/ui/components/index.js', () => components], ['**/SelectionToolbar.jsx', () => STB]],
['../../src/components/Header']
);
describe('<Header />', () => {
it('should render a title', () => {
@@ -21,13 +22,28 @@ describe('<Header />', () => {
const [{ default: Header }] = mock();
const tree = renderer.create(<Header layout={layout} />).toJSON();
expect(tree).to.eql({
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 't', props: {}, children: ['title'],
}],
}],
}, { type: 'g', props: {}, children: null }],
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 't',
props: {},
children: ['title'],
},
],
},
],
},
{ type: 'g', props: {}, children: null },
],
});
});
@@ -36,13 +52,28 @@ describe('<Header />', () => {
const [{ default: Header }] = mock();
const tree = renderer.create(<Header layout={layout} />).toJSON();
expect(tree).to.eql({
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 't', props: {}, children: ['sub'],
}],
}],
}, { type: 'g', props: {}, children: null }],
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 't',
props: {},
children: ['sub'],
},
],
},
],
},
{ type: 'g', props: {}, children: null },
],
});
});
@@ -51,13 +82,32 @@ describe('<Header />', () => {
const [{ default: Header }] = mock();
const tree = renderer.create(<Header layout={layout} sn />).toJSON();
expect(tree).to.eql({
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: null,
}],
}, { type: 'g', props: {}, children: [{
type: 'stb', props: {}, children: null,
}] }],
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: null,
},
],
},
{
type: 'g',
props: {},
children: [
{
type: 'stb',
props: {},
children: null,
},
],
},
],
});
});
});

View File

@@ -1,12 +1,16 @@
describe('ObjectAPI', () => {
const doMock = ({
createObjectSelectionAPI = () => ({}),
} = {}) => aw.mock([
['**/selections/index.js', () => ({
createObjectSelectionAPI,
})],
], ['../../src/object/object-api']);
const doMock = ({ createObjectSelectionAPI = () => ({}) } = {}) =>
aw.mock(
[
[
'**/selections/index.js',
() => ({
createObjectSelectionAPI,
}),
],
],
['../../src/object/object-api']
);
it('setState should pass props to viz', () => {
const [{ default: API }] = doMock();
@@ -75,7 +79,9 @@ describe('ObjectAPI', () => {
},
config: { env: { Promise: { resolve: sinon.spy() } } },
};
context.nebbie.types.get.withArgs({ name: 'my-type', version: 'my-sn-version' }).returns({ supernova: () => Promise.resolve('my-sn') });
context.nebbie.types.get
.withArgs({ name: 'my-type', version: 'my-sn-version' })
.returns({ supernova: () => Promise.resolve('my-sn') });
api = new API('model', context, 'viz');
api.setSupernova = sinon.spy();
});
@@ -136,7 +142,11 @@ describe('ObjectAPI', () => {
api.setType = sinon.spy();
api.setState = sinon.spy();
api.setLayout({ visualization: 'viz', version: 'prop-version' });
expect(api.setState).to.have.been.calledWithExactly({ layout: { visualization: 'viz', version: 'prop-version' }, error: null, sn: null });
expect(api.setState).to.have.been.calledWithExactly({
layout: { visualization: 'viz', version: 'prop-version' },
error: null,
sn: null,
});
expect(api.setType).to.have.been.calledWithExactly('viz', 'prop-version', 'sn-version');
});
@@ -147,7 +157,10 @@ describe('ObjectAPI', () => {
api.currentObjectType = 'viz';
api.currentPropertyVersion = '1.0.0';
api.setLayout({ visualization: 'viz', version: '1.0.0' });
expect(api.setState).to.have.been.calledWithExactly({ layout: { visualization: 'viz', version: '1.0.0' }, error: null });
expect(api.setState).to.have.been.calledWithExactly({
layout: { visualization: 'viz', version: '1.0.0' },
error: null,
});
expect(api.setType).to.not.have.been.called;
});
});

View File

@@ -25,10 +25,10 @@ describe('<SelectionToolbar />', () => {
};
const STItem = () => '';
const LocaleContext = React.createContext();
const [{ default: STB }] = aw.mock([
['**/SelectionToolbarItem.jsx', () => STItem],
['**/LocaleContext.js', () => LocaleContext],
], ['../../src/components/SelectionToolbar']);
const [{ default: STB }] = aw.mock(
[['**/SelectionToolbarItem.jsx', () => STItem], ['**/LocaleContext.js', () => LocaleContext]],
['../../src/components/SelectionToolbar']
);
const translator = {
get: sinon.stub(),
@@ -41,7 +41,7 @@ describe('<SelectionToolbar />', () => {
const c = renderer.create(
<LocaleContext.Provider value={translator}>
<STB sn={props.sn} />
</LocaleContext.Provider>,
</LocaleContext.Provider>
);
items = c.root.findAllByType(STItem);
@@ -112,10 +112,10 @@ describe('<SelectionToolbar />', () => {
};
const STItem = ({ isCustom }) => `-${isCustom}-`;
const LocaleContext = React.createContext();
const [{ default: STB }] = aw.mock([
['**/SelectionToolbarItem.jsx', () => STItem],
['**/LocaleContext.js', () => LocaleContext],
], ['../../src/components/SelectionToolbar']);
const [{ default: STB }] = aw.mock(
[['**/SelectionToolbarItem.jsx', () => STItem], ['**/LocaleContext.js', () => LocaleContext]],
['../../src/components/SelectionToolbar']
);
const translator = {
get: sinon.stub(),
@@ -124,7 +124,7 @@ describe('<SelectionToolbar />', () => {
const c = renderer.create(
<LocaleContext.Provider value={translator}>
<STB sn={props.sn} />
</LocaleContext.Provider>,
</LocaleContext.Provider>
);
expect(c.toJSON()).to.eql({

View File

@@ -1,15 +1,15 @@
const flush = () => new Promise((r) => setImmediate(r));
const flush = () => new Promise(r => setImmediate(r));
describe('viz', () => {
const doMock = ({
boot = () => {},
getter = () => {},
getPatches = () => {},
} = {}) => aw.mock([
['**/components/boot.jsx', () => boot],
['**/object/observer.js', () => ({ get: getter })],
['**/utils/patcher.js', () => getPatches],
], ['../viz.js']);
const doMock = ({ boot = () => {}, getter = () => {}, getPatches = () => {} } = {}) =>
aw.mock(
[
['**/components/boot.jsx', () => boot],
['**/object/observer.js', () => ({ get: getter })],
['**/utils/patcher.js', () => getPatches],
],
['../viz.js']
);
describe('api', () => {
let api;
@@ -77,7 +77,9 @@ describe('viz', () => {
let mounted = false;
api.mount('element').then(() => { mounted = true; });
api.mount('element').then(() => {
mounted = true;
});
setObjectProps({ layout: {}, sn: {} });
await flush();

View File

@@ -1,11 +1,6 @@
import React, {
useEffect,
useState,
} from 'react';
import React, { useEffect, useState } from 'react';
import {
Grid,
} from '@nebula.js/ui/components';
import { Grid } from '@nebula.js/ui/components';
import Requirements from './Requirements';
import CError from './Error';
@@ -24,8 +19,7 @@ const showRequirements = (sn, layout) => {
}
const minD = def.dimensions.min();
const minM = def.measures.min();
return (layout.qHyperCube.qDimensionInfo.length < minD
|| layout.qHyperCube.qMeasureInfo.length < minM);
return layout.qHyperCube.qDimensionInfo.length < minD || layout.qHyperCube.qMeasureInfo.length < minM;
};
const Content = ({ children }) => (
@@ -45,10 +39,7 @@ const Content = ({ children }) => (
</div>
);
export default function Cell({
api,
onInitial,
}) {
export default function Cell({ api, onInitial }) {
const [, setChanged] = useState(0);
useEffect(() => {
const onChanged = () => setChanged(Date.now());
@@ -65,27 +56,29 @@ export default function Cell({
const objectProps = api.objectProps();
const userProps = api.userProps();
const SN = (showRequirements(objectProps.sn, objectProps.layout) ? Requirements : Supernova);
const SN = showRequirements(objectProps.sn, objectProps.layout) ? Requirements : Supernova;
const Comp = !objectProps.sn ? Placeholder : SN;
const err = objectProps.error || false;
return (
<Grid container direction="column" spacing={0} style={{ height: '100%', padding: '8px', boxSixing: 'borderBox' }}>
<Grid item style={{ maxWidth: '100%' }}>
<Header layout={objectProps.layout} sn={objectProps.sn}>&nbsp;</Header>
<Header layout={objectProps.layout} sn={objectProps.sn}>
&nbsp;
</Header>
</Grid>
<Grid item xs>
<Content>
{err
? (<CError message={err.message} />)
: (
<Comp
key={objectProps.layout.visualization}
sn={objectProps.sn}
snContext={userProps.context}
snOptions={userProps.options}
layout={objectProps.layout}
/>
)}
{err ? (
<CError message={err.message} />
) : (
<Comp
key={objectProps.layout.visualization}
sn={objectProps.sn}
snContext={userProps.context}
snOptions={userProps.options}
layout={objectProps.layout}
/>
)}
</Content>
</Grid>
<Footer layout={objectProps.layout} />

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { prefixer } from '../utils/utils';
const Component = (props) => (
const Component = props => (
<div className={prefixer('cell__error')}>
<div>Error</div>
<div>{props.message}</div>

View File

@@ -1,18 +1,16 @@
import React from 'react';
import {
Typography,
Grid,
} from '@nebula.js/ui/components';
import { Typography, Grid } from '@nebula.js/ui/components';
const Footer = ({ layout }) => (
const Footer = ({ layout }) =>
layout && layout.showTitles && layout.footnote ? (
<Grid container>
<Grid item style={{ minWidth: 0 }}>
<Typography noWrap variant="body2">{layout.footnote}</Typography>
<Typography noWrap variant="body2">
{layout.footnote}
</Typography>
</Grid>
</Grid>
) : null
);
) : null;
export default Footer;

View File

@@ -1,16 +1,10 @@
import React from 'react';
import {
Grid,
Typography,
} from '@nebula.js/ui/components';
import { Grid, Typography } from '@nebula.js/ui/components';
import SelectionToolbar from './SelectionToolbar';
const Header = ({
layout,
sn,
}) => {
const Header = ({ layout, sn }) => {
const showTitle = layout && layout.showTitles && !!layout.title;
const showSubtitle = layout && layout.showTitles && !!layout.subtitle;
const showInSelectionActions = sn && layout && layout.qSelectionInfo && layout.qSelectionInfo.qInSelections;
@@ -18,12 +12,20 @@ const Header = ({
<Grid container wrap="nowrap" style={{ flexGrow: 0 }}>
<Grid item zeroMinWidth xs>
<Grid container wrap="nowrap" direction="column">
{showTitle && (<Typography variant="h6" noWrap>{layout.title}</Typography>)}
{showSubtitle && (<Typography variant="body2" noWrap>{layout.subtitle}</Typography>)}
{showTitle && (
<Typography variant="h6" noWrap>
{layout.title}
</Typography>
)}
{showSubtitle && (
<Typography variant="body2" noWrap>
{layout.subtitle}
</Typography>
)}
</Grid>
</Grid>
<Grid item style={{ whiteSpace: 'nowrap', minHeight: '32px' }}>
{showInSelectionActions && (<SelectionToolbar inline sn={sn} />)}
{showInSelectionActions && <SelectionToolbar inline sn={sn} />}
</Grid>
</Grid>
);

View File

@@ -1,14 +1,7 @@
import React, {
useMemo,
} from 'react';
import React, { useMemo } from 'react';
import ReactDOM from 'react-dom';
import {
createTheme,
ThemeProvider,
StylesProvider,
createGenerateClassName,
} from '@nebula.js/ui/theme';
import { createTheme, ThemeProvider, StylesProvider, createGenerateClassName } from '@nebula.js/ui/theme';
import LocaleContext from '../contexts/LocaleContext';
@@ -16,38 +9,31 @@ const THEME_PREFIX = (process.env.NEBULA_VERSION || '').replace(/[.-]/g, '_');
let counter = 0;
function NebulaApp({
children,
themeName,
translator,
}) {
const { theme, generator } = useMemo(() => ({
theme: createTheme(themeName),
generator: createGenerateClassName({
productionPrefix: `${THEME_PREFIX}-`,
disableGlobal: true,
seed: `nebulajs-${counter++}`,
function NebulaApp({ children, themeName, translator }) {
const { theme, generator } = useMemo(
() => ({
theme: createTheme(themeName),
generator: createGenerateClassName({
productionPrefix: `${THEME_PREFIX}-`,
disableGlobal: true,
seed: `nebulajs-${counter++}`,
}),
}),
}), [themeName]);
[themeName]
);
return (
<StylesProvider generateClassName={generator}>
<ThemeProvider theme={theme}>
<LocaleContext.Provider value={translator}>
<>
{children}
</>
<>{children}</>
</LocaleContext.Provider>
</ThemeProvider>
</StylesProvider>
);
}
export default function boot({
app,
theme = 'light',
translator,
}) {
export default function boot({ app, theme = 'light', translator }) {
const element = document.createElement('div');
element.style.display = 'none';
element.setAttribute('data-nebulajs-version', process.env.NEBULA_VERSION || '');
@@ -58,14 +44,10 @@ export default function boot({
const update = () => {
ReactDOM.render(
<NebulaApp
themeName={themeName}
app={app}
translator={translator}
>
<NebulaApp themeName={themeName} app={app} translator={translator}>
{components}
</NebulaApp>,
element,
element
);
};

View File

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

View File

@@ -1,11 +1,7 @@
import {
Typography,
} from '@nebula.js/ui/components';
import { Typography } from '@nebula.js/ui/components';
import React from 'react';
const Component = () => (
<Typography>More data required</Typography>
);
const Component = () => <Typography>More data required</Typography>;
export default Component;

View File

@@ -1,14 +1,9 @@
import React, {
useContext,
useMemo,
} from 'react';
import React, { useContext, useMemo } from 'react';
import Item from './SelectionToolbarItem';
import LocaleContext from '../contexts/LocaleContext';
function Component({
sn,
}) {
function Component({ sn }) {
const translator = useContext(LocaleContext);
const api = sn.component.selections;
@@ -19,30 +14,34 @@ function Component({
};
const { items, custom } = useMemo(() => {
const arr = [{
key: 'confirm',
type: 'icon-button',
label: translator.get('Selection.Confirm'),
icon: 'tick',
enabled: () => s.confirmable,
action: () => api.confirm(sn.component),
}, {
key: 'cancel',
type: 'icon-button',
label: translator.get('Selection.Cancel'),
icon: 'close',
enabled: () => s.cancelable,
action: () => api.cancel(sn.component),
}, {
key: 'clear',
type: 'icon-button',
label: translator.get('Selection.Clear'),
icon: 'clear-selections',
enabled: () => s.clearable,
action: () => api.clear(sn.component),
}];
const arr = [
{
key: 'confirm',
type: 'icon-button',
label: translator.get('Selection.Confirm'),
icon: 'tick',
enabled: () => s.confirmable,
action: () => api.confirm(sn.component),
},
{
key: 'cancel',
type: 'icon-button',
label: translator.get('Selection.Cancel'),
icon: 'close',
enabled: () => s.cancelable,
action: () => api.cancel(sn.component),
},
{
key: 'clear',
type: 'icon-button',
label: translator.get('Selection.Clear'),
icon: 'clear-selections',
enabled: () => s.clearable,
action: () => api.clear(sn.component),
},
];
const c = {};
(sn.selectionToolbar.items || []).forEach((item) => {
(sn.selectionToolbar.items || []).forEach(item => {
c[item.key] = true;
arr.push(item);
});
@@ -52,7 +51,9 @@ function Component({
return (
<div>
{items.map((itm) => <Item key={itm.key} item={itm} isCustom={!!custom[itm.key]} />)}
{items.map(itm => (
<Item key={itm.key} item={itm} isCustom={!!custom[itm.key]} />
))}
</div>
);
}

View File

@@ -1,9 +1,6 @@
import React from 'react';
import {
IconButton,
Button,
} from '@nebula.js/ui/components';
import { IconButton, Button } from '@nebula.js/ui/components';
import CloseIcon from '@nebula.js/ui/icons/Close';
import TickIcon from '@nebula.js/ui/icons/Tick';
@@ -64,11 +61,7 @@ export default class Item extends React.Component {
{Icon && <Icon />}
</Button>
) : (
<IconButton
title={item.label}
onClick={() => item.action()}
disabled={this.state.disabled}
>
<IconButton title={item.label} onClick={() => item.action()} disabled={this.state.disabled}>
{Icon && <Icon />}
</IconButton>
);

View File

@@ -24,10 +24,13 @@ const scheduleRender = (props, prev, initial, contentElement) => {
}
const prom = {};
const p = new Promise((resolve) => {
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 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();
@@ -40,7 +43,8 @@ const scheduleRender = (props, prev, initial, contentElement) => {
let left = 0;
let top = 0;
if (parentRatio > rRatio) { // parent is wider -> limit height
if (parentRatio > rRatio) {
// parent is wider -> limit height
({ height } = parentRect);
width = height * rRatio;
left = (parentRect.width - width) / 2;
@@ -53,7 +57,10 @@ const scheduleRender = (props, prev, initial, contentElement) => {
}
constrainElement(contentElement, {
top, left, width, height,
top,
left,
width,
height,
});
} else {
constrainElement(contentElement);
@@ -61,20 +68,24 @@ const scheduleRender = (props, prev, initial, contentElement) => {
initial.mount();
Promise.resolve(props.sn.component.render({
layout: props.layout,
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);
Promise.resolve(
props.sn.component.render({
layout: props.layout,
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 = () => {
@@ -137,19 +148,23 @@ class Supernova extends React.Component {
}
};
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);
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);
const update =
nextProps.sn &&
!(nextProps.layout && nextProps.layout.qSelectionInfo && nextProps.layout.qSelectionInfo.qInSelections);
if (!update) {
return false;
}
@@ -183,16 +198,19 @@ class Supernova extends React.Component {
return (
<div
style={style}
ref={(element) => { this.element = element; }}
ref={element => {
this.element = element;
}}
>
<div
style={{
position: 'absolute',
}}
ref={(element) => { this.contentElement = element; }}
ref={element => {
this.contentElement = element;
}}
/>
</div>
);
}
}

View File

@@ -2,25 +2,10 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Cell from './Cell';
export default function boot({
element,
model,
api,
nebulaContext,
onInitial,
}) {
const {
root,
} = nebulaContext;
export default function boot({ element, model, api, nebulaContext, onInitial }) {
const { root } = nebulaContext;
const portal = ReactDOM.createPortal(
<Cell
api={api}
model={model}
onInitial={onInitial}
/>,
element,
);
const portal = ReactDOM.createPortal(<Cell api={api} model={model} onInitial={onInitial} />, element);
const unmount = () => {
root.remove(portal);

View File

@@ -15,9 +15,7 @@ import useLayout from '../../hooks/useLayout';
import Row from './ListBoxRow';
export default function ListBox({
model,
}) {
export default function ListBox({ model }) {
const [layout] = useLayout(model);
const [pages, setPages] = useState(null);
const loaderRef = useRef(null);
@@ -30,51 +28,68 @@ export default function ListBox({
pages: [],
});
const onClick = useCallback((e) => {
const elemNumber = +e.currentTarget.getAttribute('data-n');
if (!Number.isNaN(elemNumber)) {
model.selectListObjectValues('/qListObjectDef', [elemNumber], true);
}
}, [model]);
const onClick = useCallback(
e => {
const elemNumber = +e.currentTarget.getAttribute('data-n');
if (!Number.isNaN(elemNumber)) {
model.selectListObjectValues('/qListObjectDef', [elemNumber], true);
}
},
[model]
);
const isItemLoaded = useCallback((index) => {
if (!pages || !local.current.validPages) {
return false;
}
local.current.checkIdx = index;
const page = pages.filter((p) => p.qArea.qTop <= index && index < p.qArea.qTop + p.qArea.qHeight)[0];
return page && page.qArea.qTop <= index && index < page.qArea.qTop + page.qArea.qHeight;
}, [layout, pages]);
const isItemLoaded = useCallback(
index => {
if (!pages || !local.current.validPages) {
return false;
}
local.current.checkIdx = index;
const page = pages.filter(p => p.qArea.qTop <= index && index < p.qArea.qTop + p.qArea.qHeight)[0];
return page && page.qArea.qTop <= index && index < page.qArea.qTop + page.qArea.qHeight;
},
[layout, pages]
);
const loadMoreItems = useCallback((startIndex, stopIndex) => {
local.current.queue.push({
start: startIndex,
stop: stopIndex,
});
const loadMoreItems = useCallback(
(startIndex, stopIndex) => {
local.current.queue.push({
start: startIndex,
stop: stopIndex,
});
const isScrolling = loaderRef.current ? loaderRef.current._listRef.state.isScrolling : false;
const isScrolling = loaderRef.current ? loaderRef.current._listRef.state.isScrolling : false;
if (local.current.queue.length > 10) {
local.current.queue.shift();
}
clearTimeout(local.current.timeout);
return new Promise((resolve) => {
local.current.timeout = setTimeout(() => {
const sorted = local.current.queue.slice(-2).sort((a, b) => a.start - b.start);
model.getListObjectData('/qListObjectDef', sorted.map((s) => ({
qTop: s.start,
qHeight: s.stop - s.start + 1,
qLeft: 0,
qWidth: 1,
}))).then((p) => {
local.current.validPages = true;
listData.current.pages = p;
setPages(p);
resolve();
});
}, isScrolling ? 500 : 0);
});
}, [layout]);
if (local.current.queue.length > 10) {
local.current.queue.shift();
}
clearTimeout(local.current.timeout);
return new Promise(resolve => {
local.current.timeout = setTimeout(
() => {
const sorted = local.current.queue.slice(-2).sort((a, b) => a.start - b.start);
model
.getListObjectData(
'/qListObjectDef',
sorted.map(s => ({
qTop: s.start,
qHeight: s.stop - s.start + 1,
qLeft: 0,
qWidth: 1,
}))
)
.then(p => {
local.current.validPages = true;
listData.current.pages = p;
setPages(p);
resolve();
});
},
isScrolling ? 500 : 0
);
});
},
[layout]
);
useEffect(() => {
local.current.queue = [];

View File

@@ -1,55 +1,53 @@
import React, {
useCallback,
} from 'react';
import React, { useCallback } from 'react';
import Lock from '@nebula.js/ui/icons/Lock';
import Unlock from '@nebula.js/ui/icons/Unlock';
import {
IconButton,
Popover,
Grid,
} from '@nebula.js/ui/components';
import { IconButton, Popover, Grid } from '@nebula.js/ui/components';
import useModel from '../../hooks/useModel';
import useLayout from '../../hooks/useLayout';
import ListBox from './ListBox';
export default function ListBoxPopover({
alignTo,
show,
close,
app,
selections,
fieldName,
stateName = '$',
}) {
const [model] = useModel({
qInfo: {
qType: 'dummy',
},
qListObjectDef: {
qStateName: stateName,
qShowAlternatives: true,
qFrequencyMode: 'N',
qReverseSort: false,
qInitialDataFetch: [{
qTop: 0, qLeft: 0, qHeight: 0, qWidth: 1,
}],
qDef: {
qSortCriterias: [{
qSortByExpression: 0,
qSortByFrequency: 0,
qSortByGreyness: 0,
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByState: 1,
}],
qFieldDefs: [fieldName],
export default function ListBoxPopover({ alignTo, show, close, app, selections, fieldName, stateName = '$' }) {
const [model] = useModel(
{
qInfo: {
qType: 'dummy',
},
qListObjectDef: {
qStateName: stateName,
qShowAlternatives: true,
qFrequencyMode: 'N',
qReverseSort: false,
qInitialDataFetch: [
{
qTop: 0,
qLeft: 0,
qHeight: 0,
qWidth: 1,
},
],
qDef: {
qSortCriterias: [
{
qSortByExpression: 0,
qSortByFrequency: 0,
qSortByGreyness: 0,
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByState: 1,
},
],
qFieldDefs: [fieldName],
},
},
},
}, app, fieldName, stateName);
app,
fieldName,
stateName
);
const lock = useCallback(() => {
model.lock('/qListObjectDef');

View File

@@ -1,16 +1,13 @@
import React from 'react';
import {
Grid,
Typography,
} from '@nebula.js/ui/components';
import { Grid, Typography } from '@nebula.js/ui/components';
import { makeStyles } from '@nebula.js/ui/theme';
import Lock from '@nebula.js/ui/icons/Lock';
import Tick from '@nebula.js/ui/icons/Tick';
const useStyles = makeStyles((theme) => ({
const useStyles = makeStyles(theme => ({
row: {
flexWrap: 'nowrap',
borderBottom: `1px solid ${theme.palette.divider}`,
@@ -39,11 +36,7 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function Row({
index,
style,
data,
}) {
export default function Row({ index, style, data }) {
const classes = useStyles();
const classArr = [classes.row];
@@ -51,10 +44,10 @@ export default function Row({
const { onClick, pages } = data;
let cell;
if (pages) {
const page = pages.filter((p) => p.qArea.qTop <= index && index < p.qArea.qTop + p.qArea.qHeight)[0];
const page = pages.filter(p => p.qArea.qTop <= index && index < p.qArea.qTop + p.qArea.qHeight)[0];
if (page) {
const area = page.qArea;
if (index >= area.qTop && (index < area.qTop + area.qHeight)) {
if (index >= area.qTop && index < area.qTop + area.qHeight) {
[cell] = page.qMatrix[index - area.qTop];
}
}
@@ -86,12 +79,12 @@ export default function Row({
>
<Grid item style={{ minWidth: 0, flexGrow: 1 }}>
<Typography className={classes.cell} noWrap>
{cell ? `${label}` : '' }
{cell ? `${label}` : ''}
</Typography>
</Grid>
<Grid item className={classes.icon}>
{locked && (<Lock size="small" />)}
{selected && (<Tick size="small" />)}
{locked && <Lock size="small" />}
{selected && <Tick size="small" />}
</Grid>
</Grid>
);

View File

@@ -1,20 +1,14 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {
Grid,
} from '@nebula.js/ui/components';
import { Grid } from '@nebula.js/ui/components';
import {
useTheme,
} from '@nebula.js/ui/theme';
import { useTheme } from '@nebula.js/ui/theme';
import SelectedFields from './SelectedFields';
import Nav from './Nav';
export function AppSelections({
api,
}) {
export function AppSelections({ api }) {
const theme = useTheme();
return (
@@ -42,14 +36,6 @@ export function AppSelections({
);
}
export default function mount({
element,
api,
}) {
return ReactDOM.createPortal(
<AppSelections
api={api}
/>,
element,
);
export default function mount({ element, api }) {
return ReactDOM.createPortal(<AppSelections api={api} />, element);
}

View File

@@ -6,18 +6,13 @@ import {
Typography,
} from '@nebula.js/ui/components';
export default function MultiState({
field,
}) {
export default function MultiState({ field }) {
return (
<Grid
container
spacing={0}
alignItems="center"
style={{ height: '100%', padding: '4px' }}
>
<Grid container spacing={0} alignItems="center" style={{ height: '100%', padding: '4px' }}>
<Grid item style={{ minWidth: 0 }}>
<Typography noWrap style={{ fontSize: '12px', lineHeight: '16px', fontWeight: 600 }}>{field.name}</Typography>
<Typography noWrap style={{ fontSize: '12px', lineHeight: '16px', fontWeight: 600 }}>
{field.name}
</Typography>
</Grid>
</Grid>
);

View File

@@ -1,13 +1,6 @@
import React, {
useEffect,
useState,
useContext,
} from 'react';
import React, { useEffect, useState, useContext } from 'react';
import {
IconButton,
Grid,
} from '@nebula.js/ui/components';
import { IconButton, Grid } from '@nebula.js/ui/components';
import SelectionsBack from '@nebula.js/ui/icons/SelectionsBack';
import SelectionsForward from '@nebula.js/ui/icons/SelectionsForward';
@@ -15,9 +8,7 @@ import ClearSelections from '@nebula.js/ui/icons/ClearSelections';
import LocaleContext from '../../contexts/LocaleContext';
export default function Nav({
api,
}) {
export default function Nav({ api }) {
const translator = useContext(LocaleContext);
const [state, setState] = useState({
@@ -30,11 +21,12 @@ export default function Nav({
if (!api) {
return undefined;
}
const onChange = () => setState({
forward: api.canGoForward(),
back: api.canGoBack(),
clear: api.canClear(),
});
const onChange = () =>
setState({
forward: api.canGoForward(),
back: api.canGoBack(),
clear: api.canClear(),
});
api.on('changed', onChange);
return () => {
api.removeListener('changed', onChange);
@@ -72,11 +64,7 @@ export default function Nav({
</IconButton>
</Grid>
<Grid item>
<IconButton
disabled={!state.clear}
title={translator.get('Selection.ClearAll')}
onClick={() => api.clear()}
>
<IconButton disabled={!state.clear} title={translator.get('Selection.ClearAll')} onClick={() => api.clear()}>
<ClearSelections />
</IconButton>
</Grid>

View File

@@ -1,18 +1,10 @@
import React, {
useRef,
useState,
useContext,
} from 'react';
import React, { useRef, useState, useContext } from 'react';
import Remove from '@nebula.js/ui/icons/Remove';
import Lock from '@nebula.js/ui/icons/Lock';
// import themes from '@nebula.js/ui/theme';
import {
IconButton,
Grid,
Typography,
} from '@nebula.js/ui/components';
import { IconButton, Grid, Typography } from '@nebula.js/ui/components';
import { makeStyles } from '@nebula.js/ui/theme';
@@ -20,7 +12,7 @@ import ListBoxPopover from '../listbox/ListBoxPopover';
import LocaleContext from '../../contexts/LocaleContext';
const useStyles = makeStyles((theme) => ({
const useStyles = makeStyles(theme => ({
item: {
cursor: 'pointer',
padding: '4px',
@@ -41,8 +33,9 @@ export default function OneField({
const classes = useStyles();
const toggleActive = (e) => {
if (e.currentTarget.contains(e.target)) { // because click in popover will propagate to parent
const toggleActive = e => {
if (e.currentTarget.contains(e.target)) {
// because click in popover will propagate to parent
setIsActive(!isActive);
}
};
@@ -60,7 +53,7 @@ export default function OneField({
} else if (numSelected > 1 && selection.qTotal) {
label = translator.get('CurrentSelections.Of', [numSelected, selection.qTotal]);
} else {
label = selection.qSelectedFieldSelectionInfo.map((v) => v.qName).join(', ');
label = selection.qSelectedFieldSelectionInfo.map(v => v.qName).join(', ');
}
if (field.states[0] !== '$') {
label = `${field.states[0]}: ${label}`;
@@ -76,26 +69,29 @@ export default function OneField({
});
return (
<Grid
container
spacing={0}
ref={alignTo}
className={classes.item}
onClick={toggleActive}
>
<Grid
item
xs
style={{ minWidth: 0, flexGrow: 1, opacity: selection.qLocked ? '0.3' : '' }}
>
<Typography noWrap style={{ fontSize: '12px', lineHeight: '16px', fontWeight: 600 }}>{selection.qField}</Typography>
<Typography noWrap style={{ fontSize: '12px', opacity: 0.55, lineHeight: '16px' }}>{label}</Typography>
<Grid container spacing={0} ref={alignTo} className={classes.item} onClick={toggleActive}>
<Grid item xs style={{ minWidth: 0, flexGrow: 1, opacity: selection.qLocked ? '0.3' : '' }}>
<Typography noWrap style={{ fontSize: '12px', lineHeight: '16px', fontWeight: 600 }}>
{selection.qField}
</Typography>
<Typography noWrap style={{ fontSize: '12px', opacity: 0.55, lineHeight: '16px' }}>
{label}
</Typography>
</Grid>
{selection.qLocked ? (<Grid item><IconButton><Lock /></IconButton></Grid>) : (
{selection.qLocked ? (
<Grid item>
<IconButton>
<Lock />
</IconButton>
</Grid>
) : (
<Grid item>
<IconButton
title={translator.get('Selection.Clear')}
onClick={(e) => { e.stopPropagation(); api.clearField(selection.qField, field.states[0]); }}
onClick={e => {
e.stopPropagation();
api.clearField(selection.qField, field.states[0]);
}}
style={{ zIndex: 1 }}
>
<Remove />
@@ -111,7 +107,7 @@ export default function OneField({
width: '100%',
}}
>
{segments.map((s) => (
{segments.map(s => (
<div
key={s.color}
style={{

View File

@@ -1,11 +1,6 @@
import React, {
useEffect,
useState,
} from 'react';
import React, { useEffect, useState } from 'react';
import {
Grid,
} from '@nebula.js/ui/components';
import { Grid } from '@nebula.js/ui/components';
import { useTheme } from '@nebula.js/ui/theme';
@@ -13,7 +8,7 @@ import OneField from './OneField';
import MultiState from './MultiState';
function collect(qSelectionObject, fields, state = '$') {
qSelectionObject.qSelections.forEach((selection) => {
qSelectionObject.qSelections.forEach(selection => {
const name = selection.qField;
const field = fields[name] = fields[name] || { name, states: [], selections: [] }; // eslint-disable-line
if (field.states.indexOf(state) === -1) {
@@ -32,14 +27,12 @@ function getItems(layout) {
collect(layout.qSelectionObject, fields);
}
if (layout.alternateStates) {
layout.alternateStates.forEach((s) => collect(s.qSelectionObject, fields, s.stateName));
layout.alternateStates.forEach(s => collect(s.qSelectionObject, fields, s.stateName));
}
return Object.keys(fields).map((key) => fields[key]);
return Object.keys(fields).map(key => fields[key]);
}
export default function SelectedFields({
api,
}) {
export default function SelectedFields({ api }) {
const [state, setState] = useState({
items: getItems(api.layout()),
});
@@ -50,9 +43,10 @@ export default function SelectedFields({
if (!api) {
return undefined;
}
const onChange = () => setState({
items: getItems(api.layout()),
});
const onChange = () =>
setState({
items: getItems(api.layout()),
});
api.on('changed', onChange);
return () => {
api.removeListener('changed', onChange);
@@ -61,7 +55,7 @@ export default function SelectedFields({
return (
<Grid container spacing={0} wrap="nowrap">
{state.items.map((s) => (
{state.items.map(s => (
<Grid
item
key={`${s.states.join('::')}::${s.name}`}
@@ -73,7 +67,7 @@ export default function SelectedFields({
borderRight: `1px solid ${theme.palette.divider}`,
}}
>
{(s.states.length > 1 ? <MultiState field={s} /> : <OneField field={s} api={api} />)}
{s.states.length > 1 ? <MultiState field={s} /> : <OneField field={s} api={api} />}
</Grid>
))}
</Grid>

View File

@@ -14,12 +14,16 @@ const mock = ({
},
SelectedFields = () => <sf />,
Nav = () => <nav />,
} = {}) => aw.mock([
['**/ui/components/index.js', () => components],
['**/ui/theme/index.js', () => theme],
['**/SelectedFields.jsx', () => SelectedFields],
['**/Nav.jsx', () => Nav],
], ['../AppSelections']);
} = {}) =>
aw.mock(
[
['**/ui/components/index.js', () => components],
['**/ui/theme/index.js', () => theme],
['**/SelectedFields.jsx', () => SelectedFields],
['**/Nav.jsx', () => Nav],
],
['../AppSelections']
);
describe('<AppSelections />', () => {
let api;
@@ -28,7 +32,7 @@ describe('<AppSelections />', () => {
canGoForward: () => 'canGoForward',
canGoBack: () => 'canGoBack',
canClear: () => 'canClear',
layout: () => (null),
layout: () => null,
back: sinon.spy(),
forward: sinon.spy(),
clear: sinon.spy(),
@@ -43,19 +47,44 @@ describe('<AppSelections />', () => {
const r = renderer.create(<AS api={api} />);
expect(r.toJSON()).to.eql({
type: 'sp', props: {}, children: [{
type: 'tp', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'nav', props: {}, children: null,
}],
}, {
type: 'g', props: {}, children: [{
type: 'sf', props: {}, children: null,
}],
}],
}],
}],
type: 'sp',
props: {},
children: [
{
type: 'tp',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 'g',
props: {},
children: [
{
type: 'nav',
props: {},
children: null,
},
],
},
{
type: 'g',
props: {},
children: [
{
type: 'sf',
props: {},
children: null,
},
],
},
],
},
],
},
],
});
});
});

View File

@@ -1,12 +1,6 @@
import {
useState,
useEffect,
} from 'react';
import { useState, useEffect } from 'react';
import {
observe,
unObserve,
} from '../object/observer';
import { observe, unObserve } from '../object/observer';
export default function useLayout(model) {
const [layout, setLayout] = useState(null);

View File

@@ -1,7 +1,4 @@
import {
useEffect,
useState,
} from 'react';
import { useEffect, useState } from 'react';
import modelCache, { key } from '../object/model-cache';

View File

@@ -1,12 +1,6 @@
import {
useState,
useEffect,
} from 'react';
import { useState, useEffect } from 'react';
import {
observe,
unObserve,
} from '../object/observer';
import { observe, unObserve } from '../object/observer';
export default function useProperties(model) {
const [properties, setProperties] = useState(null);

View File

@@ -30,7 +30,8 @@ const mergeConfigs = (base, c) => ({
locale: {
language: (c.locale ? c.locale.language : '') || base.locale.language,
},
types: [ // TODO - filter to avoid duplicates
types: [
// TODO - filter to avoid duplicates
...(base.types || []),
...(c.types || []),
],
@@ -71,13 +72,18 @@ function nuked(configuration = {}) {
root,
};
currentConfig.types.forEach((t) => context.types.register({
name: t.name,
version: t.version,
}, {
meta: t.meta,
load: t.load,
}));
currentConfig.types.forEach(t =>
context.types.register(
{
name: t.name,
version: t.version,
},
{
meta: t.meta,
load: t.load,
}
)
);
let selectionsApi = null;
let selectionsComponentReference = null;
@@ -122,7 +128,7 @@ function nuked(configuration = {}) {
return api;
}
nucleus.configured = (c) => nuked(mergeConfigs(configuration, c));
nucleus.configured = c => nuked(mergeConfigs(configuration, c));
return nucleus;
}

View File

@@ -4,19 +4,19 @@ import enUs from './translations/en-US';
const DEFAULT_LOCALE = 'en-US';
const SUPPORTED_LANGUAGES = [{
info: {
short: 'en',
long: 'en-US',
label: 'English',
translatedLabel: 'Common.English',
const SUPPORTED_LANGUAGES = [
{
info: {
short: 'en',
long: 'en-US',
label: 'English',
translatedLabel: 'Common.English',
},
data: enUs,
},
data: enUs,
}];
];
export default function locale({
language,
} = {}) {
export default function locale({ language } = {}) {
const translator = translatorFn();
const api = {
@@ -27,7 +27,7 @@ export default function locale({
translator: () => translator.api,
};
SUPPORTED_LANGUAGES.forEach((d) => translator.addLanguage(d.info, d.data));
SUPPORTED_LANGUAGES.forEach(d => translator.addLanguage(d.info, d.data));
api.locale(language || DEFAULT_LOCALE);

View File

@@ -7,8 +7,9 @@ const format = (message = '', args = []) => {
};
const getLongLanguageCode = (lang, list) => {
const code = list.filter((item) => item.long.toLowerCase() === lang.toLowerCase())[0]
|| list.filter((item) => item.short.toLowerCase() === lang.toLowerCase())[0];
const code =
list.filter(item => item.long.toLowerCase() === lang.toLowerCase())[0] ||
list.filter(item => item.short.toLowerCase() === lang.toLowerCase())[0];
if (!code) {
console.warn(`Language '${lang}' not supported, falling back to ${DEFAULT_LANGUAGE}`);
@@ -40,7 +41,7 @@ export default function translator() {
};
return {
setLanguage: (code) => {
setLanguage: code => {
currentLocale = code;
// TODO - emit change?
},
@@ -48,7 +49,7 @@ export default function translator() {
languageList.push(descr);
api.append(data, descr.long);
},
getLongCode: (lang) => getLongLanguageCode(lang, languageList),
getLongCode: lang => getLongLanguageCode(lang, languageList),
api,
};
}

View File

@@ -1,12 +1,8 @@
import {
observe,
unObserve,
cache,
} from '../observer';
import { observe, unObserve, cache } from '../observer';
describe('observer', () => {
beforeEach(() => {
Object.keys(cache).forEach((key) => delete cache[key]);
Object.keys(cache).forEach(key => delete cache[key]);
});
describe('observe', () => {
@@ -60,16 +56,16 @@ describe('observer', () => {
it('should not call callbacks if model state is CLOSED, CLOSING or CANCELLED', async () => {
const cb = sinon.spy();
let onChanged;
const layoutPromise0 = new Promise((resolve) => {
const layoutPromise0 = new Promise(resolve => {
setTimeout(resolve, 10);
});
const layoutPromise1 = new Promise((resolve) => {
const layoutPromise1 = new Promise(resolve => {
setTimeout(resolve, 10);
});
const layoutPromise2 = new Promise((resolve) => {
const layoutPromise2 = new Promise(resolve => {
setTimeout(resolve, 10);
});
const layoutPromise3 = new Promise((resolve) => {
const layoutPromise3 = new Promise(resolve => {
setTimeout(resolve, 10);
});
const model = {

View File

@@ -1,24 +1,28 @@
import populateData from './populator';
export default function create({
type,
version,
fields,
}, optional, context) {
export default function create({ type, version, fields }, optional, context) {
const t = context.nebbie.types.get({ name: type, version });
return t.initialProperties(optional.properties)
.then((mergedProps) => t.supernova().then((sn) => {
return t.initialProperties(optional.properties).then(mergedProps =>
t.supernova().then(sn => {
if (fields) {
populateData({
sn,
properties: mergedProps,
fields,
}, context);
populateData(
{
sn,
properties: mergedProps,
fields,
},
context
);
}
return context.app.createSessionObject(mergedProps)
.then((model) => context.nebbie.get({
id: model.id,
}, { ...optional, properties: {} }));
}));
return context.app.createSessionObject(mergedProps).then(model =>
context.nebbie.get(
{
id: model.id,
},
{ ...optional, properties: {} }
)
);
})
);
}

View File

@@ -1,11 +1,11 @@
import vizualizationAPI from '../viz';
import ObjectAPI from './object-api';
export function observe(model, objectAPI) {
const onChanged = () => model.getLayout().then((layout) => {
objectAPI.setLayout(layout);
});
const onChanged = () =>
model.getLayout().then(layout => {
objectAPI.setLayout(layout);
});
model.on('changed', onChanged);
model.once('closed', () => {
model.removeListener('changed', onChanged);
@@ -16,7 +16,7 @@ export function observe(model, objectAPI) {
}
export default function initiate(getCfg, optional, context) {
return context.app.getObject(getCfg.id).then((model) => {
return context.app.getObject(getCfg.id).then(model => {
const viz = vizualizationAPI({
model,
context,

View File

@@ -2,7 +2,7 @@
const idGen = [[10, 31], [0, 31], [0, 31], [0, 31], [0, 31], [0, 31]];
function toChar([min, max]) {
return (min + (Math.random() * (max - min) | 0)).toString(32);
return (min + ((Math.random() * (max - min)) | 0)).toString(32);
}
function uid() {
@@ -30,19 +30,21 @@ function removeIndex(array, index) {
return removeIdx;
}
const nxDimension = (f) => ({
const nxDimension = f => ({
qDef: {
qFieldDefs: [f],
qSortCriterias: [{
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByAscii: 1,
}],
qSortCriterias: [
{
qSortByLoadOrder: 1,
qSortByNumeric: 1,
qSortByAscii: 1,
},
],
},
qOtherTotalSpec: {},
});
const nxMeasure = (f) => ({
const nxMeasure = f => ({
qDef: {
qDef: f,
},
@@ -52,10 +54,7 @@ const nxMeasure = (f) => ({
},
});
export default function hcHandler({
hc,
def,
}) {
export default function hcHandler({ hc, def }) {
hc.qDimensions = hc.qDimensions || [];
hc.qMeasures = hc.qMeasures || [];
hc.qInterColumnSortOrder = hc.qInterColumnSortOrder || [];
@@ -70,10 +69,13 @@ export default function hcHandler({
return hc.qMeasures;
},
addDimension(d) {
const dimension = typeof d === 'string' ? nxDimension(d) : {
...d,
qDef: d.qDef || {},
};
const dimension =
typeof d === 'string'
? nxDimension(d)
: {
...d,
qDef: d.qDef || {},
};
dimension.qDef.cId = dimension.qDef.cId || uid();
dimension.qOtherTotalSpec = dimension.qOtherTotalSpec || {};
if (!dimension.qDef.cId) {
@@ -97,10 +99,13 @@ export default function hcHandler({
def.dimensions.remove(dimension, objectProperties, idx);
},
addMeasure(m) {
const measure = typeof m === 'string' ? nxMeasure(m) : {
...m,
qDef: m.qDef || {},
};
const measure =
typeof m === 'string'
? nxMeasure(m)
: {
...m,
qDef: m.qDef || {},
};
measure.qDef.cId = measure.qDef.cId || uid();
if (!measure.qDef.cId) {
measure.qDef.cId = uid();

View File

@@ -37,8 +37,7 @@ export default class ObjectAPI {
const selections = this.state.sn ? this.state.sn.component.selections : null;
if (selections && selections.id === this.model.id) {
selections.setLayout(layout);
if (layout.qSelectionInfo && layout.qSelectionInfo.qInSelections
&& !selections.isModal()) {
if (layout.qSelectionInfo && layout.qSelectionInfo.qInSelections && !selections.isModal()) {
selections.goModal('/qHyperCubeDef'); // TODO - use path from data targets
}
if (!layout.qSelectionInfo || !layout.qSelectionInfo.qInSelections) {
@@ -68,23 +67,27 @@ export default class ObjectAPI {
if (!this.currentObjectType) {
return this.context.config.env.Promise.resolve();
}
return this.context.nebbie.types.get({
name: this.currentObjectType,
version: this.currentSupernovaVersion,
}).supernova().then((SN) => {
// layout might have changed since we requested the new type,
// make sure type in layout matches the requested one
if (!this.state.layout || this.state.layout.visualization !== this.currentObjectType) {
return this.context.config.env.Promise.resolve();
}
return this.setSupernova(SN);
}).catch((e) => {
this.setState({
error: {
message: `${e.message}`,
},
return this.context.nebbie.types
.get({
name: this.currentObjectType,
version: this.currentSupernovaVersion,
})
.supernova()
.then(SN => {
// layout might have changed since we requested the new type,
// make sure type in layout matches the requested one
if (!this.state.layout || this.state.layout.visualization !== this.currentObjectType) {
return this.context.config.env.Promise.resolve();
}
return this.setSupernova(SN);
})
.catch(e => {
this.setState({
error: {
message: `${e.message}`,
},
});
});
});
}
setLayout(layout) {

View File

@@ -31,25 +31,27 @@ export function observe(model, callback, property = 'layout') {
throw new Error(`'${property}' is not observable!`);
}
if (!cache[model.id]) {
const onChanged = (filtered) => {
const onChanged = filtered => {
const c = cache[model.id];
const affected = [];
Object.keys(c.props).filter((key) => (filtered ? key === filtered : true)).forEach((key) => {
c.props[key].state = STATES.INVALID;
if (c.props[key].callbacks.length) {
affected.push(key);
}
});
Object.keys(c.props)
.filter(key => (filtered ? key === filtered : true))
.forEach(key => {
c.props[key].state = STATES.INVALID;
if (c.props[key].callbacks.length) {
affected.push(key);
}
});
affected.forEach((key) => {
affected.forEach(key => {
c.props[key].state = STATES.VALIDATING;
const method = OBSERVABLE[key].filter((m) => model[m])[0];
model[method]().then((value) => {
const method = OBSERVABLE[key].filter(m => model[m])[0];
model[method]().then(value => {
if (cache[model.id] && cache[model.id].props[key]) {
if (cache[model.id].props[key].state < STATES.CLOSED && cache[model.id].props[key].state !== STATES.VALID) {
cache[model.id].props[key].state = STATES.VALID;
cache[model.id].props[key].value = value;
cache[model.id].props[key].callbacks.forEach((cb) => cb(value));
cache[model.id].props[key].callbacks.forEach(cb => cb(value));
}
}
});
@@ -61,7 +63,7 @@ export function observe(model, callback, property = 'layout') {
props: {},
};
OBSERVABLE_KEYS.forEach((key) => {
OBSERVABLE_KEYS.forEach(key => {
cache[model.id].props[key] = {
state: STATES.INVALID,
callbacks: [],
@@ -88,8 +90,8 @@ export function observe(model, callback, property = 'layout') {
}
export function get(model, property) {
return new Promise((resolve) => {
const cb = (value) => {
return new Promise(resolve => {
const cb = value => {
unObserve(model, cb, property);
resolve(value);
};

View File

@@ -1,10 +1,6 @@
import hcHandler from './hc-handler';
export default function populateData({
sn,
properties,
fields,
}, context) {
export default function populateData({ sn, properties, fields }, context) {
const target = sn.qae.data.targets[0];
if (!target) {
context.logger.warn('Attempting to add fields to an object without a specified data target');
@@ -24,11 +20,13 @@ export default function populateData({
def: target,
});
fields.forEach((f) => {
fields.forEach(f => {
let type = 'dimension';
if ((typeof f === 'string' && f[0] === '=')
|| (typeof f === 'object' && f.qDef.qDef)
|| (typeof f === 'object' && f.qLibraryId && f.qType === 'measure')) {
if (
(typeof f === 'string' && f[0] === '=') ||
(typeof f === 'object' && f.qDef.qDef) ||
(typeof f === 'object' && f.qLibraryId && f.qType === 'measure')
) {
type = 'measure';
}

View File

@@ -7,7 +7,7 @@ import { observe } from '../object/observer';
const cache = {};
const create = (app) => {
const create = app => {
let canGoForward = false;
let canGoBack = false;
let canClear = false;
@@ -26,7 +26,8 @@ const create = (app) => {
api.emit('modal-unset');
modalObject._selections.emit('deactivated');
}
if (object && object !== null) { // TODO check model state
if (object && object !== null) {
// TODO check model state
modalObject = object;
api.emit('modal', modalObject._selections);
return modalObject.beginSelections(Array.isArray(path) ? path : [path]);
@@ -70,47 +71,55 @@ const create = (app) => {
return this.switchModal().then(() => app.clearAll());
},
clearField(field, state = '$') {
return this.switchModal().then(() => app.getField(field, state).then((f) => f.clear()));
return this.switchModal().then(() => app.getField(field, state).then(f => f.clear()));
},
};
eventmixin(api);
modelCache({
qInfo: {
qType: 'current-selections',
modelCache(
{
qInfo: {
qType: 'current-selections',
},
qSelectionObjectDef: {
qStateName: '$',
},
alternateStates: [],
},
qSelectionObjectDef: {
qStateName: '$',
},
alternateStates: [],
}, app).then((model) => {
observe(app, (appLayout) => {
const states = [...appLayout.qStateNames].map((s) => ({
app
).then(model => {
observe(app, appLayout => {
const states = [...appLayout.qStateNames].map(s => ({
stateName: s, // need this as reference in selection toolbar since qSelectionObject.qStateName is not in the layout
qSelectionObjectDef: {
qStateName: s,
},
}));
const existingStates = (lyt ? lyt.alternateStates.map((s) => s.stateName) : []).join('::');
const newStates = appLayout.qStateNames.map((s) => s).join('::');
const existingStates = (lyt ? lyt.alternateStates.map(s => s.stateName) : []).join('::');
const newStates = appLayout.qStateNames.map(s => s).join('::');
if (existingStates !== newStates) {
model.applyPatches([{
qOp: 'replace',
qPath: '/alternateStates',
qValue: JSON.stringify(states),
}], true);
model.applyPatches(
[
{
qOp: 'replace',
qPath: '/alternateStates',
qValue: JSON.stringify(states),
},
],
true
);
}
});
observe(model, (layout) => {
observe(model, layout => {
canGoBack = false;
canGoForward = false;
canClear = false;
[layout, ...layout.alternateStates].forEach((state) => {
[layout, ...layout.alternateStates].forEach(state => {
canGoBack = canGoBack || state.qSelectionObject.qBackCount > 0;
canGoForward = canGoForward || state.qSelectionObject.qForwardCount > 0;
canClear = canClear || state.qSelectionObject.qSelections.filter((s) => s.qLocked !== true).length > 0;
canClear = canClear || state.qSelectionObject.qSelections.filter(s => s.qLocked !== true).length > 0;
});
lyt = layout;
api.emit('changed');
@@ -124,7 +133,7 @@ const create = (app) => {
return api;
};
export default function (app) {
export default function(app) {
if (!cache[app.id]) {
cache[app.id] = {
selections: null,

View File

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

View File

@@ -11,7 +11,7 @@ const event = () => {
};
};
export default function (model, app) {
export default function(model, app) {
if (model._selections) {
return model._selections;
}
@@ -60,7 +60,7 @@ export default function (model, app) {
return;
}
hasSelected = true;
model[s.method](...s.params).then((qSuccess) => {
model[s.method](...s.params).then(qSuccess => {
if (!qSuccess) {
this.clear();
}
@@ -77,7 +77,7 @@ export default function (model, app) {
},
isActive: () => isActive,
isModal: () => appAPI().isModal(model),
goModal: (paths) => appAPI().switchModal(model, paths, false),
goModal: paths => appAPI().switchModal(model, paths, false),
noModal: () => appAPI().switchModal(null, null, false),
};

View File

@@ -1,12 +1,12 @@
const mock = ({
SNFactory = () => ({}),
satisfies = () => false,
load = () => null,
} = {}) => aw.mock([
['**/dist/supernova.js', () => SNFactory],
['**/semver.js', () => ({ satisfies })],
['**/load.js', () => ({ load })],
], ['../type']);
const mock = ({ SNFactory = () => ({}), satisfies = () => false, load = () => null } = {}) =>
aw.mock(
[
['**/dist/supernova.js', () => SNFactory],
['**/semver.js', () => ({ satisfies })],
['**/load.js', () => ({ load })],
],
['../type']
);
describe('type', () => {
let c;

View File

@@ -9,15 +9,19 @@ describe('types', () => {
});
describe('factory', () => {
const mock = ({
createType = () => ({}),
clearFromCache = () => {},
} = {}) => aw.mock([
['**/sn/type.js', () => createType],
['**/sn/load.js', () => ({
clearFromCache,
})],
], ['../types']);
const mock = ({ createType = () => ({}), clearFromCache = () => {} } = {}) =>
aw.mock(
[
['**/sn/type.js', () => createType],
[
'**/sn/load.js',
() => ({
clearFromCache,
}),
],
],
['../types']
);
let c;
let type;
@@ -33,17 +37,21 @@ describe('types', () => {
it('should instantiate a type when registering', () => {
c.register({ name: 'pie', version: '1.0.3' }, 'opts');
expect(type).to.have.been.calledWithExactly({
name: 'pie',
version: '1.0.3',
}, 'config', 'opts');
expect(type).to.have.been.calledWithExactly(
{
name: 'pie',
version: '1.0.3',
},
'config',
'opts'
);
});
it('should throw when registering an already registered version', () => {
c.register({ name: 'pie', version: '1.0.3' }, 'opts');
const fn = () => c.register({ name: 'pie', version: '1.0.3' }, 'opts');
expect(fn).to.throw('Supernova \'pie@1.0.3\' already registered.');
expect(fn).to.throw("Supernova 'pie@1.0.3' already registered.");
});
it('should find 1.5.1 as matching version from properties', () => {

View File

@@ -3,32 +3,37 @@ const LOADED = {};
export function load(name, version, config, loader) {
const key = `${name}__${version}`;
if (!LOADED[key]) {
const p = (loader || config.load)({
name,
version,
}, config.env);
const p = (loader || config.load)(
{
name,
version,
},
config.env
);
if (typeof p === 'undefined') {
throw new Error(`Failed to load supernova: ${name}`);
}
if (typeof p === 'string') {
throw new Error('Return value must be a Promise');
}
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}`);
});
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}`);
});
}
return LOADED[key];
}
export function clearFromCache(name) {
Object.keys(LOADED).forEach((key) => {
Object.keys(LOADED).forEach(key => {
if (key.split('__')[0] === name) {
LOADED[key] = undefined;
}

View File

@@ -9,14 +9,14 @@ export default function create(info, config, opts = {}) {
const type = {
name: info.name,
version: info.version,
supportsPropertiesVersion: (v) => {
supportsPropertiesVersion: v => {
if (v && meta && meta.deps && meta.deps.properties) {
return satisfies(v, meta.deps.properties);
}
return true;
},
supernova: () => load(type.name, type.version, config, opts.load)
.then((SNDefinition) => {
supernova: () =>
load(type.name, type.version, config, opts.load).then(SNDefinition => {
sn = sn || SNFactory(SNDefinition, config.env);
stringified = JSON.stringify(sn.qae.properties);
return sn;

View File

@@ -2,10 +2,15 @@ import type from './type';
import { clearFromCache } from './load';
export function semverSort(arr) {
const unversioned = arr.filter((v) => v === 'undefined');
return [...unversioned, ...arr.filter((v) => v !== 'undefined').map((v) => v.split('.').map((n) => parseInt(n, 10)))
.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2])
.map((n) => n.join('.'))];
const unversioned = arr.filter(v => v === 'undefined');
return [
...unversioned,
...arr
.filter(v => v !== 'undefined')
.map(v => v.split('.').map(n => parseInt(n, 10)))
.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2])
.map(n => n.join('.')),
];
}
export function typeCollection(name, config) {
@@ -13,18 +18,22 @@ export function typeCollection(name, config) {
let sortedVersions = null;
return {
get: (version) => versions[version],
get: version => versions[version],
register: (version, opts) => {
if (versions[version]) {
throw new Error(`Supernova '${name}@${version}' already registered.`);
}
versions[version] = type({
name,
version,
}, config, opts);
versions[version] = type(
{
name,
version,
},
config,
opts
);
sortedVersions = null;
},
getMatchingVersionFromProperties: (propertyVersion) => {
getMatchingVersionFromProperties: propertyVersion => {
if (!sortedVersions) {
sortedVersions = semverSort(Object.keys(versions));
}
@@ -40,9 +49,7 @@ export function typeCollection(name, config) {
};
}
export function create({
config,
}) {
export function create({ config }) {
const tc = {};
return {
@@ -65,7 +72,7 @@ export function create({
}
return tc[name].get(version);
},
clearFromCache: (name) => {
clearFromCache: name => {
if (tc[name]) {
tc[name] = undefined;
}

View File

@@ -6,10 +6,7 @@ const LOG_LEVEL = {
DEBUG: 4,
};
const loggerFn = ({
level = LOG_LEVEL.OFF,
pipe = console,
} = {}) => {
const loggerFn = ({ level = LOG_LEVEL.OFF, pipe = console } = {}) => {
let currentlevel = level;
const LOG_FN = {
@@ -21,7 +18,9 @@ const loggerFn = ({
};
const log = (lev, ...args) => {
if (!lev || currentlevel < lev) { return; }
if (!lev || currentlevel < lev) {
return;
}
(LOG_FN[lev] || LOG_FN[LOG_LEVEL.DEBUG])(...args);
};
@@ -68,7 +67,7 @@ const loggerFn = ({
* Set the current log level
* @param {number} lev - The log level
*/
level: (lev) => {
level: lev => {
if (typeof lev === 'number') {
currentlevel = lev;
}

View File

@@ -1,4 +1,6 @@
function isObject(v) { return v != null && !Array.isArray(v) && typeof v === 'object'; }
function isObject(v) {
return v != null && !Array.isArray(v) && typeof v === 'object';
}
function isEqual(a, b) {
if (isObject(a) && isObject(b)) {
@@ -12,7 +14,7 @@ function isEqual(a, b) {
export default function getPatches(path = '/', obj, old) {
const patches = [];
Object.keys(obj).forEach((prop) => {
Object.keys(obj).forEach(prop => {
const v = obj[prop];
if (typeof old[prop] === 'object' && typeof v === 'object' && !Array.isArray(v)) {
patches.push(...getPatches(`${path}${prop}/`, obj[prop], old[prop]));

View File

@@ -1,11 +1,13 @@
/* eslint import/prefer-default-export: 0 */
export const prefixer = (list) => {
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(' ');
return arr
.map(s => {
if (s[0] === '-' || s[0] === '_') {
return `nucleus${s}`;
}
return `nucleus-${s}`;
})
.join(' ');
};

View File

@@ -7,11 +7,7 @@ import eventMixin from './selections/event-mixin';
const noopi = () => {};
export default function ({
model,
context,
initialUserProps = {},
} = {}) {
export default function({ model, context, initialUserProps = {} } = {}) {
let reference = noopi;
let elementReference = null;
@@ -19,9 +15,15 @@ export default function ({
let layoutReady;
let mounted;
const whenLayoutReady = new Promise((resolve) => { layoutReady = resolve; });
const whenSupernovaReady = new Promise((resolve) => { supernovaReady = resolve; });
const whenMounted = new Promise((resolve) => { mounted = resolve; });
const whenLayoutReady = new Promise(resolve => {
layoutReady = resolve;
});
const whenSupernovaReady = new Promise(resolve => {
supernovaReady = resolve;
});
const whenMounted = new Promise(resolve => {
mounted = resolve;
});
let userProps = {
options: {},
@@ -52,7 +54,7 @@ export default function ({
cellApi.emit('changed');
};
const setUserProps = (up) => {
const setUserProps = up => {
userProps = {
...userProps,
...up,
@@ -60,7 +62,7 @@ export default function ({
update();
};
const setObjectProps = (p) => {
const setObjectProps = p => {
objectProps = {
...objectProps,
...p,
@@ -82,10 +84,7 @@ export default function ({
throw new Error('Already mounted');
}
elementReference = element;
return Promise.all([
whenLayoutReady,
whenSupernovaReady,
]).then(() => {
return Promise.all([whenLayoutReady, whenSupernovaReady]).then(() => {
reference = cell({
element,
model,
@@ -103,7 +102,7 @@ export default function ({
reference = noopi;
},
setTemporaryProperties(props) {
return get(model, 'effectiveProperties').then((current) => {
return get(model, 'effectiveProperties').then(current => {
const patches = getPatches('/', props, current);
if (patches.length) {
return model.applyPatches(patches, true);

View File

@@ -22,10 +22,7 @@ async function build(argv) {
const meta = argv.meta ? require(path.resolve(argv.meta)) : {}; // eslint-disable-line
const {
module,
main,
} = supernovaPkg;
const { module, main } = supernovaPkg;
const bundle = await rollup.rollup({
input: {
@@ -33,32 +30,31 @@ async function build(argv) {
extDefinition,
[extName]: path.resolve(__dirname, '../src/supernova-wrapper'),
},
external: [
'snDefinition',
'extDefinition',
],
external: ['snDefinition', 'extDefinition'],
plugins: [
nodeResolve(),
common(),
babel({
babelrc: false,
exclude: [
/node_modules/,
],
exclude: [/node_modules/],
presets: [
['@babel/preset-env', {
modules: false,
targets: {
browsers: ['ie 11', 'chrome 47'],
[
'@babel/preset-env',
{
modules: false,
targets: {
browsers: ['ie 11', 'chrome 47'],
},
},
}],
],
],
}),
(argv.minify && terser({
output: {
comments: /@license|@preserve|Copyright|license/,
},
})),
argv.minify &&
terser({
output: {
comments: /@license|@preserve|Copyright|license/,
},
}),
],
});
@@ -77,7 +73,7 @@ async function build(argv) {
// since that would cause requirejs in sense-client to interpret the request as text/html
// so we trim off the file extension in the modules, but then attach it to the files
const files = fs.readdirSync(targetDirectory);
files.forEach((f) => {
files.forEach(f => {
if (/^chunk-/.test(f) && !/\.js$/.test(f)) {
// attach file extension
fs.renameSync(path.resolve(targetDirectory, f), path.resolve(targetDirectory, `${f}.js`));
@@ -85,14 +81,21 @@ async function build(argv) {
});
// write .qext for the extension
fs.writeFileSync(path.resolve(targetDirectory, `${extName}.qext`), JSON.stringify({
name: extName,
description: supernovaPkg.description,
author: supernovaPkg.author,
version: supernovaPkg.version,
...meta,
type: 'visualization',
}, null, 2));
fs.writeFileSync(
path.resolve(targetDirectory, `${extName}.qext`),
JSON.stringify(
{
name: extName,
description: supernovaPkg.description,
author: supernovaPkg.author,
version: supernovaPkg.version,
...meta,
type: 'visualization',
},
null,
2
)
);
}
module.exports = build;

View File

@@ -29,38 +29,63 @@ const MODES = {
describe('permissions', () => {
describe('with live engine', () => {
it('in STATIC mode should only allow model SELECT and FETCH', () => {
expect(permissions({
interactionState: 0,
}, {})).to.eql(['select', 'fetch']);
expect(
permissions(
{
interactionState: 0,
},
{}
)
).to.eql(['select', 'fetch']);
});
it('in ANALYSIS mode should allow everything', () => {
expect(permissions({
interactionState: 1,
}, {})).to.eql(['passive', 'interact', 'select', 'fetch']);
expect(
permissions(
{
interactionState: 1,
},
{}
)
).to.eql(['passive', 'interact', 'select', 'fetch']);
});
it('in EDIT mode should only allow model SELECT and FETCH', () => {
expect(permissions({
interactionState: 2,
}, {})).to.eql(['select', 'fetch']);
expect(
permissions(
{
interactionState: 2,
},
{}
)
).to.eql(['select', 'fetch']);
});
it('in ANALYSIS mode with tooltips and selections off, should only allow FETCH', () => {
expect(permissions({
interactionState: 1,
tooltips: false,
selections: false,
}, {})).to.eql(['fetch']);
expect(
permissions(
{
interactionState: 1,
tooltips: false,
selections: false,
},
{}
)
).to.eql(['fetch']);
});
});
describe('for a snapshot', () => {
// interactive chart snapshot (shared chart)
it('in ANALYSIS mode should only allow PASSIVE and INTERACT', () => {
expect(permissions({
interactionState: 1,
}, { isSnapshot: true })).to.eql(['passive', 'interact']);
expect(
permissions(
{
interactionState: 1,
},
{ isSnapshot: true }
)
).to.eql(['passive', 'interact']);
});
// edit story

View File

@@ -16,7 +16,11 @@ function permissions(options, backendApi) {
if (showTooltip(options)) {
p.push('passive');
}
if (options.interactionState === INTERACTION_STATES.ANALYSIS && options.tooltips !== false && options.limitedInteraction !== true) {
if (
options.interactionState === INTERACTION_STATES.ANALYSIS &&
options.tooltips !== false &&
options.limitedInteraction !== true
) {
p.push('interact');
}
if (!backendApi.isSnapshot) {

View File

@@ -1,9 +1,9 @@
import EventEmitter from 'node-event-emitter';
export default (scope) => {
export default scope => {
/* eslint no-param-reassign: 0 */
const mixin = (obj) => {
Object.keys(EventEmitter.prototype).forEach((key) => {
const mixin = obj => {
Object.keys(EventEmitter.prototype).forEach(key => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
@@ -31,7 +31,7 @@ export default (scope) => {
if (s.method !== 'resetMadeSelections' && !scope.selectionsApi.selectionsMade) {
scope.backendApi.beginSelections();
}
scope.backendApi.model[s.method](...s.params).then((qSuccess) => {
scope.backendApi.model[s.method](...s.params).then(qSuccess => {
if (!qSuccess) {
scope.selectionsApi.selectionsMade = false;
this.clear();

View File

@@ -33,7 +33,7 @@ const startEngine = () => {
clearTimeout(timeout);
}
c.on('exit', (code) => {
c.on('exit', code => {
if (code !== 0) {
clear();
reject();

View File

@@ -6,16 +6,13 @@ const { watch } = require('@nebula.js/cli-build');
const webpackServe = require('./webpack.serve.js');
const {
startEngine,
stopEngine,
} = require('./engine');
const { startEngine, stopEngine } = require('./engine');
module.exports = async (argv) => {
module.exports = async argv => {
if (process.env.ACCEPT_EULA === 'yes') {
await startEngine();
}
const port = argv.port || await portfinder.getPortPromise();
const port = argv.port || (await portfinder.getPortPromise());
const host = argv.host || 'localhost';
const enigmaConfig = {
port: 9076,
@@ -70,7 +67,7 @@ module.exports = async (argv) => {
server.close();
};
['SIGINT', 'SIGTERM'].forEach((signal) => {
['SIGINT', 'SIGTERM'].forEach(signal => {
process.on(signal, close);
});

View File

@@ -3,7 +3,7 @@ import def from 'snDefinition'; // eslint-disable-line
window.snDefinition = def;
let cb = () => {};
window.hotReload = (fn) => {
window.hotReload = fn => {
cb = fn;
};

View File

@@ -5,24 +5,13 @@ const babelPath = require.resolve('babel-loader');
const babelPresetEnvPath = require.resolve('@babel/preset-env');
const babelPresetReactPath = require.resolve('@babel/preset-react');
const cfg = ({
srcDir,
distDir,
snPath,
dev = false,
}) => {
const cfg = ({ srcDir, distDir, snPath, dev = false }) => {
const config = {
mode: dev ? 'development' : 'production',
entry: {
eRender: [
path.resolve(srcDir, 'eRender'),
],
eDev: [
path.resolve(srcDir, 'eDev'),
],
eHub: [
path.resolve(srcDir, 'eHub'),
],
eRender: [path.resolve(srcDir, 'eRender')],
eDev: [path.resolve(srcDir, 'eDev')],
eHub: [path.resolve(srcDir, 'eHub')],
},
devtool: 'source-map',
output: {
@@ -37,29 +26,30 @@ const cfg = ({
},
externals: dev ? {} : 'snDefinition',
module: {
rules: [{
test: /\.jsx?$/,
sideEffects: false,
include: [
srcDir,
/nucleus/,
/ui\/icons/,
],
use: {
loader: babelPath,
options: {
presets: [
[babelPresetEnvPath, {
modules: false,
targets: {
browsers: ['last 2 chrome versions'],
},
}],
babelPresetReactPath,
],
rules: [
{
test: /\.jsx?$/,
sideEffects: false,
include: [srcDir, /nucleus/, /ui\/icons/],
use: {
loader: babelPath,
options: {
presets: [
[
babelPresetEnvPath,
{
modules: false,
targets: {
browsers: ['last 2 chrome versions'],
},
},
],
babelPresetReactPath,
],
},
},
},
}],
],
},
plugins: [
new HtmlWebpackPlugin({

View File

@@ -4,10 +4,7 @@ const webpack = require('webpack');
const sourceMapLoaderPath = require.resolve('source-map-loader');
const cfg = ({
srcDir = path.resolve(__dirname, '../dist'),
snPath = path.resolve(__dirname, 'placeholder'),
}) => {
const cfg = ({ srcDir = path.resolve(__dirname, '../dist'), snPath = path.resolve(__dirname, 'placeholder') }) => {
const config = {
mode: 'development',
entry: path.resolve(__dirname, './sn.js'),
@@ -22,11 +19,13 @@ const cfg = ({
},
},
module: {
rules: [{
enforce: 'pre',
test: /\.js$/,
loader: sourceMapLoaderPath,
}],
rules: [
{
enforce: 'pre',
test: /\.js$/,
loader: sourceMapLoaderPath,
},
],
},
plugins: [
new HtmlWebpackPlugin({

View File

@@ -5,16 +5,7 @@ const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
module.exports = async ({
host,
port,
enigmaConfig,
snPath,
snName,
dev = false,
open = true,
watcher,
}) => {
module.exports = async ({ host, port, enigmaConfig, snPath, snName, dev = false, open = true, watcher }) => {
let config;
let contentBase;
@@ -50,9 +41,7 @@ module.exports = async ({
},
quiet: true,
open,
contentBase: [
contentBase,
],
contentBase: [contentBase],
historyApiFallback: {
index: '/eHub.html',
},
@@ -66,15 +55,18 @@ module.exports = async ({
});
});
},
proxy: [{
context: '/render',
target: `http://${host}:${port}/eRender.html`,
ignorePath: true,
}, {
context: '/dev',
target: `http://${host}:${port}/eDev.html`,
ignorePath: true,
}],
proxy: [
{
context: '/render',
target: `http://${host}:${port}/eRender.html`,
ignorePath: true,
},
{
context: '/dev',
target: `http://${host}:${port}/eDev.html`,
ignorePath: true,
},
],
};
console.log('Starting development server...');
@@ -87,12 +79,12 @@ module.exports = async ({
server.close();
};
['SIGINT', 'SIGTERM'].forEach((signal) => {
['SIGINT', 'SIGTERM'].forEach(signal => {
process.on(signal, close);
});
if (watcher) {
watcher.on('event', (event) => {
watcher.on('event', event => {
if (event.code === 'ERROR') {
server.sockWrite(server.sockets, 'errors', [event.error.stack]);
}
@@ -101,8 +93,9 @@ module.exports = async ({
let initiated = false;
return new Promise((resolve, reject) => { // eslint-disable-line consistent-return
compiler.hooks.done.tap('nebula serve', (stats) => {
return new Promise((resolve, reject) => {
// eslint-disable-line consistent-return
compiler.hooks.done.tap('nebula serve', stats => {
if (!initiated) {
initiated = true;
const url = `http://${host}:${port}`;
@@ -115,7 +108,7 @@ module.exports = async ({
});
if (stats.hasErrors()) {
stats.compilation.errors.forEach((e) => {
stats.compilation.errors.forEach(e => {
console.log(chalk.red(e));
});
process.exit(1);
@@ -123,7 +116,7 @@ module.exports = async ({
}
});
server.listen(port, host, (err) => {
server.listen(port, host, err => {
if (err) {
reject(err);
}

View File

@@ -1,7 +1,5 @@
describe('creator', () => {
const [{ default: create }] = aw.mock([
['**/action-hero.js', () => () => ({})],
], ['../../src/creator']);
const [{ default: create }] = aw.mock([['**/action-hero.js', () => () => ({})]], ['../../src/creator']);
it('should return a default component api', () => {
const generator = {
@@ -16,14 +14,9 @@ describe('creator', () => {
const c = create(generator, params, env).component;
[
'created',
'mounted',
'render',
'resize',
'willUnmount',
'destroy',
].forEach((key) => expect(c[key]).to.be.a('function'));
['created', 'mounted', 'render', 'resize', 'willUnmount', 'destroy'].forEach(key =>
expect(c[key]).to.be.a('function')
);
expect(c.model).to.equal(params.model);
expect(c.app).to.equal(params.app);
@@ -50,14 +43,7 @@ describe('creator', () => {
const c = create(generator, params, env).component;
[
'created',
'mounted',
'render',
'resize',
'willUnmount',
'destroy',
].forEach((key) => {
['created', 'mounted', 'render', 'resize', 'willUnmount', 'destroy'].forEach(key => {
c[key]('a');
expect(generator.component[key]).to.have.been.calledWithExactly('a');
});

View File

@@ -1,10 +1,10 @@
// import generator from '../../src/generator';
describe('generator', () => {
const [{ default: generator }] = aw.mock([
['**/creator.js', () => (...a) => [...a]],
['**/qae.js', () => (qae) => qae || 'qae'],
], ['../../src/generator']);
const [{ default: generator }] = aw.mock(
[['**/creator.js', () => (...a) => [...a]], ['**/qae.js', () => qae => qae || 'qae']],
['../../src/generator']
);
it('should have a default qae property', () => {
expect(generator({}).qae).to.eql('qae');
@@ -15,10 +15,12 @@ describe('generator', () => {
});
it('should not override reserved properties', () => {
expect(generator({
foo: 'bar',
component: 'c',
}).definition).to.eql({
expect(
generator({
foo: 'bar',
component: 'c',
}).definition
).to.eql({
foo: 'bar',
});
});
@@ -33,10 +35,13 @@ describe('generator', () => {
});
it('should create an instance', () => {
const g = generator({}, {
translator: 't',
Promise: 'P',
});
const g = generator(
{},
{
translator: 't',
Promise: 'P',
}
);
const ret = g.create('a');
expect(ret).to.eql([g, 'a', { translator: 't', Promise: 'P' }]);
});

View File

@@ -11,9 +11,11 @@ describe('qae', () => {
});
it('should provide properties', () => {
expect(qae({
properties: { p: 'p' },
}).properties).to.eql({ p: 'p' });
expect(
qae({
properties: { p: 'p' },
}).properties
).to.eql({ p: 'p' });
});
it('should map target defaults', () => {
@@ -34,24 +36,26 @@ describe('qae', () => {
it('should map provided data', () => {
const t = qae({
data: {
targets: [{
path: 'qhc',
dimensions: {
min: () => 3,
max: () => 7,
add: () => 'a',
description: () => 'Slice',
move: () => 'c',
replace: () => 'd',
targets: [
{
path: 'qhc',
dimensions: {
min: () => 3,
max: () => 7,
add: () => 'a',
description: () => 'Slice',
move: () => 'c',
replace: () => 'd',
},
measures: {
min: 2,
max: 4,
add: () => 'b',
description: () => 'Angle',
remove: () => 'e',
},
},
measures: {
min: 2,
max: 4,
add: () => 'b',
description: () => 'Angle',
remove: () => 'e',
},
}],
],
},
}).data.targets[0];
expect(t.path).to.eql('qhc');

View File

@@ -1,15 +1,15 @@
import EventEmitter from 'node-event-emitter';
const mixin = (obj) => {
const mixin = obj => {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
Object.keys(EventEmitter.prototype).forEach(key => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
return obj;
};
const actionWrapper = (component) => (item) => {
const actionWrapper = component => item => {
const wrapped = mixin({
...item,
action() {
@@ -24,29 +24,29 @@ const actionWrapper = (component) => (item) => {
}
return true;
},
active: typeof item.active === 'function' ? function active() {
return item.active.call(wrapped, component);
} : undefined,
active:
typeof item.active === 'function'
? function active() {
return item.active.call(wrapped, component);
}
: undefined,
});
return wrapped;
};
export default function ({
sn,
component,
}) {
export default function({ sn, component }) {
const actions = {};
const selectionToolbarItems = [];
const w = actionWrapper(component);
((sn.definition.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.definition.actions || []).forEach((item) => {
(sn.definition.actions || []).forEach(item => {
const wrapped = w(item);
// TODO - check if key exists
actions[item.key] = wrapped;

View File

@@ -18,14 +18,14 @@ const defaultComponent = {
getViewState: () => {},
// temporary
setSnapshotData: (snapshot) => Promise.resolve(snapshot),
setSnapshotData: snapshot => Promise.resolve(snapshot),
};
const reservedKeys = Object.keys(defaultComponent);
const mixin = (obj) => {
const mixin = obj => {
/* eslint no-param-reassign: 0 */
Object.keys(EventEmitter.prototype).forEach((key) => {
Object.keys(EventEmitter.prototype).forEach(key => {
obj[key] = EventEmitter.prototype[key];
});
EventEmitter.init(obj);
@@ -45,7 +45,7 @@ export default function create(sn, opts) {
},
};
Object.keys(sn.component || {}).forEach((key) => {
Object.keys(sn.component || {}).forEach(key => {
if (reservedKeys.indexOf(key) !== -1) {
componentInstance[key] = sn.component[key].bind(userInstance);
} else {

View File

@@ -27,7 +27,7 @@ export default function generatorFn(UserSN, env) {
definition: {},
};
Object.keys(sn).forEach((key) => {
Object.keys(sn).forEach(key => {
if (!generator[key]) {
generator.definition[key] = sn[key];
}

View File

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

View File

@@ -54,10 +54,8 @@ export {
ListItemIcon,
ListItemText,
ListItemSecondaryAction,
FormGroup,
FormControlLabel,
Switch,
TextField,
};

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function SelectionsBack() {
return SvgIcon({
d: 'M6,15.5 L6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 L10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 L3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 6.76353751e-17,15.5522847 0,15 L0,13.5 C-3.38176876e-17,13.2238576 0.223857625,13 0.5,13 L0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M1,6.5 L1,9.5 C1,9.77614237 0.776142375,10 0.5,10 L0.5,10 C0.223857625,10 3.38176876e-17,9.77614237 0,9.5 L0,6.5 C-3.38176876e-17,6.22385763 0.223857625,6 0.5,6 L0.5,6 C0.776142375,6 1,6.22385763 1,6.5 Z M0,2.5 L0,1 C-6.76353751e-17,0.44771525 0.44771525,1.01453063e-16 1,0 L2.5,0 C2.77614237,-5.07265313e-17 3,0.223857625 3,0.5 L3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 C1,2.77614237 0.776142375,3 0.5,3 L0.5,3 C0.223857625,3 3.38176876e-17,2.77614237 0,2.5 Z M6,0.5 L6,0.5 C6,0.223857625 6.22385763,5.07265313e-17 6.5,0 L9.5,0 C9.77614237,-5.07265313e-17 10,0.223857625 10,0.5 L10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 Z M15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 L13,0.5 C13,0.223857625 13.2238576,5.07265313e-17 13.5,0 L15,0 C15.5522847,-1.01453063e-16 16,0.44771525 16,1 L16,2.5 C16,2.77614237 15.7761424,3 15.5,3 L15.5,3 C15.2238576,3 15,2.77614237 15,2.5 Z M9.1661442,6.1661442 C10.7210031,4.61128527 13.2789969,4.61128527 14.8338558,6.1661442 C16.3887147,7.72100313 16.3887147,10.2789969 14.8338558,11.8338558 C13.2789969,13.3887147 10.7210031,13.3887147 9.1661442,11.8338558 C7.61128527,10.2789969 7.61128527,7.77115987 9.1661442,6.1661442 Z M14.1316614,7.72100313 C14.3322884,7.52037618 14.3824451,7.169279 14.1316614,6.9184953 C13.8808777,6.6677116 13.5297806,6.71786834 13.3291536,6.9184953 L12.0250784,8.22257053 L10.7210031,6.9184953 C10.5203762,6.6677116 10.1191223,6.6677116 9.9184953,6.9184953 C9.6677116,7.11912226 9.6677116,7.52037618 9.9184953,7.72100313 L11.2225705,9.02507837 L9.9184953,10.3291536 C9.6677116,10.5297806 9.6677116,10.8808777 9.9184953,11.1316614 C10.169279,11.3824451 10.5203762,11.3824451 10.7210031,11.1316614 L12.0250784,9.82758621 L13.3291536,11.1316614 C13.5297806,11.3824451 13.8808777,11.3824451 14.1316614,11.1316614 C14.3322884,10.9310345 14.3824451,10.5297806 14.1316614,10.3291536 L12.8275862,9.02507837 L14.1316614,7.72100313 Z',
d:
'M6,15.5 L6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 L10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 L3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 6.76353751e-17,15.5522847 0,15 L0,13.5 C-3.38176876e-17,13.2238576 0.223857625,13 0.5,13 L0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M1,6.5 L1,9.5 C1,9.77614237 0.776142375,10 0.5,10 L0.5,10 C0.223857625,10 3.38176876e-17,9.77614237 0,9.5 L0,6.5 C-3.38176876e-17,6.22385763 0.223857625,6 0.5,6 L0.5,6 C0.776142375,6 1,6.22385763 1,6.5 Z M0,2.5 L0,1 C-6.76353751e-17,0.44771525 0.44771525,1.01453063e-16 1,0 L2.5,0 C2.77614237,-5.07265313e-17 3,0.223857625 3,0.5 L3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 C1,2.77614237 0.776142375,3 0.5,3 L0.5,3 C0.223857625,3 3.38176876e-17,2.77614237 0,2.5 Z M6,0.5 L6,0.5 C6,0.223857625 6.22385763,5.07265313e-17 6.5,0 L9.5,0 C9.77614237,-5.07265313e-17 10,0.223857625 10,0.5 L10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 Z M15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 L13,0.5 C13,0.223857625 13.2238576,5.07265313e-17 13.5,0 L15,0 C15.5522847,-1.01453063e-16 16,0.44771525 16,1 L16,2.5 C16,2.77614237 15.7761424,3 15.5,3 L15.5,3 C15.2238576,3 15,2.77614237 15,2.5 Z M9.1661442,6.1661442 C10.7210031,4.61128527 13.2789969,4.61128527 14.8338558,6.1661442 C16.3887147,7.72100313 16.3887147,10.2789969 14.8338558,11.8338558 C13.2789969,13.3887147 10.7210031,13.3887147 9.1661442,11.8338558 C7.61128527,10.2789969 7.61128527,7.77115987 9.1661442,6.1661442 Z M14.1316614,7.72100313 C14.3322884,7.52037618 14.3824451,7.169279 14.1316614,6.9184953 C13.8808777,6.6677116 13.5297806,6.71786834 13.3291536,6.9184953 L12.0250784,8.22257053 L10.7210031,6.9184953 C10.5203762,6.6677116 10.1191223,6.6677116 9.9184953,6.9184953 C9.6677116,7.11912226 9.6677116,7.52037618 9.9184953,7.72100313 L11.2225705,9.02507837 L9.9184953,10.3291536 C9.6677116,10.5297806 9.6677116,10.8808777 9.9184953,11.1316614 C10.169279,11.3824451 10.5203762,11.3824451 10.7210031,11.1316614 L12.0250784,9.82758621 L13.3291536,11.1316614 C13.5297806,11.3824451 13.8808777,11.3824451 14.1316614,11.1316614 C14.3322884,10.9310345 14.3824451,10.5297806 14.1316614,10.3291536 L12.8275862,9.02507837 L14.1316614,7.72100313 Z',
});
}

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function Close() {
return SvgIcon({
d: 'M9.34535242,8 L13.3273238,11.9819714 C13.6988326,12.3534802 13.6988326,12.955815 13.3273238,13.3273238 C12.955815,13.6988326 12.3534802,13.6988326 11.9819714,13.3273238 L8,9.34535242 L4.01802863,13.3273238 C3.64651982,13.6988326 3.04418502,13.6988326 2.67267621,13.3273238 C2.3011674,12.955815 2.3011674,12.3534802 2.67267621,11.9819714 L6.65464758,8 L2.67267621,4.01802863 C2.3011674,3.64651982 2.3011674,3.04418502 2.67267621,2.67267621 C3.04418502,2.3011674 3.64651982,2.3011674 4.01802863,2.67267621 L8,6.65464758 L11.9819714,2.67267621 C12.3534802,2.3011674 12.955815,2.3011674 13.3273238,2.67267621 C13.6988326,3.04418502 13.6988326,3.64651982 13.3273238,4.01802863 L9.34535242,8 Z',
d:
'M9.34535242,8 L13.3273238,11.9819714 C13.6988326,12.3534802 13.6988326,12.955815 13.3273238,13.3273238 C12.955815,13.6988326 12.3534802,13.6988326 11.9819714,13.3273238 L8,9.34535242 L4.01802863,13.3273238 C3.64651982,13.6988326 3.04418502,13.6988326 2.67267621,13.3273238 C2.3011674,12.955815 2.3011674,12.3534802 2.67267621,11.9819714 L6.65464758,8 L2.67267621,4.01802863 C2.3011674,3.64651982 2.3011674,3.04418502 2.67267621,2.67267621 C3.04418502,2.3011674 3.64651982,2.3011674 4.01802863,2.67267621 L8,6.65464758 L11.9819714,2.67267621 C12.3534802,2.3011674 12.955815,2.3011674 13.3273238,2.67267621 C13.6988326,3.04418502 13.6988326,3.64651982 13.3273238,4.01802863 L9.34535242,8 Z',
});
}

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function Lasso() {
return SvgIcon({
d: 'M15.9488039,5.20769129 C16.0487326,6.70662306 15.3492311,8.30548361 14.050157,9.30477145 C12.651154,10.5039169 10.8524359,10.8037032 8.85386017,10.4039881 L7.3549284,10.0042729 L5.75606786,9.70448659 C5.75606786,9.90434416 5.65613907,10.0042729 5.4562815,10.2041305 C5.05656637,10.6038456 4.55692244,10.8037032 4.05727852,10.8037032 C3.75749217,10.8037032 3.45770582,10.7037744 3.15791946,10.6038456 C3.05799068,10.903632 3.15791946,11.2034184 3.45770582,11.7030623 C5.05656637,14.0014243 3.85742095,15.9000712 3.75749217,16 L2.2585604,15.3004985 C2.2585604,15.2005697 2.95806189,14.0014243 1.95877405,12.6024213 C1.6589877,12.0028486 1.15934378,11.0035608 1.55905891,10.0042729 C1.6589877,9.80441537 1.75891648,9.6045578 1.95877405,9.40470024 C1.6589877,8.90505631 1.55905891,8.30548361 1.85884527,7.7059109 C1.55905891,7.40612455 1.25927256,7.1063382 1.15934378,6.70662306 C0.859557424,5.90719279 0.959486209,4.5081898 1.6589877,3.30904439 C1.95877405,2.6095429 2.55834676,2.0099702 3.15791946,1.51032628 C3.95734974,0.91075357 4.95663758,0.610967217 6.15578299,0.311180864 C9.05371774,-0.388320626 11.9516525,0.111323295 13.9502282,1.61025506 C15.1493736,2.50961412 15.8488751,3.80868831 15.9488039,5.20769129 Z M13.0508691,8.10562604 C13.8502994,7.40612455 14.3499433,6.40683671 14.3499433,5.30762008 C14.2500145,4.20840345 13.550513,3.40897318 12.9509403,2.90932926 C12.1515101,2.40968533 11.252151,2.0099702 10.1529344,1.81011263 C8.95378895,1.61025506 7.75464354,1.71018384 6.45556935,1.91004141 C4.75678001,2.30975655 3.65756338,3.00925804 3.05799068,4.10847467 C2.55834676,5.00783373 2.65827554,5.90719279 2.75820433,6.20697914 C2.75820433,6.30690792 2.85813311,6.40683671 3.05799068,6.40683671 C3.15791946,6.40683671 3.25784825,6.40683671 3.35777703,6.40683671 C3.45770582,6.40683671 3.45770582,6.40683671 3.45770582,6.40683671 L3.5576346,6.40683671 L3.65756338,6.40683671 C4.65685123,6.40683671 5.4562815,6.90648063 5.85599664,7.80583969 L5.85599664,8.00569726 C6.35564056,8.10562604 7.05514205,8.30548361 7.75464354,8.50534118 L9.25357531,8.90505631 C10.0530056,9.0049851 10.7525071,9.0049851 11.4520086,8.80512753 C12.0515813,8.70519875 12.5512252,8.40541239 13.0508691,8.10562604 Z',
d:
'M15.9488039,5.20769129 C16.0487326,6.70662306 15.3492311,8.30548361 14.050157,9.30477145 C12.651154,10.5039169 10.8524359,10.8037032 8.85386017,10.4039881 L7.3549284,10.0042729 L5.75606786,9.70448659 C5.75606786,9.90434416 5.65613907,10.0042729 5.4562815,10.2041305 C5.05656637,10.6038456 4.55692244,10.8037032 4.05727852,10.8037032 C3.75749217,10.8037032 3.45770582,10.7037744 3.15791946,10.6038456 C3.05799068,10.903632 3.15791946,11.2034184 3.45770582,11.7030623 C5.05656637,14.0014243 3.85742095,15.9000712 3.75749217,16 L2.2585604,15.3004985 C2.2585604,15.2005697 2.95806189,14.0014243 1.95877405,12.6024213 C1.6589877,12.0028486 1.15934378,11.0035608 1.55905891,10.0042729 C1.6589877,9.80441537 1.75891648,9.6045578 1.95877405,9.40470024 C1.6589877,8.90505631 1.55905891,8.30548361 1.85884527,7.7059109 C1.55905891,7.40612455 1.25927256,7.1063382 1.15934378,6.70662306 C0.859557424,5.90719279 0.959486209,4.5081898 1.6589877,3.30904439 C1.95877405,2.6095429 2.55834676,2.0099702 3.15791946,1.51032628 C3.95734974,0.91075357 4.95663758,0.610967217 6.15578299,0.311180864 C9.05371774,-0.388320626 11.9516525,0.111323295 13.9502282,1.61025506 C15.1493736,2.50961412 15.8488751,3.80868831 15.9488039,5.20769129 Z M13.0508691,8.10562604 C13.8502994,7.40612455 14.3499433,6.40683671 14.3499433,5.30762008 C14.2500145,4.20840345 13.550513,3.40897318 12.9509403,2.90932926 C12.1515101,2.40968533 11.252151,2.0099702 10.1529344,1.81011263 C8.95378895,1.61025506 7.75464354,1.71018384 6.45556935,1.91004141 C4.75678001,2.30975655 3.65756338,3.00925804 3.05799068,4.10847467 C2.55834676,5.00783373 2.65827554,5.90719279 2.75820433,6.20697914 C2.75820433,6.30690792 2.85813311,6.40683671 3.05799068,6.40683671 C3.15791946,6.40683671 3.25784825,6.40683671 3.35777703,6.40683671 C3.45770582,6.40683671 3.45770582,6.40683671 3.45770582,6.40683671 L3.5576346,6.40683671 L3.65756338,6.40683671 C4.65685123,6.40683671 5.4562815,6.90648063 5.85599664,7.80583969 L5.85599664,8.00569726 C6.35564056,8.10562604 7.05514205,8.30548361 7.75464354,8.50534118 L9.25357531,8.90505631 C10.0530056,9.0049851 10.7525071,9.0049851 11.4520086,8.80512753 C12.0515813,8.70519875 12.5512252,8.40541239 13.0508691,8.10562604 Z',
});
}

View File

@@ -3,6 +3,7 @@ import SvgIcon from './SvgIcon';
export default function Lock(props = {}) {
return SvgIcon({
...props,
d: 'M13,7 L8,7 L13,7 L13,4.98151367 C13,2.23029964 10.7614237,0 8,0 C5.23857625,0 3,2.23029964 3,4.98151367 L3,7 L3.75,7 L3,7 L4.5,7 L4.5,5.33193359 C4.5,3.21561511 5.54860291,1.5 8,1.5 C10.4513971,1.5 11.5,3.21561511 11.5,5.33193359 L11.5,7 L12.25,7 L3,7 C2.44771525,7 2,7.44771525 2,8 L2,15 C2,15.5522847 2.44771525,16 3,16 L13,16 C13.5522847,16 14,15.5522847 14,15 L14,8 C14,7.44771525 13.5522847,7 13,7 L3,7 L13,7 Z',
d:
'M13,7 L8,7 L13,7 L13,4.98151367 C13,2.23029964 10.7614237,0 8,0 C5.23857625,0 3,2.23029964 3,4.98151367 L3,7 L3.75,7 L3,7 L4.5,7 L4.5,5.33193359 C4.5,3.21561511 5.54860291,1.5 8,1.5 C10.4513971,1.5 11.5,3.21561511 11.5,5.33193359 L11.5,7 L12.25,7 L3,7 C2.44771525,7 2,7.44771525 2,8 L2,15 C2,15.5522847 2.44771525,16 3,16 L13,16 C13.5522847,16 14,15.5522847 14,15 L14,8 C14,7.44771525 13.5522847,7 13,7 L3,7 L13,7 Z',
});
}

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function Remove() {
return SvgIcon({
d: 'M9.41421356,8 L11.8890873,5.52512627 C12.065864,5.34834957 12.0305087,4.95944084 11.8183766,4.74730881 L11.2526912,4.18162338 C11.0405592,3.96949135 10.6516504,3.93413601 10.4748737,4.1109127 L8,6.58578644 L5.52512627,4.1109127 C5.34834957,3.93413601 4.95944084,3.96949135 4.74730881,4.18162338 L4.25233406,4.67659813 C3.96949135,4.95944084 3.93413601,5.34834957 4.1109127,5.52512627 L6.58578644,8 L4.1109127,10.4748737 C3.93413601,10.6516504 3.96949135,11.0405592 4.18162338,11.2526912 L4.67659813,11.7476659 C4.95944084,12.0305087 5.34834957,12.065864 5.52512627,11.8890873 L8,9.41421356 L10.4748737,11.8890873 C10.6516504,12.065864 11.0405592,12.0305087 11.2526912,11.8183766 L11.8183766,11.2526912 C12.0305087,11.0405592 12.065864,10.6516504 11.8890873,10.4748737 L9.41421356,8 Z M8,0 C12.4,0 16,3.6 16,8 C16,12.4 12.4,16 8,16 C3.6,16 0,12.4 0,8 C0,3.6 3.6,0 8,0 Z',
d:
'M9.41421356,8 L11.8890873,5.52512627 C12.065864,5.34834957 12.0305087,4.95944084 11.8183766,4.74730881 L11.2526912,4.18162338 C11.0405592,3.96949135 10.6516504,3.93413601 10.4748737,4.1109127 L8,6.58578644 L5.52512627,4.1109127 C5.34834957,3.93413601 4.95944084,3.96949135 4.74730881,4.18162338 L4.25233406,4.67659813 C3.96949135,4.95944084 3.93413601,5.34834957 4.1109127,5.52512627 L6.58578644,8 L4.1109127,10.4748737 C3.93413601,10.6516504 3.96949135,11.0405592 4.18162338,11.2526912 L4.67659813,11.7476659 C4.95944084,12.0305087 5.34834957,12.065864 5.52512627,11.8890873 L8,9.41421356 L10.4748737,11.8890873 C10.6516504,12.065864 11.0405592,12.0305087 11.2526912,11.8183766 L11.8183766,11.2526912 C12.0305087,11.0405592 12.065864,10.6516504 11.8890873,10.4748737 L9.41421356,8 Z M8,0 C12.4,0 16,3.6 16,8 C16,12.4 12.4,16 8,16 C3.6,16 0,12.4 0,8 C0,3.6 3.6,0 8,0 Z',
});
}

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function SelectionsBack() {
return SvgIcon({
d: 'M10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 Z M15,13.5 C15,13.2238576 15.2238576,13 15.5,13 C15.7761424,13 16,13.2238576 16,13.5 L16,15 C16,15.5522847 15.5522847,16 15,16 L13.5,16 C13.2238576,16 13,15.7761424 13,15.5 C13,15.2238576 13.2238576,15 13.5,15 L14.5,15 C14.7761424,15 15,14.7761424 15,14.5 L15,13.5 Z M15,6.5 C15,6.22385763 15.2238576,6 15.5,6 C15.7761424,6 16,6.22385763 16,6.5 L16,9.5 C16,9.77614237 15.7761424,10 15.5,10 C15.2238576,10 15,9.77614237 15,9.5 L15,6.5 Z M16,2.5 C16,2.77614237 15.7761424,3 15.5,3 C15.2238576,3 15,2.77614237 15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 C13,0.223857625 13.2238576,-5.07265313e-17 13.5,0 L15,0 C15.5522847,1.01453063e-16 16,0.44771525 16,1 L16,2.5 Z M10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 C6,0.223857625 6.22385763,-5.07265313e-17 6.5,0 L9.5,0 C9.77614237,5.07265313e-17 10,0.223857625 10,0.5 Z M1,2.5 C1,2.77614237 0.776142375,3 0.5,3 C0.223857625,3 5.18696197e-13,2.77614237 5.18696197e-13,2.5 L5.18696197e-13,1 C5.18696197e-13,0.44771525 0.44771525,-1.01453063e-16 1,0 L2.5,0 C2.77614237,5.07265313e-17 3,0.223857625 3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 5.18696197e-13,15.5522847 5.18696197e-13,15 L5.18696197e-13,13.5 C5.18696197e-13,13.2238576 0.223857625,13 0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M4,7 C7.49095643,7 10,10.1337595 10,12.1872632 C10,12.1872632 8.16051135,9.86624054 4,10 L4,12 C4,12 2.66666667,10.8333333 -1.0658141e-14,8.5 C-2.59348099e-13,8.5 1.33333333,7.33333333 4,5 C4,5 4,5.66666667 4,7 Z',
d:
'M10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 Z M15,13.5 C15,13.2238576 15.2238576,13 15.5,13 C15.7761424,13 16,13.2238576 16,13.5 L16,15 C16,15.5522847 15.5522847,16 15,16 L13.5,16 C13.2238576,16 13,15.7761424 13,15.5 C13,15.2238576 13.2238576,15 13.5,15 L14.5,15 C14.7761424,15 15,14.7761424 15,14.5 L15,13.5 Z M15,6.5 C15,6.22385763 15.2238576,6 15.5,6 C15.7761424,6 16,6.22385763 16,6.5 L16,9.5 C16,9.77614237 15.7761424,10 15.5,10 C15.2238576,10 15,9.77614237 15,9.5 L15,6.5 Z M16,2.5 C16,2.77614237 15.7761424,3 15.5,3 C15.2238576,3 15,2.77614237 15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 C13,0.223857625 13.2238576,-5.07265313e-17 13.5,0 L15,0 C15.5522847,1.01453063e-16 16,0.44771525 16,1 L16,2.5 Z M10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 C6,0.223857625 6.22385763,-5.07265313e-17 6.5,0 L9.5,0 C9.77614237,5.07265313e-17 10,0.223857625 10,0.5 Z M1,2.5 C1,2.77614237 0.776142375,3 0.5,3 C0.223857625,3 5.18696197e-13,2.77614237 5.18696197e-13,2.5 L5.18696197e-13,1 C5.18696197e-13,0.44771525 0.44771525,-1.01453063e-16 1,0 L2.5,0 C2.77614237,5.07265313e-17 3,0.223857625 3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 5.18696197e-13,15.5522847 5.18696197e-13,15 L5.18696197e-13,13.5 C5.18696197e-13,13.2238576 0.223857625,13 0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M4,7 C7.49095643,7 10,10.1337595 10,12.1872632 C10,12.1872632 8.16051135,9.86624054 4,10 L4,12 C4,12 2.66666667,10.8333333 -1.0658141e-14,8.5 C-2.59348099e-13,8.5 1.33333333,7.33333333 4,5 C4,5 4,5.66666667 4,7 Z',
});
}

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function SelectionsForward() {
return SvgIcon({
d: 'M6,15.5 L6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 L10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 L3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 6.76353751e-17,15.5522847 0,15 L0,13.5 C-3.38176876e-17,13.2238576 0.223857625,13 0.5,13 L0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M1,6.5 L1,9.5 C1,9.77614237 0.776142375,10 0.5,10 L0.5,10 C0.223857625,10 3.38176876e-17,9.77614237 0,9.5 L0,6.5 C-3.38176876e-17,6.22385763 0.223857625,6 0.5,6 L0.5,6 C0.776142375,6 1,6.22385763 1,6.5 Z M0,2.5 L0,1 C-6.76353751e-17,0.44771525 0.44771525,1.01453063e-16 1,0 L2.5,0 C2.77614237,-5.07265313e-17 3,0.223857625 3,0.5 L3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 C1,2.77614237 0.776142375,3 0.5,3 L0.5,3 C0.223857625,3 3.38176876e-17,2.77614237 0,2.5 Z M6,0.5 L6,0.5 C6,0.223857625 6.22385763,5.07265313e-17 6.5,0 L9.5,0 C9.77614237,-5.07265313e-17 10,0.223857625 10,0.5 L10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 Z M15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 L13,0.5 C13,0.223857625 13.2238576,5.07265313e-17 13.5,0 L15,0 C15.5522847,-1.01453063e-16 16,0.44771525 16,1 L16,2.5 C16,2.77614237 15.7761424,3 15.5,3 L15.5,3 C15.2238576,3 15,2.77614237 15,2.5 Z M15,13.5 C15,13.2238576 15.2238576,13 15.5,13 C15.7761424,13 16,13.2238576 16,13.5 L16,15 C16,15.5522847 15.5522847,16 15,16 L13.5,16 C13.2238576,16 13,15.7761424 13,15.5 C13,15.2238576 13.2238576,15 13.5,15 L14.5,15 C14.7761424,15 15,14.7761424 15,14.5 L15,13.5 Z M12,7 C12,5.66666667 12,5 12,5 C14.6666667,7.33333333 16,8.5 16,8.5 C13.3333333,10.8333333 12,12 12,12 L12,10 C7.83948865,9.86624054 6,12.1872632 6,12.1872632 C6,10.1337595 8.50904357,7 12,7 Z',
d:
'M6,15.5 L6,15.5 C6,15.2238576 6.22385763,15 6.5,15 L9.5,15 C9.77614237,15 10,15.2238576 10,15.5 L10,15.5 C10,15.7761424 9.77614237,16 9.5,16 L6.5,16 C6.22385763,16 6,15.7761424 6,15.5 Z M1,13.5 L1,14.5 C1,14.7761424 1.22385763,15 1.5,15 L2.5,15 C2.77614237,15 3,15.2238576 3,15.5 L3,15.5 C3,15.7761424 2.77614237,16 2.5,16 L1,16 C0.44771525,16 6.76353751e-17,15.5522847 0,15 L0,13.5 C-3.38176876e-17,13.2238576 0.223857625,13 0.5,13 L0.5,13 C0.776142375,13 1,13.2238576 1,13.5 Z M1,6.5 L1,9.5 C1,9.77614237 0.776142375,10 0.5,10 L0.5,10 C0.223857625,10 3.38176876e-17,9.77614237 0,9.5 L0,6.5 C-3.38176876e-17,6.22385763 0.223857625,6 0.5,6 L0.5,6 C0.776142375,6 1,6.22385763 1,6.5 Z M0,2.5 L0,1 C-6.76353751e-17,0.44771525 0.44771525,1.01453063e-16 1,0 L2.5,0 C2.77614237,-5.07265313e-17 3,0.223857625 3,0.5 L3,0.5 C3,0.776142375 2.77614237,1 2.5,1 L1.5,1 C1.22385763,1 1,1.22385763 1,1.5 L1,2.5 C1,2.77614237 0.776142375,3 0.5,3 L0.5,3 C0.223857625,3 3.38176876e-17,2.77614237 0,2.5 Z M6,0.5 L6,0.5 C6,0.223857625 6.22385763,5.07265313e-17 6.5,0 L9.5,0 C9.77614237,-5.07265313e-17 10,0.223857625 10,0.5 L10,0.5 C10,0.776142375 9.77614237,1 9.5,1 L6.5,1 C6.22385763,1 6,0.776142375 6,0.5 Z M15,2.5 L15,1.5 C15,1.22385763 14.7761424,1 14.5,1 L13.5,1 C13.2238576,1 13,0.776142375 13,0.5 L13,0.5 C13,0.223857625 13.2238576,5.07265313e-17 13.5,0 L15,0 C15.5522847,-1.01453063e-16 16,0.44771525 16,1 L16,2.5 C16,2.77614237 15.7761424,3 15.5,3 L15.5,3 C15.2238576,3 15,2.77614237 15,2.5 Z M15,13.5 C15,13.2238576 15.2238576,13 15.5,13 C15.7761424,13 16,13.2238576 16,13.5 L16,15 C16,15.5522847 15.5522847,16 15,16 L13.5,16 C13.2238576,16 13,15.7761424 13,15.5 C13,15.2238576 13.2238576,15 13.5,15 L14.5,15 C14.7761424,15 15,14.7761424 15,14.5 L15,13.5 Z M12,7 C12,5.66666667 12,5 12,5 C14.6666667,7.33333333 16,8.5 16,8.5 C13.3333333,10.8333333 12,12 12,12 L12,10 C7.83948865,9.86624054 6,12.1872632 6,12.1872632 C6,10.1337595 8.50904357,7 12,7 Z',
});
}

View File

@@ -10,11 +10,7 @@ function getFontSize(size) {
return '16px';
}
export default function SvgIcon({
d,
size,
style = {},
}) {
export default function SvgIcon({ d, size, style = {} }) {
const s = {
fontSize: getFontSize(size),
display: 'inline-block',
@@ -30,13 +26,7 @@ export default function SvgIcon({
};
return (
<i style={s}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 16 16"
fill="currentColor"
>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<path d={d} />
</svg>
</i>

View File

@@ -2,6 +2,7 @@ import SvgIcon from './SvgIcon';
export default function Unlock() {
return SvgIcon({
d: 'M2.5,7 L11,7 C11.5522847,7 12,7.44771525 12,8 L12,15 C12,15.5522847 11.5522847,16 11,16 L1,16 C0.44771525,16 0,15.5522847 0,15 L0,8 C0,7.44771525 0.44771525,7 1,7 L1,4.98151367 C1,2.23029964 3.23857625,0 6,0 C8.4241995,0 10.4454541,1.71883353 10.9029715,4 L9.34209114,4 C8.9671727,2.54028848 7.9088888,1.5 6,1.5 C3.54860291,1.5 2.5,3.21561511 2.5,5.33193359 L2.5,7 Z',
d:
'M2.5,7 L11,7 C11.5522847,7 12,7.44771525 12,8 L12,15 C12,15.5522847 11.5522847,16 11,16 L1,16 C0.44771525,16 0,15.5522847 0,15 L0,8 C0,7.44771525 0.44771525,7 1,7 L1,4.98151367 C1,2.23029964 3.23857625,0 6,0 C8.4241995,0 10.4454541,1.71883353 10.9029715,4 L9.34209114,4 C8.9671727,2.54028848 7.9088888,1.5 6,1.5 C3.54860291,1.5 2.5,3.21561511 2.5,5.33193359 L2.5,7 Z',
});
}

View File

@@ -3,9 +3,4 @@ import ChevronLeft from '@material-ui/icons/ChevronLeft';
import Brightness3 from '@material-ui/icons/Brightness3';
import WbSunny from '@material-ui/icons/WbSunny';
export {
ChevronRight,
ChevronLeft,
Brightness3,
WbSunny,
};
export { ChevronRight, ChevronLeft, Brightness3, WbSunny };

View File

@@ -33,11 +33,12 @@ describe('resolver', () => {
});
it('should throw when reference is cyclical', () => {
const fn = () => resolver({
foo: {
bar: '4px $foo.bar',
},
});
const fn = () =>
resolver({
foo: {
bar: '4px $foo.bar',
},
});
expect(fn).to.throw('Cyclical reference for "$foo.bar"');
});
});
@@ -49,9 +50,11 @@ describe('resolver', () => {
fontFamily: 'Arial',
},
});
expect(r.resolve({
font: '16px $typography.fontFamily',
})).to.eql({
expect(
r.resolve({
font: '16px $typography.fontFamily',
})
).to.eql({
font: '16px Arial',
});
});

View File

@@ -5,14 +5,9 @@ export default {
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 600,
fontFamily: [
'"Source Sans Pro"',
'"Segoe UI"',
'"Helvetica Neue"',
'-apple-system',
'Arial',
'sans-serif',
].join(','),
fontFamily: ['"Source Sans Pro"', '"Segoe UI"', '"Helvetica Neue"', '-apple-system', 'Arial', 'sans-serif'].join(
','
),
button: {
textTransform: 'initial',
fontWeight: 400,

View File

@@ -1,18 +1,5 @@
import {
ThemeProvider,
useTheme,
StylesProvider,
createGenerateClassName,
makeStyles,
} from '@material-ui/styles';
import { ThemeProvider, useTheme, StylesProvider, createGenerateClassName, makeStyles } from '@material-ui/styles';
import createTheme from './create';
export {
createTheme,
useTheme,
makeStyles,
ThemeProvider,
StylesProvider,
createGenerateClassName,
};
export { createTheme, useTheme, makeStyles, ThemeProvider, StylesProvider, createGenerateClassName };

Some files were not shown because too many files have changed in this diff Show More