Merge pull request #449 from nndio/deps

Fix and update packages
This commit is contained in:
Mihail Diordiev 2019-02-07 01:59:57 +02:00 committed by GitHub
commit 2a3f358002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
366 changed files with 9062 additions and 11889 deletions

9
.eslintignore Normal file
View File

@ -0,0 +1,9 @@
*.log
.idea
lib
dist
umd
build
coverage
node_modules
__snapshots__

34
.eslintrc Normal file
View File

@ -0,0 +1,34 @@
{
"parser": "babel-eslint",
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"prettier"
],
"globals": {
"chrome": true
},
"env": {
"es6": true,
"browser": true,
"jest": true,
"node": true
},
"rules": {
"eol-last": ["warn"],
"max-len": ["warn", { "code": 120 , "ignoreComments": true }],
"quotes": ["warn", "single", "avoid-escape"],
"jsx-quotes": ["warn", "prefer-double"],
"react/prop-types": 0
},
"plugins": [
"prettier",
"react",
"babel"
],
"settings": {
"react": {
"version": "detect"
}
}
}

1
.gitignore vendored
View File

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

View File

@ -8,5 +8,5 @@ cache:
- "node_modules" - "node_modules"
script: script:
- yarn build:all - yarn build:all
- yarn lint - yarn lint:all
- yarn test - 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. 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/) 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/)

14
babel.config.js Executable file
View File

@ -0,0 +1,14 @@
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-flow'],
plugins: [
[
'@babel/plugin-transform-runtime',
{
regenerator: true
}
],
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-export-default-from'
]
};

View File

