refactor: all in with material-ui (#23)

This commit is contained in:
Miralem Drek
2019-05-07 16:24:06 +02:00
committed by GitHub
parent cc09554b62
commit dbdc279d2c
63 changed files with 1587 additions and 1497 deletions

View File

@@ -1,78 +0,0 @@
/* eslint object-property-newline:0 */
import React from 'react';
import renderer from 'react-test-renderer';
import { AppSelections } from '../../src/components/AppSelections';
describe('<AppSelections />', () => {
let api;
beforeEach(() => {
api = {
canGoForward: () => 'canGoForward',
canGoBack: () => 'canGoBack',
canClear: () => 'canClear',
layout: () => (null),
back: sinon.spy(),
forward: sinon.spy(),
clear: sinon.spy(),
on: sinon.spy(),
removeListener: sinon.spy(),
};
});
describe('constructor', () => {
it('should initiate state', () => {
const sel = new AppSelections({ api });
expect(sel.state).to.eql({
back: 'canGoBack',
forward: 'canGoForward',
clear: 'canClear',
items: [],
});
});
});
describe('didMount', () => {
it('should listen to api changes', () => {
const sel = new AppSelections({ api });
sel.componentDidMount();
expect(api.on).to.have.been.calledWithExactly('changed', sel.apiChangeHandler);
});
});
describe('willUnmount', () => {
it('should stop listening to api changes', () => {
const sel = new AppSelections({ api });
sel.componentWillUnmount();
expect(api.removeListener).to.have.been.calledWithExactly('changed', sel.apiChangeHandler);
});
});
it('should render a toolbar', () => {
api.canGoBack = () => false;
const Button = ({ disabled, children }) => <b d={disabled}>{children}</b>;
const Toolbar = ({ children }) => <t>{children}</t>;
const Grid = ({ children }) => <g>{children}</g>;
const [{ AppSelections: AS }] = aw.mock([
['**/SelectionsBack.jsx', () => () => 'Back'],
['**/SelectionsForward.jsx', () => () => 'Forward'],
['**/ClearSelections.jsx', () => () => 'Clear'],
['**/ButtonInline.jsx', () => Button],
['**/Toolbar.jsx', () => Toolbar],
['**/Grid.jsx', () => Grid],
['**/Text.jsx', () => Button],
], ['../../src/components/AppSelections']);
const r = renderer.create(<AS api={api} />);
expect(r.toJSON()).to.eql({
type: 't', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [
{ type: 'b', props: { d: true }, children: ['Back'] },
{ type: 'b', props: { d: false }, children: ['Forward'] },
{ type: 'b', props: { d: false }, children: ['Clear'] },
],
}, { type: 'g', props: {}, children: null }],
}],
});
});
});

View File

@@ -1,67 +1,63 @@
/* eslint object-curly-newline: 0 */
/* eslint object-property-newline: 0 */
import React from 'react';
import renderer from 'react-test-renderer';
import Header from '../../src/components/Header';
const mock = ({
components = {
Grid: ({ children }) => <g>{children}</g>,
Typography: ({ children }) => <t>{children}</t>,
},
STB = () => <stb />,
} = {}) => aw.mock([
['**/ui/components/index.js', () => components],
['**/SelectionToolbar.jsx', () => STB],
], ['../../src/components/Header']);
describe('<Header />', () => {
it('should be empty when input is falsy', () => {
expect(renderer.create(<Header />).toJSON()).to.equal(null);
expect(renderer.create(<Header layout={{ showTitles: true }} />).toJSON()).to.equal(null);
});
it('should render a title', () => {
const layout = { showTitles: true, title: 'foo' };
const [{ default: MockedHeader }] = aw.mock([
['**/Text.jsx', () => ({ children, ...p }) => <span {...p}>{children}</span>],
], ['../../src/components/Header']);
const tree = renderer.create(<MockedHeader layout={layout} />).toJSON();
const layout = { showTitles: true, title: 'title' };
const [{ default: Header }] = mock();
const tree = renderer.create(<Header layout={layout} />).toJSON();
expect(tree).to.eql({
type: 'div',
props: {
style: { background: 'transparent', padding: '0 8px' },
},
children: [{
type: 'span',
props: { block: true, nowrap: true, size: 'large' },
children: ['foo'],
}, {
type: 'span',
props: {
faded: true,
block: true,
nowrap: true,
size: 'small',
},
children: null,
}],
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 't', props: {}, children: ['title'],
}],
}],
}, { type: 'g', props: {}, children: null }],
});
});
it('should render a subtitle', () => {
const layout = { showTitles: true, subtitle: 'foo' };
const [{ default: MockedHeader }] = aw.mock([
['**/Text.jsx', () => ({ children, ...p }) => <span {...p}>{children}</span>],
], ['../../src/components/Header']);
const tree = renderer.create(<MockedHeader layout={layout} />).toJSON();
const layout = { showTitles: true, subtitle: 'sub' };
const [{ default: Header }] = mock();
const tree = renderer.create(<Header layout={layout} />).toJSON();
expect(tree).to.eql({
type: 'div',
props: {
style: { background: 'transparent', padding: '0 8px' },
},
children: [{
type: 'span',
props: { block: true, nowrap: true, size: 'large' },
children: null,
}, {
type: 'span',
props: {
faded: true,
block: true,
nowrap: true,
size: 'small',
},
children: ['foo'],
}],
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 'g', props: {}, children: [{
type: 't', props: {}, children: ['sub'],
}],
}],
}, { type: 'g', props: {}, children: null }],
});
});
it('should render selection actions', () => {
const layout = { showTitles: false, qSelectionInfo: { qInSelections: true } };
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,
}] }],
});
});
});

View File

