mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 09:48:18 -05:00
refactor: all in with material-ui (#23)
This commit is contained in:
@@ -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 }],
|
||||
}],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
}] }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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-'],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
@@ -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}> </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}> </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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = ' '; // 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
78
packages/nucleus/src/components/selections/AppSelections.jsx
Normal file
78
packages/nucleus/src/components/selections/AppSelections.jsx
Normal 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();
|
||||
};
|
||||
}
|
||||
24
packages/nucleus/src/components/selections/MultiState.jsx
Normal file
24
packages/nucleus/src/components/selections/MultiState.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
76
packages/nucleus/src/components/selections/Nav.jsx
Normal file
76
packages/nucleus/src/components/selections/Nav.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
136
packages/nucleus/src/components/selections/OneField.jsx
Normal file
136
packages/nucleus/src/components/selections/OneField.jsx
Normal 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 = ' '; // 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
|
||||
@@ -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(),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}>
|
||||
< 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>
|
||||
);
|
||||
}
|
||||
|
||||
29
packages/serve/web/components/Cell.jsx
Normal file
29
packages/serve/web/components/Cell.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
44
packages/serve/web/components/Chart.jsx
Normal file
44
packages/serve/web/components/Chart.jsx
Normal 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%',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
45
packages/serve/web/components/Collection.jsx
Normal file
45
packages/serve/web/components/Collection.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 = (() => {
|
||||
|
||||
5
packages/serve/web/contexts/NebulaContext.js
Normal file
5
packages/serve/web/contexts/NebulaContext.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const NebulaContext = React.createContext();
|
||||
|
||||
export default NebulaContext;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,6 +27,9 @@ serverInfo.then(info => openApp(params.app).then((app) => {
|
||||
fields: params.cols || [],
|
||||
}, {
|
||||
element: document.querySelector('#chart-container'),
|
||||
context: {
|
||||
permissions: params.permissions || [],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
49
packages/ui/components/index.js
Normal file
49
packages/ui/components/index.js
Normal 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,
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const PopoverBody = ({
|
||||
children,
|
||||
}) => (
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PopoverBody;
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const PopoverHeader = ({
|
||||
children,
|
||||
}) => {
|
||||
const style = { padding: '8px' };
|
||||
return (
|
||||
<div style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopoverHeader;
|
||||
@@ -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,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
90
packages/ui/theme/create.js
Normal file
90
packages/ui/theme/create.js
Normal 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];
|
||||
}
|
||||
88
packages/ui/theme/definitions/dark.js
Normal file
88
packages/ui/theme/definitions/dark.js
Normal 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;
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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
266
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user