@ -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). The client driver provides a way to connect to the server via websockets (see the docs for the selected client).
##### JavaScript ##### JavaScript
```js ```js
var socket = socketCluster.connect({ var socket = socketCluster.connect({
hostname: 'localhost', hostname: 'localhost',
@ -23,6 +24,7 @@ var socket = socketCluster.connect({
``` ```
##### Python ##### Python
```py ```py
socket = Socketcluster.socket("ws://localhost:8000/socketcluster/") socket = Socketcluster.socket("ws://localhost:8000/socketcluster/")
socket.connect() socket.connect()
@ -35,6 +37,7 @@ socket.connect()
SocketCluster client handles reconnecting for you, but you still might want to know when the connection is established, or when it failed to connect. 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 ##### JavaScript
```js ```js
socket.on('connect', status => { socket.on('connect', status => {
// Here will come the next step // Here will come the next step
@ -48,6 +51,7 @@ socket.on('error', error => {
``` ```
##### Python ##### Python
```py ```py
def onconnect(socket): def onconnect(socket):
// Here will call the next step // 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. 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 ##### JavaScript
```js ```js
socket.emit('login', 'master', (error, channelName) => { socket.emit('login', 'master', (error, channelName) => {
if (error) { console.log(error); return; } if (error) {
console.log(error);
return;
}
channel = socket.subscribe(channelName); channel = socket.subscribe(channelName);
channel.watch(handleMessages); channel.watch(handleMessages);
socket.on(channelName, handleMessages); socket.on(channelName, handleMessages);
@ -80,6 +88,7 @@ function handleMessages(message) {
``` ```
##### Python ##### Python
```py ```py
socket.emitack("login", "master", login) 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). 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: 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`. - `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' } }`. - `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. - `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. - `id` - socket connection id, which should be either `socket.id` or should not provided and use `log-noid` channel.
##### JavaScript ##### JavaScript
```js ```js
const message = { const message = {
type: 'ACTION', type: 'ACTION',
@ -120,6 +131,7 @@ socket.emit(socket.id ? 'log' : 'log-noid', message);
``` ```
##### Python ##### Python
```py ```py
class Message: class Message:
def __init__(self, action, state): def __init__(self, action, state):
@ -133,6 +145,7 @@ socket.emit(socket.id if "log" else "log-noid", Message(action, state));
#### 5. Listening for monitor events #### 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: 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. - `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). - `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. - `START` - a monitor was opened. You could handle this event in order not to do extra tasks when the app is not monitored.
@ -141,6 +154,7 @@ When a monitor action is emitted, you'll get an event on the subscribed function
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). 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 ##### JavaScript
```js ```js
function handleMessages(message) { function handleMessages(message) {
if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') { if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') {
@ -150,6 +164,7 @@ function handleMessages(message) {
``` ```
##### Python ##### Python
```py ```py
def handleMessages(key, message): def handleMessages(key, message):
if message.type === "DISPATCH" and message.payload.type === "JUMP_TO_STATE": 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. // Consult their repositories to learn about those props.
// Here, we put LogMonitor inside a DockMonitor. // Here, we put LogMonitor inside a DockMonitor.
// Note: DockMonitor is visible by default. // Note: DockMonitor is visible by default.
<DockMonitor toggleVisibilityKey='ctrl-h' <DockMonitor
changePositionKey='ctrl-q' toggleVisibilityKey="ctrl-h"
defaultIsVisible={true}> changePositionKey="ctrl-q"
<LogMonitor theme='tomorrow' /> defaultIsVisible={true}
>
<LogMonitor theme="tomorrow" />
</DockMonitor> </DockMonitor>
); );
@ -60,9 +62,7 @@ Note that you can use `LogMonitor` directly without wrapping it in `DockMonitor`
```js ```js
// If you'd rather not use docking UI, use <LogMonitor> directly // If you'd rather not use docking UI, use <LogMonitor> directly
const DevTools = createDevTools( const DevTools = createDevTools(<LogMonitor theme="solarized" />);
<LogMonitor theme='solarized' />
);
``` ```
#### Use `DevTools.instrument()` Store Enhancer #### 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. 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` ##### `store/configureStore.js`
@ -99,7 +99,9 @@ export default function configureStore(initialState) {
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled) // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) { if (module.hot) {
module.hot.accept('../reducers', () => 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! // You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar // 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/); 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) { 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. // Note: only Redux >= 3.1.0 supports passing enhancer as third argument.
// See https://github.com/rackt/redux/releases/tag/v3.1.0 // See https://github.com/rackt/redux/releases/tag/v3.1.0
return createStore(rootReducer, initialState, enhancer); return createStore(rootReducer, initialState, enhancer);
}; }
``` ```
##### `store/configureStore.dev.js` ##### `store/configureStore.dev.js`
@ -205,7 +207,7 @@ function getDebugSessionKey() {
// You can write custom logic here! // You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar // 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/); 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) { 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) // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) { if (module.hot) {
module.hot.accept('../reducers', () => 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'; import DevTools from './containers/DevTools';
export default function showDevTools(store) { 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 // Reload in case it already exists
popup.location.reload(); popup.location.reload();
@ -366,11 +374,11 @@ Note that there are no useful props you can pass to the `DevTools` component oth
### Gotchas ### 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? ### What Next?

3
jest.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
setupFiles: ['devui/tests/setup.js']
};

View File

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

View File

@ -2,10 +2,15 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"babel-eslint": "^10.0.0", "babel-eslint": "^10.0.0",
"eslint-plugin-react": "7.4.0", "eslint": "^5.12.0",
"eslint-plugin-flowtype": "3.2.0", "eslint-config-prettier": "^3.3.0",
"lerna": "3.4.2", "eslint-plugin-babel": "^5.3.0",
"pre-commit": "^1.1.3" "eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "7.12.3",
"jest": "^24.1.0",
"lerna": "3.9.0",
"lint-staged": "^8.1.0",
"prettier": "^1.15.3"
}, },
"scripts": { "scripts": {
"lerna": "lerna", "lerna": "lerna",
@ -14,15 +19,29 @@
"publish": "lerna publish", "publish": "lerna publish",
"canary": "lerna publish --canary preminor --npm-tag alpha", "canary": "lerna publish --canary preminor --npm-tag alpha",
"next": "lerna publish --bump prerelease --npm-tag next", "next": "lerna publish --bump prerelease --npm-tag next",
"lint": "lerna run lint --since master -- --color", "lint": "eslint '**/*.{js,jsx}' --cache",
"lint:all": "lerna run lint -- --color", "lint:fix": "eslint '**/*.{js,jsx}' --fix --cache",
"test": "lerna run test --since master -- --colors", "lint:all": "eslint '**/*.{js,jsx}'",
"test:all": "lerna run test -- --colors" "prettify": "prettier '**/*.{js,jsx,json,css,html,md}' --ignore-path .eslintignore --single-quote --write",
"precommit": "lint-staged",
"test": "jest --onlyChanged",
"test:all": "jest"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"pre-commit": [ "engines": {
"lint" "node": ">=8.0.0"
] },
"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,3 +1,3 @@
{ {
"presets": ["es2015-loose", "stage-0"] "presets": ["@babel/preset-env"]
} }

View File

@ -1,4 +0,0 @@
lib
**/node_modules
**/webpack.config.js
examples/**/server.js

View File

@ -1,16 +0,0 @@
{
"extends": "eslint-config-airbnb",
"env": {
"browser": true,
"mocha": true,
"node": true
},
"rules": {
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2
},
"plugins": [
"react"
]
}

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. 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). 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 ## API
Option | Type | Default | Description | 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) | `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 | `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 | `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 | `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,15 +5,13 @@
"main": "lib/index.js", "main": "lib/index.js",
"scripts": { "scripts": {
"clean": "rimraf lib dist", "clean": "rimraf lib dist",
"lint": "eslint src", "build": "babel src --out-dir lib",
"build": "npm run build:lib && npm run build:umd && npm run build:umd:min", "build:umd": "webpack --progress --config webpack.config.umd.js",
"build:lib": "babel src --out-dir lib", "build:umd:min": "webpack --env.minimize --progress --config webpack.config.umd.js",
"build:umd": "webpack src/index.js dist/d3tooltip.js --config webpack.config.development.js",
"build:umd:min": "webpack src/index.js dist/d3tooltip.min.js --config webpack.config.production.js",
"version": "npm run build", "version": "npm run build",
"postversion": "git push && git push --tags && npm run clean", "postversion": "git push && git push --tags && npm run clean",
"prepare": "npm run clean && npm run build", "prepare": "npm run clean && npm run build",
"prepublishOnly": "npm run lint && npm run clean && npm run build" "prepublishOnly": "npm run clean && npm run build && npm run build:umd && npm run build:umd:min"
}, },
"files": [ "files": [
"lib", "lib",
@ -35,18 +33,13 @@
}, },
"homepage": "https://github.com/reduxjs/redux-devtools", "homepage": "https://github.com/reduxjs/redux-devtools",
"devDependencies": { "devDependencies": {
"babel-cli": "^6.3.15", "@babel/cli": "^7.2.3",
"babel-core": "^6.1.20", "@babel/core": "^7.2.2",
"babel-eslint": "^5.0.0-beta4", "@babel/preset-env": "^7.3.1",
"babel-loader": "^6.2.0", "babel-loader": "^8.0.5",
"babel-preset-es2015-loose": "^6.1.3",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"eslint": "^0.24",
"eslint-config-airbnb": "0.0.6",
"eslint-plugin-react": "^3.6.3",
"rimraf": "^2.3.4", "rimraf": "^2.3.4",
"webpack": "^1.9.6" "webpack": "^4.27.1",
"webpack-cli": "^3.2.0"
}, },
"dependencies": { "dependencies": {
"ramda": "^0.17.1" "ramda": "^0.17.1"

View File

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

View File

@ -1,16 +0,0 @@
'use strict';
module.exports = {
module: {
loaders: [
{ test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }
]
},
output: {
library: 'd3tooltip',
libraryTarget: 'umd'
},
resolve: {
extensions: ['', '.js']
}
};

View File

@ -1,14 +0,0 @@
'use strict';
var webpack = require('webpack');
var baseConfig = require('./webpack.config.base');
var config = Object.create(baseConfig);
config.plugins = [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
];
module.exports = config;

View File

@ -1,20 +0,0 @@
'use strict';
var webpack = require('webpack');
var baseConfig = require('./webpack.config.base');
var config = Object.create(baseConfig);
config.plugins = [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
screw_ie8: true,
warnings: false
}
})
];
module.exports = config;

View File

@ -0,0 +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
}
});

View File

@ -1,3 +1,4 @@
{ {
"presets": ["es2015", "stage-0", "react"] "presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from"]
} }

View File

@ -1,20 +0,0 @@
{
"extends": "airbnb",
"rules": {
"arrow-body-style": 0,
"prefer-arrow-callback": 0,
"func-names": 0,
"comma-dangle": ["error", "never"],
"newline-per-chained-call": 0,
"react/sort-comp": 0,
"react/jsx-no-bind": 0,
"react/jsx-uses-react": 1,
"react/prefer-stateless-function": 0
},
"env": {
"browser": true,
"node": true,
"jest": true
},
"parser": "babel-eslint"
}

View File

@ -7,5 +7,6 @@ const parse = require('git-url-parse');
var ghUrl = process.argv[2]; var ghUrl = process.argv[2];
const parsedUrl = parse(ghUrl); 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); console.log(ghPagesUrl);

View File

@ -1,33 +0,0 @@
// IMPORTANT
// ---------
// This is an auto generated file with React CDK.
// Do not modify this file.
// Use `.scripts/user/pretest.js instead`.
require('babel-core/register');
require('babel-polyfill');
// Add jsdom support, which is required for enzyme.
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
process.on('unhandledRejection', function (error) {
console.error('Unhandled Promise Rejection:');
console.error(error && error.stack || error);
});
require('./user/pretest.js');

View File

@ -1,23 +1,25 @@
import { configure, setAddon, addDecorator } from '@storybook/react'; import { configure, setAddon, addDecorator } from '@storybook/react';
import { setOptions } from '@storybook/addon-options'; import { withOptions } from '@storybook/addon-options';
import infoAddon from '@storybook/addon-info'; import { withInfo } from '@storybook/addon-info';
import { withKnobs } from '@storybook/addon-knobs'; import { withKnobs } from '@storybook/addon-knobs';
import { withTheme } from './themeAddon/theme'; import { withTheme } from './themeAddon/theme';
import '../src/presets.js'; import '../src/presets.js';
setAddon(infoAddon); addDecorator(
setOptions({ withOptions({
name: 'DevUI', name: 'DevUI',
url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui', url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui',
goFullScreen: false, goFullScreen: false,
showLeftPanel: true, showStoriesPanel: true,
showDownPanel: true, showAddonPanel: true,
showSearchBox: false, showSearchBox: false,
downPanelInRight: true addonPanelInRight: true
}); })
);
addDecorator(withTheme); addDecorator(withTheme);
addDecorator(withKnobs); addDecorator(withKnobs);
addDecorator(withInfo);
const req = require.context('../src/', true, /stories\/index\.js$/); const req = require.context('../src/', true, /stories\/index\.js$/);

View File

@ -11,10 +11,11 @@
min-height: 400px; min-height: 400px;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: "Helvetica Neue", "Lucida Grande", sans-serif; font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif;
font-size: 11px; font-size: 11px;
} }
#root, #root > div, #root > div > div { #root,
#root > div {
height: 100%; height: 100%;
} }
#root > div > div { #root > div > div {
@ -22,5 +23,6 @@
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto;
} }
</style> </style>

View File

@ -25,6 +25,8 @@ export default class Panel extends React.Component {
this.setState(state); this.setState(state);
}; };
onClick = () => {};
render() { render() {
const { theme, scheme, light } = this.state; const { theme, scheme, light } = this.state;
return ( return (
@ -50,6 +52,7 @@ export default class Panel extends React.Component {
} }
]} ]}
onFieldChange={this.onChange} onFieldChange={this.onChange}
onFieldClick={this.onClick}
/> />
</FormWrapper> </FormWrapper>
); );

View File

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

View File

@ -1,22 +1,8 @@
import React from 'react'; import React from 'react';
import addons from '@storybook/addons'; import addons from '@storybook/addons';
import styled from 'styled-components';
import { EVENT_ID_DATA, DEFAULT_THEME_STATE } from './constant'; import { EVENT_ID_DATA, DEFAULT_THEME_STATE } from './constant';
import { Container } from '../../src'; import { Container } from '../../src';
const ContainerStyled = styled(Container)`
> div {
height: 100%;
width: 100%;
> div {
height: 100%;
width: 100%;
overflow-y: auto;
}
}
`;
const channel = addons.getChannel(); const channel = addons.getChannel();
class Theme extends React.Component { class Theme extends React.Component {
@ -35,7 +21,7 @@ class Theme extends React.Component {
}; };
render() { render() {
return <ContainerStyled themeData={this.state}>{this.props.children}</ContainerStyled>; return <Container themeData={this.state}>{this.props.children}</Container>;
} }
} }