@@ -38,7 +38,7 @@ describe('<SelectionToolbar />', () => {
const item = st.state.items[3];
expect(item).to.containSubset({
key: 'confirm',
type: 'fade-button',
type: 'icon-button',
label: 'Confirm',
icon: 'tick',
});
@@ -52,7 +52,7 @@ describe('<SelectionToolbar />', () => {
const item = st.state.items[2];
expect(st.state.items[2]).to.containSubset({
key: 'cancel',
type: 'fade-button',
type: 'icon-button',
label: 'Cancel',
icon: 'close',
});
@@ -65,7 +65,7 @@ describe('<SelectionToolbar />', () => {
const item = st.state.items[1];
expect(st.state.items[1]).to.containSubset({
key: 'clear',
type: 'fade-button',
type: 'icon-button',
label: 'Clear',
icon: 'clear-selections',
});
@@ -123,7 +123,7 @@ describe('<SelectionToolbar />', () => {
const c = renderer.create(<STB sn={props.sn} />);
expect(c.toJSON()).to.eql({
type: 'div',
props: { className: 'a' },
props: {},
children: ['-true-', '-false-', '-false-', '-false-'],
});
});

View File

@@ -25,7 +25,6 @@
"node-event-emitter": "^0.0.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-leonardo-ui": "^0.15.0",
"react-test-renderer": "^16.8.6",
"react-window": "^1.8.1",
"react-window-infinite-loader": "^1.0.3"

View File

@@ -1,156 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import SelectionsBack from '@nebula.js/ui/icons/SelectionsBack';
import SelectionsForward from '@nebula.js/ui/icons/SelectionsForward';
import ClearSelections from '@nebula.js/ui/icons/ClearSelections';
import ButtonInline from '@nebula.js/ui/components/ButtonInline';
import Toolbar from '@nebula.js/ui/components/Toolbar';
import Grid from '@nebula.js/ui/components/Grid';
import Text from '@nebula.js/ui/components/Text';
import SelectedField from './SelectedField';
function collect(qSelectionObject, fields, state = '$') {
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) {
field.states.push(state);
field.selections.push(selection);
}
});
}
function getItems(layout) {
if (!layout) {
return [];
}
const fields = {};
if (layout.qSelectionObject) {
collect(layout.qSelectionObject, fields);
}
if (layout.alternateStates) {
layout.alternateStates.forEach(s => collect(s.qSelectionObject, fields, s.stateName));
}
return Object.keys(fields).map(key => fields[key]);
}
function MultiState({
field,
}) {
return (
<Grid
spacing="small"
styled={{
width: '148px',
justifyContent: 'space-between',
height: '48px',
boxSizing: 'border-box',
background: '$palette.background.default',
borderRight: '1px solid $palette.divider',
}}
>
<Grid vertical spacing="small" style={{ overflow: 'hidden' }}>
<Text size="small" weight="semibold" nowrap>{field.name}</Text>
</Grid>
</Grid>
);
}
export class AppSelections extends React.Component {
constructor(props) {
super(props);
this.state = {
forward: this.props.api.canGoForward(),
back: this.props.api.canGoBack(),
clear: this.props.api.canClear(),
items: getItems(this.props.api.layout()),
};
this.onBack = () => {
this.props.api.back();
};
this.onForward = () => {
this.props.api.forward();
};
this.onClear = () => {
this.props.api.clear();
};
this.apiChangeHandler = () => {
this.setState({
forward: this.props.api.canGoForward(),
back: this.props.api.canGoBack(),
clear: this.props.api.canClear(),
items: getItems(this.props.api.layout()),
});
};
}
componentDidMount() {
this.props.api.on('changed', this.apiChangeHandler);
}
componentWillUnmount() {
this.props.api.removeListener('changed', this.apiChangeHandler);
}
render() {
return (
<Toolbar style={{ backgroundColor: '#E5E5E5' }}>
<Grid spacing="none">
<Grid styled={{ background: '$palette.background.default', borderRight: '1px solid $palette.divider' }}>
<ButtonInline
style={{ marginRight: '8px' }}
disabled={!this.state.back}
onClick={this.onBack}
>
<SelectionsBack />
</ButtonInline>
<ButtonInline
style={{ marginRight: '8px' }}
disabled={!this.state.forward}
onClick={this.onForward}
>
<SelectionsForward />
</ButtonInline>
<ButtonInline
disabled={!this.state.clear}
onClick={this.onClear}
>
<ClearSelections />
</ButtonInline>
</Grid>
<Grid spacing="none">
{this.state.items.map(s => (s.states.length > 1 ? <MultiState field={s} /> : <SelectedField field={s} api={this.props.api} />))}
</Grid>
</Grid>
</Toolbar>
);
}
}
export default function mount({
element,
api,
}) {
ReactDOM.render(
<AppSelections
api={api}
/>,
element,
);
const unmount = () => {
ReactDOM.unmountComponentAtNode(element);
};
return () => {
unmount();
};
}

View File

@@ -1,7 +1,17 @@
import React from 'react';
import React, {
useMemo,
} from 'react';
import Grid from '@nebula.js/ui/components/Grid';
import themes from '@nebula.js/ui/theme';
import {
createTheme,
ThemeProvider,
StylesProvider,
createGenerateClassName,
} from '@nebula.js/ui/theme';
import {
Grid,
} from '@nebula.js/ui/components';
import Requirements from './Requirements';
import CError from './Error';
@@ -9,7 +19,12 @@ import Header from './Header';
import Footer from './Footer';
import Supernova from './Supernova';
import Placeholder from './Placeholder';
import SelectionToolbar from './SelectionToolbar';
const generateClassName = createGenerateClassName({
productionPrefix: 'n',
disableGlobal: true,
seed: 'nebula',
});
const showRequirements = (sn, layout) => {
if (!sn || !sn.generator || !sn.generator.qae || !layout || !layout.qHyperCube) {
@@ -42,48 +57,23 @@ const Content = ({ children }) => (
</div>
);
class Cell extends React.Component {
constructor(...args) {
super(...args);
const theme = themes('light');
this.styledClasses = ['nebulajs', ...theme.style({
fontSize: '$typography.medium.fontSize',
lineHeight: '$typography.medium.lineHeight',
fontWeight: '$typography.weight.regular',
fontFamily: '$typography.fontFamily',
color: '$palette.text.primary',
})].join(' ');
this.state = {};
}
componentDidCatch() {
this.setState({
error: {
message: 'Failed to render',
},
});
}
render() {
const {
objectProps,
userProps,
} = this.props;
const SN = (showRequirements(objectProps.sn, objectProps.layout) ? Requirements : Supernova);
const Comp = !objectProps.sn ? Placeholder : SN;
const err = objectProps.error || this.state.error;
return (
<div className={this.styledClasses} style={{ height: '100%' }}>
{
objectProps.sn
&& objectProps.layout.qSelectionInfo
&& objectProps.layout.qSelectionInfo.qInSelections
&& <SelectionToolbar sn={objectProps.sn} />
}
<Grid vertical style={{ height: '100%' }}>
<Header layout={objectProps.layout}>&nbsp;</Header>
<Grid.Item>
export default function Cell({
objectProps,
userProps,
themeDefinition = {},
}) {
const theme = useMemo(() => createTheme(themeDefinition), [JSON.stringify(themeDefinition)]);
const SN = (showRequirements(objectProps.sn, objectProps.layout) ? Requirements : Supernova);
const Comp = !objectProps.sn ? Placeholder : SN;
const err = objectProps.error || false;
return (
<StylesProvider generateClassName={generateClassName}>
<ThemeProvider theme={theme}>
<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>
</Grid>
<Grid item xs>
<Content>
{err
? (<CError {...err} />)
@@ -98,12 +88,10 @@ class Cell extends React.Component {
)
}
</Content>
</Grid.Item>
</Grid>
<Footer layout={objectProps.layout} />
</Grid>
</div>
);
}
</ThemeProvider>
</StylesProvider>
);
}
export default Cell;

View File

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

View File

@@ -1,15 +1,32 @@
import React from 'react';
import Text from '@nebula.js/ui/components/Text';
import {
Grid,
Typography,
} from '@nebula.js/ui/components';
import SelectionToolbar from './SelectionToolbar';
const Header = ({
layout,
}) => (
layout && layout.showTitles && (layout.title || layout.subtitle) ? (
<div style={{ background: 'transparent', padding: '0 8px' }}>
<Text size="large" nowrap block>{layout.title}</Text>
<Text faded size="small" nowrap block>{layout.subtitle}</Text>
</div>
) : null
);
sn,
}) => {
const showTitle = layout && layout.showTitles && !!layout.title;
const showSubtitle = layout && layout.showTitles && !!layout.subtitle;
const showInSelectionActions = sn && layout && layout.qSelectionInfo && layout.qSelectionInfo.qInSelections;
return (
<Grid container wrap="nowrap" style={{ flexGrow: 0, flexWrap: 'nowrap' }}>
<Grid item wrap="nowrap" style={{ flexGrow: 1, minWidth: 0 }}>
<Grid container direction="column" style={{ flexGrow: 1, flexWrap: 'nowrap', minWidth: 0 }}>
{showTitle && (<Typography variant="h6" noWrap>{layout.title}</Typography>)}
{showSubtitle && (<Typography variant="body2" noWrap>{layout.subtitle}</Typography>)}
</Grid>
</Grid>
<Grid item style={{ flexWrap: 'nowrap', whiteSpace: 'nowrap', minHeight: '32px' }}>
{showInSelectionActions && (<SelectionToolbar inline sn={sn} />)}
</Grid>
</Grid>
);
};
export default Header;

View File

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

View File

@@ -1,126 +0,0 @@
import React, {
useRef,
useState,
useMemo,
} 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 ButtonInline from '@nebula.js/ui/components/ButtonInline';
import Grid from '@nebula.js/ui/components/Grid';
import Text from '@nebula.js/ui/components/Text';
import ListBoxPopover from './listbox/ListBoxPopover';
export default function SelectedField({
field,
api,
theme = themes('light'),
}) {
const alignTo = useRef();
const [isActive, setIsActive] = useState(false);
const classes = useMemo(() => theme.style({
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
cursor: 'pointer',
'&:hover': {
background: '$palette.background.hover',
},
'&:focus': {
outline: 'none',
boxShadow: 'inset 0 0 0 2px $bluePale',
},
}), [theme]);
const toggleActive = () => setIsActive(!isActive);
const keyToggleActive = e => e.key === ' ' && setIsActive(!isActive);
const selection = field.selections[0];
const counts = selection.qStateCounts;
const green = (counts.qSelected + counts.qLocked) / selection.qTotal;
const white = counts.qAlternative / selection.qTotal;
const grey = (counts.qExcluded + counts.qLockedExcluded + counts.qSelectedExcluded) / selection.qTotal;
const numSelected = counts.qSelected + counts.qSelectedExcluded + counts.qLocked + counts.qLockedExcluded;
let label = '&nbsp;'; // FIXME translate
if (selection.qTotal === numSelected && selection.qTotal > 1) {
label = 'All';
} else if (numSelected > 1 && selection.qTotal) {
label = `${numSelected} of ${selection.qTotal}`;
} else {
label = selection.qSelectedFieldSelectionInfo.map(v => v.qName).join(', ');
}
if (field.states[0] !== '$') {
label = `${field.states[0]}: ${label}`;
}
return (
<Grid
spacing="small"
styled={{
position: 'relative',
width: '148px',
justifyContent: 'space-between',
background: '$palette.background.default',
borderRight: '1px solid $palette.divider',
}}
>
<Grid
vertical
spacing="small"
style={{ alignItems: 'normal', overflow: 'hidden', opacity: selection.qLocked ? '0.3' : '' }}
>
<Text size="small" weight="semibold" nowrap>{selection.qField}</Text>
<Text size="small" faded nowrap>{label}</Text>
</Grid>
<div
role="button"
ref={alignTo}
className={classes}
onClick={toggleActive}
onKeyDown={keyToggleActive}
tabIndex="0"
/>
{selection.qLocked ? (<Grid><Lock /></Grid>) : (
<Grid spacing="none">
<ButtonInline
onClick={(e) => { e.stopPropagation(); api.clearField(selection.qField, field.states[0]); }}
style={{ zIndex: 1 }}
>
<Remove />
</ButtonInline>
</Grid>
)}
<Grid
spacing="none"
style={{
height: '4px',
position: 'absolute',
bottom: '0',
left: '0',
width: '100%',
}}
>
<div style={{ background: '#6CB33F', height: '100%', width: `${green * 100}%` }} />
<div style={{ background: '#D8D8D8', height: '100%', width: `${white * 100}%` }} />
<div style={{ background: '#B4B4B4', height: '100%', width: `${grey * 100}%` }} />
</Grid>
{isActive && (
<ListBoxPopover
alignTo={alignTo}
show={isActive}
close={() => setIsActive(false)}
app={api.model}
fieldName={selection.qField}
stateName={field.states[0]}
/>
)}
</Grid>
);
}

View File

@@ -1,5 +1,4 @@
import React from 'react';
import themes from '@nebula.js/ui/theme';
import Item from './SelectionToolbarItem';
@@ -14,28 +13,13 @@ class Component extends React.Component {
clearable: api.canClear(),
};
const theme = themes('light');
this.styledClasses = theme.style({
position: 'absolute',
left: '0',
right: '0',
top: '-48px',
padding: '8px',
boxSizing: 'border-box',
background: '$palette.background.default',
display: 'flex',
justifyContent: 'flex-end',
});
const items = [];
// TODO - translations
items.push({
key: 'confirm',
type: 'fade-button',
type: 'icon-button',
label: 'Confirm',
// variant: 'success',
icon: 'tick',
enabled: () => this.state.confirmable,
action: () => api.confirm(props.sn.component),
@@ -43,9 +27,8 @@ class Component extends React.Component {
items.push({
key: 'cancel',
type: 'fade-button',
type: 'icon-button',
label: 'Cancel',
// variant: 'danger',
icon: 'close',
enabled: () => this.state.cancelable,
action: () => api.cancel(props.sn.component),
@@ -53,7 +36,7 @@ class Component extends React.Component {
items.push({
key: 'clear',
type: 'fade-button',
type: 'icon-button',
label: 'Clear',
icon: 'clear-selections',
enabled: () => this.state.clearable,
@@ -84,7 +67,7 @@ class Component extends React.Component {
render() {
return (
<div className={this.styledClasses}>
<div>
{this.state.items.map(itm => <Item key={itm.key} item={itm} isCustom={!!this.custom[itm.key]} />)}
</div>
);

View File

@@ -1,6 +1,9 @@
import React from 'react';
import ButtonInline from '@nebula.js/ui/components/ButtonInline';
import {
IconButton,
Button,
} from '@nebula.js/ui/components';
import CloseIcon from '@nebula.js/ui/icons/Close';
import TickIcon from '@nebula.js/ui/icons/Tick';
@@ -16,7 +19,7 @@ export default class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
active: props.item.active && props.item.active(),
// active: props.item.active && props.item.active(),
disabled: props.item.enabled && !props.item.enabled(),
};
}
@@ -31,7 +34,7 @@ export default class Item extends React.Component {
if (this.props.item.active && this.props.item.action && this.props.item.on) {
this.onChange = () => {
this.setState({
active: this.props.item.active && this.props.item.active(),
// active: this.props.item.active && this.props.item.active(),
disabled: this.props.item.enabled && !this.props.item.enabled(),
});
};
@@ -48,16 +51,26 @@ export default class Item extends React.Component {
}
render() {
const props = this.props.item;
const Icon = ICONS[props.icon] || '';
return (
<ButtonInline
onClick={() => props.action()}
active={this.state.active || false}
const { item } = this.props;
const Icon = ICONS[item.icon] || '';
return item.type === 'button' ? (
<Button
title={item.label}
variant="contained"
style={{ backgroundColor: item.color }}
onClick={() => item.action()}
disabled={this.state.disabled}
>
{Icon && <Icon />}
</ButtonInline>
</Button>
) : (
<IconButton
title={item.label}
onClick={() => item.action()}
disabled={this.state.disabled}
>
{Icon && <Icon />}
</IconButton>
);
}
}

View File

@@ -5,21 +5,18 @@ import React, {
useState,
useCallback,
useRef,
useMemo,
// useMemo,
} from 'react';
import { FixedSizeList } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import themes from '@nebula.js/ui/theme';
import useLayout from '../../hooks/useLayout';
import Row from './ListBoxRow';
export default function ListBox({
model,
theme = themes('light'),
}) {
const [layout] = useLayout(model);
const [pages, setPages] = useState(null);
@@ -33,12 +30,8 @@ export default function ListBox({
pages: [],
});
const listClass = useMemo(() => theme.style({
backgroundColor: '$palette.background.lightest',
}), [theme]);
const onClick = useCallback((e) => {
const elemNumber = +e.target.getAttribute('data-n');
const elemNumber = +e.currentTarget.getAttribute('data-n');
if (!Number.isNaN(elemNumber)) {
model.selectListObjectValues('/qListObjectDef', [elemNumber], true);
}
@@ -113,7 +106,7 @@ export default function ListBox({
return (
<FixedSizeList
useIsScrolling
className={listClass}
style={{}}
height={8 * ITEM_HEIGHT}
itemCount={count}
itemData={{ onClick, pages }}

View File

@@ -4,8 +4,12 @@ import React, {
import Lock from '@nebula.js/ui/icons/Lock';
import Unlock from '@nebula.js/ui/icons/Unlock';
import ButtonInline from '@nebula.js/ui/components/ButtonInline';
import Popover from '@nebula.js/ui/components/popover';
import {
IconButton,
Popover,
Grid,
} from '@nebula.js/ui/components';
import useModel from '../../hooks/useModel';
import useLayout from '../../hooks/useLayout';
@@ -58,27 +62,41 @@ export default function ListBoxPopover({
const isLocked = layout ? layout.qListObject.qDimensionInfo.qLocked === true : false;
const open = show && Boolean(alignTo.current);
return (
<Popover
onOutside={close}
alignTo={alignTo.current}
show={alignTo.current && show}
open={open}
onClose={close}
anchorEl={alignTo.current}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
PaperProps={{
style: { minWidth: '250px' },
}}
>
<Popover.Header>
{isLocked ? (
<ButtonInline onClick={unlock}>
<Unlock />
</ButtonInline>
) : (
<ButtonInline onClick={lock}>
<Lock />
</ButtonInline>
)
}
</Popover.Header>
<Popover.Body>
<ListBox model={model} />
</Popover.Body>
<Grid container direction="column" spacing={0}>
<Grid item>
{isLocked ? (
<IconButton onClick={unlock}>
<Unlock />
</IconButton>
) : (
<IconButton onClick={lock}>
<Lock />
</IconButton>
)}
</Grid>
<Grid item xs>
<ListBox model={model} />
</Grid>
</Grid>
</Popover>
);
}

View File

@@ -1,45 +1,51 @@
import React, { useMemo } from 'react';
import React from 'react';
import {
Grid,
Typography,
} from '@nebula.js/ui/components';
import { makeStyles } from '@nebula.js/ui/theme';
import themes from '@nebula.js/ui/theme';
import Text from '@nebula.js/ui/components/Text';
import Grid from '@nebula.js/ui/components/Grid';
import Lock from '@nebula.js/ui/icons/Lock';
import Tick from '@nebula.js/ui/icons/Tick';
const useStyles = makeStyles(theme => ({
row: {
flexWrap: 'nowrap',
borderBottom: `1px solid ${theme.palette.divider}`,
},
cell: {
padding: '8px',
whiteSpace: 'nowrap',
fontSize: '12px',
lineHeight: '16px',
},
icon: {
padding: theme.spacing(1),
},
D: {
background: '#fff',
},
S: {
background: '#6CB33F',
color: '#fff',
},
A: {
background: '#fafafa',
},
X: {
background: '#eee',
},
}));
export default function Row({
index,
style,
data,
theme = themes('light'),
}) {
const {
D,
S,
A,
X,
} = useMemo(() => ({
D: theme.style({
boxSizing: 'border-box',
borderBottom: '1px solid $palette.divider',
background: '$palette.background.default',
color: '$palette.text.primary',
justifyContent: 'space-between',
'&:focus': {
outline: 'none',
boxShadow: 'inset 0 0 0 2px $bluePale',
},
}),
S: theme.style({
background: '$palette.green',
color: '$palette.grey.100',
}),
A: theme.style({
background: '$palette.grey.85',
}),
X: theme.style({
background: '$palette.grey.70',
}),
}), [theme]);
const classes = useStyles();
const classArr = [classes.row];
let label = '';
const { onClick, pages } = data;
@@ -53,7 +59,6 @@ export default function Row({
}
}
}
const classes = [D];
let locked = false;
let selected = false;
if (cell) {
@@ -61,27 +66,33 @@ export default function Row({
locked = cell.qState === 'L' || cell.qState === 'XL';
selected = cell.qState === 'S' || cell.qState === 'XS';
if (cell.qState === 'S' || cell.qState === 'L') {
classes.push(S);
classArr.push(classes.S);
} else if (cell.qState === 'A') {
classes.push(A);
classArr.push(classes.A);
} else if (cell.qState === 'X' || cell.qState === 'XS' || cell.qState === 'XL') {
classes.push(X);
classArr.push(classes.X);
}
}
return (
<Grid
className={classes.join(' ')}
container
spacing={0}
className={classArr.join(' ')}
style={style}
onClick={onClick}
role="row"
tabIndex={0}
data-n={cell && cell.qElemNumber}
>
<Text nowrap>
{cell ? `${label}` : '' }
</Text>
{locked && (<Lock size="small" />)}
{selected && (<Tick size="small" />)}
<Grid item style={{ minWidth: 0, flexGrow: 1 }}>
<Typography className={classes.cell} noWrap>
{cell ? `${label}` : '' }
</Typography>
</Grid>
<Grid item className={classes.icon}>
{locked && (<Lock size="small" />)}
{selected && (<Tick size="small" />)}
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,78 @@
import React, {
useMemo,
} from 'react';
import ReactDOM from 'react-dom';
import {
Grid,
} from '@nebula.js/ui/components';
import {
createTheme,
ThemeProvider,
StylesProvider,
createGenerateClassName,
} from '@nebula.js/ui/theme';
import SelectedFields from './SelectedFields';
import Nav from './Nav';
const generateClassName = createGenerateClassName({
productionPrefix: 'n',
disableGlobal: true,
seed: 'nebula',
});
export function AppSelections({
api,
}) {
const theme = useMemo(() => createTheme(), []);
return (
<StylesProvider generateClassName={generateClassName}>
<ThemeProvider theme={theme}>
<Grid
container
spacing={0}
wrap="nowrap"
style={{
backgroundColor: '#fff',
minHeight: '40px',
}}
>
<Grid
item
style={{
borderRight: `1px solid ${theme.palette.divider}`,
}}
>
<Nav api={api} />
</Grid>
<Grid item xs style={{ backgroundColor: '#E5E5E5', overflow: 'hidden' }}>
<SelectedFields api={api} />
</Grid>
</Grid>
</ThemeProvider>
</StylesProvider>
);
}
export default function mount({
element,
api,
}) {
ReactDOM.render(
<AppSelections
api={api}
/>,
element,
);
const unmount = () => {
ReactDOM.unmountComponentAtNode(element);
};
return () => {
unmount();
};
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import {
// IconButton,
Grid,
Typography,
} from '@nebula.js/ui/components';
export default function MultiState({
field,
}) {
return (
<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>
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,76 @@
import React, {
useEffect,
useState,
} from 'react';
import {
IconButton,
Grid,
} from '@nebula.js/ui/components';
import SelectionsBack from '@nebula.js/ui/icons/SelectionsBack';
import SelectionsForward from '@nebula.js/ui/icons/SelectionsForward';
import ClearSelections from '@nebula.js/ui/icons/ClearSelections';
export default function Nav({
api,
}) {
const [state, setState] = useState({
forward: api.canGoForward(),
back: api.canGoBack(),
clear: api.canClear(),
});
useEffect(() => {
if (!api) {
return undefined;
}
const onChange = () => setState({
forward: api.canGoForward(),
back: api.canGoBack(),
clear: api.canClear(),
});
api.on('changed', onChange);
return () => {
api.removeListener('changed', onChange);
};
}, [api]);
return (
<Grid
container
wrap="nowrap"
style={{
height: '100%',
alignItems: 'center',
}}
>
<Grid item>
<IconButton
style={{ marginRight: '8px' }}
disabled={!state.back}
onClick={() => api.back()}
>
<SelectionsBack />
</IconButton>
</Grid>
<Grid item>
<IconButton
style={{ marginRight: '8px' }}
disabled={!state.forward}
onClick={() => api.forward()}
>
<SelectionsForward />
</IconButton>
</Grid>
<Grid item>
<IconButton
disabled={!state.clear}
onClick={() => api.clear()}
>
<ClearSelections />
</IconButton>
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,136 @@
import React, {
useRef,
useState,
// useMemo,
} 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 { makeStyles } from '@nebula.js/ui/theme';
import ListBoxPopover from '../listbox/ListBoxPopover';
const useStyles = makeStyles(() => ({
item: {
cursor: 'pointer',
padding: '4px',
'&:hover': {
backgroundColor: '#eee',
},
},
}));
export default function OneField({
field,
api,
// theme = themes('light'),
}) {
const alignTo = useRef();
const [isActive, setIsActive] = useState(false);
const classes = useStyles();
const toggleActive = (e) => {
if (e.currentTarget.contains(e.target)) { // because click in popover will propagate to parent
setIsActive(!isActive);
}
};
const selection = field.selections[0];
const counts = selection.qStateCounts;
const green = (counts.qSelected + counts.qLocked) / selection.qTotal;
const white = counts.qAlternative / selection.qTotal;
const grey = (counts.qExcluded + counts.qLockedExcluded + counts.qSelectedExcluded) / selection.qTotal;
const numSelected = counts.qSelected + counts.qSelectedExcluded + counts.qLocked + counts.qLockedExcluded;
let label = '&nbsp;'; // FIXME translate
if (selection.qTotal === numSelected && selection.qTotal > 1) {
label = 'All';
} else if (numSelected > 1 && selection.qTotal) {
label = `${numSelected} of ${selection.qTotal}`;
} else {
label = selection.qSelectedFieldSelectionInfo.map(v => v.qName).join(', ');
}
if (field.states[0] !== '$') {
label = `${field.states[0]}: ${label}`;
}
const segments = [
{ color: '#6CB33F', ratio: green },
{ color: '#D8D8D8', ratio: white },
{ color: '#B4B4B4', ratio: grey },
];
segments.forEach((s, i) => {
s.offset = i ? segments[i - 1].offset + segments[i - 1].ratio : 0; // eslint-disable-line
});
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>
{selection.qLocked ? (<Grid item><IconButton><Lock /></IconButton></Grid>) : (
<Grid item>
<IconButton
onClick={(e) => { e.stopPropagation(); api.clearField(selection.qField, field.states[0]); }}
style={{ zIndex: 1 }}
>
<Remove />
</IconButton>
</Grid>
)}
<div
style={{
height: '4px',
position: 'absolute',
bottom: '0',
left: '0',
width: '100%',
}}
>
{segments.map(s => (
<div
key={s.color}
style={{
position: 'absolute',
background: s.color,
height: '100%',
top: 0,
width: `${s.ratio * 100}%`,
left: `${s.offset * 100}%`,
}}
/>
))}
</div>
{isActive && (
<ListBoxPopover
alignTo={alignTo}
show={isActive}
close={() => setIsActive(false)}
app={api.model}
fieldName={selection.qField}
stateName={field.states[0]}
/>
)}
</Grid>
);
}

View File

@@ -0,0 +1,81 @@
import React, {
useEffect,
useState,
} from 'react';
import {
Grid,
} from '@nebula.js/ui/components';
import { useTheme } from '@nebula.js/ui/theme';
import OneField from './OneField';
import MultiState from './MultiState';
function collect(qSelectionObject, fields, state = '$') {
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) {
field.states.push(state);
field.selections.push(selection);
}
});
}
function getItems(layout) {
if (!layout) {
return [];
}
const fields = {};
if (layout.qSelectionObject) {
collect(layout.qSelectionObject, fields);
}
if (layout.alternateStates) {
layout.alternateStates.forEach(s => collect(s.qSelectionObject, fields, s.stateName));
}
return Object.keys(fields).map(key => fields[key]);
}
export default function SelectedFields({
api,
}) {
const [state, setState] = useState({
items: getItems(api.layout()),
});
const theme = useTheme();
useEffect(() => {
if (!api) {
return undefined;
}
const onChange = () => setState({
items: getItems(api.layout()),
});
api.on('changed', onChange);
return () => {
api.removeListener('changed', onChange);
};
}, [api]);
return (
<Grid container spacing={0} wrap="nowrap">
{state.items.map(s => (
<Grid
item
key={`${s.states.join('::')}::${s.name}`}
style={{
position: 'relative',
maxWidth: '240px',
minWidth: '96px',
background: '#fff',
borderRight: `1px solid ${theme.palette.divider}`,
}}
>
{(s.states.length > 1 ? <MultiState field={s} /> : <OneField field={s} api={api} />)}
</Grid>
))}
</Grid>
);
}

View File

@@ -0,0 +1,61 @@
/* eslint object-property-newline:0 */
import React from 'react';
import renderer from 'react-test-renderer';
const mock = ({
components = {
Grid: ({ children }) => <g>{children}</g>,
},
theme = {
createTheme: () => ({}),
ThemeProvider: ({ children }) => <tp>{children}</tp>,
StylesProvider: ({ children }) => <sp>{children}</sp>,
createGenerateClassName: () => () => 'gen',
},
SelectedFields = () => <sf />,
Nav = () => <nav />,
} = {}) => aw.mock([
['**/ui/components/index.js', () => components],
['**/ui/theme/index.js', () => theme],
['**/SelectedFields.jsx', () => SelectedFields],
['**/Nav.jsx', () => Nav],
], ['../AppSelections']);
describe('<AppSelections />', () => {
let api;
beforeEach(() => {
api = {
canGoForward: () => 'canGoForward',
canGoBack: () => 'canGoBack',
canClear: () => 'canClear',
layout: () => (null),
back: sinon.spy(),
forward: sinon.spy(),
clear: sinon.spy(),
on: sinon.spy(),
removeListener: sinon.spy(),
};
});
it.skip('should render a toolbar', () => {
api.canGoBack = () => false;
const [{ AppSelections: AS }] = mock();
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,
}],
}],
}],
}],
});
});
});

View File

@@ -1,6 +1,6 @@
/* eslint no-underscore-dangle: 0 */
import eventmixin from './event-mixin';
import visual from '../components/AppSelections';
import visual from '../components/selections/AppSelections';
import modelCache from '../object/model-cache';
import { observe } from '../object/observer';

View File

@@ -1,4 +1,5 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const cfg = ({
@@ -38,7 +39,8 @@ const cfg = ({
sideEffects: false,
include: [
srcDir,
/react-leonardo-ui/,
/nucleus/,
/ui\/icons/,
],
use: {
loader: 'babel-loader',
@@ -72,7 +74,7 @@ const cfg = ({
filename: 'eHub.html',
chunks: ['eHub'],
}),
// new webpack.HotModuleReplacementPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
};

View File

@@ -6,7 +6,7 @@ const cfg = ({
snPath = path.resolve(__dirname, 'placeholder'),
}) => {
const config = {
mode: 'development',
mode: 'production',
entry: path.resolve(__dirname, './sn.js'),
devtool: 'source-map',
output: {

View File

@@ -5,9 +5,6 @@ const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const nm = require.resolve('leonardo-ui');
const nmPath = nm.substring(0, nm.lastIndexOf('node_modules') + 12);
module.exports = async ({
host,
port,
@@ -53,7 +50,6 @@ module.exports = async ({
open: true,
contentBase: [
contentBase,
nmPath,
],
historyApiFallback: {
index: '/eHub.html',

View File

@@ -17,7 +17,7 @@
"dist"
],
"scripts": {
"build": "webpack --config ./lib/webpack.build.js",
"build": "cross-env NODE_ENV=production webpack --config ./lib/webpack.build.js",
"lint": "eslint web",
"prepublishOnly": "rm -rf dist && npm run build"
},
@@ -26,7 +26,6 @@
"chalk": "^2.4.2",
"execa": "^1.0.0",
"html-webpack-plugin": "^3.2.0",
"leonardo-ui": "1.6.0",
"portfinder": "^1.0.20",
"webpack": "^4.29.3",
"webpack-dev-server": "^3.1.14",
@@ -36,12 +35,13 @@
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"@material-ui/core": "^4.0.0-beta.0",
"@nebula.js/nucleus": "0.1.0-alpha.9",
"@nebula.js/ui": "0.1.0-alpha.9",
"babel-loader": "^8.0.5",
"enigma.js": "^2.4.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-leonardo-ui": "^0.15.0",
"webpack-cli": "^3.3.1"
}
}

View File

@@ -2,25 +2,31 @@ import React, {
useEffect,
useState,
useRef,
useMemo,
} from 'react';
import {
Button,
} from 'react-leonardo-ui';
import nucleus from '@nebula.js/nucleus';
import snDefinition from 'snDefinition'; // eslint-disable-line
import {
Toolbar,
createTheme,
ThemeProvider,
} from '@nebula.js/ui/theme';
import {
Grid,
Panel,
} from '../ui-components';
Toolbar,
Button,
Divider,
} from '@nebula.js/ui/components';
import Properties from './Properties';
import Stage from './Stage';
import AppContext from '../contexts/AppContext';
import NebulaContext from '../contexts/NebulaContext';
const defaultTheme = createTheme();
export default function App({
app,
@@ -30,50 +36,66 @@ export default function App({
const [sn, setSupernova] = useState(null);
const sel = useRef(null);
const nebbie = useMemo(() => nucleus(app)
.load((type, config) => config.Promise.resolve(snDefinition)), [app]);
nebbie.types.supernova(info.supernova.name).then(setSupernova);
useEffect(() => {
const nebbie = nucleus(app)
.load((type, config) => config.Promise.resolve(snDefinition));
nebbie.types.supernova(info.supernova.name).then(setSupernova);
nebbie.selections().mount(sel.current);
const create = () => {
nebbie.create({
type: info.supernova.name,
}, {}).then((v) => {
v.context({
permissions: ['passive', 'interact', 'select', 'fetch'],
});
setViz(v);
});
nebbie.create({
type: info.supernova.name,
fields: [],
}, {
context: {
permissions: ['passive', 'interact', 'select', 'fetch'],
},
properties: {
qInfo: {
qId: '__temporary_nebula__',
qType: info.supernova.name,
},
},
}).then(setViz);
const unload = () => {
app.destroySessionObject('__temporary_nebula__');
};
const render = () => {
create();
window.addEventListener('beforeunload', unload);
return () => {
window.removeEventListener('beforeunload', unload);
};
render();
}, []);
return (
<AppContext.Provider value={app}>
<Grid vertical noSpacing>
<Toolbar style={{ backgroundColor: '#fff' }}>
<a href={window.location.origin}>
<Button>
<Button.Icon name="arrow-left" />
<Button.Text>Hub</Button.Text>
</Button>
</a>
</Toolbar>
<div ref={sel} style={{ flex: '0 0 auto' }} />
<Grid noSpacing className="content">
<Stage viz={viz} sn={sn} />
<Panel>
<Properties sn={sn} model={viz && viz.model} />
</Panel>
</Grid>
</Grid>
<ThemeProvider theme={defaultTheme}>
<NebulaContext.Provider value={nebbie}>
<Grid container wrap="nowrap" direction="column">
<Grid item>
<Toolbar variant="dense" style={{ background: '#fff' }}>
<Button variant="outlined" href={window.location.origin}>
&lt; Hub
</Button>
</Toolbar>
<Divider />
</Grid>
<Grid item>
<div ref={sel} style={{ flex: '0 0 auto' }} />
<Divider />
</Grid>
<Grid item xs>
<Grid container style={{ height: '100%' }}>
<Grid item xs>
<Stage viz={viz} />
</Grid>
<Grid item style={{ background: '#fff' }}>
<Properties sn={sn} viz={viz} />
</Grid>
</Grid>
</Grid>
</Grid>
</NebulaContext.Provider>
</ThemeProvider>
</AppContext.Provider>
);
}

View File

@@ -0,0 +1,29 @@
import React from 'react';
import {
Grid,
Card,
} from '@nebula.js/ui/components';
import Chart from './Chart';
export default function ({
object,
onSelected,
}) {
return (
<Card style={{ minHeight: 400, position: 'relative', overflow: 'visible' }}>
<Grid
container
style={{
position: 'absolute',
height: '100%',
}}
>
<Grid item xs={12}>
<Chart id={object.qInfo.qId} onSelected={onSelected} />
</Grid>
</Grid>
</Card>
);
}

View File

@@ -0,0 +1,44 @@
import React, {
useEffect,
useContext,
useRef,
// useState,
} from 'react';
import NebulaContext from '../contexts/NebulaContext';
export default function Chart({
id,
// onSelected,
}) {
const nebbie = useContext(NebulaContext);
const el = useRef();
// const [viz, setViz] = useState(null);
useEffect(() => {
const n = nebbie.get({
id,
}, {
context: {
permissions: ['passive', 'interact', 'select', 'fetch'],
},
element: el.current,
});
// n.then(setViz);
return () => {
n.then((v) => {
v.close();
// v.unmount();
});
};
}, [id]);
return (
<div
ref={el}
// onClick={() => onSelected(viz)}
style={{
height: '100%',
}}
/>
);
}

View File

@@ -0,0 +1,45 @@
import React, {
useEffect,
useContext,
useState,
} from 'react';
import useLayout from '@nebula.js/nucleus/src/hooks/useLayout';
import {
Grid,
} from '@nebula.js/ui/components';
import AppContext from '../contexts/AppContext';
import Cell from './Cell';
export default function Collection({
type,
onSelectedCell,
}) {
const app = useContext(AppContext);
const [layout] = useLayout(app);
const [objects, setObjects] = useState([]);
useEffect(() => {
app.getObjects({
qTypes: [type],
qIncludeSessionObjects: true,
qData: {
title: '/qMetaDef/title',
},
}).then((list) => {
setObjects(list);
});
}, [layout, type]);
return (
<Grid container spacing={2} style={{ padding: '12px' }}>
{objects.map(c => (
<Grid item xs={12} md={6} lg={4} key={c.qInfo.qId}>
<Cell object={c} onSelected={onSelectedCell} />
</Grid>
))}
</Grid>
);
}

View File

@@ -1,56 +0,0 @@
import React, {
useContext,
useEffect,
useState,
} from 'react';
import {
List,
Icon,
} from 'react-leonardo-ui';
import useModel from '@nebula.js/nucleus/src/hooks/useModel';
import useLayout from '@nebula.js/nucleus/src/hooks/useLayout';
import AppContext from '../contexts/AppContext';
const Field = ({ qName }) => (
<List.Item key={qName}>
<List.Text>{qName}</List.Text>
<List.Aside>
<Icon name="draggable" />
</List.Aside>
</List.Item>
);
export default function FieldList() {
const app = useContext(AppContext);
const [fields, setFields] = useState([]);
const [model] = useModel({
qInfo: {
qType: 'FieldList',
qId: 'FieldList',
},
qFieldListDef: {
qShowDerivedFelds: false,
qShowHidden: false,
qShowSemantic: true,
qShowSrcTables: true,
qShowSystem: false,
},
}, [app && app.id]);
const [layout] = useLayout(model, app);
useEffect(() => {
setFields(layout.qFieldList.qItems);
}, [layout]);
return (
<List>
<List.Header>Fields</List.Header>
{fields.map(Field)}
</List>
);
}

View File

@@ -4,23 +4,22 @@ import React, {
import {
Popover,
Input,
List,
} from 'react-leonardo-ui';
ListSubheader,
ListItem,
ListItemText,
} from '@nebula.js/ui/components';
import useModel from '@nebula.js/nucleus/src/hooks/useModel';
import useLayout from '@nebula.js/nucleus/src/hooks/useLayout';
import {
Label,
} from '../ui-components';
import AppContext from '../contexts/AppContext';
const Field = ({ field, onSelect }) => (
<List.Item onClick={() => onSelect(field.qName)} data-key={field.qName}>
<List.Text>{field.qName}</List.Text>
</List.Item>
<ListItem button onClick={() => onSelect(field.qName)} data-key={field.qName}>
<ListItemText>{field.qName}</ListItemText>
</ListItem>
);
export default function FieldsPopover({
@@ -46,15 +45,6 @@ export default function FieldsPopover({
const [layout] = useLayout(model, app);
const onConfirm = (e) => {
if (e.key === 'Enter') {
onSelected({
field: e.target.value,
});
close();
}
};
const fields = layout ? (layout.qFieldList.qItems || []) : [];
const onSelect = (s) => {
@@ -66,21 +56,27 @@ export default function FieldsPopover({
return (
<Popover
onOutside={close}
alignTo={alignTo.current ? alignTo.current.element : null}
show={show}
open={show}
onClose={close}
anchorEl={alignTo.current}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
PaperProps={{
style: { minWidth: '250px' },
}}
>
<Popover.Header>
<Input onKeyPress={onConfirm} />
</Popover.Header>
<Popover.Body nopad>
{fields.length > 0 && (
<List>
<Label>From a field</Label>
{fields.map(field => <Field key={field.qName} field={field} onSelect={onSelect} />)}
</List>
)}
</Popover.Body>
{fields.length > 0 && (
<List dense>
<ListSubheader>Fields</ListSubheader>
{fields.map(field => <Field key={field.qName} field={field} onSelect={onSelect} />)}
</List>
)}
</Popover>
);
}

View File

@@ -1,13 +1,19 @@
import React, { useRef, useState } from 'react';
import { Button, List, Icon } from 'react-leonardo-ui';
import useProperties from '@nebula.js/nucleus/src/hooks/useProperties';
import {
Toolbar,
Grid,
Label,
} from '../ui-components';
IconButton,
Button,
List,
ListItem,
ListItemText,
ListItemSecondaryAction,
Divider,
Typography,
} from '@nebula.js/ui/components';
import Remove from '@nebula.js/ui/icons/Remove';
import useProperties from '@nebula.js/nucleus/src/hooks/useProperties';
import FieldsPopover from './FieldsPopover';
@@ -24,7 +30,8 @@ const Fields = ({
label: 'Dimensions',
add: 'Add dimension',
title(dim) {
return dim.qDef.qFieldDefs[0];
// TODO - get library item
return dim.qLibraryId || dim.qDef.qFieldDefs[0];
},
def(f) {
return {
@@ -38,7 +45,7 @@ const Fields = ({
label: 'Measures',
add: 'Add measure',
title(m) {
return m.qDef.qDef;
return m.qLibraryId || m.qDef.qDef;
},
def(f) {
return {
@@ -67,24 +74,25 @@ const Fields = ({
return (
<div>
<Label weight="semibold">{t.label}</Label>
<List>
<Typography variant="overline">{t.label}</Typography>
<List dense>
{arr.map((d, i) => (
<List.Item>
<List.Text>{t.title(d)}</List.Text>
<List.Aside onClick={() => onRemove(i)}><Icon name="remove" /></List.Aside>
</List.Item>
<ListItem>
<ListItemText>{t.title(d)}</ListItemText>
<ListItemSecondaryAction>
<IconButton onClick={() => onRemove(i)}><Remove /></IconButton>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
<Grid>
<Button
onClick={onAdd}
active={isActive}
ref={btn}
>
{t.add}
</Button>
</Grid>
<Button
variant="outlined"
fullWidth
onClick={onAdd}
ref={btn}
>
{t.add}
</Button>
{isActive && (
<FieldsPopover
alignTo={btn}
@@ -98,22 +106,36 @@ const Fields = ({
};
export default function Properties({
model,
viz,
sn,
}) {
const [properties] = useProperties(model);
const [properties] = useProperties(viz ? viz.model : null);
if (!sn || !properties) {
if (!sn) {
return null;
}
if (!viz || !properties) {
return (
<div style={{
minWidth: '250px',
padding: '8px',
}}
>
<Typography>Nothing selected</Typography>
</div>
);
}
return (
<div>
<Toolbar>
<Label weight="semibold">Properties</Label>
</Toolbar>
<Fields properties={properties} model={model} type="dimension" />
<Fields properties={properties} model={model} type="measure" />
<div style={{
minWidth: '250px',
padding: '8px',
}}
>
<Fields properties={properties} model={viz.model} type="dimension" />
<Divider />
<Fields properties={properties} model={viz.model} type="measure" />
</div>
);
}

View File

@@ -8,7 +8,10 @@ import React, {
import {
Button,
Dialog,
} from 'react-leonardo-ui';
DialogTitle,
DialogContent,
DialogActions,
} from '@nebula.js/ui/components';
export default function PropertiesDialog({
model,
@@ -46,17 +49,17 @@ export default function PropertiesDialog({
}, [model && model.id, show]);
return (
<Dialog show={show}>
<Dialog.Header>
<Dialog.Title>Modify object properties</Dialog.Title>
</Dialog.Header>
<Dialog.Body>
<Dialog open={show} scroll="paper" maxWidth="lg" onClose={close}>
<DialogTitle>
Modify object properties
</DialogTitle>
<DialogContent dividers>
<textarea value={objectProps} onChange={onChange} ref={text} cols="100" rows="40" />
</Dialog.Body>
<Dialog.Footer>
<Button onClick={close}>Cancel</Button>
<Button variant="success" onClick={onConfirm}>Confirm</Button>
</Dialog.Footer>
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={close}>Cancel</Button>
<Button variant="outlined" onClick={onConfirm}>Confirm</Button>
</DialogActions>
</Dialog>
);
}

View File

@@ -4,10 +4,16 @@ import React, {
useCallback,
useState,
} from 'react';
import { Button } from 'react-leonardo-ui';
import {
Button,
Grid,
Card,
Toolbar,
Divider,
} from '@nebula.js/ui/components';
import PropsDialog from './PropertiesDialog';
import { Grid, Toolbar, Card } from '../ui-components';
export default function Stage({
viz,
@@ -21,23 +27,30 @@ export default function Stage({
viz && viz.mount(c.current);
}, [viz]);
const closeDialog = useCallback(() => { setDialogOpen(false); });
const closeDialog = useCallback(() => { setDialogOpen(false); }, []);
return (
<Card>
<Grid vertical noSpacing style={{ height: '100%' }}>
<Toolbar style={{ textAlign: 'right' }}>
<Button onClick={() => setDialogOpen(true)} disabled={!model}>Props</Button>
{model && (
<PropsDialog
show={dialogOpen}
close={closeDialog}
model={model}
/>
)}
</Toolbar>
<div ref={c} style={{ position: 'relative' }} />
</Grid>
</Card>
<div style={{ padding: '12px', height: '100%', boxSizing: 'border-box' }}>
<Card style={{ height: '100%' }}>
<Grid container direction="column" style={{ height: '100%' }}>
<Grid item>
<Toolbar>
<Button
variant="outlined"
disabled={!model}
onClick={() => setDialogOpen(true)}
>
Props
</Button>
<PropsDialog model={model} show={dialogOpen} close={closeDialog} />
</Toolbar>
<Divider />
</Grid>
<Grid item xs>
<div ref={c} style={{ position: 'relative', height: '100%' }} />
</Grid>
</Grid>
</Card>
</div>
);
}

View File

@@ -1,5 +1,5 @@
import enigma from 'enigma.js';
import qixSchema from 'enigma.js/schemas/3.2.json';
import qixSchema from 'enigma.js/schemas/12.34.11.json';
import SenseUtilities from 'enigma.js/sense-utilities';
const params = (() => {

View File

@@ -0,0 +1,5 @@
import React from 'react';
const NebulaContext = React.createContext();
export default NebulaContext;

View File

@@ -5,7 +5,9 @@
<base href="/">
<title>Nebula mashup</title>
<link rel="stylesheet" href="leonardo-ui/dist/leonardo-ui.css" type="text/css"/>
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:light" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:semibold" rel="stylesheet">
<style>
html, body {
@@ -17,46 +19,17 @@
body {
background: #ddd;
color: #666;
font: normal 14px/16px "Source Sans Pro", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
@font-face {
font-family: "LUI icons";
src: url(leonardo-ui/dist/lui-icons.woff) format('woff'),
url(leonardo-ui/dist/lui-icons.ttf) format('truetype');
overflow: auto;
}
#app, #app > * {
height: 100%;
}
.nebula-grid {
display: flex;
padding: 0 8px;
}
.nebula-grid > * {
flex: 1 0 auto;
margin: 8px 0;
}
.nebula-grid.no-spacing {
padding: 0;
}
.nebula-grid.no-spacing > * {
margin: 0;
}
.nebula-grid.vertical {
flex-direction: column;
}
textarea {
font-family: Monaco, monospace;
color: #333;
color: #404040;
border-color: #ccc;
}

View File

@@ -5,8 +5,6 @@
<base href="/">
<title>Nebula stripped</title>
<link rel="stylesheet" href="leonardo-ui/dist/leonardo-ui.css" type="text/css"/>
<style>
html, body {
height: 100%;
@@ -17,17 +15,10 @@
body {
background: linear-gradient(110deg, #92498F 0%, #45B3B2 100%);
color: #666;
font: normal 14px/16px "Source Sans Pro", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
@font-face {
font-family: "LUI icons";
src: url(leonardo-ui/dist/lui-icons.woff) format('woff'),
url(leonardo-ui/dist/lui-icons.ttf) format('truetype');
}
#chart-container {
position: absolute;
left: 8px;

View File

@@ -27,6 +27,9 @@ serverInfo.then(info => openApp(params.app).then((app) => {
fields: params.cols || [],
}, {
element: document.querySelector('#chart-container'),
context: {
permissions: params.permissions || [],
},
});
};

View File

@@ -1,20 +0,0 @@
import React from 'react';
export default function Card({
children,
style,
}) {
const inlineStyle = {
backgroundColor: '#fff',
borderRadius: '4px',
border: '1px solid #ccc',
boxShadow: '0px 16px 8px -8px rgba(100, 100, 100, 0.2)',
margin: '8px',
...style,
};
return (
<div style={inlineStyle}>
{children}
</div>
);
}

View File

@@ -1,20 +0,0 @@
import React from 'react';
export default function Grid({
children,
vertical,
noSpacing,
style,
}) {
const classNames = [
'nebula-grid',
vertical ? 'vertical' : 'horizontal',
noSpacing ? 'no-spacing' : '',
].join(' ');
// const style = {};
return (
<div className={classNames} style={style}>
{children}
</div>
);
}

View File

@@ -1,39 +0,0 @@
import React from 'react';
export default function Label({
children,
style,
size = 'medium',
weight = 'regular',
}) {
const sizes = {
small: '12px',
medium: '14px',
large: '16px',
xlarge: '24px',
};
const weights = {
light: 300,
regular: 400,
semibold: 600,
bold: 800,
};
const fontSize = sizes[size];
const fontWeight = weights[weight];
const lineHeight = parseInt(fontSize, 10) < 16 ? 16 : 24;
const inlineStyle = {
margin: '8px',
fontSize,
fontWeight,
lineHeight: `${lineHeight}px`,
display: 'inline-block',
...style,
};
return (
<span style={inlineStyle}>
{children}
</span>
);
}

View File

@@ -1,19 +0,0 @@
import React from 'react';
export default function Panel({
children,
style,
variant = 'right',
}) {
const inlineStyle = {
backgroundColor: '#fff',
flex: '0 0 300px',
[variant === 'right' ? 'borderLeft' : 'borderRight']: '1px solid #ccc',
...style,
};
return (
<div style={inlineStyle}>
{children}
</div>
);
}

View File

@@ -1,17 +0,0 @@
import React from 'react';
export default function Toolbar({ children, style }) {
const inlineStyle = {
background: '#fafafa',
flex: '0 0 24px',
padding: '8px',
borderBottom: '1px solid #ccc',
...style,
};
return (
<div style={inlineStyle}>
{children}
</div>
);
}

View File

@@ -1,13 +0,0 @@
import Toolbar from './Toolbar';
import Grid from './Grid';
import Panel from './Panel';
import Card from './Card';
import Label from './Label';
export {
Toolbar,
Grid,
Panel,
Card,
Label,
};

View File

@@ -1,48 +0,0 @@
import React, { useMemo } from 'react';
import themes from '../theme';
export default function ButtonInline({
children,
disabled,
theme = themes('light'),
...props
}) {
const s = {
};
const className = useMemo(() => theme.style({
padding: '$spacing.4',
border: '0px solid transparent',
background: 'transparent',
borderRadius: '$shape.borderRadius',
cursor: 'pointer',
color: '$palette.text.primary',
font: 'normal 14px/0 $typography.fontFamily',
'&:hover': {
background: '$palette.background.hover',
},
'&:active': {
background: '$palette.background.active',
},
'&:disabled': {
opacity: '0.3',
cursor: 'default',
},
}), [theme]);
return (
<button
className={className}
type="button"
disabled={disabled}
style={s}
{...props}
>
{children}
</button>
);
}

View File

@@ -1,53 +0,0 @@
import React from 'react';
import themes from '../theme';
export default function Grid({
children,
vertical,
spacing,
style,
styled,
className = '',
theme = themes('light'),
...props
}) {
let space = '8px';
if (spacing === 'none') {
space = '0px';
} else if (spacing === 'small') {
space = '4px';
}
const inlineStyle = {
padding: space,
display: 'flex',
flexDirection: vertical ? 'column' : '',
alignItems: vertical ? '' : 'center',
};
const classes = theme.style([inlineStyle, styled, { boxSizing: 'border-box' }]);
return (
<div
className={[className, classes].join(' ')}
style={style}
{...props}
>
{children}
</div>
);
}
Grid.Item = function GridItem({
children,
style,
theme = themes('light'),
}) {
const className = theme.style([{
flex: '1 0 auto',
}, style]);
return (
<div className={className}>
{children}
</div>
);
};

View File

@@ -1,56 +0,0 @@
import React, { useMemo } from 'react';
import themes from '../theme';
const sizes = {
small: { fontSize: '$typography.small.fontSize', lineHeight: '$typography.small.lineHeight' },
medium: { fontSize: '$typography.medium.fontSize', lineHeight: '$typography.small.lineHeight' },
large: { fontSize: '$typography.large.fontSize', lineHeight: '$typography.large.lineHeight' },
xlarge: { fontSize: '$typography.xlarge.fontSize', lineHeight: '$typography.xlarge.lineHeight' },
};
const weights = {
light: '$typography.weight.light',
regular: '$typography.weight.regular',
semibold: '$typography.weight.semibold',
};
export default function Text({
children,
style,
size = 'medium',
weight = 'regular',
nowrap,
faded,
block,
theme = themes('light'),
}) {
const { fontSize, lineHeight } = sizes[size];
const fontWeight = weights[weight];
const nowrapClass = useMemo(() => (nowrap ? theme.style({
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}) : ''), [nowrap]);
const familyClass = useMemo(() => theme.style({
fontFamily: '$typography.fontFamily',
}), [theme]);
const inlineClass = theme.style({
fontSize,
fontWeight,
lineHeight,
display: block ? 'block' : 'inline-block',
color: faded ? '$palette.text.secondary' : '$palette.text.primary',
});
const classes = [nowrapClass, familyClass, inlineClass];
return (
<span className={classes.join(' ')} style={style}>
{children}
</span>
);
}

View File

@@ -1,25 +0,0 @@
import React, { useMemo } from 'react';
import themes from '../theme';
export default function Toolbar({
children,
style,
theme = themes('light'),
}) {
const className = useMemo(() => theme.style({
background: '$palette.grey.98',
color: '$palette.text.primary',
height: '48px',
fontFamily: '$typography.fontFamily',
}), [theme]);
return (
<div
className={className}
style={style}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,49 @@
import Button from '@material-ui/core/Button';
import ButtonBase from '@material-ui/core/ButtonBase';
import IconButton from '@material-ui/core/IconButton';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import Popover from '@material-ui/core/Popover';
import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
import Toolbar from '@material-ui/core/Toolbar';
import Divider from '@material-ui/core/Divider';
import Drawer from '@material-ui/core/Drawer';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogActions from '@material-ui/core/DialogActions';
import List from '@material-ui/core/List';
import ListSubheader from '@material-ui/core/ListSubheader';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
export {
Button,
ButtonBase,
IconButton,
Grid,
Card,
CardContent,
Typography,
Popover,
TableRow,
TableCell,
Toolbar,
Divider,
Drawer,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
List,
ListSubheader,
ListItem,
ListItemText,
ListItemSecondaryAction,
};

View File

@@ -1,11 +0,0 @@
import React from 'react';
const PopoverBody = ({
children,
}) => (
<div>
{children}
</div>
);
export default PopoverBody;

View File

@@ -1,186 +0,0 @@
import React, {
useState,
useRef,
useEffect,
useMemo,
} from 'react';
import { oppositeDock, positionToElement } from 'react-leonardo-ui/src/positioner';
import themes from '../../theme';
export default function PopoverContent({
children,
alignTo,
theme = themes('light'),
}) {
const [p, setP] = useState(null);
const ref = useRef(null);
const {
container,
arrowContent,
arrowLeft,
arrowTop,
arrowBottom,
arrowRight,
} = useMemo(() => ({
container: theme.style({
position: 'relative',
display: 'flex',
flexDirection: 'column',
borderRadius: '$shape.borderRadius',
margin: 'auto',
minWidth: '250px',
border: '1px solid transparent',
zIndex: '1021',
backgroundColor: '$palette.background.lightest',
borderColor: '$palette.black.05',
boxShadow: '$shadows.3',
}),
arrowContent: theme.style({
position: 'absolute',
'&::before': {
content: '""',
position: 'absolute',
width: 0,
height: 0,
},
'&::after': {
content: '""',
position: 'absolute',
width: 0,
height: 0,
},
}),
arrowTop: theme.style({
position: 'absolute',
top: 0,
'&::before': {
left: '-8px',
bottom: 0,
borderLeft: '8px solid transparent',
borderRight: '8px solid transparent',
borderBottom: '8px solid $palette.black.05',
},
'&::after': {
left: '-8px',
bottom: '-1px',
borderLeft: '8px solid transparent',
borderRight: '8px solid transparent',
borderBottom: '8px solid $palette.background.lightest',
},
}),
arrowBottom: theme.style({
position: 'absolute',
bottom: 0,
'&::before': {
left: '-8px',
top: 0,
borderLeft: '8px solid transparent',
borderRight: '8px solid transparent',
borderTop: '8px solid $palette.black.05',
},
'&::after': {
left: '-8px',
top: '-1px',
borderLeft: '8px solid transparent',
borderRight: '8px solid transparent',
borderTop: '8px solid $palette.background.lightest',
},
}),
arrowLeft: theme.style({
position: 'absolute',
left: 0,
top: '50%',
'&::before': {
top: '-8px',
right: 0,
borderRight: '8px solid $palette.black.05',
borderTop: '8px solid transparent',
borderBottom: '8px solid transparent',
},
'&::after': {
top: '-8px',
right: '-1px',
borderRight: '8px solid $palette.background.lightest',
borderTop: '8px solid transparent',
borderBottom: '8px solid transparent',
},
}),
arrowRight: theme.style({
position: 'absolute',
left: 0,
top: '50%',
'&::before': {
top: '-8px',
left: 0,
borderLeft: '8px solid $palette.black.05',
borderTop: '8px solid transparent',
borderBottom: '8px solid transparent',
},
'&::after': {
top: '-8px',
left: '-1px',
borderLeft: '8px solid $palette.background.lightest',
borderTop: '8px solid transparent',
borderBottom: '8px solid transparent',
},
}),
}), [theme]);
const arrows = {
left: arrowLeft,
right: arrowRight,
top: arrowTop,
bottom: arrowBottom,
};
const style = {
visibility: p ? 'visible' : 'hidden',
position: 'absolute',
maxWidth: '500px',
top: p ? `${p.position.top}px` : '-99999px',
left: p ? `${p.position.left}px` : '-99999px',
};
useEffect(() => {
const pp = positionToElement(ref.current, alignTo, 'bottom', {
dock: 'bottom',
offset: 8,
minWindowOffset: 8,
minEdgeOffset: 4,
});
setP(pp);
}, [alignTo]);
const arrow = {
dock: '',
style: {},
};
if (p) {
arrow.dock = oppositeDock(p.dock);
if (arrow.dock === 'top' || arrow.dock === 'bottom') {
arrow.style.left = `${p.toPosition.left - p.position.left}px`;
} else {
arrow.style.top = `${p.toPosition.top - p.position.top}px`;
}
}
const arrowElem = (
<div
className={[arrowContent, arrows[arrow.dock]].join(' ')}
style={arrow.style}
/>
);
return (
<div
className={container}
ref={ref}
role="dialog"
style={style}
>
{children}
{arrowElem}
</div>
);
}

View File

@@ -1,14 +0,0 @@
import React from 'react';
const PopoverHeader = ({
children,
}) => {
const style = { padding: '8px' };
return (
<div style={style}>
{children}
</div>
);
};
export default PopoverHeader;

View File

@@ -1,41 +0,0 @@
import React, { useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
import PopoverContent from './Content';
export default function Popover({
onOutside,
alignTo,
children,
} = {}) {
const ref = useRef(null);
useEffect(() => {
if (typeof onOutside !== 'function') {
return undefined;
}
const outsideEvent = 'ontouchend' in window ? 'touchend' : 'click';
const out = (e) => {
const element = ref.current;
if (element && !element.contains(e.target)) {
onOutside(e);
}
};
document.addEventListener(outsideEvent, out);
return () => {
document.removeEventListener(outsideEvent, out);
};
}, [onOutside]);
return (
createPortal(
<div ref={ref}>
<PopoverContent alignTo={alignTo}>
{children}
</PopoverContent>
</div>,
document.body,
)
);
}

View File

@@ -1,8 +0,0 @@
import Popover from './Popover';
import Body from './Body';
import Header from './Header';
Popover.Header = Header;
Popover.Body = Body;
export default Popover;

View File

@@ -8,8 +8,9 @@
"keywords": [],
"scripts": {},
"devDependencies": {
"@material-ui/core": "^4.0.0-beta.1",
"@material-ui/styles": "^4.0.0-beta.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-leonardo-ui": "^0.15.0"
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,90 @@
import { createMuiTheme } from '@material-ui/core/styles';
const cache = {};
export default function create(definition = {}) {
const key = JSON.stringify(definition);
if (cache[key]) {
return cache[key];
}
cache[key] = createMuiTheme({
palette: {
primary: {
light: '#fafafa',
main: '#fff',
dark: '#f0f0f0',
contrastText: '#444',
},
secondary: {
light: '#7cc84c',
main: '#6cb33f',
dark: '#589f35',
contrastText: '#fff',
},
},
typography: {
fontSize: 14,
htmlFontSize: 16,
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 600,
fontFamily: [
'"Source Sans Pro"',
'"Segoe UI"',
'"Helvetica Neue"',
'-apple-system',
'Arial',
'sans-serif',
].join(','),
button: {
textTransform: 'initial',
fontWeight: 400,
},
},
shape: {
borderRadius: 2,
},
shadows: [
'none',
'0px 1px 2px 0px rgba(0,0,0,0.15)',
'0px 2px 4px 0px rgba(0,0,0,0.15)',
'0px 4px 10px 0px rgba(0,0,0,0.15)',
'0px 6px 20px 0px rgba(0,0,0,0.15)',
'0px 3px 5px -1px rgba(0,0,0,0.2),0px 5px 8px 0px rgba(0,0,0,0.14),0px 1px 14px 0px rgba(0,0,0,0.12)',
'0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)',
'0px 4px 5px -2px rgba(0,0,0,0.2),0px 7px 10px 1px rgba(0,0,0,0.14),0px 2px 16px 1px rgba(0,0,0,0.12)',
'0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12)',
'0px 5px 6px -3px rgba(0,0,0,0.2),0px 9px 12px 1px rgba(0,0,0,0.14),0px 3px 16px 2px rgba(0,0,0,0.12)',
'0px 6px 6px -3px rgba(0,0,0,0.2),0px 10px 14px 1px rgba(0,0,0,0.14),0px 4px 18px 3px rgba(0,0,0,0.12),',
'0px 6px 7px -4px rgba(0,0,0,0.2),0px 11px 15px 1px rgba(0,0,0,0.14),0px 4px 20px 3px rgba(0,0,0,0.12)',
'0px 7px 8px -4px rgba(0,0,0,0.2),0px 12px 17px 2px rgba(0,0,0,0.14),0px 5px 22px 4px rgba(0,0,0,0.12)',
'0px 7px 8px -4px rgba(0,0,0,0.2),0px 13px 19px 2px rgba(0,0,0,0.14),0px 5px 24px 4px rgba(0,0,0,0.12)',
'0px 7px 9px -4px rgba(0,0,0,0.2),0px 14px 21px 2px rgba(0,0,0,0.14),0px 5px 26px 4px rgba(0,0,0,0.12)',
'0px 8px 9px -5px rgba(0,0,0,0.2),0px 15px 22px 2px rgba(0,0,0,0.14),0px 6px 28px 5px rgba(0,0,0,0.12)',
'0px 8px 10px -5px rgba(0,0,0,0.2),0px 16px 24px 2px rgba(0,0,0,0.14),0px 6px 30px 5px rgba(0,0,0,0.12)',
'0px 8px 11px -5px rgba(0,0,0,0.2),0px 17px 26px 2px rgba(0,0,0,0.14),0px 6px 32px 5px rgba(0,0,0,0.12)',
'0px 9px 11px -5px rgba(0,0,0,0.2),0px 18px 28px 2px rgba(0,0,0,0.14),0px 7px 34px 6px rgba(0,0,0,0.12)',
'0px 9px 12px -6px rgba(0,0,0,0.2),0px 19px 29px 2px rgba(0,0,0,0.14),0px 7px 36px 6px rgba(0,0,0,0.12)',
'0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.14),0px 8px 38px 7px rgba(0,0,0,0.12)',
'0px 10px 13px -6px rgba(0,0,0,0.2),0px 21px 33px 3px rgba(0,0,0,0.14),0px 8px 40px 7px rgba(0,0,0,0.12)',
'0px 10px 14px -6px rgba(0,0,0,0.2),0px 22px 35px 3px rgba(0,0,0,0.14),0px 8px 42px 7px rgba(0,0,0,0.12)',
'0px 11px 14px -7px rgba(0,0,0,0.2),0px 23px 36px 3px rgba(0,0,0,0.14),0px 9px 44px 8px rgba(0,0,0,0.12)',
'0px 11px 15px -7px rgba(0,0,0,0.2),0px 24px 38px 3px rgba(0,0,0,0.14),0px 9px 46px 8px rgba(0,0,0,0.12)',
],
props: {
MuiButtonBase: {
disableRipple: true,
},
},
overrides: {
MuiIconButton: {
root: {
padding: 8,
borderRadius: 2,
},
},
},
});
return cache[key];
}

View File

@@ -0,0 +1,88 @@
const theme = {
palette: {
grey: {
100: '#ffffff',
98: '#fafafa',
95: '#f2f2f2',
90: '#e6e6e6',
85: '#D9D9D9',
70: '#B3B3B3',
30: '#4d4d4d',
25: '#404040',
20: '#333333',
15: '#262626',
10: '#1a1a1a',
},
black: {
'03': 'rgba(0, 0, 0, 0.03)',
'05': 'rgba(0, 0, 0, 0.05)',
10: 'rgba(0, 0, 0, 0.10)',
15: 'rgba(0, 0, 0, 0.15)',
30: 'rgba(0, 0, 0, 0.30)',
55: 'rgba(0, 0, 0, 0.55)',
},
white: {
10: 'rgba(255, 255, 255, 0.1)',
60: 'rgba(255, 255, 255, 0.6)',
},
text: {
primary: '$palette.grey.100',
secondary: '$palette.white.60',
},
divider: '$palette.black.30',
background: {
lightest: '$palette.grey.30',
lighter: '$palette.grey.20',
darker: '$palette.grey.15',
darkest: '$palette.grey.10',
default: '$palette.background.lightest',
hover: '$palette.white.10',
focus: '$palette.white.10 ',
active: '$palette.black.10',
},
green: '#6CB33F',
},
typography: {
fontFamily: '"Source Sans Pro", Arial, sans-serif',
weight: {
light: '300',
regular: '400',
semibold: '600',
},
small: {
fontSize: '12px',
lineHeight: '16px',
},
medium: {
fontSize: '14px',
lineHeight: '16px',
},
large: {
fontSize: '16px',
lineHeight: '24px',
},
xlarge: {
fontSize: '24px',
lineHeight: '32px',
},
},
shadows: {
0: 'none',
1: '0 1px 2px $palette.black.15',
2: '0 2px 4px $palette.black.15',
3: '0 4px 10px $palette.black.15',
4: '0 6px 20px $palette.black.15',
},
shape: {
borderRadius: '2px',
},
spacing: {
0: '0',
1: '2px',
3: '4px',
4: '8px',
5: '16px',
},
};
export default theme;

View File

@@ -1,16 +1,18 @@
import theme from './theme';
import {
ThemeProvider,
useTheme,
StylesProvider,
createGenerateClassName,
makeStyles,
} from '@material-ui/styles';
import light from './definitions/light';
import createTheme from './create';
const cache = {};
export default function (name) {
if (name !== 'light') {
throw new Error('No theme');
}
if (!cache[name]) {
cache[name] = theme(light);
}
return cache[name];
}
export {
createTheme,
useTheme,
makeStyles,
ThemeProvider,
StylesProvider,
createGenerateClassName,
};

View File

@@ -1,67 +0,0 @@
import resolver from './resolver';
const rxAppend = /^&/;
const rxCap = /[A-Z]/g;
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);
}
function uid() {
return idGen.map(toChar).join('');
}
function parse(id, style, sheet, r) {
const rule = [];
const post = {};
Object.keys(style).forEach((prop) => {
if (rxAppend.test(prop)) {
post[prop.slice(1)] = style[prop];
} else {
const value = r.get(style[prop]);
rule.push(`${prop.replace(rxCap, '-$&').toLowerCase()}: ${value}`);
}
});
sheet.insertRule(`.${id} { ${rule.join(';')} }`, sheet.cssRules.length);
Object.keys(post).forEach((key) => {
parse(`${id}${key}`, post[key], sheet, r);
});
}
const theme = (definition) => {
const res = resolver(definition);
let injectedSheet;
if (typeof window !== 'undefined') {
const el = document.createElement('style');
el.setAttribute('data-id', 'nebula-ui');
el.setAttribute('data-version', '0.1.0');
injectedSheet = document.head.appendChild(el).sheet;
}
const cache = {};
return {
style(s) {
const styles = Array.isArray(s) ? s : [s];
return styles.filter(Boolean).map((style) => {
const key = JSON.stringify(style);
if (cache[key]) {
return cache[key].className;
}
const className = uid();
parse(className, style, injectedSheet, res);
cache[key] = {
style,
className,
};
return className;
}).join(' ');
},
clear() {
},
};
};
export default theme;

View File

@@ -36,6 +36,30 @@ const GLOBALS = {
const EXTERNALS = [
'react',
'react-dom',
'@material-ui/core',
'@material-ui/styles',
];
const propTypes = [
'array',
'bool',
'func',
'number',
'object',
'string',
'symbol',
'any',
'arrayOf',
'element',
'instanceOf',
'node',
'objectOf',
'oneOf',
'oneOfType',
'shape',
'exact',
'elementType',
];
const config = (isEsm) => {
@@ -73,8 +97,24 @@ const config = (isEsm) => {
}),
commonjs({
namedExports: {
react: ['useState', 'useEffect', 'useRef', 'useContext', 'useCallback', 'useMemo', 'createElement', 'PureComponent'],
'react-dom': ['createPortal'],
react: [
'useState',
'useEffect',
'useRef',
'useContext',
'useCallback',
'useMemo',
'createElement',
'PureComponent',
'isValidElement',
'Children',
'cloneElement',
],
'react-dom': ['createPortal', 'findDOMNode'],
'react-is': ['ForwardRef'],
'react-transition-group/node_modules/prop-types/index.js': propTypes,
'prop-types/index.js': propTypes,
'@material-ui/utils/node_modules/prop-types': propTypes,
},
}),
babel({

266
yarn.lock
View File

@@ -1019,6 +1019,12 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
dependencies:
regenerator-runtime "^0.13.2"
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
@@ -1195,6 +1201,10 @@
dependencies:
find-up "^2.1.0"
"@emotion/hash@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.7.1.tgz#9833722341379fb7d67f06a4b00ab3c37913da53"
"@iamstarkov/listr-update-renderer@0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz#d7c48092a2dcf90fd672b6c8b458649cb350c77e"
@@ -1981,6 +1991,73 @@
mkdirp "^0.5.1"
rimraf "^2.5.2"
"@material-ui/core@^4.0.0-beta.0", "@material-ui/core@^4.0.0-beta.1":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.0.0-beta.1.tgz#5f8f47b66e92f266abbad6cfcc9efa6e5d87e54c"
dependencies:
"@babel/runtime" "^7.2.0"
"@material-ui/styles" "^4.0.0-beta.1"
"@material-ui/system" "^4.0.0-beta.1"
"@material-ui/types" "^4.0.0-beta.2"
"@material-ui/utils" "^4.0.0-beta.1"
"@types/react-transition-group" "^2.0.16"
clsx "^1.0.2"
convert-css-length "^1.0.2"
csstype "^2.5.2"
debounce "^1.1.0"
deepmerge "^3.0.0"
hoist-non-react-statics "^3.2.1"
is-plain-object "^2.0.4"
normalize-scroll-left "^0.1.2"
popper.js "^1.14.1"
prop-types "^15.7.2"
react-event-listener "^0.6.6"
react-transition-group "^4.0.0"
warning "^4.0.1"
"@material-ui/styles@^4.0.0-beta.1":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.0.0-beta.1.tgz#a552fe402763f0579cbd39e79a9e42fbf6762139"
dependencies:
"@babel/runtime" "^7.2.0"
"@emotion/hash" "^0.7.1"
"@material-ui/types" "^4.0.0-beta.2"
"@material-ui/utils" "^4.0.0-beta.1"
clsx "^1.0.2"
deepmerge "^3.0.0"
hoist-non-react-statics "^3.2.1"
jss "^10.0.0-alpha.16"
jss-plugin-camel-case "^10.0.0-alpha.16"
jss-plugin-default-unit "^10.0.0-alpha.16"
jss-plugin-global "^10.0.0-alpha.16"
jss-plugin-nested "^10.0.0-alpha.16"
jss-plugin-props-sort "^10.0.0-alpha.16"
jss-plugin-rule-value-function "^10.0.0-alpha.16"
jss-plugin-vendor-prefixer "^10.0.0-alpha.16"
prop-types "^15.7.2"
warning "^4.0.1"
"@material-ui/system@^4.0.0-beta.1":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.0.0-beta.1.tgz#6e170d72b8f4944f1d9d915f435967d1a7bcdac3"
dependencies:
"@babel/runtime" "^7.2.0"
deepmerge "^3.0.0"
prop-types "^15.7.2"
warning "^4.0.1"
"@material-ui/types@^4.0.0-beta.2":
version "4.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-4.0.0-beta.2.tgz#aa686548f6eca0b055249cdeb089973f33ab6976"
"@material-ui/utils@^4.0.0-beta.1":
version "4.0.0-beta.1"
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.0.0-beta.1.tgz#d5d566e38e21287a095e030d0bf465ea4f195633"
dependencies:
"@babel/runtime" "^7.2.0"
prop-types "^15.7.2"
react-is "^16.8.0"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -2046,10 +2123,27 @@
version "9.6.41"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.41.tgz#e57c3152eb2e7ec748c733cebd0c095b437c5d37"
"@types/prop-types@*":
version "15.7.1"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6"
"@types/q@^1.5.1":
version "1.5.1"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18"
"@types/react-transition-group@^2.0.16":
version "2.9.1"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.9.1.tgz#66c9ca5d0b20bae72fe6b797e0d362b996d55e9f"
dependencies:
"@types/react" "*"
"@types/react@*":
version "16.8.16"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.16.tgz#2bf980b4fb29cceeb01b2c139b3e185e57d3e08e"
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/rimraf@^0.0.28":
version "0.0.28"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-0.0.28.tgz#5562519bc7963caca8abf7f128cae3b594d41d06"
@@ -3363,6 +3457,10 @@ cloneable-readable@^1.0.0:
process-nextick-args "^2.0.0"
readable-stream "^2.3.5"
clsx@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec"
cmd-shim@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb"
@@ -3552,6 +3650,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
console-polyfill@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/console-polyfill/-/console-polyfill-0.1.2.tgz#96cfed51caf78189f699572e6f18271dc37c0e30"
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@@ -3669,6 +3771,13 @@ conventional-recommended-bump@^4.0.4:
meow "^4.0.0"
q "^1.5.1"
convert-css-length@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/convert-css-length/-/convert-css-length-1.0.2.tgz#32f38a8ac55d78372ff43562532564366c871ccc"
dependencies:
console-polyfill "^0.1.2"
parse-unit "^1.0.1"
convert-source-map@^1.1.0, convert-source-map@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
@@ -3925,6 +4034,13 @@ css-url-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec"
css-vendor@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.2.tgz#dd75b41064944c8eaa001796a204782164c69dfd"
dependencies:
"@babel/runtime" "^7.3.1"
is-in-browser "^1.0.2"
css-what@2.1, css-what@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
@@ -4005,6 +4121,10 @@ csso@^3.5.1:
dependencies:
css-tree "1.0.0-alpha.29"
csstype@^2.2.0, csstype@^2.5.2:
version "2.6.4"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.4.tgz#d585a6062096e324e7187f80e04f92bd0f00e37f"
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -4047,6 +4167,10 @@ dateformat@^3.0.0, dateformat@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
debounce@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
debug-log@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f"
@@ -4128,6 +4252,10 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
default-gateway@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.0.1.tgz#3a7d071ca610a2831190341bd0666382b9dbc340"
@@ -4367,6 +4495,12 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
dom-helpers@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
dependencies:
"@babel/runtime" "^7.1.2"
dom-serializer@0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
@@ -5894,6 +6028,12 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
dependencies:
react-is "^16.7.0"
homedir-polyfill@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
@@ -6072,6 +6212,10 @@ husky@^1.3.1:
run-node "^1.0.0"
slash "^2.0.0"
hyphenate-style-name@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
iconv-lite@0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
@@ -6532,6 +6676,10 @@ is-glob@^4.0.1:
dependencies:
is-extglob "^2.1.1"
is-in-browser@^1.0.2, is-in-browser@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
is-installed-globally@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
@@ -6965,6 +7113,66 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
jss-plugin-camel-case@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.16.tgz#36023c9aa35fd2e898f117be136f31dfa76ffef9"
dependencies:
"@babel/runtime" "^7.3.1"
hyphenate-style-name "^1.0.3"
jss "10.0.0-alpha.16"
jss-plugin-default-unit@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.16.tgz#ef96b529fcb9f8d730c14a489a1d7e71e243447e"
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.0.0-alpha.16"
jss-plugin-global@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.16.tgz#6da34ad63e0a4669a35412d716d39820bd10ede4"
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.0.0-alpha.16"
jss-plugin-nested@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.16.tgz#282ce431cc6c7c4b2e2509b80dc5cc1de7f7102f"
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.0.0-alpha.16"
tiny-warning "^1.0.2"
jss-plugin-props-sort@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.16.tgz#d91566d6c73ebd906ff81fdfb93135d16bbfb067"
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.0.0-alpha.16"
jss-plugin-rule-value-function@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.16.tgz#42bf684dae0a73a02df0a3297b747becf9854449"
dependencies:
"@babel/runtime" "^7.3.1"
jss "10.0.0-alpha.16"
jss-plugin-vendor-prefixer@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.16.tgz#f59d92db7331d6615b33aa108ee54cbf1ab6ce84"
dependencies:
"@babel/runtime" "^7.3.1"
css-vendor "^2.0.1"
jss "10.0.0-alpha.16"
jss@10.0.0-alpha.16, jss@^10.0.0-alpha.16:
version "10.0.0-alpha.16"
resolved "https://registry.yarnpkg.com/jss/-/jss-10.0.0-alpha.16.tgz#0555e8b667e08dbd2cc94f6125be5a8b8b022833"
dependencies:
"@babel/runtime" "^7.3.1"
is-in-browser "^1.1.3"
tiny-warning "^1.0.2"
jsx-ast-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
@@ -7116,10 +7324,6 @@ lcid@^2.0.0:
dependencies:
invert-kv "^2.0.0"
leonardo-ui@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/leonardo-ui/-/leonardo-ui-1.6.0.tgz#65eab0a4fd9a54f87a75d08e4440900555f56c24"
lerna@^3.10.7:
version "3.10.7"
resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.10.7.tgz#9d308b1fee1697f89fe90e6bc37e51c03b531557"
@@ -7505,7 +7709,7 @@ lolex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-3.0.0.tgz#f04ee1a8aa13f60f1abd7b0e8f4213ec72ec193e"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
dependencies:
@@ -8212,6 +8416,10 @@ normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
normalize-scroll-left@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz#6b79691ba79eb5fb107fa5edfbdc06b55caee2aa"
normalize-url@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
@@ -8857,6 +9065,10 @@ parse-passwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
parse-unit@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parse-unit/-/parse-unit-1.0.1.tgz#7e1bb6d5bef3874c28e392526a2541170291eecf"
parseurl@^1.3.2, parseurl@~1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
@@ -9040,6 +9252,10 @@ pngjs@^3.0.0, pngjs@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
popper.js@^1.14.1:
version "1.15.0"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"
portfinder@^1.0.20, portfinder@^1.0.9:
version "1.0.20"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a"
@@ -9462,6 +9678,14 @@ promzard@^0.3.0:
dependencies:
read "1"
prop-types@^15.6.0, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
@@ -9652,14 +9876,18 @@ react-dom@^16.8.6:
prop-types "^15.6.2"
scheduler "^0.13.6"
react-is@^16.8.6:
react-event-listener@^0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.6.tgz#758f7b991cad9086dd39fd29fad72127e1d8962a"
dependencies:
"@babel/runtime" "^7.2.0"
prop-types "^15.6.0"
warning "^4.0.1"
react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
react-leonardo-ui@^0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/react-leonardo-ui/-/react-leonardo-ui-0.15.0.tgz#6111aa6177605f9540f5df89c542f87812069a57"
react-test-renderer@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1"
@@ -9669,6 +9897,14 @@ react-test-renderer@^16.8.6:
react-is "^16.8.6"
scheduler "^0.13.6"
react-transition-group@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.0.0.tgz#1d82b20d78aa09eac6268ceef0349307146942c6"
dependencies:
dom-helpers "^3.4.0"
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-window-infinite-loader@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/react-window-infinite-loader/-/react-window-infinite-loader-1.0.3.tgz#6bc39ac4fc7472f4d57f1558b3375177db0615a9"
@@ -11249,6 +11485,10 @@ timsort@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
tiny-warning@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28"
tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
@@ -11716,6 +11956,12 @@ walk@2.3.x:
dependencies:
foreachasync "^3.0.0"
warning@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
dependencies:
loose-envify "^1.0.0"
watchpack@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"