Use prettier

This commit is contained in:
nndio 2019-01-10 20:51:14 +02:00
parent 458f9019aa
commit 6907c48147
302 changed files with 5352 additions and 3638 deletions

File diff suppressed because one or more lines are too long

View File

@ -6,3 +6,4 @@ umd
build
coverage
node_modules
__snapshots__

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ umd
build
coverage
.idea
.eslintcache

View File

@ -8,5 +8,5 @@ cache:
- "node_modules"
script:
- yarn build:all
- yarn lint
- yarn lint:all
- yarn test

View File

@ -11,4 +11,3 @@ Project maintainers have the right and responsibility to remove, edit, or reject
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)

View File

@ -6,7 +6,7 @@ By installing [`redux-devtools-cli`](https://github.com/reduxjs/redux-devtools/t
We're using [SocketCluster](http://socketcluster.io/) for realtime communication, which provides a fast and scalable webSocket layer and a minimal pub/sub system. You need to include one of [its clients](https://github.com/SocketCluster/client-drivers) in your app to communicate with RemotedevServer. Currently there are clients for [JavaScript (NodeJS)](https://github.com/SocketCluster/socketcluster-client), [Java](https://github.com/sacOO7/socketcluster-client-java), [Python](https://github.com/sacOO7/socketcluster-client-python), [C](https://github.com/sacOO7/socketcluster-client-C), [Objective-C](https://github.com/abpopov/SocketCluster-ios-client) and [.NET/C#](https://github.com/sacOO7/SocketclusterClientDotNet).
By default, the websocket server is running on `ws://localhost:8000/socketcluster/`.
By default, the websocket server is running on `ws://localhost:8000/socketcluster/`.
### Messaging lifecycle
@ -15,6 +15,7 @@ By default, the websocket server is running on `ws://localhost:8000/socketcluste
The client driver provides a way to connect to the server via websockets (see the docs for the selected client).
##### JavaScript
```js
var socket = socketCluster.connect({
hostname: 'localhost',
@ -23,18 +24,20 @@ var socket = socketCluster.connect({
```
##### Python
```py
socket = Socketcluster.socket("ws://localhost:8000/socketcluster/")
socket = Socketcluster.socket("ws://localhost:8000/socketcluster/")
socket.connect()
```
> Note that JavaScript client composes the url from `hostname` and `port`, adding `/socketcluster/` path automatically. For other clients, you should specify that path. For example, for `ObjectiveC` it would be `self.client.initWithHost("localhost/socketcluster/", onPort: 8000, securely: false)`.
#### 2. Disconnecting and reconnecting
SocketCluster client handles reconnecting for you, but you still might want to know when the connection is established, or when it failed to connect.
##### JavaScript
```js
socket.on('connect', status => {
// Here will come the next step
@ -48,6 +51,7 @@ socket.on('error', error => {
```
##### Python
```py
def onconnect(socket):
// Here will call the next step
@ -66,9 +70,13 @@ socket.setBasicListener(onconnect, ondisconnect, onConnectError)
We're not providing an authorizing mechanism yet. All you have to do is to emit a `login` event, and you'll get a `channelName` you should subscribe for, and watch for messages and events. Make sure to pass the `master` event, otherwise it should be a monitor, not a client app.
##### JavaScript
```js
socket.emit('login', 'master', (error, channelName) => {
if (error) { console.log(error); return; }
if (error) {
console.log(error);
return;
}
channel = socket.subscribe(channelName);
channel.watch(handleMessages);
socket.on(channelName, handleMessages);
@ -80,6 +88,7 @@ function handleMessages(message) {
```
##### Python
```py
socket.emitack("login", "master", login)
@ -99,6 +108,7 @@ You could just emit the `login` event, and omit subscribing (and point `5` bello
To send your data to the monitor use `log` or `log-noid` channel. The latter will add the socket id to the message from the server side (useful when the message was sent before the connection was established).
The message object includes the following:
- `type` - usually should be `ACTION`. If you want to indicate that we're starting a new log (clear all actions emitted before and add `@@INIT`), use `INIT`. In case you have a lifted state similar to one provided by [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument), use `STATE`.
- `action` - the action object. It is recommended to lift it in another object, and add `timestamp` to show when the action was fired off: `{ timestamp: Date.now(), action: { type: 'SOME_ACTION' } }`.
- `payload` - usually the state or lifted state object.
@ -107,6 +117,7 @@ The message object includes the following:
- `id` - socket connection id, which should be either `socket.id` or should not provided and use `log-noid` channel.
##### JavaScript
```js
const message = {
type: 'ACTION',
@ -120,6 +131,7 @@ socket.emit(socket.id ? 'log' : 'log-noid', message);
```
##### Python
```py
class Message:
def __init__(self, action, state):
@ -133,14 +145,16 @@ socket.emit(socket.id if "log" else "log-noid", Message(action, state));
#### 5. Listening for monitor events
When a monitor action is emitted, you'll get an event on the subscribed function. The argument object includes a `type` key, which can be:
- `DISPATCH` - a monitor action dispatched on Redux DevTools monitor, like `{ type: 'DISPATCH', payload: { type: 'JUMP_TO_STATE', 'index': 2 }`. See [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js) for details. Additionally to that API, you'll get also a stringified `state` object when needed. So, for example, for time travelling (`JUMP_TO_STATE`) you can just parse and set the state (see the example). Usually implementing this type of actions would be enough.
- `ACTION` - the user requested to dispatch an action remotely like `{ type: 'ACTION', action: '{ type: \'INCREMENT_COUNTER\' }' }`. The `action` can be either a stringified javascript object which should be evalled or a function which arguments should be evalled like [here](https://github.com/zalmoxisus/remotedev-utils/blob/master/src/index.js#L62-L70).
- `START` - a monitor was opened. You could handle this event in order not to do extra tasks when the app is not monitored.
- `STOP` - a monitor was closed. You can take this as no need to send data to the monitor. I there are several monitors and one was closed, all others will send `START` event to acknowledge that we still have to send data.
- `STOP` - a monitor was closed. You can take this as no need to send data to the monitor. I there are several monitors and one was closed, all others will send `START` event to acknowledge that we still have to send data.
See [`mobx-remotedev`](https://github.com/zalmoxisus/mobx-remotedev/blob/master/src/monitorActions.js) for an example of implementation without [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js).
##### JavaScript
```js
function handleMessages(message) {
if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') {
@ -150,6 +164,7 @@ function handleMessages(message) {
```
##### Python
```py
def handleMessages(key, message):
if message.type === "DISPATCH" and message.payload.type === "JUMP_TO_STATE":

View File

@ -46,10 +46,12 @@ const DevTools = createDevTools(
// Consult their repositories to learn about those props.
// Here, we put LogMonitor inside a DockMonitor.
// Note: DockMonitor is visible by default.
<DockMonitor toggleVisibilityKey='ctrl-h'
changePositionKey='ctrl-q'
defaultIsVisible={true}>
<LogMonitor theme='tomorrow' />
<DockMonitor
toggleVisibilityKey="ctrl-h"
changePositionKey="ctrl-q"
defaultIsVisible={true}
>
<LogMonitor theme="tomorrow" />
</DockMonitor>
);
@ -60,9 +62,7 @@ Note that you can use `LogMonitor` directly without wrapping it in `DockMonitor`
```js
// If you'd rather not use docking UI, use <LogMonitor> directly
const DevTools = createDevTools(
<LogMonitor theme='solarized' />
);
const DevTools = createDevTools(<LogMonitor theme="solarized" />);
```
#### Use `DevTools.instrument()` Store Enhancer
@ -75,7 +75,7 @@ The easiest way to apply several store enhancers in a row is to use the [`compos
You can add additional options to it: `DevTools.instrument({ maxAge: 50, shouldCatchErrors: true })`. See [`redux-devtools-instrument`'s API](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-instrument#api) for more details.
Its important that you should add `DevTools.instrument()` *after* `applyMiddleware` in your `compose()` function arguments. This is because `applyMiddleware` is potentially asynchronous, but `DevTools.instrument()` expects all actions to be plain objects rather than actions interpreted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk). So make sure `applyMiddleware()` goes first in the `compose()` call, and `DevTools.instrument()` goes after it.
Its important that you should add `DevTools.instrument()` _after_ `applyMiddleware` in your `compose()` function arguments. This is because `applyMiddleware` is potentially asynchronous, but `DevTools.instrument()` expects all actions to be plain objects rather than actions interpreted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk). So make sure `applyMiddleware()` goes first in the `compose()` call, and `DevTools.instrument()` goes after it.
##### `store/configureStore.js`
@ -99,7 +99,9 @@ export default function configureStore(initialState) {
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')/*.default if you use Babel 6+ */)
store.replaceReducer(
require('../reducers') /*.default if you use Babel 6+ */
)
);
}
@ -126,7 +128,7 @@ function getDebugSessionKey() {
// You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar
const matches = window.location.href.match(/[?&]debug_session=([^&#]+)\b/);
return (matches && matches.length > 0)? matches[1] : null;
return matches && matches.length > 0 ? matches[1] : null;
}
export default function configureStore(initialState) {
@ -181,7 +183,7 @@ export default function configureStore(initialState) {
// Note: only Redux >= 3.1.0 supports passing enhancer as third argument.
// See https://github.com/rackt/redux/releases/tag/v3.1.0
return createStore(rootReducer, initialState, enhancer);
};
}
```
##### `store/configureStore.dev.js`
@ -205,7 +207,7 @@ function getDebugSessionKey() {
// You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar
const matches = window.location.href.match(/[?&]debug_session=([^&]+)\b/);
return (matches && matches.length > 0)? matches[1] : null;
return matches && matches.length > 0 ? matches[1] : null;
}
export default function configureStore(initialState) {
@ -216,7 +218,9 @@ export default function configureStore(initialState) {
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')/*.default if you use Babel 6+ */)
store.replaceReducer(
require('../reducers') /*.default if you use Babel 6+ */
)
);
}
@ -346,7 +350,11 @@ import { render } from 'react-dom';
import DevTools from './containers/DevTools';
export default function showDevTools(store) {
const popup = window.open(null, 'Redux DevTools', 'menubar=no,location=no,resizable=yes,scrollbars=no,status=no');
const popup = window.open(
null,
'Redux DevTools',
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'
);
// Reload in case it already exists
popup.location.reload();
@ -366,11 +374,11 @@ Note that there are no useful props you can pass to the `DevTools` component oth
### Gotchas
* **Your reducers have to be pure and free of side effects to work correctly with DevTools.** For example, even generating a random ID in reducer makes it impure and non-deterministic. Instead, do this in action creators.
- **Your reducers have to be pure and free of side effects to work correctly with DevTools.** For example, even generating a random ID in reducer makes it impure and non-deterministic. Instead, do this in action creators.
* **Make sure to only apply `DevTools.instrument()` and render `<DevTools>` in development!** In production, this will be terribly slow because actions just accumulate forever. As described above, you need to use conditional `require`s and use `DefinePlugin` (Webpack) or `loose-envify` (Browserify) together with Uglify to remove the dead code. Here is [an example](https://github.com/erikras/react-redux-universal-hot-example/) that adds Redux DevTools handling the production case correctly.
- **Make sure to only apply `DevTools.instrument()` and render `<DevTools>` in development!** In production, this will be terribly slow because actions just accumulate forever. As described above, you need to use conditional `require`s and use `DefinePlugin` (Webpack) or `loose-envify` (Browserify) together with Uglify to remove the dead code. Here is [an example](https://github.com/erikras/react-redux-universal-hot-example/) that adds Redux DevTools handling the production case correctly.
* **It is important that `DevTools.instrument()` store enhancer should be added to your middleware stack *after* `applyMiddleware` in the `compose`d functions, as `applyMiddleware` is potentially asynchronous.** Otherwise, DevTools wont see the raw actions emitted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk).
- **It is important that `DevTools.instrument()` store enhancer should be added to your middleware stack _after_ `applyMiddleware` in the `compose`d functions, as `applyMiddleware` is potentially asynchronous.** Otherwise, DevTools wont see the raw actions emitted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk).
### What Next?

View File

@ -8,9 +8,5 @@
"allowBranch": "master"
}
},
"ignoreChanges": [
"**/test/**",
"**/examples/**",
"**/*.md"
]
"ignoreChanges": ["**/test/**", "**/examples/**", "**/*.md"]
}

View File

@ -9,7 +9,8 @@
"eslint-plugin-react": "7.12.3",
"jest": "^23.6.0",
"lerna": "3.9.0",
"pre-commit": "^1.1.3"
"lint-staged": "^8.1.0",
"prettier": "^1.15.3"
},
"scripts": {
"lerna": "lerna",
@ -21,13 +22,23 @@
"lint": "eslint '**/*.{js,jsx}' --cache",
"lint:fix": "eslint '**/*.{js,jsx}' --fix --cache",
"lint:all": "eslint '**/*.{js,jsx}'",
"prettify": "prettier '**/*.{js,jsx,json,css,html,md}' --ignore-path .eslintignore --single-quote --write",
"precommit": "lint-staged",
"test": "jest --onlyChanged",
"test:all": "jest"
},
"workspaces": [
"packages/*"
],
"pre-commit": [
"lint"
]
"lint-staged": {
"*.{js,jsx}": [
"prettier --single-quote --write",
"yarn lint:fix",
"git add"
],
"*.{json,css,html,md}": [
"prettier --single-quote --write",
"git add"
]
}
}

View File

@ -1,5 +1,4 @@
d3tooltip
=========================
# d3tooltip
This tooltip aims for a minimal yet highly configurable API. It has a long way to go, but the essentials are there.
It was created by [@romseguy](https://github.com/romseguy) and merged from [`romseguy/d3tooltip`](https://github.com/romseguy/d3tooltip).
@ -47,9 +46,9 @@ vis.selectAll('circle').data(someData).enter()
## API
Option | Type | Default | Description
--------------------------|--------------|---------------------|--------------------------------------------------------------
`root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select)
`left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element
`top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element
`offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option
| Option | Type | Default | Description |
| -------- | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select) |
| `left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element |
| `top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element |
| `offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option |

View File

@ -5,19 +5,14 @@ const { prependClass, functor } = utils.default || utils;
const defaultOptions = {
left: undefined, // mouseX
top: undefined, // mouseY
offset: {left: 0, top: 0},
offset: { left: 0, top: 0 },
root: undefined
};
export default function tooltip(d3, className = 'tooltip', options = {}) {
const {
left,
top,
offset,
root
} = {...defaultOptions, ...options};
const { left, top, offset, root } = { ...defaultOptions, ...options };
let attrs = {'class': className};
let attrs = { class: className };
let text = () => '';
let styles = {};
@ -33,7 +28,8 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
anchor.selectAll(`div.${className}`).remove();
el = anchor.append('div')
el = anchor
.append('div')
.attr(prependClass(className)(attrs))
.style({
position: 'absolute',
@ -49,12 +45,10 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
let [mouseX, mouseY] = d3.mouse(rootNode);
let [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top];
el
.style({
left: x + 'px',
top: y + 'px'
})
.html(() => text(node));
el.style({
left: x + 'px',
top: y + 'px'
}).html(() => text(node));
},
'mouseout.tip': () => el.remove()
@ -63,14 +57,14 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
tip.attr = function setAttr(d) {
if (is(Object, d)) {
attrs = {...attrs, ...d};
attrs = { ...attrs, ...d };
}
return this;
};
tip.style = function setStyle(d) {
if (is(Object, d)) {
styles = {...styles, ...d};
styles = { ...styles, ...d };
}
return this;
};

View File

@ -1,31 +1,29 @@
const path = require('path');
module.exports = (env = {}) => (
{
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
library: 'd3tooltip',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist'),
filename: env.minimize ? 'd3tooltip.min.js' : 'd3tooltip.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
optimization: {
minimize: !!env.minimize
},
performance: {
hints: false
}
module.exports = (env = {}) => ({
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
library: 'd3tooltip',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist'),
filename: env.minimize ? 'd3tooltip.min.js' : 'd3tooltip.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
optimization: {
minimize: !!env.minimize
},
performance: {
hints: false
}
);
});

View File

@ -7,5 +7,6 @@ const parse = require('git-url-parse');
var ghUrl = process.argv[2];
const parsedUrl = parse(ghUrl);
const ghPagesUrl = 'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name;
const ghPagesUrl =
'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name;
console.log(ghPagesUrl);

View File

@ -14,7 +14,7 @@ var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
Object.keys(document.defaultView).forEach(property => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
@ -25,9 +25,9 @@ global.navigator = {
userAgent: 'node.js'
};
process.on('unhandledRejection', function (error) {
process.on('unhandledRejection', function(error) {
console.error('Unhandled Promise Rejection:');
console.error(error && error.stack || error);
console.error((error && error.stack) || error);
});
require('./user/pretest.js');

View File

@ -5,15 +5,17 @@ import { withKnobs } from '@storybook/addon-knobs';
import { withTheme } from './themeAddon/theme';
import '../src/presets.js';
addDecorator(withOptions({
name: 'DevUI',
url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui',
goFullScreen: false,
showStoriesPanel: true,
showAddonPanel: true,
showSearchBox: false,
addonPanelInRight: true
}));
addDecorator(
withOptions({
name: 'DevUI',
url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui',
goFullScreen: false,
showStoriesPanel: true,
showAddonPanel: true,
showSearchBox: false,
addonPanelInRight: true
})
);
addDecorator(withTheme);
addDecorator(withKnobs);

View File

@ -11,10 +11,11 @@
min-height: 400px;
margin: 0;
padding: 0;
font-family: "Helvetica Neue", "Lucida Grande", sans-serif;
font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif;
font-size: 11px;
}
#root, #root > div {
#root,
#root > div {
height: 100%;
}
#root > div > div {

View File

@ -7,10 +7,7 @@ addons.register(ADDON_ID, api => {
const channel = addons.getChannel();
addons.addPanel(PANEL_ID, {
title: 'Theme',
render: ({ active }) => (
active ?
<Panel channel={channel} api={api} />
: null
)
render: ({ active }) =>
active ? <Panel channel={channel} api={api} /> : null
});
});

View File

@ -4,7 +4,8 @@
font-style: normal;
font-weight: 400;
src: local('Source Code Pro'), local('SourceCodePro-Regular'),
url('./source-code-pro-v6-latin/source-code-pro-v6-latin-regular.woff2') format('woff2');
url('./source-code-pro-v6-latin/source-code-pro-v6-latin-regular.woff2')
format('woff2');
}
/* source-sans-pro-regular - latin */
@font-face {
@ -12,7 +13,8 @@
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'),
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-regular.woff2') format('woff2');
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-regular.woff2')
format('woff2');
}
/* source-sans-pro-600 - latin */
@font-face {
@ -20,7 +22,8 @@
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'),
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-600.woff2') format('woff2');
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-600.woff2')
format('woff2');
}
/* roboto-regular - latin */
@ -29,7 +32,7 @@
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'),
url('./roboto-v15-latin/roboto-v15-latin-regular.woff2') format('woff2');
url('./roboto-v15-latin/roboto-v15-latin-regular.woff2') format('woff2');
}
/* roboto-mono-regular - latin */
@font-face {
@ -37,7 +40,8 @@
font-style: normal;
font-weight: 400;
src: local('Roboto Mono'), local('RobotoMono-Regular'),
url('./roboto-mono-v4-latin/roboto-mono-v4-latin-regular.woff2') format('woff2');
url('./roboto-mono-v4-latin/roboto-mono-v4-latin-regular.woff2')
format('woff2');
}
/* Generated with https://google-webfonts-helper.herokuapp.com */

View File

@ -1,15 +1,16 @@
{
"name": "devui",
"version": "1.0.0-3",
"description":
"Reusable React components for building DevTools monitors and apps.",
"files": ["lib", "fonts"],
"description": "Reusable React components for building DevTools monitors and apps.",
"files": [
"lib",
"fonts"
],
"repository": {
"type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"
},
"author":
"Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"license": "MIT",
"scripts": {
"start": "npm run storybook",

View File

@ -10,13 +10,15 @@ const CommonWrapper = createStyledComponent(commonStyle);
export default class Button extends Component {
shouldComponentUpdate(nextProps) {
return nextProps.children !== this.props.children ||
return (
nextProps.children !== this.props.children ||
nextProps.disabled !== this.props.disabled ||
nextProps.mark !== this.props.mark ||
nextProps.size !== this.props.size ||
nextProps.primary !== this.props.primary ||
nextProps.tooltipPosition !== this.props.tooltipPosition ||
nextProps.title !== this.props.title;
nextProps.title !== this.props.title
);
}
onMouseUp = e => {
@ -56,15 +58,32 @@ export default class Button extends Component {
Button.propTypes = {
children: PropTypes.any.isRequired,
title: PropTypes.string,
tooltipPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right']),
tooltipPosition: PropTypes.oneOf([
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
]),
onClick: PropTypes.func,
type: PropTypes.string,
disabled: PropTypes.bool,
primary: PropTypes.bool,
size: PropTypes.oneOf(['big', 'normal', 'small']),
mark: PropTypes.oneOf([false, 'base08', 'base09', 'base0A', 'base0B',
'base0C', 'base0D', 'base0E', 'base0F']),
mark: PropTypes.oneOf([
false,
'base08',
'base09',
'base0A',
'base0B',
'base0C',
'base0D',
'base0E',
'base0F'
]),
theme: PropTypes.object
};

View File

@ -16,44 +16,62 @@ export const Container = styled.div`
storiesOf('Button', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<Button
title={text('Title', 'Hello Tooltip! \\a And from new line hello!')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
primary={boolean('primary', true)}
size={select('size', ['big', 'normal', 'small'], 'normal')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
</Container>
)
)
.add(
'mark',
() => (
<Container>
<Button
mark={select('mark', ['base08', 'base09', 'base0A', 'base0B',
'base0C', 'base0D', 'base0E', 'base0F'], 'base08')}
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
size={select('size', ['big', 'normal', 'small'], 'normal')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<MdFiberManualRecord />
</Button>
</Container>
)
);
.add('default', () => (
<Container>
<Button
title={text('Title', 'Hello Tooltip! \\a And from new line hello!')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
primary={boolean('primary', true)}
size={select('size', ['big', 'normal', 'small'], 'normal')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
</Container>
))
.add('mark', () => (
<Container>
<Button
mark={select(
'mark',
[
'base08',
'base09',
'base0A',
'base0B',
'base0C',
'base0D',
'base0E',
'base0F'
],
'base08'
)}
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
size={select('size', ['big', 'normal', 'small'], 'normal')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<MdFiberManualRecord />
</Button>
</Container>
));

View File

@ -2,7 +2,7 @@ import { css } from 'styled-components';
import { fadeIn } from '../../utils/animations';
import colorEffect from '../../utils/color';
const both = (tooltipPosition) => {
const both = tooltipPosition => {
switch (tooltipPosition) {
case 'bottom':
return `
@ -46,7 +46,7 @@ const both = (tooltipPosition) => {
}
};
const before = (tooltipPosition) => {
const before = tooltipPosition => {
switch (tooltipPosition) {
case 'bottom-left':
return `
@ -110,12 +110,13 @@ const after = (tooltipPosition, color) => {
}
};
const getDirection = (tooltipPosition) => {
return (tooltipPosition.indexOf('-') > 0) ?
tooltipPosition.substring(0, tooltipPosition.indexOf('-')) : tooltipPosition;
const getDirection = tooltipPosition => {
return tooltipPosition.indexOf('-') > 0
? tooltipPosition.substring(0, tooltipPosition.indexOf('-'))
: tooltipPosition;
};
const getSize = (size) => {
const getSize = size => {
switch (size) {
case 'big':
return 'min-height: 34px; padding: 2px 12px;';
@ -144,8 +145,13 @@ export const commonStyle = ({ theme, mark, size }) => css`
pointer-events: none;
}
${mark && `
background-color: ${colorEffect(theme[mark], 'fade', theme.light ? 0.92 : 0.82)};
${mark &&
`
background-color: ${colorEffect(
theme[mark],
'fade',
theme.light ? 0.92 : 0.82
)};
> svg {
color: ${theme[mark]};
@ -158,7 +164,13 @@ export const commonStyle = ({ theme, mark, size }) => css`
}
`;
export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size }) => css`
export const tooltipStyle = ({
theme,
tooltipTitle,
tooltipPosition,
mark,
size
}) => css`
${commonStyle({ theme, mark, size })}
&:before {
@ -170,7 +182,9 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
border-radius: 3px;
background: ${theme.base01};
border: 1px solid ${theme.base02};
box-shadow: 1px 1px 2px -1px ${theme.base02}, 1px 1px 2px 0px ${theme.base02};
box-shadow: 1px 1px 2px -1px ${theme.base02}, 1px 1px 2px 0px ${
theme.base02
};
}
&:after,
@ -194,7 +208,8 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''}
}
${theme.type !== 'material' && `
${theme.type !== 'material' &&
`
&:after {
content: "";
border-style: solid;

View File

@ -11,21 +11,30 @@ export const style = ({ theme, primary, disabled }) => css`
margin: auto 0;
border: 1px solid ${theme.base02};
border-radius: 4px;
${primary ? `
${
primary
? `
background-color: ${theme.base05};
color: ${theme.base00};
` : `
`
: `
background-color: ${theme.base01};
color: ${theme.base05};
`}
${disabled ? `
`
}
${
disabled
? `
cursor: not-allowed;
opacity: 0.6;
` : `
`
: `
cursor: pointer;
`}
`
}
${!disabled && `
${!disabled &&
`
&:hover,
&:focus {
background-color: ${primary ? theme.base07 : theme.base02};

View File

@ -13,20 +13,24 @@ export const style = ({ theme, primary, disabled }) => css`
text-transform: uppercase;
margin: auto 0;
background-color: ${primary ? theme.base05 : theme.base01};
${disabled ? `
${disabled
? `
cursor: not-allowed;
color: ${theme.base04};
opacity: 0.6;
` : `
`
: `
cursor: pointer;
color: ${primary ? theme.base00 : theme.base05};
`}
${!disabled ? `
${!disabled
? `
box-shadow:
0 2px 2px 0 ${theme.base03},
0 3px 1px -2px ${theme.base02},
0 1px 5px 0 ${theme.base02};
` : ''}
`
: ''}
&:hover, &:focus:not(:active) {

View File

@ -10,15 +10,23 @@ export const MainContainerWrapper = styled.div`
color: ${props => props.theme.base07};
font-size: 12px;
div, input, textarea, keygen, select, button {
font-family: ${props => props.theme.fontFamily || 'monaco, monospace'};
}
div,
input,
textarea,
keygen,
select,
button {
font-family: ${props => props.theme.fontFamily || 'monaco, monospace'};
}
.CodeMirror div, pre, .monitor-LogMonitor * {
font-family: ${props => props.theme.codeFontFamily || props.theme.fontFamily || 'monospace'};
}
.CodeMirror div,
pre,
.monitor-LogMonitor * {
font-family: ${props =>
props.theme.codeFontFamily || props.theme.fontFamily || 'monospace'};
}
.monitor {
.monitor {
flex-grow: 1;
display: flex;
flex-flow: column nowrap;

View File

@ -12,8 +12,10 @@ export default class ContextMenu extends Component {
}
componentWillReceiveProps(nextProps) {
if (nextProps.items !== this.props.items ||
nextProps.visible !== this.props.visible) {
if (
nextProps.items !== this.props.items ||
nextProps.visible !== this.props.visible
) {
this.updateItems(nextProps.items);
}
}
@ -32,7 +34,7 @@ export default class ContextMenu extends Component {
e.target.blur();
};
onClick = (e) => {
onClick = e => {
this.props.onClick(e.target.value);
};
@ -78,7 +80,7 @@ export default class ContextMenu extends Component {
});
}
menuRef = (c) => {
menuRef = c => {
this.menu = c;
};

View File

@ -16,17 +16,14 @@ export const Container = styled.div`
storiesOf('ContextMenu', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<ContextMenu
visible
onClick={action('menu item clicked')}
x={number('x', 100)}
y={number('y', 100)}
items={items}
/>
</Container>
)
);
.add('default', () => (
<Container>
<ContextMenu
visible
onClick={action('menu item clicked')}
x={number('x', 100)}
y={number('y', 100)}
items={items}
/>
</Container>
));

View File

@ -1,11 +1,13 @@
import { css } from 'styled-components';
export default ({ theme, left, top, visible }) => css`
${visible ? `
${visible
? `
visibility: visible;
opacity: 1;
transition: opacity 0.2s linear;
` : `
`
: `
visibility: hidden;
opacity: 0;
transition: visibility 0s 0.2s, opacity 0.2s linear;
@ -36,7 +38,7 @@ export default ({ theme, left, top, visible }) => css`
color: ${theme.base07};
}
&:focus {
outline:0;
outline: 0;
}
}
`;

View File

@ -52,47 +52,47 @@ export default class Dialog extends (PureComponent || Component) {
{!noHeader && (
<div className="mc-dialog--header">
<div>{schema ? schema.title || title : title}</div>
{!modal && <button onClick={onDismiss}>×</button>}
{!modal && <button onClick={onDismiss}>×</button>}
</div>
)}
<div className="mc-dialog--body">
{children}
{schema && (
<Form {...rest}>
{!noFooter &&
(
{!noFooter && (
<input
type="submit"
ref={this.getFormButtonRef}
className="mc-dialog--hidden"
/>
)
}
)}
</Form>
)}
</div>
{
!noFooter &&
(actions ?
<div className="mc-dialog--footer">
{submitText ?
[...actions,
<Button key="default-submit" primary onClick={this.onSubmit}>
{!noFooter &&
(actions ? (
<div className="mc-dialog--footer">
{submitText
? [
...actions,
<Button
key="default-submit"
primary
onClick={this.onSubmit}
>
{submitText}
</Button>
]
: actions
}
</div>
:
<div className="mc-dialog--footer">
<Button onClick={onDismiss}>Cancel</Button>
<Button primary onClick={this.onSubmit}>
{submitText || 'Submit'}
</Button>
</div>
)
}
: actions}
</div>
) : (
<div className="mc-dialog--footer">
<Button onClick={onDismiss}>Cancel</Button>
<Button primary onClick={this.onSubmit}>
{submitText || 'Submit'}
</Button>
</div>
))}
</div>
</DialogWrapper>
);

View File

@ -7,39 +7,33 @@ import { schema, uiSchema, formData } from '../../Form/stories/schema';
storiesOf('Dialog', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Dialog
title={text('title', 'Dialog Title')}
submitText={text('submitText', 'Submit!')}
open={boolean('open', true)}
noHeader={boolean('noHeader', false)}
noFooter={boolean('noFooter', false)}
modal={boolean('modal', false)}
fullWidth={boolean('fullWidth', false)}
onDismiss={action('dialog dismissed')}
onSubmit={action('dialog submitted')}
>
{text('children', 'Hello Dialog!')}
</Dialog>
)
)
.add(
'with form',
() => (
<Dialog
open={boolean('open', true)}
noHeader={boolean('noHeader', false)}
noFooter={boolean('noFooter', false)}
fullWidth={boolean('fullWidth', false)}
submitText={text('submitText', 'Submit!')}
formData={object('formData', formData)}
schema={object('schema', schema)}
uiSchema={object('uiSchema', uiSchema)}
onChange={action('form changed')}
onSubmit={action('form submitted')}
onDismiss={action('dialog dismissed')}
/>
)
);
.add('default', () => (
<Dialog
title={text('title', 'Dialog Title')}
submitText={text('submitText', 'Submit!')}
open={boolean('open', true)}
noHeader={boolean('noHeader', false)}
noFooter={boolean('noFooter', false)}
modal={boolean('modal', false)}
fullWidth={boolean('fullWidth', false)}
onDismiss={action('dialog dismissed')}
onSubmit={action('dialog submitted')}
>
{text('children', 'Hello Dialog!')}
</Dialog>
))
.add('with form', () => (
<Dialog
open={boolean('open', true)}
noHeader={boolean('noHeader', false)}
noFooter={boolean('noFooter', false)}
fullWidth={boolean('fullWidth', false)}
submitText={text('submitText', 'Submit!')}
formData={object('formData', formData)}
schema={object('schema', schema)}
uiSchema={object('uiSchema', uiSchema)}
onChange={action('form changed')}
onSubmit={action('form submitted')}
onDismiss={action('dialog dismissed')}
/>
));

View File

@ -36,10 +36,8 @@ export const style = ({ theme, open, fullWidth }) => css`
border: 1px outset ${theme.base01};
border-radius: 2px;
background-color: ${theme.base00};
box-shadow:
0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header {
display: flex;
@ -76,15 +74,23 @@ export const style = ({ theme, open, fullWidth }) => css`
> form {
padding: 0;
> .form-group { margin-bottom: 0; }
> div > fieldset {
legend { display: none; }
#root__description { margin-top: 0; }
> .form-group {
margin-bottom: 0;
}
.mc-dialog--hidden { display: none; }
> div > fieldset {
legend {
display: none;
}
#root__description {
margin-top: 0;
}
}
.mc-dialog--hidden {
display: none;
}
}
}
@ -102,7 +108,7 @@ export const style = ({ theme, open, fullWidth }) => css`
margin: 0 -16px -16px;
padding: 2px 10px;
border-top: 1px solid ${theme.base03};
> button {
margin-left: 5px;
}

View File

@ -35,10 +35,8 @@ export const style = ({ theme, open, fullWidth }) => css`
margin-bottom: 16px;
border: none;
background-color: ${theme.base00};
box-shadow:
0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header {
display: flex;
@ -73,15 +71,23 @@ export const style = ({ theme, open, fullWidth }) => css`
> form {
padding: 0;
> .form-group { margin-bottom: 0; }
> div > fieldset {
legend { display: none; }
#root__description { margin-top: 0; }
> .form-group {
margin-bottom: 0;
}
.mc-dialog--hidden { display: none; }
> div > fieldset {
legend {
display: none;
}
#root__description {
margin-top: 0;
}
}
.mc-dialog--hidden {
display: none;
}
}
}

View File

@ -4,28 +4,27 @@ import styled from 'styled-components';
import CodeMirror from 'codemirror';
import { defaultStyle, themedStyle } from './styles';
const EditorContainer = styled.div('',
({ theme }) => (theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme))
const EditorContainer = styled.div('', ({ theme }) =>
theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)
);
export default class Editor extends Component {
componentDidMount() {
this.cm = CodeMirror( // eslint-disable-line new-cap
this.node,
{
value: this.props.value,
mode: this.props.mode,
lineNumbers: this.props.lineNumbers,
lineWrapping: this.props.lineWrapping,
readOnly: this.props.readOnly,
autofocus: this.props.autofocus,
foldGutter: this.props.foldGutter,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
}
);
this.cm = CodeMirror(this.node, {
value: this.props.value,
mode: this.props.mode,
lineNumbers: this.props.lineNumbers,
lineWrapping: this.props.lineWrapping,
readOnly: this.props.readOnly,
autofocus: this.props.autofocus,
foldGutter: this.props.foldGutter,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
});
if (this.props.onChange) {
this.cm.on('change', (doc, change) => { this.props.onChange(doc.getValue(), change); });
this.cm.on('change', (doc, change) => {
this.props.onChange(doc.getValue(), change);
});
}
}

View File

@ -34,7 +34,9 @@ export default class WithTabs extends Component {
}
]}
selected={this.state.selected}
onClick={selected => { this.setState({ selected }); }}
onClick={selected => {
this.setState({ selected });
}}
align={select('align', ['left', 'right', 'center'], 'left')}
/>
);

View File

@ -30,11 +30,6 @@ storiesOf('Editor', module)
),
{ info: 'Based on [CodeMirror](http://codemirror.net/).' }
)
.add(
'with tabs',
() => (
<WithTabs
lineNumbers={boolean('lineNumbers', true)}
/>
)
);
.add('with tabs', () => (
<WithTabs lineNumbers={boolean('lineNumbers', true)} />
));

View File

@ -19,37 +19,79 @@ export const themedStyle = theme => css`
background-color: ${theme.base00};
color: ${theme.base04};
.cm-header { color: ${theme.base05}; }
.cm-quote { color: ${theme.base09}; }
.cm-header {
color: ${theme.base05};
}
.cm-quote {
color: ${theme.base09};
}
.cm-keyword { color: ${theme.base0F}; }
.cm-atom { color: ${theme.base0F}; }
.cm-number { color: ${theme.base0F}; }
.cm-def { color: ${theme.base0D}; }
.cm-keyword {
color: ${theme.base0F};
}
.cm-atom {
color: ${theme.base0F};
}
.cm-number {
color: ${theme.base0F};
}
.cm-def {
color: ${theme.base0D};
}
.cm-variable { color: ${theme.base05}; }
.cm-variable-2 { color: ${theme.base0A}; }
.cm-variable-3 { color: ${theme.base0E}; }
.cm-variable {
color: ${theme.base05};
}
.cm-variable-2 {
color: ${theme.base0A};
}
.cm-variable-3 {
color: ${theme.base0E};
}
.cm-property { color: ${theme.base0C}; }
.cm-operator { color: ${theme.base0E}; }
.cm-property {
color: ${theme.base0C};
}
.cm-operator {
color: ${theme.base0E};
}
.cm-comment {
color: ${theme.base05};
font-style: italic;
}
.cm-string { color: ${theme.base0B}; }
.cm-string-2 { color: ${theme.base0A}; }
.cm-string {
color: ${theme.base0B};
}
.cm-string-2 {
color: ${theme.base0A};
}
.cm-meta { color: ${theme.base0B}; }
.cm-qualifier { color: ${theme.base0A}; }
.cm-builtin { color: ${theme.base0F}; }
.cm-bracket { color: ${theme.base09}; }
.CodeMirror-matchingbracket { color: ${theme.base0B}; }
.CodeMirror-nonmatchingbracket { color: ${theme.base08}; }
.cm-tag { color: ${theme.base02}; }
.cm-attribute { color: ${theme.base0C}; }
.cm-meta {
color: ${theme.base0B};
}
.cm-qualifier {
color: ${theme.base0A};
}
.cm-builtin {
color: ${theme.base0F};
}
.cm-bracket {
color: ${theme.base09};
}
.CodeMirror-matchingbracket {
color: ${theme.base0B};
}
.CodeMirror-nonmatchingbracket {
color: ${theme.base08};
}
.cm-tag {
color: ${theme.base02};
}
.cm-attribute {
color: ${theme.base0C};
}
.cm-hr {
color: transparent;
@ -62,7 +104,9 @@ export const themedStyle = theme => css`
cursor: pointer;
}
.cm-special { color: ${theme.base0E}; }
.cm-special {
color: ${theme.base0E};
}
.cm-em {
color: #999;
@ -70,7 +114,9 @@ export const themedStyle = theme => css`
text-decoration-style: dotted;
}
.cm-strong { color: ${theme.base01}; }
.cm-strong {
color: ${theme.base01};
}
.cm-error,
.cm-invalidchar {
@ -78,7 +124,9 @@ export const themedStyle = theme => css`
border-bottom: 1px dotted ${theme.base08};
}
div.CodeMirror-selected { background: ${theme.base01}; }
div.CodeMirror-selected {
background: ${theme.base01};
}
.CodeMirror-line::selection,
.CodeMirror-line > span::selection,
@ -106,17 +154,27 @@ export const themedStyle = theme => css`
padding: 0 5px;
}
.CodeMirror-guttermarker-subtle { color: ${theme.base05}; }
.CodeMirror-guttermarker { color: ${theme.base09}; }
.CodeMirror-guttermarker-subtle {
color: ${theme.base05};
}
.CodeMirror-guttermarker {
color: ${theme.base09};
}
.CodeMirror-gutter .CodeMirror-gutter-text {
color: ${theme.base05};
}
.CodeMirror-cursor { border-left: 1px solid #819090; }
.CodeMirror-cursor {
border-left: 1px solid #819090;
}
.cm-fat-cursor .CodeMirror-cursor { background: ${theme.base02}; }
.cm-animate-fat-cursor { background-color: ${theme.base02}; }
.cm-fat-cursor .CodeMirror-cursor {
background: ${theme.base02};
}
.cm-animate-fat-cursor {
background-color: ${theme.base02};
}
.CodeMirror-activeline-background {
background: ${theme.base07};

View File

@ -10,16 +10,30 @@ const FormContainer = createStyledComponent(styles, JSONSchemaForm);
export default class Form extends (PureComponent || Component) {
render() {
const { widgets, children, submitText, primaryButton, noSubmit, ...rest } = this.props;
const {
widgets,
children,
submitText,
primaryButton,
noSubmit,
...rest
} = this.props;
return (
<FormContainer {...rest} widgets={{ ...customWidgets, ...widgets }}>
{
noSubmit ? <noscript /> :
children ||
<Button size="big" primary={primaryButton} theme={rest.theme} type="submit">
{submitText || 'Submit'}
</Button>
}
{noSubmit ? (
<noscript />
) : (
children || (
<Button
size="big"
primary={primaryButton}
theme={rest.theme}
type="submit"
>
{submitText || 'Submit'}
</Button>
)
)}
</FormContainer>
);
}
@ -33,5 +47,7 @@ Form.propTypes = {
schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object,
formData: PropTypes.any,
widgets: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object]))
widgets: PropTypes.objectOf(
PropTypes.oneOfType([PropTypes.func, PropTypes.object])
)
};

View File

@ -19,8 +19,9 @@ storiesOf('Form', module)
onSubmit={action('form submitted')}
/>
),
{ info:
'Wrapper around [`react-jsonschema-form`](https://github.com/mozilla-services/react-jsonschema-form)'
+ ' with custom widgets.'
{
info:
'Wrapper around [`react-jsonschema-form`](https://github.com/mozilla-services/react-jsonschema-form)' +
' with custom widgets.'
}
);

View File

@ -27,31 +27,19 @@ module.exports = {
title: 'A multiple choices list',
items: {
type: 'string',
enum: [
'foo',
'bar',
'fuzz'
]
enum: ['foo', 'bar', 'fuzz']
},
uniqueItems: true
},
numberEnum: {
type: 'number',
title: 'Number enum',
enum: [
1,
2,
3
]
enum: [1, 2, 3]
},
numberEnumRadio: {
type: 'number',
title: 'Number enum',
enum: [
1,
2,
3
]
enum: [1, 2, 3]
},
integerRange: {
title: 'Integer range',

View File

@ -1,368 +1,368 @@
import { css } from 'styled-components';
export default ({ theme }) => css`
padding: 10px;
line-height: 1.846;
font-size: 14px;
color: ${theme.base06};
padding: 10px;
line-height: 1.846;
font-size: 14px;
color: ${theme.base06};
fieldset {
padding: 0;
margin: 0;
border: 0;
min-width: 0;
}
fieldset {
padding: 0;
margin: 0;
border: 0;
min-width: 0;
}
legend {
display: block;
width: 100%;
padding: 0;
font-size: 20px;
color: ${theme.base04};
border: 0;
}
legend {
display: block;
width: 100%;
padding: 0;
font-size: 20px;
color: ${theme.base04};
border: 0;
}
label {
display: inline-block;
max-width: 100%;
font-weight: bold;
}
label {
display: inline-block;
max-width: 100%;
font-weight: bold;
}
.form-control {
display: block;
box-sizing: border-box;
font-size: 12px;
width: 100%;
color: ${theme.base05};
background-color: transparent;
background-image: none;
line-height: ${theme.inputInternalHeight}px;
padding: 0 ${theme.inputPadding}px;
border-style: solid;
border-width: ${theme.inputBorderWidth}px;
border-color: ${theme.inputBorderColor};
border-radius: ${theme.inputBorderRadius}px;
}
.form-control {
display: block;
box-sizing: border-box;
font-size: 12px;
width: 100%;
color: ${theme.base05};
background-color: transparent;
background-image: none;
line-height: ${theme.inputInternalHeight}px;
padding: 0 ${theme.inputPadding}px;
border-style: solid;
border-width: ${theme.inputBorderWidth}px;
border-color: ${theme.inputBorderColor};
border-radius: ${theme.inputBorderRadius}px;
}
.form-control:focus,
input.form-control:focus {
outline: 0;
${theme.inputFocusedStyle}
}
.form-control:focus,
input.form-control:focus {
outline: 0;
${theme.inputFocusedStyle}
}
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
background-color: transparent;
opacity: 1;
}
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
background-color: transparent;
opacity: 1;
}
.form-control[disabled],
fieldset[disabled] .form-control {
cursor: not-allowed;
}
.form-control[disabled],
fieldset[disabled] .form-control {
cursor: not-allowed;
}
textarea.form-control {
height: auto;
}
textarea.form-control {
height: auto;
}
.form-group {
margin-bottom: 5px;
}
.form-group {
margin-bottom: 5px;
}
.radio,
.checkbox {
position: relative;
display: block;
margin-top: 10px;
margin-bottom: 10px;
}
.radio,
.checkbox {
position: relative;
display: block;
margin-top: 10px;
margin-bottom: 10px;
}
.radio label,
.checkbox label {
min-height: 23px;
padding-left: 20px;
margin-bottom: 0;
font-weight: normal;
cursor: pointer;
}
.radio label,
.checkbox label {
min-height: 23px;
padding-left: 20px;
margin-bottom: 0;
font-weight: normal;
cursor: pointer;
}
.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
position: absolute;
margin-left: -20px;
margin-top: 4px \\9;
}
.radio input[type='radio'],
.radio-inline input[type='radio'],
.checkbox input[type='checkbox'],
.checkbox-inline input[type='checkbox'] {
position: absolute;
margin-left: -20px;
margin-top: 4px \\9;
}
.radio + .radio,
.checkbox + .checkbox {
margin-top: -5px;
}
.radio + .radio,
.checkbox + .checkbox {
margin-top: -5px;
}
.radio-inline,
.checkbox-inline {
position: relative;
display: inline-block;
padding-left: 25px;
margin-bottom: 0;
vertical-align: middle;
font-weight: normal;
cursor: pointer;
}
.radio-inline,
.checkbox-inline {
position: relative;
display: inline-block;
padding-left: 25px;
margin-bottom: 0;
vertical-align: middle;
font-weight: normal;
cursor: pointer;
}
.radio-inline + .radio-inline,
.checkbox-inline + .checkbox-inline {
margin-top: 0;
margin-left: 10px;
}
.radio-inline + .radio-inline,
.checkbox-inline + .checkbox-inline {
margin-top: 0;
margin-left: 10px;
}
.radio label,
.radio-inline label,
.checkbox label,
.checkbox-inline label {
padding-left: 25px;
}
.radio label,
.radio-inline label,
.checkbox label,
.checkbox-inline label {
padding-left: 25px;
}
.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="radio"],
.checkbox-inline input[type="radio"],
.radio input[type="checkbox"],
.radio-inline input[type="checkbox"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
margin-left: -25px;
}
.radio input[type='radio'],
.radio-inline input[type='radio'],
.checkbox input[type='radio'],
.checkbox-inline input[type='radio'],
.radio input[type='checkbox'],
.radio-inline input[type='checkbox'],
.checkbox input[type='checkbox'],
.checkbox-inline input[type='checkbox'] {
margin-left: -25px;
}
input[type="radio"],
.radio input[type="radio"],
.radio-inline input[type="radio"] {
position: relative;
margin-top: 6px;
margin-right: 4px;
vertical-align: top;
border: none;
background-color: transparent;
appearance: none;
cursor: pointer;
}
input[type='radio'],
.radio input[type='radio'],
.radio-inline input[type='radio'] {
position: relative;
margin-top: 6px;
margin-right: 4px;
vertical-align: top;
border: none;
background-color: transparent;
appearance: none;
cursor: pointer;
}
input[type="radio"]:focus,
.radio input[type="radio"]:focus,
.radio-inline input[type="radio"]:focus {
outline: none;
}
input[type='radio']:focus,
.radio input[type='radio']:focus,
.radio-inline input[type='radio']:focus {
outline: none;
}
input[type="radio"]:before,
.radio input[type="radio"]:before,
.radio-inline input[type="radio"]:before,
input[type="radio"]:after,
.radio input[type="radio"]:after,
.radio-inline input[type="radio"]:after {
content: "";
display: block;
width: 18px;
height: 18px;
border-radius: 50%;
transition: 240ms;
box-sizing: border-box;
}
input[type='radio']:before,
.radio input[type='radio']:before,
.radio-inline input[type='radio']:before,
input[type='radio']:after,
.radio input[type='radio']:after,
.radio-inline input[type='radio']:after {
content: '';
display: block;
width: 18px;
height: 18px;
border-radius: 50%;
transition: 240ms;
box-sizing: border-box;
}
input[type="radio"]:before,
.radio input[type="radio"]:before,
.radio-inline input[type="radio"]:before {
position: absolute;
left: 0;
top: -3px;
background-color: ${theme.base0D};
transform: scale(0);
}
input[type='radio']:before,
.radio input[type='radio']:before,
.radio-inline input[type='radio']:before {
position: absolute;
left: 0;
top: -3px;
background-color: ${theme.base0D};
transform: scale(0);
}
input[type="radio"]:after,
.radio input[type="radio"]:after,
.radio-inline input[type="radio"]:after {
position: relative;
top: -3px;
border: 2px solid ${theme.base03};
}
input[type='radio']:after,
.radio input[type='radio']:after,
.radio-inline input[type='radio']:after {
position: relative;
top: -3px;
border: 2px solid ${theme.base03};
}
input[type="radio"]:checked:before,
.radio input[type="radio"]:checked:before,
.radio-inline input[type="radio"]:checked:before {
transform: scale(0.5);
}
input[type='radio']:checked:before,
.radio input[type='radio']:checked:before,
.radio-inline input[type='radio']:checked:before {
transform: scale(0.5);
}
input[type="radio"]:disabled:checked:before,
.radio input[type="radio"]:disabled:checked:before,
.radio-inline input[type="radio"]:disabled:checked:before {
background-color: ${theme.base03};
}
input[type='radio']:disabled:checked:before,
.radio input[type='radio']:disabled:checked:before,
.radio-inline input[type='radio']:disabled:checked:before {
background-color: ${theme.base03};
}
input[type="radio"]:checked:after,
.radio input[type="radio"]:checked:after,
.radio-inline input[type="radio"]:checked:after {
border-color: ${theme.base0D};
}
input[type='radio']:checked:after,
.radio input[type='radio']:checked:after,
.radio-inline input[type='radio']:checked:after {
border-color: ${theme.base0D};
}
input[type="radio"]:disabled:after,
.radio input[type="radio"]:disabled:after,
.radio-inline input[type="radio"]:disabled:after,
input[type="radio"]:disabled:checked:after,
.radio input[type="radio"]:disabled:checked:after,
.radio-inline input[type="radio"]:disabled:checked:after {
border-color: ${theme.base03};
}
input[type='radio']:disabled:after,
.radio input[type='radio']:disabled:after,
.radio-inline input[type='radio']:disabled:after,
input[type='radio']:disabled:checked:after,
.radio input[type='radio']:disabled:checked:after,
.radio-inline input[type='radio']:disabled:checked:after {
border-color: ${theme.base03};
}
input[type="checkbox"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
position: relative;
border: none;
margin-bottom: -4px;
appearance: none;
cursor: pointer;
}
input[type='checkbox'],
.checkbox input[type='checkbox'],
.checkbox-inline input[type='checkbox'] {
position: relative;
border: none;
margin-bottom: -4px;
appearance: none;
cursor: pointer;
}
input[type="checkbox"]:focus,
.checkbox input[type="checkbox"]:focus,
.checkbox-inline input[type="checkbox"]:focus {
outline: none;
}
input[type='checkbox']:focus,
.checkbox input[type='checkbox']:focus,
.checkbox-inline input[type='checkbox']:focus {
outline: none;
}
input[type="checkbox"]:focus:after,
.checkbox input[type="checkbox"]:focus:after,
.checkbox-inline input[type="checkbox"]:focus:after {
border-color: ${theme.base0D};
}
input[type='checkbox']:focus:after,
.checkbox input[type='checkbox']:focus:after,
.checkbox-inline input[type='checkbox']:focus:after {
border-color: ${theme.base0D};
}
input[type="checkbox"]:after,
.checkbox input[type="checkbox"]:after,
.checkbox-inline input[type="checkbox"]:after {
content: "";
display: block;
width: 18px;
height: 18px;
margin-top: -2px;
margin-right: 5px;
border: 2px solid ${theme.base03};
border-radius: 4px;
transition: 240ms;
box-sizing: border-box;
}
input[type='checkbox']:after,
.checkbox input[type='checkbox']:after,
.checkbox-inline input[type='checkbox']:after {
content: '';
display: block;
width: 18px;
height: 18px;
margin-top: -2px;
margin-right: 5px;
border: 2px solid ${theme.base03};
border-radius: 4px;
transition: 240ms;
box-sizing: border-box;
}
input[type="checkbox"]:checked:before,
.checkbox input[type="checkbox"]:checked:before,
.checkbox-inline input[type="checkbox"]:checked:before {
content: "";
position: absolute;
top: 0;
left: 6px;
display: table;
width: 6px;
height: 12px;
border: 2px solid #fff;
border-top-width: 0;
border-left-width: 0;
transform: rotate(45deg);
box-sizing: border-box;
}
input[type='checkbox']:checked:before,
.checkbox input[type='checkbox']:checked:before,
.checkbox-inline input[type='checkbox']:checked:before {
content: '';
position: absolute;
top: 0;
left: 6px;
display: table;
width: 6px;
height: 12px;
border: 2px solid #fff;
border-top-width: 0;
border-left-width: 0;
transform: rotate(45deg);
box-sizing: border-box;
}
input[type="checkbox"]:checked:after,
.checkbox input[type="checkbox"]:checked:after,
.checkbox-inline input[type="checkbox"]:checked:after {
background-color: ${theme.base0D};
border-color: ${theme.base0D};
}
input[type='checkbox']:checked:after,
.checkbox input[type='checkbox']:checked:after,
.checkbox-inline input[type='checkbox']:checked:after {
background-color: ${theme.base0D};
border-color: ${theme.base0D};
}
input[type="checkbox"]:disabled:after,
.checkbox input[type="checkbox"]:disabled:after,
.checkbox-inline input[type="checkbox"]:disabled:after {
border-color: ${theme.base03};
}
input[type='checkbox']:disabled:after,
.checkbox input[type='checkbox']:disabled:after,
.checkbox-inline input[type='checkbox']:disabled:after {
border-color: ${theme.base03};
}
input[type="checkbox"]:disabled:checked:after,
.checkbox input[type="checkbox"]:disabled:checked:after,
.checkbox-inline input[type="checkbox"]:disabled:checked:after {
background-color: ${theme.base03};
border-color: transparent;
}
input[type='checkbox']:disabled:checked:after,
.checkbox input[type='checkbox']:disabled:checked:after,
.checkbox-inline input[type='checkbox']:disabled:checked:after {
background-color: ${theme.base03};
border-color: transparent;
}
input[type="radio"][disabled],
input[type="checkbox"][disabled],
input[type="radio"].disabled,
input[type="checkbox"].disabled,
fieldset[disabled] input[type="radio"],
fieldset[disabled] input[type="checkbox"] {
cursor: not-allowed;
}
input[type='radio'][disabled],
input[type='checkbox'][disabled],
input[type='radio'].disabled,
input[type='checkbox'].disabled,
fieldset[disabled] input[type='radio'],
fieldset[disabled] input[type='checkbox'] {
cursor: not-allowed;
}
.radio-inline.disabled,
.checkbox-inline.disabled,
fieldset[disabled] .radio-inline,
fieldset[disabled] .checkbox-inline {
cursor: not-allowed;
}
.radio-inline.disabled,
.checkbox-inline.disabled,
fieldset[disabled] .radio-inline,
fieldset[disabled] .checkbox-inline {
cursor: not-allowed;
}
.radio.disabled label,
.checkbox.disabled label,
fieldset[disabled] .radio label,
fieldset[disabled] .checkbox label {
cursor: not-allowed;
}
.radio.disabled label,
.checkbox.disabled label,
fieldset[disabled] .radio label,
fieldset[disabled] .checkbox label {
cursor: not-allowed;
}
.has-error .help-block,
.has-error .control-label,
.has-error .radio,
.has-error .checkbox,
.has-error .radio-inline,
.has-error .checkbox-inline,
.has-error.radio label,
.has-error.checkbox label,
.has-error.radio-inline label,
.has-error.checkbox-inline label {
color: ${theme.base08};
}
.has-error .help-block,
.has-error .control-label,
.has-error .radio,
.has-error .checkbox,
.has-error .radio-inline,
.has-error .checkbox-inline,
.has-error.radio label,
.has-error.checkbox label,
.has-error.radio-inline label,
.has-error.checkbox-inline label {
color: ${theme.base08};
}
.panel {
border: none;
border-radius: 2px;
box-shadow: 0 1px 4px ${theme.base03};
margin-bottom: 23px;
}
.panel {
border: none;
border-radius: 2px;
box-shadow: 0 1px 4px ${theme.base03};
margin-bottom: 23px;
}
.panel-heading {
padding: 5px 15px;
}
.panel-heading {
padding: 5px 15px;
}
.panel-title {
margin-top: 0;
margin-bottom: 0;
font-size: 15px;
}
.panel-title {
margin-top: 0;
margin-bottom: 0;
font-size: 15px;
}
.panel-danger {
box-shadow: 0 1px 4px ${theme.base08};
}
.panel-danger {
box-shadow: 0 1px 4px ${theme.base08};
}
.panel-danger>.panel-heading {
color: ${theme.base00};
background-color: ${theme.base08};
}
.panel-danger > .panel-heading {
color: ${theme.base00};
background-color: ${theme.base08};
}
.text-danger {
color: ${theme.base08};
}
.text-danger {
color: ${theme.base08};
}
.list-group {
padding: 0;
margin: 0;
}
.list-group {
padding: 0;
margin: 0;
}
.list-group-item {
position: relative;
display: block;
padding: 10px 15px;
}
.list-group-item {
position: relative;
display: block;
padding: 10px 15px;
}
`;

View File

@ -8,7 +8,9 @@ const SelectWidget = ({ options, multi, ...rest }) => (
);
const RangeWidget = ({
schema, readonly, autofocus,
schema,
readonly,
autofocus,
label, // eslint-disable-line
options, // eslint-disable-line
formContext, // eslint-disable-line

View File

@ -11,16 +11,22 @@ const NotificationWrapper = createStyledComponent(styles);
export default class Notification extends Component {
shouldComponentUpdate(nextProps) {
return nextProps.children !== this.props.children ||
nextProps.type !== this.props.type;
return (
nextProps.children !== this.props.children ||
nextProps.type !== this.props.type
);
}
getIcon = () => {
switch (this.props.type) {
case 'warning': return <WarningIcon />;
case 'error': return <ErrorIcon />;
case 'success': return <SuccessIcon />;
default: return null;
case 'warning':
return <WarningIcon />;
case 'error':
return <ErrorIcon />;
case 'success':
return <SuccessIcon />;
default:
return null;
}
};
@ -29,9 +35,11 @@ export default class Notification extends Component {
<NotificationWrapper type={this.props.type} theme={this.props.theme}>
{this.getIcon()}
<span>{this.props.children}</span>
{this.props.onClose &&
<button onClick={this.props.onClose}><CloseIcon /></button>
}
{this.props.onClose && (
<button onClick={this.props.onClose}>
<CloseIcon />
</button>
)}
</NotificationWrapper>
);
}

View File

@ -15,18 +15,17 @@ export const Container = styled.div`
storiesOf('Notification', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<Notification
type={
select('type', ['info', 'success', 'warning', 'error'], 'warning')
}
onClose={action('notification closed')}
>
{text('Message', 'Hello Notification')}
</Notification>
</Container>
)
);
.add('default', () => (
<Container>
<Notification
type={select(
'type',
['info', 'success', 'warning', 'error'],
'warning'
)}
onClose={action('notification closed')}
>
{text('Message', 'Hello Notification')}
</Notification>
</Container>
));

View File

@ -49,7 +49,8 @@ export default ({ theme, type }) => css`
opacity: 0.8;
}
& > button:hover, & > button:active {
& > button:hover,
& > button:active {
opacity: 1;
}

View File

@ -7,8 +7,10 @@ const SegmentedWrapper = createStyledComponent(styles);
export default class SegmentedControl extends Component {
shouldComponentUpdate(nextProps) {
return nextProps.disabled !== this.props.disabled ||
nextProps.selected !== this.props.selected;
return (
nextProps.disabled !== this.props.disabled ||
nextProps.selected !== this.props.selected
);
}
onClick = e => {

View File

@ -15,16 +15,13 @@ export const Container = styled.div`
storiesOf('SegmentedControl', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<SegmentedControl
values={['Button1', 'Button2', 'Button3']}
selected={text('selected', 'Button1')}
onClick={action('button selected')}
disabled={boolean('Disabled', false)}
/>
</Container>
)
);
.add('default', () => (
<Container>
<SegmentedControl
values={['Button1', 'Button2', 'Button3']}
selected={text('selected', 'Button1')}
onClick={action('button selected')}
disabled={boolean('Disabled', false)}
/>
</Container>
));

View File

@ -5,7 +5,8 @@ export default ({ theme, disabled }) => css`
display: flex;
flex-shrink: 0;
> [data-selected], > [data-selected]:hover {
> [data-selected],
> [data-selected]:hover {
background-color: ${theme.base04};
color: ${theme.base00};
}
@ -19,10 +20,12 @@ export default ({ theme, disabled }) => css`
border: 1px solid ${color(theme.base03, 'alpha', 0.4)};
border-left-width: 0;
padding: 5px 10px;
${disabled ? `
${disabled
? `
cursor: not-allowed;
opacity: 0.6;
` : `
`
: `
cursor: pointer;
color: ${theme.base05};
background-color: ${theme.base01};

View File

@ -13,17 +13,22 @@ export default class Select extends (PureComponent || Component) {
}
Select.propTypes = {
autosize: PropTypes.bool, // whether to enable autosizing or not
clearable: PropTypes.bool, // should it be possible to reset value
disabled: PropTypes.bool, // whether the Select is disabled or not
isLoading: PropTypes.bool, // whether the Select is loading externally or not
menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options
multi: PropTypes.bool, // multi-value input
searchable: PropTypes.bool, // whether to enable searching feature or not
simpleValue: PropTypes.bool, // pass the value with label to onChange
value: PropTypes.any, // initial field value
valueKey: PropTypes.string, // path of the label value in option objects
openOuterUp: PropTypes.bool // value to control the opening direction
autosize: PropTypes.bool, // whether to enable autosizing or not
clearable: PropTypes.bool, // should it be possible to reset value
disabled: PropTypes.bool, // whether the Select is disabled or not
isLoading: PropTypes.bool, // whether the Select is loading externally or not
menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options
multi: PropTypes.bool, // multi-value input
searchable: PropTypes.bool, // whether to enable searching feature or not
simpleValue: PropTypes.bool, // pass the value with label to onChange
value: PropTypes.any, // initial field value
valueKey: PropTypes.string, // path of the label value in option objects
openOuterUp: PropTypes.bool // value to control the opening direction
};
Select.defaultProps = { autosize: true, clearable: false, simpleValue: true, menuMaxHeight: 200 };
Select.defaultProps = {
autosize: true,
clearable: false,
simpleValue: true,
menuMaxHeight: 200
};

View File

@ -12,7 +12,7 @@ export const Container = styled.div`
width: 100%;
justify-content: center;
align-items: center;
> div {
width: 90%;
}
@ -39,8 +39,9 @@ storiesOf('Select', module)
/>
</Container>
),
{ info:
'Wrapper around [React Select](https://github.com/JedWatson/react-select) with themes '
+ 'and new props like `openOuterUp` and `menuMaxHeight`.'
{
info:
'Wrapper around [React Select](https://github.com/JedWatson/react-select) with themes ' +
'and new props like `openOuterUp` and `menuMaxHeight`.'
}
);

View File

@ -59,10 +59,9 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
}
&.is-open > .Select-control {
border-radius: ${openOuterUp ?
`0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px` :
`${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0`
};
border-radius: ${openOuterUp
? `0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px`
: `${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0`};
}
&.is-searchable {
@ -212,9 +211,7 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
.Select-arrow {
border-color: ${theme.base03} transparent transparent;
border-style: solid;
border-width:
${theme.selectArrowWidth}px
${theme.selectArrowWidth}px
border-width: ${theme.selectArrowWidth}px ${theme.selectArrowWidth}px
${theme.selectArrowWidth / 2}px;
display: inline-block;
height: 0;
@ -317,7 +314,8 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
border-bottom-right-radius: ${theme.inputBorderRadius}px;
border-top-right-radius: ${theme.inputBorderRadius}px;
cursor: default;
padding: ${Math.floor(theme.inputPadding / 4)}px ${Math.floor(theme.inputPadding / 2)}px;
padding: ${Math.floor(theme.inputPadding / 4)}px
${Math.floor(theme.inputPadding / 2)}px;
}
a.Select-value-label {

View File

@ -9,12 +9,14 @@ const ContainerWithValue = createStyledComponent(containerStyle);
export default class Slider extends Component {
shouldComponentUpdate(nextProps) {
return nextProps.label !== this.props.label ||
return (
nextProps.label !== this.props.label ||
nextProps.value !== this.props.value ||
nextProps.max !== this.props.max ||
nextProps.min !== this.props.min ||
nextProps.withValue !== this.props.withValue ||
nextProps.disabled !== this.props.disabled;
nextProps.disabled !== this.props.disabled
);
}
onChange = e => {
@ -25,7 +27,7 @@ export default class Slider extends Component {
const { label, sublabel, withValue, theme, ...rest } = this.props;
const { value, max, min, disabled } = rest;
const absMax = max - min;
const percent = (value - min) / absMax * 100;
const percent = ((value - min) / absMax) * 100;
const slider = <input {...rest} onChange={this.onChange} type="range" />;
return (
@ -35,13 +37,19 @@ export default class Slider extends Component {
withLabel={!!label}
theme={theme}
>
{label && <label>{label} {sublabel && <span>{sublabel}</span>}</label>}
{!withValue ? slider :
{label && (
<label>
{label} {sublabel && <span>{sublabel}</span>}
</label>
)}
{!withValue ? (
slider
) : (
<ContainerWithValue theme={theme}>
{slider}
<div>{value}</div>
</ContainerWithValue>
}
)}
</SliderWrapper>
);
}

View File

@ -15,20 +15,17 @@ export const Container = styled.div`
storiesOf('Slider', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<Slider
value={number('value', 0)}
min={number('min', 0)}
max={number('max', 100)}
label={text('label', 'Slider label')}
sublabel={text('sublabel', '(sublabel)')}
withValue={boolean('withValue', false)}
disabled={boolean('disabled', false)}
onChange={action('slider changed')}
/>
</Container>
)
);
.add('default', () => (
<Container>
<Slider
value={number('value', 0)}
min={number('min', 0)}
max={number('max', 100)}
label={text('label', 'Slider label')}
sublabel={text('sublabel', '(sublabel)')}
withValue={boolean('withValue', false)}
disabled={boolean('disabled', false)}
onChange={action('slider changed')}
/>
</Container>
));

View File

@ -42,21 +42,32 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
border-radius: 0.8em/1.1em;
font-size: 1em;
cursor: pointer;
background: linear-gradient(${theme.base02}, ${theme.base00}) padding-box, 50% 50% border-box;
background: linear-gradient(${theme.base02}, ${
theme.base00
}) padding-box, 50% 50% border-box;
background-size: 100% 100%;
}
${prefixSelectors('input', ['webkit-slider-runnable-track', 'moz-range-track', 'ms-track'], `{
${prefixSelectors(
'input',
['webkit-slider-runnable-track', 'moz-range-track', 'ms-track'],
`{
position: relative;
height: 0.8em;
border-radius: 0.5em;
box-shadow: 0 0 .125em ${theme.base04};
background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${theme.base01})
background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${
theme.base01
})
no-repeat ${theme.base00};
background-size: ${percent}% 100%;
}`)}
}`
)}
${prefixSelectors('input', ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], `{
${prefixSelectors(
'input',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
position: relative;
appearance: none;
cursor: ew-resize;
@ -68,13 +79,16 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
height: 1.5em;
border-radius: 50%;
cursor: pointer;
}`)}
}`
)}
${prefixSelectors('input:focus:not(:active)',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
${prefixSelectors(
'input:focus:not(:active)',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
box-shadow: 0 0 1px 2px ${theme.base0D};
}`)}
}`
)}
input::-moz-focus-outer {
border: 0;

View File

@ -19,7 +19,9 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
width: 100%;
color: ${theme.base06};
> span { color: ${theme.base04}; }
> span {
color: ${theme.base04};
}
}
input {
@ -32,8 +34,12 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
cursor: pointer;
color: inherit;
background-color: ${theme.base02};
background-image:
linear-gradient(90deg, currentcolor, currentcolor ${percent}%, transparent ${percent}%);
background-image: linear-gradient(
90deg,
currentcolor,
currentcolor ${percent}%,
transparent ${percent}%
);
background-clip: content-box;
height: 0.5em;
border-radius: 999px;
@ -41,7 +47,10 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
font-size: 1em;
}
${prefixSelectors('input', ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], `{
${prefixSelectors(
'input',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
width: 1.5em;
height: 1.5em;
background-image: none;
@ -53,14 +62,17 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
border 0.18s ${animationCurve},
box-shadow 0.18s ${animationCurve},
background 0.28s ${animationCurve};
}`)}
}`
)}
${prefixSelectors('input:focus:not(:active)',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
${prefixSelectors(
'input:focus:not(:active)',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
box-shadow: 0 0 0 8px ${color(theme.base0D, 'alpha', 0.5)};
transform: scale(1.2);
}`)}
}`
)}
input::-moz-focus-outer {
border: 0;

View File

@ -75,7 +75,9 @@ export default class Tabs extends Component {
return (
<TabsContainer position={this.props.position}>
{tabsHeader}
<div><this.SelectedComponent {...(this.selector && this.selector())} /></div>
<div>
<this.SelectedComponent {...this.selector && this.selector()} />
</div>
</TabsContainer>
);
}

View File

@ -22,9 +22,11 @@ export default class TabsHeader extends Component {
}
componentWillReceiveProps(nextProps) {
if (nextProps.tabs !== this.props.tabs ||
if (
nextProps.tabs !== this.props.tabs ||
nextProps.selected !== this.props.selected ||
nextProps.collapsible !== this.props.collapsible) {
nextProps.collapsible !== this.props.collapsible
) {
this.setState({ hiddenTabs: [], visibleTabs: nextProps.tabs.slice() });
}
}
@ -47,7 +49,9 @@ export default class TabsHeader extends Component {
if (this.iconWidth === 0) {
const tabButtons = this.tabsRef.children;
if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') {
this.iconWidth = tabButtons[tabButtons.length - 1].getBoundingClientRect().width;
this.iconWidth = tabButtons[
tabButtons.length - 1
].getBoundingClientRect().width;
shouldCollapse = true;
}
} else if (this.state.hiddenTabs.length === 0) {
@ -96,14 +100,15 @@ export default class TabsHeader extends Component {
if (tabsRefRight >= tabsWrapperRight - this.iconWidth) {
if (
this.props.position === 'right' && hiddenTabs.length > 0 &&
this.props.position === 'right' &&
hiddenTabs.length > 0 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width
tabsWrapperRef.getBoundingClientRect().width
) {
while (
i < tabs.length - 1 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width
tabsWrapperRef.getBoundingClientRect().width
) {
hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
@ -111,12 +116,16 @@ export default class TabsHeader extends Component {
}
} else {
while (
i > 0 && tabButtons[i] &&
tabButtons[i].getBoundingClientRect().right >= tabsWrapperRight - this.iconWidth
i > 0 &&
tabButtons[i] &&
tabButtons[i].getBoundingClientRect().right >=
tabsWrapperRight - this.iconWidth
) {
if (tabButtons[i].value !== selected) {
hiddenTabs.unshift(...visibleTabs.splice(i, 1));
this.hiddenTabsWidth.unshift(tabButtons[i].getBoundingClientRect().width);
this.hiddenTabsWidth.unshift(
tabButtons[i].getBoundingClientRect().width
);
} else {
tabsWrapperRight -= tabButtons[i].getBoundingClientRect().width;
}
@ -125,9 +134,10 @@ export default class TabsHeader extends Component {
}
} else {
while (
i < tabs.length - 1 && tabButtons[i] &&
tabButtons[i].getBoundingClientRect().right +
this.hiddenTabsWidth[0] < tabsWrapperRight - this.iconWidth
i < tabs.length - 1 &&
tabButtons[i] &&
tabButtons[i].getBoundingClientRect().right + this.hiddenTabsWidth[0] <
tabsWrapperRight - this.iconWidth
) {
hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
@ -150,7 +160,7 @@ export default class TabsHeader extends Component {
this.tabsRef = node;
};
expandMenu = (e) => {
expandMenu = e => {
const rect = e.currentTarget.children[0].getBoundingClientRect();
this.setState({
contextMenu: {
@ -171,11 +181,14 @@ export default class TabsHeader extends Component {
>
<div ref={this.getTabsRef}>
{visibleTabs}
{this.props.collapsible && visibleTabs.length < this.props.items.length &&
<button onClick={this.expandMenu} value="expandIcon"><CollapseIcon /></button>
}
{this.props.collapsible &&
visibleTabs.length < this.props.items.length && (
<button onClick={this.expandMenu} value="expandIcon">
<CollapseIcon />
</button>
)}
</div>
{this.props.collapsible && contextMenu &&
{this.props.collapsible && contextMenu && (
<ContextMenu
items={hiddenTabs}
onClick={this.props.onClick}
@ -183,7 +196,7 @@ export default class TabsHeader extends Component {
y={contextMenu.top}
visible={this.state.subMenuOpened}
/>
}
)}
</TabsWrapper>
);
}
@ -198,4 +211,3 @@ TabsHeader.propTypes = {
collapsible: PropTypes.bool,
selected: PropTypes.string
};

View File

@ -38,4 +38,5 @@ export const tabs = [
];
export const simple10Tabs = [];
for (let i = 1; i <= 10; i++) simple10Tabs.push({ name: `Tab${i}`, value: `${i}` });
for (let i = 1; i <= 10; i++)
simple10Tabs.push({ name: `Tab${i}`, value: `${i}` });

View File

@ -16,29 +16,25 @@ const Container = styled.div`
storiesOf('Tabs', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container><Tabs
.add('default', () => (
<Container>
<Tabs
tabs={simple10Tabs}
selected={text('selected', '2')}
main={boolean('main', true)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'left')}
/></Container>
)
)
.add(
'with content',
() => (
<Tabs
tabs={tabs}
selected={text('selected', 'Tab2')}
main={boolean('main', false)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', false)}
position={select('position', ['left', 'right', 'center'], 'left')}
/>
)
);
</Container>
))
.add('with content', () => (
<Tabs
tabs={tabs}
selected={text('selected', 'Tab2')}
main={boolean('main', false)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', false)}
position={select('position', ['left', 'right', 'center'], 'left')}
/>
));

View File

@ -11,10 +11,14 @@ export const TabsContainer = styled.div`
height: 100%;
> div > div:first-child {
${props => props.position !== 'left' && `
${props =>
props.position !== 'left' &&
`
margin-left: auto !important;
`}
${props => props.position === 'center' && `
${props =>
props.position === 'center' &&
`
margin-right: auto !important;
`}
}

View File

@ -7,7 +7,8 @@ export const style = ({ theme, main }) => css`
background-color: ${theme.base01};
width: 100%;
overflow: hidden;
${!main && `
${!main &&
`
border-top: 1px solid ${theme.base01};
border-bottom: 1px solid ${theme.base02};
`}
@ -39,9 +40,10 @@ export const style = ({ theme, main }) => css`
}
> [data-selected] {
${main ?
`border-bottom: 2px solid ${theme.base0D};` :
`
${
main
? `border-bottom: 2px solid ${theme.base0D};`
: `
background-color: ${theme.base00};
border: 1px solid ${theme.base02};
border-bottom: 1px solid ${theme.base00};

View File

@ -8,7 +8,8 @@ export const style = ({ theme, main }) => css`
background-color: ${theme.base01};
width: 100%;
overflow: hidden;
${!main && `
${!main &&
`
border-top: 1px solid ${theme.base01};
border-bottom: 1px solid ${theme.base02};
`}
@ -37,7 +38,7 @@ export const style = ({ theme, main }) => css`
border-bottom: 2px solid ${theme.base03};
color: ${theme.base04};
}
&.collapsed{
&.collapsed {
display: none;
}

View File

@ -2,12 +2,27 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import styled from 'styled-components';
import { withKnobs, text, number, boolean, select } from '@storybook/addon-knobs';
import {
withKnobs,
text,
number,
boolean,
select
} from '@storybook/addon-knobs';
import PlayIcon from 'react-icons/lib/md/play-arrow';
import RecordIcon from 'react-icons/lib/md/fiber-manual-record';
import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left';
import RightIcon from 'react-icons/lib/md/keyboard-arrow-right';
import { Toolbar, Divider, Spacer, Button, Select, Slider, SegmentedControl, Tabs } from '../../';
import {
Toolbar,
Divider,
Spacer,
Button,
Select,
Slider,
SegmentedControl,
Tabs
} from '../../';
import { options } from '../../Select/stories/options';
import { simple10Tabs } from '../../Tabs/stories/data';
@ -26,132 +41,165 @@ export const SliderContainer = styled.div`
storiesOf('Toolbar', module)
.addDecorator(withKnobs)
.add(
'default',
() => (
<Container>
<Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}>
.add('default', () => (
<Container>
<Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Divider />
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<RecordIcon />
</Button>
<Divider />
<Spacer />
<Select options={options} />
</Toolbar>
</Container>
))
.add('tabs', () => (
<Container>
<Toolbar>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Tabs
tabs={simple10Tabs}
selected={text('selected', '2')}
main={boolean('main', true)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'center')}
/>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
</Toolbar>
</Container>
))
.add('with slider', () => (
<Container>
<SliderContainer>
<Toolbar noBorder fullHeight compact>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled={boolean('Disabled', false)}
title={text('play title', 'Play')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
<PlayIcon />
</Button>
<Divider />
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<RecordIcon />
</Button>
<Divider />
<Spacer />
<Select options={options} />
</Toolbar>
</Container>
)
)
.add(
'tabs',
() => (
<Container>
<Toolbar>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Tabs
tabs={simple10Tabs}
selected={text('selected', '2')}
main={boolean('main', true)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'center')}
<Slider
value={number('value', 80)}
min={number('min', 0)}
max={number('max', 100)}
label={text('label', 'Slider label')}
withValue={boolean('withValue', false)}
onChange={action('slider changed')}
/>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
title="Previous state"
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled
onClick={action('previous state clicked')}
>
{text('Label', 'Hello Button')}
<LeftIcon />
</Button>
<Button
title="Next state"
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
onClick={action('next state clicked')}
>
<RightIcon />
</Button>
<SegmentedControl
values={['live', '1x']}
selected={select('selected', ['live', '1x'], 'live')}
onClick={action('button selected')}
/>
</Toolbar>
</Container>
)
)
.add(
'with slider',
() => (
<Container>
<SliderContainer>
<Toolbar noBorder fullHeight compact>
<Button
title={text('play title', 'Play')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
onClick={action('button clicked')}
>
<PlayIcon />
</Button>
<Slider
value={number('value', 80)}
min={number('min', 0)}
max={number('max', 100)}
label={text('label', 'Slider label')}
withValue={boolean('withValue', false)}
onChange={action('slider changed')}
/>
<Button
title="Previous state"
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled
onClick={action('previous state clicked')}
>
<LeftIcon />
</Button>
<Button
title="Next state"
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
onClick={action('next state clicked')}
>
<RightIcon />
</Button>
<SegmentedControl
values={['live', '1x']}
selected={select('selected', ['live', '1x'], 'live')}
onClick={action('button selected')}
/>
</Toolbar>
</SliderContainer>
</Container>
)
);
</SliderContainer>
</Container>
));

View File

@ -14,8 +14,7 @@ const Toolbar = styled.div`
text-align: center;
position: relative;
${({ borderPosition, theme }) =>
borderPosition && `border-${borderPosition}: 1px solid ${theme.base02};`
}
borderPosition && `border-${borderPosition}: 1px solid ${theme.base02};`}
& > div {
margin: auto ${props => (props.noBorder ? '0' : '1px;')};

View File

@ -1,4 +1,4 @@
export default (colors) => ({
export default colors => ({
...colors,
fontFamily: "'Source Sans Pro', sans-serif",
codeFontFamily: "'Source Code Pro', monospace",

View File

@ -1,4 +1,4 @@
export default (colors) => ({
export default colors => ({
fontFamily: "'Roboto', sans-serif",
codeFontFamily: "'Roboto Mono', monospace",
inputPadding: 5,

View File

@ -36,7 +36,9 @@ export const ripple = theme => `
top: 0;
left: 0;
pointer-events: none;
background-image: radial-gradient(circle, ${theme.base07} 11%, transparent 11%);
background-image: radial-gradient(circle, ${
theme.base07
} 11%, transparent 11%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);

View File

@ -7,6 +7,10 @@ import Color from 'color';
effect('#000000', 'alpha', 0.5);
*/
export default (color, effect, val) => new Color(color)[effect](val).hsl().string();
export default (color, effect, val) =>
new Color(color)
[effect](val)
.hsl()
.string();
// TODO: memoize it

View File

@ -1,17 +1,19 @@
import styled from 'styled-components';
import getDefaultTheme from '../themes/default';
const getStyle = (styles, type) => (
typeof styles === 'object' ? styles[type] || styles.default : styles
);
const getStyle = (styles, type) =>
typeof styles === 'object' ? styles[type] || styles.default : styles;
export default (styles, component) =>
styled(component || 'div')`${
props => (
props.theme.type ? getStyle(styles, props.theme.type) :
// used outside of container (theme provider)
getStyle(styles, 'default')({ ...props, theme: getDefaultTheme(props.theme) })
)
}`;
styled(component || 'div')`
${props =>
props.theme.type
? getStyle(styles, props.theme.type)
: // used outside of container (theme provider)
getStyle(styles, 'default')({
...props,
theme: getDefaultTheme(props.theme)
})}
`;
// TODO: memoize it?

View File

@ -5,7 +5,10 @@ import * as additionalSchemes from '../colorSchemes';
import invertColors from '../utils/invertColors';
export const schemes = { ...baseSchemes, ...additionalSchemes };
export const listSchemes = () => Object.keys(schemes).slice(1).sort(); // remove `__esModule`
export const listSchemes = () =>
Object.keys(schemes)
.slice(1)
.sort(); // remove `__esModule`
export const listThemes = () => Object.keys(themes);
export const getTheme = ({ theme: type, scheme, light }) => {

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Button } from '../src';
describe('Button', function () {
describe('Button', function() {
it('renders correctly', () => {
const wrapper = render(<Button>Text</Button>);
expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -3,10 +3,12 @@ import { render } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Container } from '../src';
describe('Container', function () {
describe('Container', function() {
it('renders correctly', () => {
const wrapper = render(
<Container themeData={{ theme: 'default', scheme: 'default', invert: false }}>
<Container
themeData={{ theme: 'default', scheme: 'default', invert: false }}
>
Text
</Container>
);

View File

@ -4,29 +4,23 @@ import { renderToJson } from 'enzyme-to-json';
import { ContextMenu } from '../src';
import { items } from '../src/ContextMenu/stories/data';
describe('ContextMenu', function () {
describe('ContextMenu', function() {
it('renders correctly', () => {
const wrapper = render(
<ContextMenu
items={items}
onClick={() => {}}
x={100}
y={100}
/>
<ContextMenu items={items} onClick={() => {}} x={100} y={100} />
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});
it('should handle the click event', () => {
const onClick = jest.fn();
const wrapper = mount(
<ContextMenu
items={items}
onClick={onClick}
x={100}
y={100}
/>);
<ContextMenu items={items} onClick={onClick} x={100} y={100} />
);
wrapper.find('button').first().simulate('click');
wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled();
});
});

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Dialog } from '../src';
describe('Dialog', function () {
describe('Dialog', function() {
it('renders correctly', () => {
const wrapper = render(<Dialog />);
expect(renderToJson(wrapper)).toMatchSnapshot();
@ -11,11 +11,7 @@ describe('Dialog', function () {
it('renders with props', () => {
const wrapper = render(
<Dialog
title="Dialog Title"
open
fullWidth
>
<Dialog title="Dialog Title" open fullWidth>
Hello Dialog!
</Dialog>
);
@ -31,7 +27,10 @@ describe('Dialog', function () {
const onDismiss = jest.fn();
const wrapper = mount(<Dialog open onDismiss={onDismiss} />);
wrapper.find('button').first().simulate('click');
wrapper
.find('button')
.first()
.simulate('click');
expect(onDismiss).toBeCalled();
});
@ -39,7 +38,10 @@ describe('Dialog', function () {
const onSubmit = jest.fn();
const wrapper = mount(<Dialog open onSubmit={onSubmit} />);
wrapper.find('button').last().simulate('click');
wrapper
.find('button')
.last()
.simulate('click');
expect(onSubmit).toBeCalled();
});
});

View File

@ -4,10 +4,10 @@ import { mountToJson } from 'enzyme-to-json';
import { Editor } from '../src';
import 'codemirror/mode/javascript/javascript';
describe('Editor', function () {
describe('Editor', function() {
const getBoundingClientRect = jest.fn();
const getClientRects = jest.fn();
document.body.createTextRange = function () {
document.body.createTextRange = function() {
return {
getBoundingClientRect() {
getBoundingClientRect();

View File

@ -4,14 +4,11 @@ import { shallowToJson } from 'enzyme-to-json';
import { Form } from '../src';
import { schema, uiSchema, formData } from '../src/Form/stories/schema';
describe('Form', function () {
describe('Form', function() {
it('renders correctly', () => {
const wrapper = shallow(
<Form
formData={formData}
schema={schema}
uiSchema={uiSchema}
/>);
<Form formData={formData} schema={schema} uiSchema={uiSchema} />
);
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
@ -23,18 +20,15 @@ describe('Form', function () {
formData={formData}
schema={schema}
uiSchema={uiSchema}
/>);
/>
);
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
it('renders with no button', () => {
const wrapper = shallow(
<Form
formData={formData}
schema={schema}
uiSchema={uiSchema}
noSubmit
/>);
<Form formData={formData} schema={schema} uiSchema={uiSchema} noSubmit />
);
expect(shallowToJson(wrapper)).toMatchSnapshot();
});

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Notification } from '../src';
describe('Notification', function () {
describe('Notification', function() {
it('renders correctly', () => {
const wrapper = render(<Notification>Message</Notification>);
expect(renderToJson(wrapper)).toMatchSnapshot();
@ -11,14 +11,18 @@ describe('Notification', function () {
it('renders with props', () => {
const wrapper = render(
<Notification type="error" onClose={() => {}}>Message</Notification>
<Notification type="error" onClose={() => {}}>
Message
</Notification>
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});
it('should handle the click event', () => {
const onClose = jest.fn();
const wrapper = mount(<Notification onClose={onClose}>Message</Notification>);
const wrapper = mount(
<Notification onClose={onClose}>Message</Notification>
);
wrapper.find('button').simulate('click');
expect(onClose).toBeCalled();

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { SegmentedControl } from '../src';
describe('SegmentedControl', function () {
describe('SegmentedControl', function() {
it('renders correctly', () => {
const wrapper = render(
<SegmentedControl
@ -17,15 +17,19 @@ describe('SegmentedControl', function () {
});
it('should handle the click event', () => {
const onClick = jest.fn();
const wrapper = mount(<SegmentedControl
values={['Button1', 'Button2', 'Button3']}
selected="Button1"
disabled={false}
onClick={onClick}
/>
const wrapper = mount(
<SegmentedControl
values={['Button1', 'Button2', 'Button3']}
selected="Button1"
disabled={false}
onClick={onClick}
/>
);
wrapper.find('button').first().simulate('click');
wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled();
});
});

View File

@ -4,7 +4,7 @@ import { renderToJson, mountToJson } from 'enzyme-to-json';
import { Select } from '../src';
import { options } from '../src/Select/stories/options';
describe('Select', function () {
describe('Select', function() {
it('renders correctly', () => {
const wrapper = render(<Select options={options} onChange={() => {}} />);
expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Slider } from '../src';
describe('Slider', function () {
describe('Slider', function() {
it('renders correctly', () => {
const wrapper = render(<Slider />);
expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -4,7 +4,7 @@ import { renderToJson } from 'enzyme-to-json';
import { Tabs } from '../src';
import { tabs, simple10Tabs } from '../src/Tabs/stories/data';
describe('Tabs', function () {
describe('Tabs', function() {
it('renders correctly', () => {
const wrapper = render(<Tabs tabs={tabs} onClick={() => {}} />);
expect(renderToJson(wrapper)).toMatchSnapshot();
@ -12,22 +12,14 @@ describe('Tabs', function () {
it('renders with props', () => {
const wrapper = render(
<Tabs
tabs={tabs}
onClick={() => {}}
selected="Tab2"
/>
<Tabs tabs={tabs} onClick={() => {}} selected="Tab2" />
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});
it('renders tabs without inner components', () => {
const wrapper = render(
<Tabs
tabs={simple10Tabs}
onClick={() => {}}
selected="5"
/>
<Tabs tabs={simple10Tabs} onClick={() => {}} selected="5" />
);
expect(renderToJson(wrapper)).toMatchSnapshot();
});
@ -36,7 +28,10 @@ describe('Tabs', function () {
const onClick = jest.fn();
const wrapper = mount(<Tabs tabs={tabs} onClick={onClick} />);
wrapper.find('button').first().simulate('click');
wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled();
});
});

View File

@ -3,7 +3,7 @@ import { render } from 'enzyme';
import { renderToJson } from 'enzyme-to-json';
import { Toolbar, Divider, Spacer, Button } from '../src';
describe('Toolbar', function () {
describe('Toolbar', function() {
it('renders correctly', () => {
const wrapper = render(
<Toolbar>
@ -17,9 +17,7 @@ describe('Toolbar', function () {
});
it('renders with props', () => {
const wrapper = render(
<Toolbar borderPosition="top" />
);
const wrapper = render(<Toolbar borderPosition="top" />);
expect(renderToJson(wrapper)).toMatchSnapshot();
});
});

View File

@ -2,10 +2,10 @@
exports[`Button renders correctly 1`] = `
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
Text
</button>

View File

@ -2,7 +2,7 @@
exports[`Container renders correctly 1`] = `
<div
class="sc-bdVaJa dRsjcb"
class="sc-bdVaJa ODaHo"
>
Text
</div>

View File

@ -2,7 +2,7 @@
exports[`ContextMenu renders correctly 1`] = `
<div
class="sc-EHOje dIpPFG"
class="sc-EHOje cfLLnh"
>
<button
value="Menu Item 1"

View File

@ -2,7 +2,7 @@
exports[`Dialog renders correctly 1`] = `
<div
class="sc-iwsKbI fOBkrR"
class="sc-iwsKbI islPis"
>
<div />
<div>
@ -21,19 +21,19 @@ exports[`Dialog renders correctly 1`] = `
class="mc-dialog--footer"
>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
Cancel
</button>
</div>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat iMkoYt"
class="sc-htpNat cvNnmn"
>
Submit
</button>
@ -45,7 +45,7 @@ exports[`Dialog renders correctly 1`] = `
exports[`Dialog renders modal 1`] = `
<div
class="sc-iwsKbI fOBkrR"
class="sc-iwsKbI islPis"
>
<div />
<div>
@ -61,19 +61,19 @@ exports[`Dialog renders modal 1`] = `
class="mc-dialog--footer"
>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
Cancel
</button>
</div>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat iMkoYt"
class="sc-htpNat cvNnmn"
>
Submit
</button>
@ -85,7 +85,7 @@ exports[`Dialog renders modal 1`] = `
exports[`Dialog renders with props 1`] = `
<div
class="sc-iwsKbI fpUYbC"
class="sc-iwsKbI hRSLqU"
open=""
>
<div />
@ -109,19 +109,19 @@ exports[`Dialog renders with props 1`] = `
class="mc-dialog--footer"
>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
Cancel
</button>
</div>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat iMkoYt"
class="sc-htpNat cvNnmn"
>
Submit
</button>

View File

@ -14,7 +14,7 @@ exports[`Editor renders correctly 1`] = `
innerRef={[Function]}
>
<div
className="sc-gZMcBi eROHUl"
className="sc-gZMcBi bFOJgt"
/>
</styled.div>
</Editor>

View File

@ -2,7 +2,7 @@
exports[`Notification renders correctly 1`] = `
<div
class="sc-fjdhpX fmuodm"
class="sc-fjdhpX gcvrGp"
type="info"
>
<span>
@ -13,7 +13,7 @@ exports[`Notification renders correctly 1`] = `
exports[`Notification renders with props 1`] = `
<div
class="sc-fjdhpX fmuodm"
class="sc-fjdhpX gcvrGp"
type="error"
>
<svg

View File

@ -2,7 +2,7 @@
exports[`SegmentedControl renders correctly 1`] = `
<div
class="sc-jTzLTM jHNWjD"
class="sc-jTzLTM bwMlok"
>
<button
data-selected="true"

View File

@ -2,7 +2,7 @@
exports[`Select renders correctly 1`] = `
<div
class="Select sc-bZQynM lpCNM is-searchable Select--single"
class="Select sc-bZQynM eKUTFA is-searchable Select--single"
>
<div
class="Select-control"
@ -47,7 +47,7 @@ exports[`Select renders correctly 1`] = `
exports[`Select renders with props 1`] = `
<div
class="Select sc-bZQynM dVPEwd has-value is-clearable is-disabled is-loading Select--multi"
class="Select sc-bZQynM lbesTc has-value is-clearable is-disabled is-loading Select--multi"
>
<div
class="Select-control"
@ -155,7 +155,7 @@ exports[`Select should select another option 1`] = `
autosize={true}
backspaceRemoves={true}
backspaceToRemoveMessage="Press backspace to remove {label}"
className="sc-bZQynM lpCNM"
className="sc-bZQynM eKUTFA"
clearAllText="Clear all"
clearRenderer={[Function]}
clearValueText="Clear value"
@ -215,7 +215,7 @@ exports[`Select should select another option 1`] = `
valueKey="value"
>
<div
className="Select sc-bZQynM lpCNM is-open is-searchable Select--single"
className="Select sc-bZQynM eKUTFA is-open is-searchable Select--single"
>
<div
className="Select-control"
@ -404,7 +404,7 @@ exports[`Select shouldn't find any results 1`] = `
autosize={true}
backspaceRemoves={true}
backspaceToRemoveMessage="Press backspace to remove {label}"
className="sc-bZQynM lpCNM"
className="sc-bZQynM eKUTFA"
clearAllText="Clear all"
clearRenderer={[Function]}
clearValueText="Clear value"
@ -464,7 +464,7 @@ exports[`Select shouldn't find any results 1`] = `
valueKey="value"
>
<div
className="Select sc-bZQynM lpCNM is-open is-searchable Select--single"
className="Select sc-bZQynM eKUTFA is-open is-searchable Select--single"
>
<div
className="Select-control"

View File

@ -2,7 +2,7 @@
exports[`Slider renders correctly 1`] = `
<div
class="sc-gzVnrw gTqmBD"
class="sc-gzVnrw hKBSWW"
>
<input
max="100"
@ -15,7 +15,7 @@ exports[`Slider renders correctly 1`] = `
exports[`Slider renders with props 1`] = `
<div
class="sc-gzVnrw csqRof"
class="sc-gzVnrw gnJNaZ"
disabled=""
>
<label>

View File

@ -5,7 +5,7 @@ exports[`Tabs renders correctly 1`] = `
class="sc-VigVT fmiisu"
>
<div
class="sc-gqjmRU gEvsQs"
class="sc-gqjmRU kpDKzc"
>
<div>
<button
@ -33,7 +33,7 @@ exports[`Tabs renders tabs without inner components 1`] = `
class="sc-VigVT fmiisu"
>
<div
class="sc-gqjmRU gEvsQs"
class="sc-gqjmRU kpDKzc"
>
<div>
<button
@ -97,7 +97,7 @@ exports[`Tabs renders with props 1`] = `
class="sc-VigVT fmiisu"
>
<div
class="sc-gqjmRU gEvsQs"
class="sc-gqjmRU kpDKzc"
>
<div>
<button

View File

@ -5,10 +5,10 @@ exports[`Toolbar renders correctly 1`] = `
class="sc-jzJRlG bBrbNn"
>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
1
</button>
@ -20,10 +20,10 @@ exports[`Toolbar renders correctly 1`] = `
class="sc-kAzzGY lcEaIs"
/>
<div
class="sc-ifAKCX jTewnV"
class="sc-ifAKCX evScRP"
>
<button
class="sc-htpNat fWcQZ"
class="sc-htpNat ldLqpm"
>
2
</button>

View File

@ -5,15 +5,16 @@ The following opinions must be taken into account since the primary use case of
- Objects and arrays deeply nested within collections are not converted into a tree structure. See `someNestedObject` and `someNestedArray` in the [output](https://github.com/romseguy/map2tree#output) below, or the [corresponding test](https://github.com/romseguy/map2tree/blob/master/test/map2tree.js#L140).
- Provides support for [Immutable.js](https://github.com/facebook/immutable-js) data structures (only List and Map though).
# Usage
```javascript
map2tree(someMap, options = {
key: 'state', // the name you want for as the root node of the output tree
pushMethod: 'push' // use 'unshift' to change the order children nodes are added
})
map2tree(
someMap,
(options = {
key: 'state', // the name you want for as the root node of the output tree
pushMethod: 'push' // use 'unshift' to change the order children nodes are added
})
);
```
# Input
@ -22,16 +23,16 @@ map2tree(someMap, options = {
const someMap = {
someReducer: {
todos: [
{title: 'map', someNestedObject: {foo: 'bar'}},
{title: 'to', someNestedArray: ['foo', 'bar']},
{title: 'tree'},
{title: 'map2tree'}
{ title: 'map', someNestedObject: { foo: 'bar' } },
{ title: 'to', someNestedArray: ['foo', 'bar'] },
{ title: 'tree' },
{ title: 'map2tree' }
],
completedCount: 1
},
otherReducer: {
foo: 0,
bar:{key: 'value'}
bar: { key: 'value' }
}
};
```

View File

@ -11,7 +11,7 @@
"test": "jest",
"prepare": "npm run build && npm run build:umd",
"prepublishOnly": "npm run test && npm run clean && npm run build && npm run build:umd && npm run build:umd:min"
},
},
"repository": {
"type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git"

View File

@ -19,16 +19,24 @@ function visit(parent, visitFn, childrenFn) {
function getNode(tree, key) {
let node = null;
visit(tree, d => {
if (d.name === key) {
node = d;
}
}, d => d.children);
visit(
tree,
d => {
if (d.name === key) {
node = d;
}
},
d => d.children
);
return node;
}
export default function map2tree(root, options = {}, tree = {name: options.key || 'state', children: []}) {
export default function map2tree(
root,
options = {},
tree = { name: options.key || 'state', children: [] }
) {
if (!isPlainObject(root) && root && !root.toJS) {
return {};
}
@ -41,8 +49,11 @@ export default function map2tree(root, options = {}, tree = {name: options.key |
}
mapValues(root && root.toJS ? root.toJS() : root, (maybeImmutable, key) => {
const value = maybeImmutable && maybeImmutable.toJS ? maybeImmutable.toJS() : maybeImmutable;
let newNode = {name: key};
const value =
maybeImmutable && maybeImmutable.toJS
? maybeImmutable.toJS()
: maybeImmutable;
let newNode = { name: key };
if (isArray(value)) {
newNode.children = [];
@ -61,7 +72,7 @@ export default function map2tree(root, options = {}, tree = {name: options.key |
currentNode.children[pushMethod](newNode);
map2tree(value, {key, pushMethod}, tree);
map2tree(value, { key, pushMethod }, tree);
});
return tree;

View File

@ -3,7 +3,7 @@ import immutable from 'immutable';
test('# rootNodeKey', () => {
const map = {};
const options = {key: 'foo'};
const options = { key: 'foo' };
expect(map2tree(map, options).name).toBe('foo');
});
@ -16,9 +16,7 @@ describe('# shallow map', () => {
const expected = {
name: 'state',
children: [
{name: 'a', value: null}
]
children: [{ name: 'a', value: null }]
};
expect(map2tree(map)).toEqual(expected);
@ -33,10 +31,7 @@ describe('# shallow map', () => {
const expected = {
name: 'state',
children: [
{name: 'a', value: 'foo'},
{name: 'b', value: 'bar'}
]
children: [{ name: 'a', value: 'foo' }, { name: 'b', value: 'bar' }]
};
expect(map2tree(map)).toEqual(expected);
@ -45,14 +40,12 @@ describe('# shallow map', () => {
test('## object', () => {
const map = {
a: {aa: 'foo'}
a: { aa: 'foo' }
};
const expected = {
name: 'state',
children: [
{name: 'a', children: [{name: 'aa', value: 'foo'}]}
]
children: [{ name: 'a', children: [{ name: 'aa', value: 'foo' }] }]
};
expect(map2tree(map)).toEqual(expected);
@ -61,24 +54,27 @@ describe('# shallow map', () => {
test('## immutable Map', () => {
const map = {
a: immutable.fromJS({aa: 'foo', ab: 'bar'})
a: immutable.fromJS({ aa: 'foo', ab: 'bar' })
};
const expected = {
name: 'state',
children: [
{name: 'a', children: [{name: 'aa', value: 'foo'}, {name: 'ab', value: 'bar'}]}
{
name: 'a',
children: [{ name: 'aa', value: 'foo' }, { name: 'ab', value: 'bar' }]
}
]
};
expect(map2tree(map)).toEqual(expected);
})
});
});
describe('# deep map', () => {
test('## null', () => {
const map = {
a: {aa: null}
a: { aa: null }
};
const expected = {
@ -102,7 +98,7 @@ describe('# deep map', () => {
test('## object', () => {
const map = {
a: {aa: {aaa: 'foo'}}
a: { aa: { aaa: 'foo' } }
};
const expected = {
@ -113,9 +109,7 @@ describe('# deep map', () => {
children: [
{
name: 'aa',
children: [
{name: 'aaa', value: 'foo'}
]
children: [{ name: 'aaa', value: 'foo' }]
}
]
}
@ -129,21 +123,18 @@ describe('# deep map', () => {
describe('# array map', () => {
const map = {
a: [
1,
2
]
a: [1, 2]
};
test('## push', () => {
const expected = {
name: 'state',
children: [{
name: 'a',
children: [
{name: 'a[0]', value: 1},
{name: 'a[1]', value: 2}]
}]
children: [
{
name: 'a',
children: [{ name: 'a[0]', value: 1 }, { name: 'a[1]', value: 2 }]
}
]
};
expect(map2tree(map)).toEqual(expected);
@ -151,16 +142,15 @@ describe('# array map', () => {
});
test('## unshift', () => {
const options = {pushMethod: 'unshift'};
const options = { pushMethod: 'unshift' };
const expected = {
name: 'state',
children: [{
name: 'a',
children: [
{name: 'a[1]', value: 2},
{name: 'a[0]', value: 1}
]
}]
children: [
{
name: 'a',
children: [{ name: 'a[1]', value: 2 }, { name: 'a[0]', value: 1 }]
}
]
};
expect(map2tree(map, options)).toEqual(expected);
@ -169,33 +159,28 @@ describe('# array map', () => {
test('## null', () => {
const map = {
a: [
null
]
a: [null]
};
const expected = {
name: 'state',
children: [{
name: 'a',
children: [
{name: 'a[0]', value: null}
]
}]
children: [
{
name: 'a',
children: [{ name: 'a[0]', value: null }]
}
]
};
expect(map2tree(map)).toEqual(expected);
expect(map2tree(immutable.fromJS(map))).toEqual(expected);
})
});
});
describe('# collection map', () => {
test('## value', () => {
const map = {
a: [
{aa: 1},
{aa: 2}
]
a: [{ aa: 1 }, { aa: 2 }]
};
const expected = {
@ -204,8 +189,8 @@ describe('# collection map', () => {
{
name: 'a',
children: [
{name: 'a[0]', object: {aa: 1}},
{name: 'a[1]', object: {aa: 2}}
{ name: 'a[0]', object: { aa: 1 } },
{ name: 'a[1]', object: { aa: 2 } }
]
}
]
@ -217,9 +202,7 @@ describe('# collection map', () => {
test('## object', () => {
const map = {
a: [
{aa: {aaa: 'foo'}}
]
a: [{ aa: { aaa: 'foo' } }]
};
const expected = {
@ -227,14 +210,12 @@ describe('# collection map', () => {
children: [
{
name: 'a',
children: [
{name: 'a[0]', object: {aa: {aaa: 'foo'}}}
]
children: [{ name: 'a[0]', object: { aa: { aaa: 'foo' } } }]
}
]
};
expect(map2tree(map)).toEqual(expected);
expect(map2tree(immutable.fromJS(map))).toEqual(expected);
})
});
});

View File

@ -1,31 +1,29 @@
const path = require('path');
module.exports = (env = {}) => (
{
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
library: 'd3tooltip',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist'),
filename: env.minimize ? 'map2tree.min.js' : 'map2tree.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
optimization: {
minimize: !!env.minimize
},
performance: {
hints: false
}
module.exports = (env = {}) => ({
mode: 'production',
entry: {
app: ['./src/index.js']
},
output: {
library: 'd3tooltip',
libraryTarget: 'umd',
path: path.resolve(__dirname, 'dist'),
filename: env.minimize ? 'map2tree.min.js' : 'map2tree.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
optimization: {
minimize: !!env.minimize
},
performance: {
hints: false
}
);
});

View File

@ -2,7 +2,6 @@ The MIT License (MIT)
Copyright (c) 2015 Shusaku Uesugi, (c) 2016-present Alexander Kuznetsov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights

View File

@ -33,6 +33,7 @@ Check out [examples](examples) directory for more details.
### Theming
This component now uses [react-base16-styling](https://github.com/alexkuz/react-base16-styling) module, which allows to customize component via `theme` property, which can be the following:
- [base16](http://chriskempson.github.io/base16) theme data. [The example theme data can be found here](https://github.com/gaearon/redux-devtools/tree/75322b15ee7ba03fddf10ac3399881e302848874/src/react/themes).
- object that contains style objects, strings (that treated as classnames) or functions. A function is used to extend its first argument `{ style, className }` and should return an object with the same structure. Other arguments depend on particular context (and should be described here). See [createStylingFromTheme.js](https://github.com/alexkuz/react-json-tree/blob/feature-refactor-styling/src/createStylingFromTheme.js) for the list of styling object keys. Also, this object can extend `base16` theme via `extend` property.
@ -62,8 +63,7 @@ const theme = {
<div>
<JSONTree data={data} theme={theme} invertTheme={false} />
</div>
</div>;
```
#### Result (Monokai theme, dark background):
@ -74,21 +74,24 @@ const theme = {
```jsx
<div>
<JSONTree data={data} theme={{
extend: theme,
// underline keys for literal values
valueLabel: {
textDecoration: 'underline'
},
// switch key for objects to uppercase when object is expanded.
// `nestedNodeLabel` receives additional arguments `expanded` and `keyPath`
nestedNodeLabel: ({ style }, nodeType, expanded) => ({
style: {
...style,
textTransform: expanded ? 'uppercase' : style.textTransform
}
})
}} />
<JSONTree
data={data}
theme={{
extend: theme,
// underline keys for literal values
valueLabel: {
textDecoration: 'underline'
},
// switch key for objects to uppercase when object is expanded.
// `nestedNodeLabel` receives additional arguments `expanded` and `keyPath`
nestedNodeLabel: ({ style }, nodeType, expanded) => ({
style: {
...style,
textTransform: expanded ? 'uppercase' : style.textTransform
}
})
}}
/>
</div>
```
@ -120,8 +123,8 @@ You can pass the following properties to customize rendered labels and values:
```jsx
<JSONTree
labelRenderer={raw => <strong>{raw}</strong>}
valueRenderer={raw => <em>{raw}</em>}
labelRenderer={raw => <strong>{raw}</strong>}
valueRenderer={raw => <em>{raw}</em>}
/>
```

View File

@ -1,5 +1,4 @@
react-hot-boilerplate
=====================
# react-hot-boilerplate
The minimal dev environment to enable live-editing React components.
@ -32,16 +31,16 @@ This boilerplate is purposefully simple to show the minimal configuration for Re
### Dependencies
* React
* Webpack
* [webpack-dev-server](https://github.com/webpack/webpack-dev-server)
* [babel-loader](https://github.com/babel/babel-loader)
* [react-hot-loader](https://github.com/gaearon/react-hot-loader)
- React
- Webpack
- [webpack-dev-server](https://github.com/webpack/webpack-dev-server)
- [babel-loader](https://github.com/babel/babel-loader)
- [react-hot-loader](https://github.com/gaearon/react-hot-loader)
### Resources
* [Demo video](http://vimeo.com/100010922)
* [react-hot-loader on Github](https://github.com/gaearon/react-hot-loader)
* [Integrating JSX live reload into your workflow](http://gaearon.github.io/react-hot-loader/getstarted/)
* [Troubleshooting guide](https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md)
* Ping dan_abramov on Twitter or #reactjs IRC
- [Demo video](http://vimeo.com/100010922)
- [react-hot-loader on Github](https://github.com/gaearon/react-hot-loader)
- [Integrating JSX live reload into your workflow](http://gaearon.github.io/react-hot-loader/getstarted/)
- [Troubleshooting guide](https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md)
- Ping dan_abramov on Twitter or #reactjs IRC

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