View File

@ -1,4 +0,0 @@
module.exports = function (config) {
// This is the default webpack config defined in the `../webpack.config.js`
// modify as you need.
};

View File

@ -1,26 +1,8 @@
const path = require('path'); const path = require('path');
const updateConfig = require('./user/modify_webpack_config');
const config = { module.exports = (baseConfig, env, defaultConfig) => {
module: { // Add custom webpack config here like:
rules: [ // defaultConfig.module.rules.push
{
test: /\.css?$/, return defaultConfig;
use: [{ loader: 'style-loader' }, { loader: 'raw-loader' }],
include: path.resolve(__dirname, '../')
},
{
test: /\.json?$/,
loader: 'json-loader',
include: path.resolve(__dirname, '../')
},
{
test: /\.woff2?(\?\S*)?$/,
loader: 'url?limit=65000&mimetype=application/font-woff'
}
]
}
}; };
updateConfig(config);
module.exports = config;

View File

@ -4,7 +4,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Code Pro'), local('SourceCodePro-Regular'), 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 */ /* source-sans-pro-regular - latin */
@font-face { @font-face {
@ -12,7 +13,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), 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 */ /* source-sans-pro-600 - latin */
@font-face { @font-face {
@ -20,7 +22,8 @@
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), 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 */ /* roboto-regular - latin */
@ -29,7 +32,7 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), 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 */ /* roboto-mono-regular - latin */
@font-face { @font-face {
@ -37,7 +40,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Roboto Mono'), local('RobotoMono-Regular'), 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 */ /* Generated with https://google-webfonts-helper.herokuapp.com */

View File

@ -1,78 +1,60 @@
{ {
"name": "devui", "name": "devui",
"version": "1.0.0-3", "version": "1.0.0-3",
"description": "description": "Reusable React components for building DevTools monitors and apps.",
"Reusable React components for building DevTools monitors and apps.", "files": [
"files": ["lib", "fonts"], "lib",
"fonts"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git" "url": "https://github.com/reduxjs/redux-devtools.git"
}, },
"author": "author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"start": "npm run storybook", "start": "npm run storybook",
"build": "rimraf ./lib && babel ./src --out-dir ./lib --ignore tests,stories", "build": "rimraf ./lib && babel ./src --out-dir ./lib --ignore tests,stories",
"lint": "eslint src",
"lintfix": "eslint src --fix",
"lint:css": "stylelint './src/**/styles/*.js'", "lint:css": "stylelint './src/**/styles/*.js'",
"format": "prettier-eslint --write ./src/**/*.js",
"test:update": "npm run jest -- -u", "test:update": "npm run jest -- -u",
"test": "jest --no-cache", "test": "jest --no-cache",
"storybook": "start-storybook -p 9001 -c .storybook -s ./fonts", "storybook": "start-storybook -p 9001 -c .storybook -s ./fonts",
"publish-storybook": "bash .scripts/publish_storybook.sh", "publish-storybook": "bash .scripts/publish_storybook.sh",
"prepare": "npm run build", "prepare": "npm run build",
"prepublishOnly": "npm run lint && npm run test && npm run build" "prepublishOnly": "npm run test && npm run build"
}, },
"bugs": { "bugs": {
"url": "https://github.com/reduxjs/redux-devtools/issues" "url": "https://github.com/reduxjs/redux-devtools/issues"
}, },
"homepage": "https://github.com/reduxjs/redux-devtools", "homepage": "https://github.com/reduxjs/redux-devtools",
"devDependencies": { "devDependencies": {
"@storybook/addon-actions": "^3.2.13", "@babel/cli": "^7.2.3",
"@storybook/addon-info": "^3.2.13", "@babel/core": "^7.2.2",
"@storybook/addon-knobs": "^3.2.13", "@babel/plugin-proposal-class-properties": "^7.3.0",
"@storybook/addon-options": "^3.2.13", "@babel/plugin-proposal-decorators": "^7.3.0",
"@storybook/react": "^3.2.13", "@babel/plugin-proposal-export-default-from": "^7.2.0",
"babel-cli": "^6.26.0", "@babel/plugin-transform-runtime": "^7.2.0",
"babel-core": "^6.26.0", "@babel/preset-env": "^7.3.1",
"babel-eslint": "^6.0.2", "@babel/preset-react": "^7.0.0",
"babel-jest": "^21.2.0", "@storybook/addon-actions": "^4.1.4",
"babel-loader": "^7.1.2", "@storybook/addon-info": "^4.1.4",
"babel-plugin-transform-runtime": "^6.23.0", "@storybook/addon-knobs": "^4.1.4",
"babel-polyfill": "^6.26.0", "@storybook/addon-options": "^4.1.4",
"babel-preset-es2015": "^6.24.1", "@storybook/react": "4.0.9",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.16.0",
"css-loader": "^0.28.7",
"enzyme": "^3.1.0", "enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2", "enzyme-adapter-react-16": "^1.0.2",
"enzyme-to-json": "^3.1.4", "enzyme-to-json": "^3.1.4",
"eslint": "^2.7.0",
"eslint-config-airbnb": "^7.0.0",
"eslint-plugin-babel": "^3.2.0",
"eslint-plugin-jsx-a11y": "^0.6.2",
"eslint-plugin-react": "^4.3.0",
"file-loader": "^1.1.5",
"git-url-parse": "^7.0.1", "git-url-parse": "^7.0.1",
"jest": "^21.2.1", "jest": "^24.1.0",
"jsdom": "^11.3.0", "jsdom": "^11.3.0",
"node-sass": "^3.13.0",
"postcss-loader": "^2.0.8",
"prettier-eslint-cli": "^4.4.0",
"raw-loader": "^0.5.1",
"react": "^16.0.0", "react": "^16.0.0",
"react-addons-test-utils": "^15.6.2", "react-addons-test-utils": "^15.6.2",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-test-renderer": "^16.0.0", "react-test-renderer": "^16.0.0",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"sass-loader": "^4.0.2",
"style-loader": "^0.19.0",
"stylelint": "^7.6.0", "stylelint": "^7.6.0",
"stylelint-config-standard": "^15.0.0", "stylelint-config-standard": "^15.0.0",
"stylelint-processor-styled-components": "^0.0.4", "stylelint-processor-styled-components": "^0.0.4"
"url-loader": "^0.6.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^0.14.9 || ^15.3.0" "react": "^0.14.9 || ^15.3.0"

View File

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

View File

@ -16,46 +16,62 @@ export const Container = styled.div`
storiesOf('Button', module) storiesOf('Button', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add('default', () => (
'default', <Container>
'', <Button
() => ( title={text('Title', 'Hello Tooltip! \\a And from new line hello!')}
<Container> tooltipPosition={select('tooltipPosition', [
<Button 'top',
title={text('Title', 'Hello Tooltip! \\a And from new line hello!')} 'bottom',
tooltipPosition={ 'left',
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'bottom-left',
} 'bottom-right',
primary={boolean('primary', true)} 'top-left',
size={select('size', ['big', 'normal', 'small'], 'normal')} 'top-right'
disabled={boolean('Disabled', false)} ])}
onClick={action('button clicked')} primary={boolean('primary', true)}
> size={select('size', ['big', 'normal', 'small'], 'normal')}
{text('Label', 'Hello Button')} disabled={boolean('Disabled', false)}
</Button> onClick={action('button clicked')}
</Container> >
) {text('Label', 'Hello Button')}
) </Button>
.addWithInfo( </Container>
'mark', ))
'', .add('mark', () => (
() => ( <Container>
<Container> <Button
<Button mark={select(
mark={select('mark', ['base08', 'base09', 'base0A', 'base0B', 'mark',
'base0C', 'base0D', 'base0E', 'base0F'], 'base08')} [
title={text('Title', 'Hello Tooltip')} 'base08',
tooltipPosition={ 'base09',
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'base0A',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'base0B',
} 'base0C',
size={select('size', ['big', 'normal', 'small'], 'normal')} 'base0D',
disabled={boolean('Disabled', false)} 'base0E',
onClick={action('button clicked')} 'base0F'
> ],
<MdFiberManualRecord /> 'base08'
</Button> )}
</Container> 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 { fadeIn } from '../../utils/animations';
import colorEffect from '../../utils/color'; import colorEffect from '../../utils/color';
const both = (tooltipPosition) => { const both = tooltipPosition => {
switch (tooltipPosition) { switch (tooltipPosition) {
case 'bottom': case 'bottom':
return ` return `
@ -46,7 +46,7 @@ const both = (tooltipPosition) => {
} }
}; };
const before = (tooltipPosition) => { const before = tooltipPosition => {
switch (tooltipPosition) { switch (tooltipPosition) {
case 'bottom-left': case 'bottom-left':
return ` return `
@ -110,12 +110,13 @@ const after = (tooltipPosition, color) => {
} }
}; };
const getDirection = (tooltipPosition) => { const getDirection = tooltipPosition => {
return (tooltipPosition.indexOf('-') > 0) ? return tooltipPosition.indexOf('-') > 0
tooltipPosition.substring(0, tooltipPosition.indexOf('-')) : tooltipPosition; ? tooltipPosition.substring(0, tooltipPosition.indexOf('-'))
: tooltipPosition;
}; };
const getSize = (size) => { const getSize = size => {
switch (size) { switch (size) {
case 'big': case 'big':
return 'min-height: 34px; padding: 2px 12px;'; return 'min-height: 34px; padding: 2px 12px;';
@ -144,8 +145,13 @@ export const commonStyle = ({ theme, mark, size }) => css`
pointer-events: none; pointer-events: none;
} }
${mark && ` ${mark &&
background-color: ${colorEffect(theme[mark], 'fade', theme.light ? 0.92 : 0.82)}; `
background-color: ${colorEffect(
theme[mark],
'fade',
theme.light ? 0.92 : 0.82
)};
> svg { > svg {
color: ${theme[mark]}; 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 })} ${commonStyle({ theme, mark, size })}
&:before { &:before {
@ -170,7 +182,9 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
border-radius: 3px; border-radius: 3px;
background: ${theme.base01}; background: ${theme.base01};
border: 1px solid ${theme.base02}; 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, &:after,
@ -194,7 +208,8 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''} ${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''}
} }
${theme.type !== 'material' && ` ${theme.type !== 'material' &&
`
&:after { &:after {
content: ""; content: "";
border-style: solid; border-style: solid;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,40 +7,33 @@ import { schema, uiSchema, formData } from '../../Form/stories/schema';
storiesOf('Dialog', module) storiesOf('Dialog', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add('default', () => (
'default', <Dialog
'', title={text('title', 'Dialog Title')}
() => ( submitText={text('submitText', 'Submit!')}
<Dialog open={boolean('open', true)}
title={text('title', 'Dialog Title')} noHeader={boolean('noHeader', false)}
children={text('children', 'Hello Dialog!')} noFooter={boolean('noFooter', false)}
submitText={text('submitText', 'Submit!')} modal={boolean('modal', false)}
open={boolean('open', true)} fullWidth={boolean('fullWidth', false)}
noHeader={boolean('noHeader', false)} onDismiss={action('dialog dismissed')}
noFooter={boolean('noFooter', false)} onSubmit={action('dialog submitted')}
modal={boolean('modal', false)} >
fullWidth={boolean('fullWidth', false)} {text('children', 'Hello Dialog!')}
onDismiss={action('dialog dismissed')} </Dialog>
onSubmit={action('dialog submitted')} ))
/> .add('with form', () => (
) <Dialog
) open={boolean('open', true)}
.addWithInfo( noHeader={boolean('noHeader', false)}
'with form', noFooter={boolean('noFooter', false)}
'', fullWidth={boolean('fullWidth', false)}
() => ( submitText={text('submitText', 'Submit!')}
<Dialog formData={object('formData', formData)}
open={boolean('open', true)} schema={object('schema', schema)}
noHeader={boolean('noHeader', false)} uiSchema={object('uiSchema', uiSchema)}
noFooter={boolean('noFooter', false)} onChange={action('form changed')}
fullWidth={boolean('fullWidth', false)} onSubmit={action('form submitted')}
submitText={text('submitText', 'Submit!')} onDismiss={action('dialog dismissed')}
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: 1px outset ${theme.base01};
border-radius: 2px; border-radius: 2px;
background-color: ${theme.base00}; background-color: ${theme.base00};
box-shadow: box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
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);
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header { > div.mc-dialog--header {
display: flex; display: flex;
@ -77,14 +75,22 @@ export const style = ({ theme, open, fullWidth }) => css`
> form { > form {
padding: 0; padding: 0;
> .form-group { margin-bottom: 0; } > .form-group {
margin-bottom: 0;
> div > fieldset {
legend { display: none; }
#root__description { margin-top: 0; }
} }
.mc-dialog--hidden { display: none; } > div > fieldset {
legend {
display: none;
}
#root__description {
margin-top: 0;
}
}
.mc-dialog--hidden {
display: none;
}
} }
} }

View File

@ -35,10 +35,8 @@ export const style = ({ theme, open, fullWidth }) => css`
margin-bottom: 16px; margin-bottom: 16px;
border: none; border: none;
background-color: ${theme.base00}; background-color: ${theme.base00};
box-shadow: box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
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);
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header { > div.mc-dialog--header {
display: flex; display: flex;
@ -74,14 +72,22 @@ export const style = ({ theme, open, fullWidth }) => css`
> form { > form {
padding: 0; padding: 0;
> .form-group { margin-bottom: 0; } > .form-group {
margin-bottom: 0;
> div > fieldset {
legend { display: none; }
#root__description { margin-top: 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 CodeMirror from 'codemirror';
import { defaultStyle, themedStyle } from './styles'; import { defaultStyle, themedStyle } from './styles';
const EditorContainer = styled.div('', const EditorContainer = styled.div('', ({ theme }) =>
({ theme }) => (theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)) theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)
); );
export default class Editor extends Component { export default class Editor extends Component {
componentDidMount() { componentDidMount() {
this.cm = CodeMirror( // eslint-disable-line new-cap this.cm = CodeMirror(this.node, {
this.node, value: this.props.value,
{ mode: this.props.mode,
value: this.props.value, lineNumbers: this.props.lineNumbers,
mode: this.props.mode, lineWrapping: this.props.lineWrapping,
lineNumbers: this.props.lineNumbers, readOnly: this.props.readOnly,
lineWrapping: this.props.lineWrapping, autofocus: this.props.autofocus,
readOnly: this.props.readOnly, foldGutter: this.props.foldGutter,
autofocus: this.props.autofocus, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
foldGutter: this.props.foldGutter, });
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
}
);
if (this.props.onChange) { 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} selected={this.state.selected}
onClick={selected => { this.setState({ selected }); }} onClick={selected => {
this.setState({ selected });
}}
align={select('align', ['left', 'right', 'center'], 'left')} align={select('align', ['left', 'right', 'center'], 'left')}
/> />
); );

View File

@ -15,9 +15,8 @@ function getThemes() {
storiesOf('Editor', module) storiesOf('Editor', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add(
'default', 'default',
'Based on [CodeMirror](http://codemirror.net/).',
() => ( () => (
<Editor <Editor
value={text('value', value)} value={text('value', value)}
@ -28,14 +27,9 @@ storiesOf('Editor', module)
onChange={action('change')} onChange={action('change')}
autofocus autofocus
/> />
) ),
{ info: 'Based on [CodeMirror](http://codemirror.net/).' }
) )
.addWithInfo( .add('with tabs', () => (
'with tabs', <WithTabs lineNumbers={boolean('lineNumbers', true)} />
'', ));
() => (
<WithTabs
lineNumbers={boolean('lineNumbers', true)}
/>
)
);

View File

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

View File

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

View File

@ -7,9 +7,8 @@ import { schema, uiSchema, formData } from './schema';
storiesOf('Form', module) storiesOf('Form', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add(
'default', 'default',
'Wrapper around [`react-jsonschema-form`](https://github.com/mozilla-services/react-jsonschema-form) with custom widgets.',
() => ( () => (
<Form <Form
formData={object('formData', formData)} formData={object('formData', formData)}
@ -19,5 +18,10 @@ storiesOf('Form', module)
onChange={action('form changed')} onChange={action('form changed')}
onSubmit={action('form submitted')} onSubmit={action('form submitted')}
/> />
) ),
{
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', title: 'A multiple choices list',
items: { items: {
type: 'string', type: 'string',
enum: [ enum: ['foo', 'bar', 'fuzz']
'foo',
'bar',
'fuzz'
]
}, },
uniqueItems: true uniqueItems: true
}, },
numberEnum: { numberEnum: {
type: 'number', type: 'number',
title: 'Number enum', title: 'Number enum',
enum: [ enum: [1, 2, 3]
1,
2,
3
]
}, },
numberEnumRadio: { numberEnumRadio: {
type: 'number', type: 'number',
title: 'Number enum', title: 'Number enum',
enum: [ enum: [1, 2, 3]
1,
2,
3
]
}, },
integerRange: { integerRange: {
title: 'Integer range', title: 'Integer range',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -20,9 +20,8 @@ export const Container = styled.div`
storiesOf('Select', module) storiesOf('Select', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add(
'default', 'default',
'Wrapper around [React Select](https://github.com/JedWatson/react-select) with themes and new props like `openOuterUp` and `menuMaxHeight`.',
() => ( () => (
<Container> <Container>
<Select <Select
@ -39,5 +38,10 @@ storiesOf('Select', module)
openOuterUp={boolean('openOuterUp', false)} openOuterUp={boolean('openOuterUp', false)}
/> />
</Container> </Container>
) ),
{
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 { &.is-open > .Select-control {
border-radius: ${openOuterUp ? border-radius: ${openOuterUp
`0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px` : ? `0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px`
`${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0` : `${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0`};
};
} }
&.is-searchable { &.is-searchable {
@ -212,9 +211,7 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
.Select-arrow { .Select-arrow {
border-color: ${theme.base03} transparent transparent; border-color: ${theme.base03} transparent transparent;
border-style: solid; border-style: solid;
border-width: border-width: ${theme.selectArrowWidth}px ${theme.selectArrowWidth}px
${theme.selectArrowWidth}px
${theme.selectArrowWidth}px
${theme.selectArrowWidth / 2}px; ${theme.selectArrowWidth / 2}px;
display: inline-block; display: inline-block;
height: 0; height: 0;
@ -317,7 +314,8 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
border-bottom-right-radius: ${theme.inputBorderRadius}px; border-bottom-right-radius: ${theme.inputBorderRadius}px;
border-top-right-radius: ${theme.inputBorderRadius}px; border-top-right-radius: ${theme.inputBorderRadius}px;
cursor: default; 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 { a.Select-value-label {

View File

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

View File

@ -15,21 +15,17 @@ export const Container = styled.div`
storiesOf('Slider', module) storiesOf('Slider', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add('default', () => (
'default', <Container>
'', <Slider
() => ( value={number('value', 0)}
<Container> min={number('min', 0)}
<Slider max={number('max', 100)}
value={number('value', 0)} label={text('label', 'Slider label')}
min={number('min', 0)} sublabel={text('sublabel', '(sublabel)')}
max={number('max', 100)} withValue={boolean('withValue', false)}
label={text('label', 'Slider label')} disabled={boolean('disabled', false)}
sublabel={text('sublabel', '(sublabel)')} onChange={action('slider changed')}
withValue={boolean('withValue', false)} />
disabled={boolean('disabled', false)} </Container>
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; border-radius: 0.8em/1.1em;
font-size: 1em; font-size: 1em;
cursor: pointer; 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%; 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; position: relative;
height: 0.8em; height: 0.8em;
border-radius: 0.5em; border-radius: 0.5em;
box-shadow: 0 0 .125em ${theme.base04}; 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}; no-repeat ${theme.base00};
background-size: ${percent}% 100%; 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; position: relative;
appearance: none; appearance: none;
cursor: ew-resize; cursor: ew-resize;
@ -68,13 +79,16 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
height: 1.5em; height: 1.5em;
border-radius: 50%; border-radius: 50%;
cursor: pointer; cursor: pointer;
}`)} }`
)}
${prefixSelectors('input:focus:not(:active)', ${prefixSelectors(
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], 'input:focus:not(:active)',
`{ ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
box-shadow: 0 0 1px 2px ${theme.base0D}; box-shadow: 0 0 1px 2px ${theme.base0D};
}`)} }`
)}
input::-moz-focus-outer { input::-moz-focus-outer {
border: 0; border: 0;

View File

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

View File

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

View File

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

View File

@ -38,4 +38,5 @@ export const tabs = [
]; ];
export const simple10Tabs = []; 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,31 +16,25 @@ const Container = styled.div`
storiesOf('Tabs', module) storiesOf('Tabs', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add('default', () => (
'default', <Container>
'', <Tabs
() => (
<Container><Tabs
tabs={simple10Tabs} tabs={simple10Tabs}
selected={text('selected', '2')} selected={text('selected', '2')}
main={boolean('main', true)} main={boolean('main', true)}
onClick={action('tab selected')} onClick={action('tab selected')}
collapsible={boolean('collapsible', true)} collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'left')} position={select('position', ['left', 'right', 'center'], 'left')}
/></Container>
)
)
.addWithInfo(
'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%; height: 100%;
> div > div:first-child { > div > div:first-child {
${props => props.position !== 'left' && ` ${props =>
props.position !== 'left' &&
`
margin-left: auto !important; margin-left: auto !important;
`} `}
${props => props.position === 'center' && ` ${props =>
props.position === 'center' &&
`
margin-right: auto !important; margin-right: auto !important;
`} `}
} }

View File

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

View File

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

View File

@ -2,12 +2,27 @@ import React from 'react';
import { storiesOf } from '@storybook/react'; import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import styled from 'styled-components'; 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 PlayIcon from 'react-icons/lib/md/play-arrow';
import RecordIcon from 'react-icons/lib/md/fiber-manual-record'; import RecordIcon from 'react-icons/lib/md/fiber-manual-record';
import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left'; import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left';
import RightIcon from 'react-icons/lib/md/keyboard-arrow-right'; 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 { options } from '../../Select/stories/options';
import { simple10Tabs } from '../../Tabs/stories/data'; import { simple10Tabs } from '../../Tabs/stories/data';
@ -26,135 +41,165 @@ export const SliderContainer = styled.div`
storiesOf('Toolbar', module) storiesOf('Toolbar', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.addWithInfo( .add('default', () => (
'default', <Container>
'', <Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}>
() => ( <Button
<Container> title={text('Title', 'Hello Tooltip')}
<Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}> 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 <Button
title={text('Title', 'Hello Tooltip')} title={text('play title', 'Play')}
tooltipPosition={ tooltipPosition={select('tooltipPosition', [
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'top',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'bottom',
} 'left',
disabled={boolean('Disabled', false)} 'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
onClick={action('button clicked')} onClick={action('button clicked')}
> >
{text('Label', 'Hello Button')} <PlayIcon />
</Button> </Button>
<Divider /> <Slider
<Button value={number('value', 80)}
title={text('Title', 'Hello Tooltip')} min={number('min', 0)}
tooltipPosition={ max={number('max', 100)}
select('tooltipPosition', ['top', 'bottom', 'left', 'right', label={text('label', 'Slider label')}
'bottom-left', 'bottom-right', 'top-left', 'top-right']) withValue={boolean('withValue', false)}
} onChange={action('slider changed')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<RecordIcon />
</Button>
<Divider />
<Spacer />
<Select options={options} />
</Toolbar>
</Container>
)
)
.addWithInfo(
'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 <Button
title={text('Title', 'Hello Tooltip')} title="Previous state"
tooltipPosition={ tooltipPosition={select('tooltipPosition', [
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'top',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'bottom',
} 'left',
disabled={boolean('Disabled', false)} 'right',
onClick={action('button clicked')} 'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled
onClick={action('previous state clicked')}
> >
{text('Label', 'Hello Button')} <LeftIcon />
</Button> </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> </Toolbar>
</Container> </SliderContainer>
) </Container>
) ));
.addWithInfo(
'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>
)
);

View File

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

View File

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

View File

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

View File

@ -36,7 +36,9 @@ export const ripple = theme => `
top: 0; top: 0;
left: 0; left: 0;
pointer-events: none; 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-repeat: no-repeat;
background-position: 50%; background-position: 50%;
transform: scale(10, 10); transform: scale(10, 10);

View File

@ -7,6 +7,10 @@ import Color from 'color';
effect('#000000', 'alpha', 0.5); 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 // TODO: memoize it

View File

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

View File

@ -5,7 +5,10 @@ import * as additionalSchemes from '../colorSchemes';
import invertColors from '../utils/invertColors'; import invertColors from '../utils/invertColors';
export const schemes = { ...baseSchemes, ...additionalSchemes }; 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 listThemes = () => Object.keys(themes);
export const getTheme = ({ theme: type, scheme, light }) => { 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 { renderToJson } from 'enzyme-to-json';
import { Button } from '../src'; import { Button } from '../src';
describe('Button', function () { describe('Button', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Button>Text</Button>); const wrapper = render(<Button>Text</Button>);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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