mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 08:30:02 +03:00
Convert
This commit is contained in:
parent
93d6bc4350
commit
f21ab41244
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from"]
|
||||
"presets": [
|
||||
"@babel/env",
|
||||
"@babel/react",
|
||||
"@babel/typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/proposal-class-properties"
|
||||
],
|
||||
"sourceType": "unambiguous"
|
||||
}
|
||||
|
|
2
packages/devui/.eslintignore
Normal file
2
packages/devui/.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
lib
|
||||
demo
|
21
packages/devui/.eslintrc.js
Normal file
21
packages/devui/.eslintrc.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
extends: '../../.eslintrc',
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: '../../eslintrc.ts.react.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json']
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['test/*.ts', 'test/*.tsx'],
|
||||
extends: '../../eslintrc.ts.react.jest.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./test/tsconfig.json']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
2
packages/devui/.prettierignore
Normal file
2
packages/devui/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
lib
|
||||
demo
|
|
@ -1,9 +1,9 @@
|
|||
import { configure, setAddon, addDecorator } from '@storybook/react';
|
||||
import { configure, addDecorator } from '@storybook/react';
|
||||
import { withOptions } from '@storybook/addon-options';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
import { withKnobs } from '@storybook/addon-knobs';
|
||||
import { withTheme } from './themeAddon/theme';
|
||||
import '../src/presets.js';
|
||||
import '../src/presets';
|
||||
|
||||
addDecorator(
|
||||
withOptions({
|
||||
|
@ -21,7 +21,7 @@ addDecorator(withTheme);
|
|||
addDecorator(withKnobs);
|
||||
addDecorator(withInfo);
|
||||
|
||||
const req = require.context('../src/', true, /stories\/index\.js$/);
|
||||
const req = require.context('../src/', true, /stories\/index\.tsx$/);
|
||||
|
||||
function loadStories() {
|
||||
req.keys().forEach(filename => req(filename));
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import Form from '@storybook/addon-knobs/dist/components/PropForm';
|
||||
import styled from 'styled-components';
|
||||
import { EVENT_ID_DATA, DEFAULT_THEME_STATE } from './constant';
|
||||
import { listSchemes, listThemes } from '../../src/utils/theme';
|
||||
import { listSchemes, listThemes } from '../../lib/utils/theme';
|
||||
|
||||
const FormWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
|
14
packages/devui/.storybook/webpack.config.js
Executable file → Normal file
14
packages/devui/.storybook/webpack.config.js
Executable file → Normal file
|
@ -1,8 +1,12 @@
|
|||
const path = require('path');
|
||||
const TSDocgenPlugin = require('react-docgen-typescript-webpack-plugin');
|
||||
|
||||
module.exports = (baseConfig, env, defaultConfig) => {
|
||||
// Add custom webpack config here like:
|
||||
// defaultConfig.module.rules.push
|
||||
|
||||
return defaultConfig;
|
||||
module.exports = (baseConfig, env, config) => {
|
||||
config.module.rules.push({
|
||||
test: /\.(ts|tsx)$/,
|
||||
loader: require.resolve('babel-loader')
|
||||
});
|
||||
config.plugins.push(new TSDocgenPlugin()); // optional
|
||||
config.resolve.extensions.push('.ts', '.tsx');
|
||||
return config;
|
||||
};
|
||||
|
|
|
@ -12,13 +12,22 @@
|
|||
},
|
||||
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit",
|
||||
"type-check:watch": "npm run type-check -- --watch",
|
||||
"start": "npm run storybook",
|
||||
"build": "rimraf ./lib && babel ./src --out-dir ./lib --ignore tests,stories",
|
||||
"clean": "rimraf lib",
|
||||
"build": "npm run build:types && npm run build:js",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:js": "babel src --out-dir lib --ignore tests,stories --extensions \".ts,.tsx\" --source-maps inline",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
||||
"lint:css": "stylelint './src/**/styles/*.js'",
|
||||
"test:update": "npm run jest -- -u",
|
||||
"test": "jest --no-cache",
|
||||
"storybook": "start-storybook -p 9001 -c .storybook -s ./fonts",
|
||||
"storybook": "npm run build && start-storybook -p 9001 -c .storybook -s ./fonts",
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
|
@ -36,11 +45,22 @@
|
|||
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@storybook/addon-actions": "^4.1.4",
|
||||
"@storybook/addon-info": "^4.1.4",
|
||||
"@storybook/addon-knobs": "^4.1.4",
|
||||
"@storybook/addon-options": "^4.1.4",
|
||||
"@storybook/react": "4.0.9",
|
||||
"@storybook/addon-actions": "^4.1.18",
|
||||
"@storybook/addon-info": "^4.1.18",
|
||||
"@storybook/addon-knobs": "^4.1.18",
|
||||
"@storybook/addon-options": "^4.1.18",
|
||||
"@storybook/react": "^4.1.18",
|
||||
"@types/codemirror": "^0.0.95",
|
||||
"@types/color": "^2.0.1",
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"@types/react-icons": "^2.2.7",
|
||||
"@types/react-jsonschema-form": "^1.7.3",
|
||||
"@types/react-select": "^1.3.4",
|
||||
"@types/storybook__addon-actions": "^3.4.3",
|
||||
"@types/storybook__addon-knobs": "^4.0.5",
|
||||
"@types/storybook__react": "^4.0.2",
|
||||
"babel-loader": "^8.1.0",
|
||||
"csstype": "^2.6.10",
|
||||
"enzyme": "^3.1.0",
|
||||
"enzyme-adapter-react-16": "^1.0.2",
|
||||
"enzyme-to-json": "^3.1.4",
|
||||
|
@ -48,18 +68,20 @@
|
|||
"jest": "^24.1.0",
|
||||
"jsdom": "^11.3.0",
|
||||
"react": "^16.0.0",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-docgen-typescript-webpack-plugin": "^1.1.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-test-renderer": "^16.0.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"stylelint": "^7.6.0",
|
||||
"stylelint-config-standard": "^15.0.0",
|
||||
"stylelint-processor-styled-components": "^0.0.4"
|
||||
"stylelint-processor-styled-components": "^0.0.4",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.9 || ^15.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/prop-types": "^15.6.0",
|
||||
"base16": "^1.0.0",
|
||||
"codemirror": "^5.21.0",
|
||||
"color": "^2.0.0",
|
||||
|
@ -73,6 +95,5 @@
|
|||
},
|
||||
"jest": {
|
||||
"setupTestFrameworkScriptFile": "<rootDir>/tests/setup.js"
|
||||
},
|
||||
"main": "lib/index.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import * as styles from './styles';
|
||||
import { commonStyle, tooltipStyle } from './styles/common';
|
||||
|
||||
const ButtonWrapper = createStyledComponent(styles, 'button');
|
||||
const TooltipWrapper = createStyledComponent(tooltipStyle);
|
||||
const CommonWrapper = createStyledComponent(commonStyle);
|
||||
|
||||
export default class Button extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (
|
||||
nextProps.children !== this.props.children ||
|
||||
nextProps.disabled !== this.props.disabled ||
|
||||
nextProps.mark !== this.props.mark ||
|
||||
nextProps.size !== this.props.size ||
|
||||
nextProps.primary !== this.props.primary ||
|
||||
nextProps.tooltipPosition !== this.props.tooltipPosition ||
|
||||
nextProps.title !== this.props.title
|
||||
);
|
||||
}
|
||||
|
||||
onMouseUp = e => {
|
||||
e.target.blur();
|
||||
};
|
||||
|
||||
render() {
|
||||
const button = (
|
||||
<ButtonWrapper
|
||||
theme={this.props.theme}
|
||||
aria-label={this.props.title}
|
||||
primary={this.props.primary}
|
||||
disabled={this.props.disabled}
|
||||
onMouseUp={this.onMouseUp}
|
||||
onClick={this.props.onClick}
|
||||
type={this.props.type}
|
||||
>
|
||||
{this.props.children}
|
||||
</ButtonWrapper>
|
||||
);
|
||||
|
||||
const Wrapper = this.props.title ? TooltipWrapper : CommonWrapper;
|
||||
return (
|
||||
<Wrapper
|
||||
theme={this.props.theme}
|
||||
tooltipTitle={this.props.title}
|
||||
tooltipPosition={this.props.tooltipPosition}
|
||||
size={this.props.size}
|
||||
mark={this.props.mark}
|
||||
>
|
||||
{button}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
title: PropTypes.string,
|
||||
tooltipPosition: PropTypes.oneOf([
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'bottom-left',
|
||||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
]),
|
||||
onClick: PropTypes.func,
|
||||
type: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
primary: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['big', 'normal', 'small']),
|
||||
mark: PropTypes.oneOf([
|
||||
false,
|
||||
'base08',
|
||||
'base09',
|
||||
'base0A',
|
||||
'base0B',
|
||||
'base0C',
|
||||
'base0D',
|
||||
'base0E',
|
||||
'base0F'
|
||||
]),
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
Button.defaultProps = {
|
||||
tooltipPosition: 'top'
|
||||
};
|
128
packages/devui/src/Button/Button.tsx
Normal file
128
packages/devui/src/Button/Button.tsx
Normal file
|
@ -0,0 +1,128 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import * as styles from './styles';
|
||||
import { commonStyle, tooltipStyle } from './styles/common';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const ButtonWrapper = createStyledComponent(styles, 'button');
|
||||
const TooltipWrapper = createStyledComponent(tooltipStyle);
|
||||
const CommonWrapper = createStyledComponent(commonStyle);
|
||||
|
||||
export type TooltipPosition =
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-right'
|
||||
| 'top-left'
|
||||
| 'top-right';
|
||||
|
||||
export type Size = 'big' | 'normal' | 'small';
|
||||
|
||||
export type Mark =
|
||||
| 'base08'
|
||||
| 'base09'
|
||||
| 'base0A'
|
||||
| 'base0B'
|
||||
| 'base0C'
|
||||
| 'base0D'
|
||||
| 'base0E'
|
||||
| 'base0F';
|
||||
|
||||
interface Props {
|
||||
children: unknown;
|
||||
title?: string;
|
||||
tooltipPosition: TooltipPosition;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
type?: 'button' | 'reset' | 'submit';
|
||||
disabled?: boolean;
|
||||
primary?: boolean;
|
||||
size?: Size;
|
||||
mark?: Mark | false;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
export default class Button extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
return (
|
||||
nextProps.children !== this.props.children ||
|
||||
nextProps.disabled !== this.props.disabled ||
|
||||
nextProps.mark !== this.props.mark ||
|
||||
nextProps.size !== this.props.size ||
|
||||
nextProps.primary !== this.props.primary ||
|
||||
nextProps.tooltipPosition !== this.props.tooltipPosition ||
|
||||
nextProps.title !== this.props.title
|
||||
);
|
||||
}
|
||||
|
||||
onMouseUp: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
e.currentTarget.blur();
|
||||
};
|
||||
|
||||
render() {
|
||||
const button = (
|
||||
<ButtonWrapper
|
||||
theme={this.props.theme}
|
||||
aria-label={this.props.title}
|
||||
primary={this.props.primary}
|
||||
disabled={this.props.disabled}
|
||||
onMouseUp={this.onMouseUp}
|
||||
onClick={this.props.onClick}
|
||||
type={this.props.type}
|
||||
>
|
||||
{this.props.children}
|
||||
</ButtonWrapper>
|
||||
);
|
||||
|
||||
const Wrapper = this.props.title ? TooltipWrapper : CommonWrapper;
|
||||
return (
|
||||
<Wrapper
|
||||
theme={this.props.theme}
|
||||
tooltipTitle={this.props.title}
|
||||
tooltipPosition={this.props.tooltipPosition}
|
||||
size={this.props.size}
|
||||
mark={this.props.mark}
|
||||
>
|
||||
{button}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
title: PropTypes.string,
|
||||
tooltipPosition: PropTypes.oneOf([
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'bottom-left',
|
||||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
]),
|
||||
onClick: PropTypes.func,
|
||||
type: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
primary: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['big', 'normal', 'small']),
|
||||
mark: PropTypes.oneOf([
|
||||
false,
|
||||
'base08',
|
||||
'base09',
|
||||
'base0A',
|
||||
'base0B',
|
||||
'base0C',
|
||||
'base0D',
|
||||
'base0E',
|
||||
'base0F'
|
||||
]),
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
tooltipPosition: 'top'
|
||||
};
|
||||
}
|
23
packages/devui/src/Button/stories/index.js → packages/devui/src/Button/stories/index.tsx
Executable file → Normal file
23
packages/devui/src/Button/stories/index.js → packages/devui/src/Button/stories/index.tsx
Executable file → Normal file
|
@ -5,6 +5,7 @@ import { action } from '@storybook/addon-actions';
|
|||
import { withKnobs, text, boolean, select } from '@storybook/addon-knobs';
|
||||
import MdFiberManualRecord from 'react-icons/lib/md/fiber-manual-record';
|
||||
import Button from '../';
|
||||
import { Mark, Size, TooltipPosition } from '../Button';
|
||||
|
||||
export const Container = styled.div`
|
||||
display: flex;
|
||||
|
@ -20,7 +21,9 @@ storiesOf('Button', module)
|
|||
<Container>
|
||||
<Button
|
||||
title={text('Title', 'Hello Tooltip! \\a And from new line hello!')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -29,9 +32,11 @@ storiesOf('Button', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
primary={boolean('primary', true)}
|
||||
size={select('size', ['big', 'normal', 'small'], 'normal')}
|
||||
size={select<Size>('size', ['big', 'normal', 'small'], 'normal')}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
|
@ -42,7 +47,7 @@ storiesOf('Button', module)
|
|||
.add('mark', () => (
|
||||
<Container>
|
||||
<Button
|
||||
mark={select(
|
||||
mark={select<Mark>(
|
||||
'mark',
|
||||
[
|
||||
'base08',
|
||||
|
@ -57,7 +62,9 @@ storiesOf('Button', module)
|
|||
'base08'
|
||||
)}
|
||||
title={text('Title', 'Hello Tooltip')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -66,8 +73,10 @@ storiesOf('Button', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
size={select('size', ['big', 'normal', 'small'], 'normal')}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
size={select<Size>('size', ['big', 'normal', 'small'], 'normal')}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
|
@ -1,8 +1,10 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { fadeIn } from '../../utils/animations';
|
||||
import colorEffect from '../../utils/color';
|
||||
import { Mark, Size, TooltipPosition } from '../Button';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
const both = tooltipPosition => {
|
||||
const both = (tooltipPosition: TooltipPosition) => {
|
||||
switch (tooltipPosition) {
|
||||
case 'bottom':
|
||||
return `
|
||||
|
@ -46,7 +48,7 @@ const both = tooltipPosition => {
|
|||
}
|
||||
};
|
||||
|
||||
const before = tooltipPosition => {
|
||||
const before = (tooltipPosition: TooltipPosition) => {
|
||||
switch (tooltipPosition) {
|
||||
case 'bottom-left':
|
||||
return `
|
||||
|
@ -69,7 +71,7 @@ const before = tooltipPosition => {
|
|||
}
|
||||
};
|
||||
|
||||
const after = (tooltipPosition, color) => {
|
||||
const after = (tooltipPosition: TooltipPosition, color: string) => {
|
||||
switch (tooltipPosition) {
|
||||
case 'bottom':
|
||||
return `
|
||||
|
@ -110,13 +112,13 @@ const after = (tooltipPosition, color) => {
|
|||
}
|
||||
};
|
||||
|
||||
const getDirection = tooltipPosition => {
|
||||
const getDirection = (tooltipPosition: TooltipPosition) => {
|
||||
return tooltipPosition.indexOf('-') > 0
|
||||
? tooltipPosition.substring(0, tooltipPosition.indexOf('-'))
|
||||
: tooltipPosition;
|
||||
};
|
||||
|
||||
const getSize = size => {
|
||||
const getSize = (size: Size | undefined) => {
|
||||
switch (size) {
|
||||
case 'big':
|
||||
return 'min-height: 34px; padding: 2px 12px;';
|
||||
|
@ -127,7 +129,16 @@ const getSize = size => {
|
|||
}
|
||||
};
|
||||
|
||||
export const commonStyle = ({ theme, mark, size }) => css`
|
||||
interface CommonStyleProps {
|
||||
size: Size | undefined;
|
||||
mark: Mark | false | undefined;
|
||||
}
|
||||
|
||||
export const commonStyle = ({
|
||||
theme,
|
||||
mark,
|
||||
size
|
||||
}: ThemedStyledProps<CommonStyleProps, Theme>) => css`
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
@ -145,8 +156,8 @@ export const commonStyle = ({ theme, mark, size }) => css`
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
${mark &&
|
||||
`
|
||||
${mark
|
||||
? `
|
||||
background-color: ${colorEffect(
|
||||
theme[mark],
|
||||
'fade',
|
||||
|
@ -160,21 +171,29 @@ export const commonStyle = ({ theme, mark, size }) => css`
|
|||
stroke-opacity: 0.2;
|
||||
user-select: none;
|
||||
}
|
||||
`}
|
||||
`
|
||||
: ''}
|
||||
}
|
||||
`;
|
||||
|
||||
interface TooltipStyleProps {
|
||||
tooltipTitle: string | undefined;
|
||||
tooltipPosition: TooltipPosition;
|
||||
size: Size | undefined;
|
||||
mark: Mark | false | undefined;
|
||||
}
|
||||
|
||||
export const tooltipStyle = ({
|
||||
theme,
|
||||
tooltipTitle,
|
||||
tooltipPosition,
|
||||
mark,
|
||||
size
|
||||
}) => css`
|
||||
}: ThemedStyledProps<TooltipStyleProps, Theme>) => css`
|
||||
${commonStyle({ theme, mark, size })}
|
||||
|
||||
&:before {
|
||||
content: "${tooltipTitle}";
|
||||
content: "${tooltipTitle ?? ''}";
|
||||
white-space: pre;
|
||||
color: ${theme.base06};
|
||||
line-height: 16px;
|
||||
|
@ -208,8 +227,9 @@ export const tooltipStyle = ({
|
|||
${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''}
|
||||
}
|
||||
|
||||
${theme.type !== 'material' &&
|
||||
`
|
||||
${
|
||||
theme.type !== 'material'
|
||||
? `
|
||||
&:after {
|
||||
content: "";
|
||||
border-style: solid;
|
||||
|
@ -218,7 +238,9 @@ export const tooltipStyle = ({
|
|||
${after(tooltipPosition, theme.base02)}
|
||||
${getDirection(tooltipPosition)}: 7px;
|
||||
}
|
||||
`}
|
||||
`
|
||||
: ''
|
||||
}
|
||||
|
||||
&:hover:after,
|
||||
&:hover:before {
|
|
@ -1,6 +1,16 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, primary, disabled }) => css`
|
||||
export interface StyleProps {
|
||||
primary: boolean | undefined;
|
||||
disabled: boolean | undefined;
|
||||
}
|
||||
|
||||
export const style = ({
|
||||
theme,
|
||||
primary,
|
||||
disabled
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
outline: none;
|
||||
|
@ -33,14 +43,17 @@ export const style = ({ theme, primary, disabled }) => css`
|
|||
`
|
||||
}
|
||||
|
||||
${!disabled &&
|
||||
`
|
||||
${
|
||||
!disabled
|
||||
? `
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: ${primary ? theme.base07 : theme.base02};
|
||||
box-shadow: 1px 1px 2px ${theme.base03};
|
||||
}
|
||||
`}
|
||||
`
|
||||
: ''
|
||||
}
|
||||
&:focus {
|
||||
border: 1px solid ${theme.base0D};
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { ripple } from '../../utils/animations';
|
||||
import { StyleProps } from './default';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, primary, disabled }) => css`
|
||||
export const style = ({
|
||||
theme,
|
||||
primary,
|
||||
disabled
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
outline: none;
|
|
@ -1,10 +1,23 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { getTheme } from '../utils/theme';
|
||||
import { Theme } from '../themes/default';
|
||||
import { getTheme, ThemeData } from '../utils/theme';
|
||||
import { MainContainerWrapper, ContainerWrapper } from './styles';
|
||||
|
||||
const Container = ({ themeData, className, theme, children }) => {
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
themeData?: ThemeData;
|
||||
theme?: Theme;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Container: React.FunctionComponent<Props> = ({
|
||||
themeData,
|
||||
className,
|
||||
theme,
|
||||
children
|
||||
}) => {
|
||||
if (!themeData) {
|
||||
return (
|
||||
<ContainerWrapper className={className} theme={theme}>
|
||||
|
@ -24,8 +37,8 @@ const Container = ({ themeData, className, theme, children }) => {
|
|||
|
||||
Container.propTypes = {
|
||||
children: PropTypes.node,
|
||||
themeData: PropTypes.object,
|
||||
theme: PropTypes.object,
|
||||
themeData: PropTypes.any,
|
||||
theme: PropTypes.any,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
|
@ -2,16 +2,34 @@ import React, { Component } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles/index';
|
||||
import { ReactButtonElement } from '../Tabs/Tabs';
|
||||
|
||||
const ContextMenuWrapper = createStyledComponent(styles);
|
||||
|
||||
export default class ContextMenu extends Component {
|
||||
constructor(props) {
|
||||
type Item = { name: string; value?: string } | ReactButtonElement;
|
||||
|
||||
function isReactButtonElement(item: Item): item is ReactButtonElement {
|
||||
return (item as ReactButtonElement).type === 'button';
|
||||
}
|
||||
|
||||
interface Props {
|
||||
items: Item[];
|
||||
onClick: (value: string) => void;
|
||||
x: number;
|
||||
y: number;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
export default class ContextMenu extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.updateItems(props.items);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
menu?: HTMLDivElement | null;
|
||||
items?: React.ReactNode[];
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
if (
|
||||
nextProps.items !== this.props.items ||
|
||||
nextProps.visible !== this.props.visible
|
||||
|
@ -24,25 +42,25 @@ export default class ContextMenu extends Component {
|
|||
this.amendPosition();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (prevProps.x !== this.props.x || prevProps.y !== this.props.y) {
|
||||
this.amendPosition();
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp = e => {
|
||||
e.target.blur();
|
||||
onMouseUp: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
e.currentTarget.blur();
|
||||
};
|
||||
|
||||
onClick = e => {
|
||||
this.props.onClick(e.target.value);
|
||||
onClick: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
this.props.onClick(e.currentTarget.value);
|
||||
};
|
||||
|
||||
amendPosition() {
|
||||
const { x, y } = this.props;
|
||||
const { scrollTop, scrollLeft } = document.documentElement;
|
||||
const { innerWidth, innerHeight } = window;
|
||||
const rect = this.menu.getBoundingClientRect();
|
||||
const rect = this.menu!.getBoundingClientRect();
|
||||
let left = x + scrollLeft;
|
||||
let top = y + scrollTop;
|
||||
|
||||
|
@ -59,14 +77,14 @@ export default class ContextMenu extends Component {
|
|||
left = rect.width < innerWidth ? (innerWidth - rect.width) / 2 : 0;
|
||||
}
|
||||
|
||||
this.menu.style.top = `${top}px`;
|
||||
this.menu.style.left = `${left}px`;
|
||||
this.menu!.style.top = `${top}px`;
|
||||
this.menu!.style.left = `${left}px`;
|
||||
}
|
||||
|
||||
updateItems(items) {
|
||||
updateItems(items: Item[]) {
|
||||
this.items = items.map(item => {
|
||||
if (isReactButtonElement(item)) return item;
|
||||
const value = item.value || item.name;
|
||||
if (item.type === 'button') return item;
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
|
@ -80,7 +98,7 @@ export default class ContextMenu extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
menuRef = c => {
|
||||
menuRef: React.RefCallback<HTMLDivElement> = c => {
|
||||
this.menu = c;
|
||||
};
|
||||
|
||||
|
@ -96,12 +114,12 @@ export default class ContextMenu extends Component {
|
|||
</ContextMenuWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ContextMenu.propTypes = {
|
||||
static propTypes = {
|
||||
items: PropTypes.array.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
x: PropTypes.number.isRequired,
|
||||
y: PropTypes.number.isRequired,
|
||||
visible: PropTypes.bool
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export default from './ContextMenu';
|
1
packages/devui/src/ContextMenu/index.ts
Normal file
1
packages/devui/src/ContextMenu/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './ContextMenu';
|
|
@ -1,6 +1,18 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export default ({ theme, left, top, visible }) => css`
|
||||
interface StyleProps {
|
||||
left: number;
|
||||
top: number;
|
||||
visible: boolean | undefined;
|
||||
}
|
||||
|
||||
export default ({
|
||||
theme,
|
||||
left,
|
||||
top,
|
||||
visible
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
${visible
|
||||
? `
|
||||
visibility: visible;
|
|
@ -4,20 +4,60 @@ import createStyledComponent from '../utils/createStyledComponent';
|
|||
import * as styles from './styles';
|
||||
import Button from '../Button';
|
||||
import Form from '../Form';
|
||||
import { Props as FormProps } from '../Form/Form';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const DialogWrapper = createStyledComponent(styles);
|
||||
|
||||
export default class Dialog extends (PureComponent || Component) {
|
||||
interface Props {
|
||||
open?: boolean;
|
||||
title?: string;
|
||||
children?: React.ReactNode;
|
||||
actions?: React.ReactNode[];
|
||||
submitText?: string;
|
||||
fullWidth?: boolean;
|
||||
noHeader?: boolean;
|
||||
noFooter?: boolean;
|
||||
modal?: boolean;
|
||||
onDismiss: (
|
||||
e: React.MouseEvent<HTMLButtonElement | HTMLDivElement> | false
|
||||
) => void;
|
||||
onSubmit: () => void;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
type Rest<P> = Omit<
|
||||
Props & FormProps<P>,
|
||||
| 'modal'
|
||||
| 'open'
|
||||
| 'fullWidth'
|
||||
| 'title'
|
||||
| 'children'
|
||||
| 'actions'
|
||||
| 'noHeader'
|
||||
| 'noFooter'
|
||||
| 'submitText'
|
||||
| 'onDismiss'
|
||||
>;
|
||||
function isForm<P>(rest?: FormProps<P>): rest is FormProps<P> {
|
||||
return (rest as FormProps<P>).schema !== undefined;
|
||||
}
|
||||
|
||||
export default class Dialog<P> extends (PureComponent || Component)<
|
||||
Props | (Props & FormProps<P>)
|
||||
> {
|
||||
submitButton?: HTMLInputElement | null;
|
||||
|
||||
onSubmit = () => {
|
||||
if (this.submitButton) this.submitButton.click();
|
||||
else this.props.onSubmit();
|
||||
};
|
||||
|
||||
getFormButtonRef = node => {
|
||||
getFormButtonRef: React.RefCallback<HTMLInputElement> = node => {
|
||||
this.submitButton = node;
|
||||
};
|
||||
|
||||
onKeyDown = e => {
|
||||
onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = e => {
|
||||
if (e.keyCode === 27 /* esc */) {
|
||||
e.preventDefault();
|
||||
this.props.onDismiss(false);
|
||||
|
@ -38,7 +78,7 @@ export default class Dialog extends (PureComponent || Component) {
|
|||
onDismiss,
|
||||
...rest
|
||||
} = this.props;
|
||||
const schema = rest.schema;
|
||||
const schema = (rest as Props & FormProps<P>).schema;
|
||||
|
||||
return (
|
||||
<DialogWrapper
|
||||
|
@ -47,7 +87,7 @@ export default class Dialog extends (PureComponent || Component) {
|
|||
onKeyDown={this.onKeyDown}
|
||||
theme={rest.theme}
|
||||
>
|
||||
<div onClick={!modal && onDismiss} />
|
||||
<div onClick={!modal ? onDismiss : undefined} />
|
||||
<div>
|
||||
{!noHeader && (
|
||||
<div className="mc-dialog--header">
|
||||
|
@ -57,8 +97,8 @@ export default class Dialog extends (PureComponent || Component) {
|
|||
)}
|
||||
<div className="mc-dialog--body">
|
||||
{children}
|
||||
{schema && (
|
||||
<Form {...rest}>
|
||||
{isForm(rest as FormProps<P>) && (
|
||||
<Form {...(rest as FormProps<P>)}>
|
||||
{!noFooter && (
|
||||
<input
|
||||
type="submit"
|
||||
|
@ -97,9 +137,8 @@ export default class Dialog extends (PureComponent || Component) {
|
|||
</DialogWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Dialog.propTypes = {
|
||||
static propTypes = {
|
||||
open: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
|
@ -112,4 +151,5 @@ Dialog.propTypes = {
|
|||
onDismiss: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
};
|
||||
}
|
0
packages/devui/src/Dialog/stories/index.js → packages/devui/src/Dialog/stories/index.tsx
Executable file → Normal file
0
packages/devui/src/Dialog/stories/index.js → packages/devui/src/Dialog/stories/index.tsx
Executable file → Normal file
|
@ -1,6 +1,16 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, open, fullWidth }) => css`
|
||||
export interface StyleProps {
|
||||
open: boolean | undefined;
|
||||
fullWidth: boolean | undefined;
|
||||
}
|
||||
|
||||
export const style = ({
|
||||
theme,
|
||||
open,
|
||||
fullWidth
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: 0px;
|
|
@ -1,6 +1,12 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { StyleProps } from './default';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, open, fullWidth }) => css`
|
||||
export const style = ({
|
||||
theme,
|
||||
open,
|
||||
fullWidth
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
right: 0px;
|
|
@ -1,88 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { defaultStyle, themedStyle } from './styles';
|
||||
|
||||
const EditorContainer = styled.div('', ({ theme }) =>
|
||||
theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)
|
||||
);
|
||||
|
||||
export default class Editor extends Component {
|
||||
componentDidMount() {
|
||||
this.cm = CodeMirror(this.node, {
|
||||
value: this.props.value,
|
||||
mode: this.props.mode,
|
||||
lineNumbers: this.props.lineNumbers,
|
||||
lineWrapping: this.props.lineWrapping,
|
||||
readOnly: this.props.readOnly,
|
||||
autofocus: this.props.autofocus,
|
||||
foldGutter: this.props.foldGutter,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
|
||||
});
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.cm.on('change', (doc, change) => {
|
||||
this.props.onChange(doc.getValue(), change);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.value !== this.cm.getValue()) {
|
||||
this.cm.setValue(nextProps.value);
|
||||
}
|
||||
if (nextProps.readOnly !== this.props.readOnly) {
|
||||
this.cm.setOption('readOnly', nextProps.readOnly);
|
||||
}
|
||||
if (nextProps.lineNumbers !== this.props.lineNumbers) {
|
||||
this.cm.setOption('lineNumbers', nextProps.lineNumbers);
|
||||
}
|
||||
if (nextProps.lineWrapping !== this.props.lineWrapping) {
|
||||
this.cm.setOption('lineWrapping', nextProps.lineWrapping);
|
||||
}
|
||||
if (nextProps.foldGutter !== this.props.foldGutter) {
|
||||
this.cm.setOption('foldGutter', nextProps.foldGutter);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const node = this.node;
|
||||
node.removeChild(node.children[0]);
|
||||
this.cm = null;
|
||||
}
|
||||
|
||||
getRef = node => {
|
||||
this.node = node;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <EditorContainer innerRef={this.getRef} theme={this.props.theme} />;
|
||||
}
|
||||
}
|
||||
|
||||
Editor.propTypes = {
|
||||
value: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
lineNumbers: PropTypes.bool,
|
||||
lineWrapping: PropTypes.bool,
|
||||
readOnly: PropTypes.bool,
|
||||
theme: PropTypes.object,
|
||||
foldGutter: PropTypes.bool,
|
||||
autofocus: PropTypes.bool,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
Editor.defaultProps = {
|
||||
value: '',
|
||||
mode: 'javascript',
|
||||
lineNumbers: true,
|
||||
lineWrapping: false,
|
||||
readOnly: false,
|
||||
foldGutter: true,
|
||||
autofocus: false
|
||||
};
|
108
packages/devui/src/Editor/Editor.tsx
Normal file
108
packages/devui/src/Editor/Editor.tsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { defaultStyle, themedStyle } from './styles';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const EditorContainer = styled.div(
|
||||
('' as unknown) as TemplateStringsArray,
|
||||
({ theme }: { theme: Theme }) =>
|
||||
theme.scheme === 'default' && theme.light
|
||||
? defaultStyle
|
||||
: themedStyle(theme)
|
||||
);
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
mode: string;
|
||||
lineNumbers: boolean;
|
||||
lineWrapping: boolean;
|
||||
readOnly: boolean;
|
||||
theme?: Theme;
|
||||
foldGutter: boolean;
|
||||
autofocus: boolean;
|
||||
onChange: (value: string, change: CodeMirror.EditorChangeLinkedList) => void;
|
||||
}
|
||||
|
||||
export default class Editor extends Component<Props> {
|
||||
cm?: CodeMirror.Editor | null;
|
||||
node?: HTMLDivElement | null;
|
||||
|
||||
componentDidMount() {
|
||||
this.cm = CodeMirror(this.node!, {
|
||||
value: this.props.value,
|
||||
mode: this.props.mode,
|
||||
lineNumbers: this.props.lineNumbers,
|
||||
lineWrapping: this.props.lineWrapping,
|
||||
readOnly: this.props.readOnly,
|
||||
autofocus: this.props.autofocus,
|
||||
foldGutter: this.props.foldGutter,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
|
||||
});
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.cm.on('change', (doc, change) => {
|
||||
this.props.onChange(doc.getValue(), change);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
if (nextProps.value !== this.cm!.getValue()) {
|
||||
this.cm!.setValue(nextProps.value);
|
||||
}
|
||||
if (nextProps.readOnly !== this.props.readOnly) {
|
||||
this.cm!.setOption('readOnly', nextProps.readOnly);
|
||||
}
|
||||
if (nextProps.lineNumbers !== this.props.lineNumbers) {
|
||||
this.cm!.setOption('lineNumbers', nextProps.lineNumbers);
|
||||
}
|
||||
if (nextProps.lineWrapping !== this.props.lineWrapping) {
|
||||
this.cm!.setOption('lineWrapping', nextProps.lineWrapping);
|
||||
}
|
||||
if (nextProps.foldGutter !== this.props.foldGutter) {
|
||||
this.cm!.setOption('foldGutter', nextProps.foldGutter);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const node = this.node!;
|
||||
node.removeChild(node.children[0]);
|
||||
this.cm = null;
|
||||
}
|
||||
|
||||
getRef: React.RefCallback<HTMLDivElement> = node => {
|
||||
this.node = node;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <EditorContainer innerRef={this.getRef} theme={this.props.theme} />;
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
mode: PropTypes.string,
|
||||
lineNumbers: PropTypes.bool,
|
||||
lineWrapping: PropTypes.bool,
|
||||
readOnly: PropTypes.bool,
|
||||
theme: PropTypes.object,
|
||||
foldGutter: PropTypes.bool,
|
||||
autofocus: PropTypes.bool,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
value: '',
|
||||
mode: 'javascript',
|
||||
lineNumbers: true,
|
||||
lineWrapping: false,
|
||||
readOnly: false,
|
||||
foldGutter: true,
|
||||
autofocus: false
|
||||
};
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
import React, { Component, ComponentType } from 'react';
|
||||
import Editor from '../';
|
||||
import Tabs from '../../Tabs';
|
||||
|
||||
|
@ -11,8 +10,17 @@ const value2 = `
|
|||
const func2 = () => {}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
lineNumbers: boolean;
|
||||
}
|
||||
|
||||
interface TabProps {
|
||||
value: string;
|
||||
lineNumbers: boolean;
|
||||
}
|
||||
|
||||
/* eslint-disable react/prop-types */
|
||||
export default class WithTabs extends Component {
|
||||
export default class WithTabs extends Component<Props> {
|
||||
state = {
|
||||
selected: 'Function 1'
|
||||
};
|
||||
|
@ -20,16 +28,16 @@ export default class WithTabs extends Component {
|
|||
render() {
|
||||
const { lineNumbers } = this.props;
|
||||
return (
|
||||
<Tabs
|
||||
<Tabs<TabProps>
|
||||
tabs={[
|
||||
{
|
||||
name: 'Function 1',
|
||||
component: Editor,
|
||||
component: (Editor as unknown) as ComponentType<TabProps>,
|
||||
selector: () => ({ value: value1, lineNumbers })
|
||||
},
|
||||
{
|
||||
name: 'Function 2',
|
||||
component: Editor,
|
||||
component: (Editor as unknown) as ComponentType<TabProps>,
|
||||
selector: () => ({ value: value2, lineNumbers })
|
||||
}
|
||||
]}
|
||||
|
@ -37,7 +45,6 @@ export default class WithTabs extends Component {
|
|||
onClick={selected => {
|
||||
this.setState({ selected });
|
||||
}}
|
||||
align={select('align', ['left', 'right', 'center'], 'left')}
|
||||
/>
|
||||
);
|
||||
}
|
0
packages/devui/src/Editor/stories/index.js → packages/devui/src/Editor/stories/index.tsx
Executable file → Normal file
0
packages/devui/src/Editor/stories/index.js → packages/devui/src/Editor/stories/index.tsx
Executable file → Normal file
|
@ -1,4 +1,5 @@
|
|||
import { css } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const defaultStyle = `
|
||||
height: 100%;
|
||||
|
@ -10,7 +11,7 @@ export const defaultStyle = `
|
|||
}
|
||||
`;
|
||||
|
||||
export const themedStyle = theme => css`
|
||||
export const themedStyle = (theme: Theme) => css`
|
||||
height: 100%;
|
||||
|
||||
> div {
|
|
@ -1,14 +1,23 @@
|
|||
import React, { PureComponent, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import JSONSchemaForm from 'react-jsonschema-form';
|
||||
import JSONSchemaForm, { FormProps } from 'react-jsonschema-form';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles';
|
||||
import Button from '../Button';
|
||||
import customWidgets from './widgets';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const FormContainer = createStyledComponent(styles, JSONSchemaForm);
|
||||
|
||||
export default class Form extends (PureComponent || Component) {
|
||||
export interface Props<T> extends FormProps<T> {
|
||||
children?: React.ReactNode;
|
||||
submitText?: string;
|
||||
primaryButton?: boolean;
|
||||
noSubmit?: boolean;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
export default class Form<T> extends (PureComponent || Component)<Props<T>> {
|
||||
render() {
|
||||
const {
|
||||
widgets,
|
||||
|
@ -19,7 +28,10 @@ export default class Form extends (PureComponent || Component) {
|
|||
...rest
|
||||
} = this.props;
|
||||
return (
|
||||
<FormContainer {...rest} widgets={{ ...customWidgets, ...widgets }}>
|
||||
<FormContainer
|
||||
{...(rest as Props<unknown>)}
|
||||
widgets={{ ...customWidgets, ...widgets }}
|
||||
>
|
||||
{noSubmit ? (
|
||||
<noscript />
|
||||
) : (
|
||||
|
@ -37,9 +49,8 @@ export default class Form extends (PureComponent || Component) {
|
|||
</FormContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Form.propTypes = {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
submitText: PropTypes.string,
|
||||
primaryButton: PropTypes.bool,
|
||||
|
@ -50,4 +61,5 @@ Form.propTypes = {
|
|||
widgets: PropTypes.objectOf(
|
||||
PropTypes.oneOfType([PropTypes.func, PropTypes.object])
|
||||
)
|
||||
};
|
||||
};
|
||||
}
|
0
packages/devui/src/Form/stories/index.js → packages/devui/src/Form/stories/index.tsx
Executable file → Normal file
0
packages/devui/src/Form/stories/index.js → packages/devui/src/Form/stories/index.tsx
Executable file → Normal file
|
@ -1,89 +0,0 @@
|
|||
module.exports = {
|
||||
schema: {
|
||||
title: 'Example form',
|
||||
description: 'A simple form example.',
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Full name'
|
||||
},
|
||||
age: {
|
||||
type: 'integer',
|
||||
title: 'Age'
|
||||
},
|
||||
bio: {
|
||||
type: 'string',
|
||||
title: 'Bio'
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
title: 'Password',
|
||||
minLength: 3
|
||||
},
|
||||
multipleChoicesList: {
|
||||
type: 'array',
|
||||
title: 'A multiple choices list',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: ['foo', 'bar', 'fuzz']
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
numberEnum: {
|
||||
type: 'number',
|
||||
title: 'Number enum',
|
||||
enum: [1, 2, 3]
|
||||
},
|
||||
numberEnumRadio: {
|
||||
type: 'number',
|
||||
title: 'Number enum',
|
||||
enum: [1, 2, 3]
|
||||
},
|
||||
integerRange: {
|
||||
title: 'Integer range',
|
||||
type: 'integer',
|
||||
minimum: 42,
|
||||
maximum: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
name: {
|
||||
'ui:autofocus': true
|
||||
},
|
||||
age: {
|
||||
'ui:widget': 'updown'
|
||||
},
|
||||
bio: {
|
||||
'ui:widget': 'textarea'
|
||||
},
|
||||
password: {
|
||||
'ui:widget': 'password',
|
||||
'ui:help': 'Hint: Make it strong!'
|
||||
},
|
||||
date: {
|
||||
'ui:widget': 'alt-datetime'
|
||||
},
|
||||
multipleChoicesList: {
|
||||
'ui:widget': 'checkboxes'
|
||||
},
|
||||
numberEnumRadio: {
|
||||
'ui:widget': 'radio',
|
||||
'ui:options': {
|
||||
inline: true
|
||||
}
|
||||
},
|
||||
integerRange: {
|
||||
'ui:widget': 'range'
|
||||
}
|
||||
},
|
||||
formData: {
|
||||
name: 'Chuck Norris',
|
||||
age: 75,
|
||||
bio: 'Roundhouse kicking asses since 1940',
|
||||
password: 'noneed',
|
||||
integerRange: 52
|
||||
}
|
||||
};
|
91
packages/devui/src/Form/stories/schema.ts
Normal file
91
packages/devui/src/Form/stories/schema.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
import { JSONSchema6 } from 'json-schema';
|
||||
|
||||
export const schema: JSONSchema6 = {
|
||||
title: 'Example form',
|
||||
description: 'A simple form example.',
|
||||
type: 'object',
|
||||
required: ['name'],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Full name'
|
||||
},
|
||||
age: {
|
||||
type: 'integer',
|
||||
title: 'Age'
|
||||
},
|
||||
bio: {
|
||||
type: 'string',
|
||||
title: 'Bio'
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
title: 'Password',
|
||||
minLength: 3
|
||||
},
|
||||
multipleChoicesList: {
|
||||
type: 'array',
|
||||
title: 'A multiple choices list',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: ['foo', 'bar', 'fuzz']
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
numberEnum: {
|
||||
type: 'number',
|
||||
title: 'Number enum',
|
||||
enum: [1, 2, 3]
|
||||
},
|
||||
numberEnumRadio: {
|
||||
type: 'number',
|
||||
title: 'Number enum',
|
||||
enum: [1, 2, 3]
|
||||
},
|
||||
integerRange: {
|
||||
title: 'Integer range',
|
||||
type: 'integer',
|
||||
minimum: 42,
|
||||
maximum: 100
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const uiSchema = {
|
||||
name: {
|
||||
'ui:autofocus': true
|
||||
},
|
||||
age: {
|
||||
'ui:widget': 'updown'
|
||||
},
|
||||
bio: {
|
||||
'ui:widget': 'textarea'
|
||||
},
|
||||
password: {
|
||||
'ui:widget': 'password',
|
||||
'ui:help': 'Hint: Make it strong!'
|
||||
},
|
||||
date: {
|
||||
'ui:widget': 'alt-datetime'
|
||||
},
|
||||
multipleChoicesList: {
|
||||
'ui:widget': 'checkboxes'
|
||||
},
|
||||
numberEnumRadio: {
|
||||
'ui:widget': 'radio',
|
||||
'ui:options': {
|
||||
inline: true
|
||||
}
|
||||
},
|
||||
integerRange: {
|
||||
'ui:widget': 'range'
|
||||
}
|
||||
};
|
||||
|
||||
export const formData = {
|
||||
name: 'Chuck Norris',
|
||||
age: 75,
|
||||
bio: 'Roundhouse kicking asses since 1940',
|
||||
password: 'noneed',
|
||||
integerRange: 52
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export default ({ theme }) => css`
|
||||
export default ({ theme }: ThemedStyledProps<{}, Theme>) => css`
|
||||
padding: 10px;
|
||||
line-height: 1.846;
|
||||
font-size: 14px;
|
|
@ -1,31 +0,0 @@
|
|||
import React from 'react';
|
||||
import Select from '../Select';
|
||||
import Slider from '../Slider';
|
||||
|
||||
/* eslint-disable react/prop-types */
|
||||
const SelectWidget = ({ options, multi, ...rest }) => (
|
||||
<Select options={options.enumOptions} multiple={multi} {...rest} />
|
||||
);
|
||||
|
||||
const RangeWidget = ({
|
||||
schema,
|
||||
readonly,
|
||||
autofocus,
|
||||
label, // eslint-disable-line
|
||||
options, // eslint-disable-line
|
||||
formContext, // eslint-disable-line
|
||||
registry, // eslint-disable-line
|
||||
...rest
|
||||
}) => (
|
||||
<Slider
|
||||
{...rest}
|
||||
autoFocus={autofocus}
|
||||
readOnly={readonly}
|
||||
min={schema.minimum}
|
||||
max={schema.maximum}
|
||||
step={schema.multipleOf}
|
||||
withValue
|
||||
/>
|
||||
);
|
||||
|
||||
export default { SelectWidget, RangeWidget };
|
31
packages/devui/src/Form/widgets.tsx
Normal file
31
packages/devui/src/Form/widgets.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import Select from '../Select';
|
||||
import Slider from '../Slider';
|
||||
import { FieldProps, Widget, WidgetProps } from 'react-jsonschema-form';
|
||||
import { Options } from 'react-select';
|
||||
|
||||
/* eslint-disable react/prop-types */
|
||||
const SelectWidget: Widget = ({
|
||||
options,
|
||||
onFocus,
|
||||
onBlur,
|
||||
...rest
|
||||
}: WidgetProps) => (
|
||||
<Select options={options.enumOptions as Options} {...rest} />
|
||||
);
|
||||
|
||||
const RangeWidget: Widget = (({
|
||||
schema,
|
||||
readonly,
|
||||
autofocus,
|
||||
label, // eslint-disable-line
|
||||
options, // eslint-disable-line
|
||||
formContext, // eslint-disable-line
|
||||
registry, // eslint-disable-line
|
||||
...rest
|
||||
}: WidgetProps & { registry: FieldProps['registry'] }) =>
|
||||
(
|
||||
<Slider {...rest} min={schema.minimum} max={schema.maximum} withValue />
|
||||
) as unknown) as Widget;
|
||||
|
||||
export default { SelectWidget, RangeWidget };
|
|
@ -6,11 +6,21 @@ import ErrorIcon from 'react-icons/lib/md/error';
|
|||
import SuccessIcon from 'react-icons/lib/md/check-circle';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const NotificationWrapper = createStyledComponent(styles);
|
||||
|
||||
export default class Notification extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
export type Type = 'info' | 'success' | 'warning' | 'error';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
type: Type;
|
||||
onClose?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
export default class Notification extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
return (
|
||||
nextProps.children !== this.props.children ||
|
||||
nextProps.type !== this.props.type
|
||||
|
@ -43,15 +53,15 @@ export default class Notification extends Component {
|
|||
</NotificationWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notification.propTypes = {
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired,
|
||||
type: PropTypes.oneOf(['info', 'success', 'warning', 'error']),
|
||||
onClose: PropTypes.func,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
};
|
||||
|
||||
Notification.defaultProps = {
|
||||
static defaultProps = {
|
||||
type: 'info'
|
||||
};
|
||||
};
|
||||
}
|
|
@ -4,6 +4,7 @@ import { action } from '@storybook/addon-actions';
|
|||
import styled from 'styled-components';
|
||||
import { withKnobs, text, select } from '@storybook/addon-knobs';
|
||||
import Notification from '../';
|
||||
import { Type } from '../Notification';
|
||||
|
||||
export const Container = styled.div`
|
||||
display: flex;
|
||||
|
@ -18,7 +19,7 @@ storiesOf('Notification', module)
|
|||
.add('default', () => (
|
||||
<Container>
|
||||
<Notification
|
||||
type={select(
|
||||
type={select<Type>(
|
||||
'type',
|
||||
['info', 'success', 'warning', 'error'],
|
||||
'warning'
|
|
@ -1,6 +1,8 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
import { Type } from '../Notification';
|
||||
|
||||
const getBackground = (theme, type) => {
|
||||
const getBackground = (theme: Theme, type: Type) => {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
return `background-color: ${theme.base0B};`;
|
||||
|
@ -13,7 +15,11 @@ const getBackground = (theme, type) => {
|
|||
}
|
||||
};
|
||||
|
||||
export default ({ theme, type }) => css`
|
||||
interface StyleProps {
|
||||
type: Type;
|
||||
}
|
||||
|
||||
export default ({ theme, type }: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-shrink: 0;
|
|
@ -2,23 +2,32 @@ import React, { Component } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const SegmentedWrapper = createStyledComponent(styles);
|
||||
|
||||
export default class SegmentedControl extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
interface Props {
|
||||
values: string[];
|
||||
selected?: string;
|
||||
onClick: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
export default class SegmentedControl extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
return (
|
||||
nextProps.disabled !== this.props.disabled ||
|
||||
nextProps.selected !== this.props.selected
|
||||
);
|
||||
}
|
||||
|
||||
onClick = e => {
|
||||
this.props.onClick(e.target.value);
|
||||
onClick: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
this.props.onClick(e.currentTarget.value);
|
||||
};
|
||||
|
||||
onMouseUp = e => {
|
||||
e.target.blur();
|
||||
onMouseUp: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
e.currentTarget.blur();
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -39,12 +48,12 @@ export default class SegmentedControl extends Component {
|
|||
</SegmentedWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SegmentedControl.propTypes = {
|
||||
static propTypes = {
|
||||
values: PropTypes.array.isRequired,
|
||||
selected: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import color from '../../utils/color';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export default ({ theme, disabled }) => css`
|
||||
interface StyleProps {
|
||||
disabled: boolean | undefined;
|
||||
}
|
||||
|
||||
export default ({
|
||||
theme,
|
||||
disabled
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import React, { PureComponent, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactSelect from 'react-select';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles';
|
||||
|
||||
const SelectContainer = createStyledComponent(styles, ReactSelect);
|
||||
|
||||
export default class Select extends (PureComponent || Component) {
|
||||
render() {
|
||||
return <SelectContainer {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
Select.propTypes = {
|
||||
autosize: PropTypes.bool, // whether to enable autosizing or not
|
||||
clearable: PropTypes.bool, // should it be possible to reset value
|
||||
disabled: PropTypes.bool, // whether the Select is disabled or not
|
||||
isLoading: PropTypes.bool, // whether the Select is loading externally or not
|
||||
menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options
|
||||
multi: PropTypes.bool, // multi-value input
|
||||
searchable: PropTypes.bool, // whether to enable searching feature or not
|
||||
simpleValue: PropTypes.bool, // pass the value with label to onChange
|
||||
value: PropTypes.any, // initial field value
|
||||
valueKey: PropTypes.string, // path of the label value in option objects
|
||||
openOuterUp: PropTypes.bool // value to control the opening direction
|
||||
};
|
||||
|
||||
Select.defaultProps = {
|
||||
autosize: true,
|
||||
clearable: false,
|
||||
simpleValue: true,
|
||||
menuMaxHeight: 200
|
||||
};
|
42
packages/devui/src/Select/Select.tsx
Normal file
42
packages/devui/src/Select/Select.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import React, { PureComponent, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactSelect, { ReactSelectProps } from 'react-select';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import styles from './styles';
|
||||
|
||||
const SelectContainer = createStyledComponent<Props>(
|
||||
styles,
|
||||
(ReactSelect as unknown) as React.ComponentClass<Props>
|
||||
);
|
||||
|
||||
interface Props extends Omit<ReactSelectProps, 'ref'> {
|
||||
menuMaxHeight: number;
|
||||
openOuterUp?: boolean;
|
||||
}
|
||||
|
||||
export default class Select extends (PureComponent || Component)<Props> {
|
||||
render() {
|
||||
return <SelectContainer {...this.props} />;
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
autosize: PropTypes.bool, // whether to enable autosizing or not
|
||||
clearable: PropTypes.bool, // should it be possible to reset value
|
||||
disabled: PropTypes.bool, // whether the Select is disabled or not
|
||||
isLoading: PropTypes.bool, // whether the Select is loading externally or not
|
||||
menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options
|
||||
multi: PropTypes.bool, // multi-value input
|
||||
searchable: PropTypes.bool, // whether to enable searching feature or not
|
||||
simpleValue: PropTypes.bool, // pass the value with label to onChange
|
||||
value: PropTypes.any, // initial field value
|
||||
valueKey: PropTypes.string, // path of the label value in option objects
|
||||
openOuterUp: PropTypes.bool // value to control the opening direction
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
autosize: true,
|
||||
clearable: false,
|
||||
simpleValue: true,
|
||||
menuMaxHeight: 200
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export default from './Select';
|
1
packages/devui/src/Select/index.ts
Normal file
1
packages/devui/src/Select/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './Select';
|
0
packages/devui/src/Select/stories/index.js → packages/devui/src/Select/stories/index.tsx
Executable file → Normal file
0
packages/devui/src/Select/stories/index.js → packages/devui/src/Select/stories/index.tsx
Executable file → Normal file
|
@ -1,7 +1,17 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { fadeIn, spinner } from '../../utils/animations';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export default ({ theme, openOuterUp, menuMaxHeight }) => css`
|
||||
interface StyleProps {
|
||||
openOuterUp?: boolean;
|
||||
menuMaxHeight: number;
|
||||
}
|
||||
|
||||
export default ({
|
||||
theme,
|
||||
openOuterUp,
|
||||
menuMaxHeight
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
&.Select {
|
||||
position: relative;
|
||||
|
|
@ -3,12 +3,25 @@ import PropTypes from 'prop-types';
|
|||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import * as styles from './styles';
|
||||
import { containerStyle } from './styles/common';
|
||||
import { Theme } from '../themes/default';
|
||||
|
||||
const SliderWrapper = createStyledComponent(styles);
|
||||
const ContainerWithValue = createStyledComponent(containerStyle);
|
||||
|
||||
export default class Slider extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
interface Props {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
label?: string;
|
||||
sublabel?: string;
|
||||
withValue?: boolean;
|
||||
disabled?: boolean;
|
||||
onChange: (value: number) => void;
|
||||
theme?: Theme;
|
||||
}
|
||||
|
||||
export default class Slider extends Component<Props> {
|
||||
shouldComponentUpdate(nextProps: Props) {
|
||||
return (
|
||||
nextProps.label !== this.props.label ||
|
||||
nextProps.value !== this.props.value ||
|
||||
|
@ -19,7 +32,7 @@ export default class Slider extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
onChange = e => {
|
||||
onChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
this.props.onChange(parseFloat(e.target.value));
|
||||
};
|
||||
|
||||
|
@ -53,9 +66,8 @@ export default class Slider extends Component {
|
|||
</SliderWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Slider.propTypes = {
|
||||
static propTypes = {
|
||||
value: PropTypes.number,
|
||||
min: PropTypes.number,
|
||||
max: PropTypes.number,
|
||||
|
@ -65,6 +77,7 @@ Slider.propTypes = {
|
|||
disabled: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
theme: PropTypes.object
|
||||
};
|
||||
};
|
||||
|
||||
Slider.defaultProps = { value: 0, min: 0, max: 100 };
|
||||
static defaultProps = { value: 0, min: 0, max: 100 };
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export default from './Slider';
|
1
packages/devui/src/Slider/index.ts
Normal file
1
packages/devui/src/Slider/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './Slider';
|
0
packages/devui/src/Slider/stories/index.js → packages/devui/src/Slider/stories/index.tsx
Executable file → Normal file
0
packages/devui/src/Slider/stories/index.js → packages/devui/src/Slider/stories/index.tsx
Executable file → Normal file
|
@ -1,6 +1,7 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const containerStyle = ({ theme }) => css`
|
||||
export const containerStyle = ({ theme }: ThemedStyledProps<{}, Theme>) => css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -6,10 +6,22 @@ Based on:
|
|||
http://codepen.io/thebabydino/pen/YPOPxr
|
||||
*/
|
||||
|
||||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { prefixSelectors } from '../../utils/autoPrefix';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, percent, disabled, withLabel }) => css`
|
||||
export interface StyleProps {
|
||||
percent: number;
|
||||
disabled: boolean;
|
||||
withLabel: boolean;
|
||||
}
|
||||
|
||||
export const style = ({
|
||||
theme,
|
||||
percent,
|
||||
disabled,
|
||||
withLabel
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
@ -56,9 +68,7 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
|
|||
height: 0.8em;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 0 0 .125em ${theme.base04};
|
||||
background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${
|
||||
theme.base01
|
||||
})
|
||||
background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${theme.base01})
|
||||
no-repeat ${theme.base00};
|
||||
background-size: ${percent}% 100%;
|
||||
}`
|
|
@ -1,9 +1,16 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { prefixSelectors } from '../../utils/autoPrefix';
|
||||
import color from '../../utils/color';
|
||||
import { animationCurve } from '../../utils/animations';
|
||||
import { StyleProps } from './default';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, percent, disabled, withLabel }) => css`
|
||||
export const style = ({
|
||||
theme,
|
||||
percent,
|
||||
disabled,
|
||||
withLabel
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
|
@ -3,27 +3,54 @@ import PropTypes from 'prop-types';
|
|||
import TabsHeader from './TabsHeader';
|
||||
import { TabsContainer } from './styles/common';
|
||||
|
||||
export default class Tabs extends Component {
|
||||
constructor(props) {
|
||||
export type ReactButtonElement = React.ReactElement<
|
||||
JSX.IntrinsicElements['button'],
|
||||
'button'
|
||||
>;
|
||||
|
||||
export interface Tab<P> {
|
||||
name: string;
|
||||
value?: string;
|
||||
component?: React.ComponentType<P>;
|
||||
selector?: (tab: this) => P;
|
||||
}
|
||||
|
||||
export type Position = 'left' | 'right' | 'center';
|
||||
|
||||
interface Props<P> {
|
||||
tabs: Tab<P>[];
|
||||
selected?: string;
|
||||
main?: boolean;
|
||||
onClick: (value: string) => void;
|
||||
collapsible?: boolean;
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export default class Tabs<P> extends Component<Props<P>> {
|
||||
constructor(props: Props<P>) {
|
||||
super(props);
|
||||
this.updateTabs(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
tabsHeader?: ReactButtonElement[];
|
||||
SelectedComponent?: React.ComponentType<P>;
|
||||
selector?: () => P;
|
||||
|
||||
componentWillReceiveProps(nextProps: Props<P>) {
|
||||
if (nextProps.selected !== this.props.selected) {
|
||||
this.updateTabs(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp = e => {
|
||||
e.target.blur();
|
||||
onMouseUp: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
e.currentTarget.blur();
|
||||
};
|
||||
|
||||
onClick = e => {
|
||||
this.props.onClick(e.target.value);
|
||||
onClick: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
this.props.onClick(e.currentTarget.value);
|
||||
};
|
||||
|
||||
updateTabs(props) {
|
||||
updateTabs(props: Props<P>) {
|
||||
const tabs = props.tabs;
|
||||
const selected = props.selected;
|
||||
|
||||
|
@ -34,7 +61,7 @@ export default class Tabs extends Component {
|
|||
isSelected = true;
|
||||
if (tab.component) {
|
||||
this.SelectedComponent = tab.component;
|
||||
if (tab.selector) this.selector = () => tab.selector(tab);
|
||||
if (tab.selector) this.selector = () => tab.selector!(tab);
|
||||
}
|
||||
}
|
||||
return (
|
||||
|
@ -54,7 +81,7 @@ export default class Tabs extends Component {
|
|||
render() {
|
||||
const tabsHeader = (
|
||||
<TabsHeader
|
||||
tabs={this.tabsHeader}
|
||||
tabs={this.tabsHeader!}
|
||||
items={this.props.tabs}
|
||||
main={this.props.main}
|
||||
collapsible={this.props.collapsible}
|
||||
|
@ -76,20 +103,20 @@ export default class Tabs extends Component {
|
|||
<TabsContainer position={this.props.position}>
|
||||
{tabsHeader}
|
||||
<div>
|
||||
<this.SelectedComponent {...this.selector && this.selector()} />
|
||||
<this.SelectedComponent {...(this.selector! && this.selector!())} />
|
||||
</div>
|
||||
</TabsContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Tabs.propTypes = {
|
||||
static propTypes = {
|
||||
tabs: PropTypes.array.isRequired,
|
||||
selected: PropTypes.string,
|
||||
main: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
collapsible: PropTypes.bool,
|
||||
position: PropTypes.oneOf(['left', 'right', 'center'])
|
||||
};
|
||||
};
|
||||
|
||||
Tabs.defaultProps = { position: 'left' };
|
||||
static defaultProps = { position: 'left' };
|
||||
}
|
|
@ -5,11 +5,29 @@ import CollapseIcon from 'react-icons/lib/fa/angle-double-right';
|
|||
import ContextMenu from '../ContextMenu';
|
||||
import createStyledComponent from '../utils/createStyledComponent';
|
||||
import * as styles from './styles';
|
||||
import { ReactButtonElement, Tab } from './Tabs';
|
||||
|
||||
const TabsWrapper = createStyledComponent(styles);
|
||||
|
||||
export default class TabsHeader extends Component {
|
||||
constructor(props) {
|
||||
interface Props<P> {
|
||||
tabs: ReactButtonElement[];
|
||||
items: Tab<P>[];
|
||||
main: boolean | undefined;
|
||||
onClick: (value: string) => void;
|
||||
position: 'left' | 'right' | 'center';
|
||||
collapsible: boolean | undefined;
|
||||
selected: string | undefined;
|
||||
}
|
||||
|
||||
interface State {
|
||||
visibleTabs: ReactButtonElement[];
|
||||
hiddenTabs: ReactButtonElement[];
|
||||
subMenuOpened: boolean;
|
||||
contextMenu: { top: number; left: number } | undefined;
|
||||
}
|
||||
|
||||
export default class TabsHeader<P> extends Component<Props<P>, State> {
|
||||
constructor(props: Props<P>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visibleTabs: props.tabs.slice(),
|
||||
|
@ -21,7 +39,13 @@ export default class TabsHeader extends Component {
|
|||
this.hiddenTabsWidth = [];
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
iconWidth: number;
|
||||
hiddenTabsWidth: number[];
|
||||
tabsWrapperRef?: HTMLDivElement | null;
|
||||
tabsRef?: HTMLDivElement | null;
|
||||
resizeDetector?: HTMLIFrameElement;
|
||||
|
||||
componentWillReceiveProps(nextProps: Props<P>) {
|
||||
if (
|
||||
nextProps.tabs !== this.props.tabs ||
|
||||
nextProps.selected !== this.props.selected ||
|
||||
|
@ -38,7 +62,7 @@ export default class TabsHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps: Props<P>) {
|
||||
const { collapsible } = this.props;
|
||||
if (!collapsible) {
|
||||
if (prevProps.collapsible !== collapsible) this.disableResizeEvents();
|
||||
|
@ -47,8 +71,11 @@ export default class TabsHeader extends Component {
|
|||
|
||||
let shouldCollapse = false;
|
||||
if (this.iconWidth === 0) {
|
||||
const tabButtons = this.tabsRef.children;
|
||||
if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') {
|
||||
const tabButtons = this.tabsRef!.children;
|
||||
if (
|
||||
(this.tabsRef!.children[tabButtons.length - 1] as HTMLButtonElement)
|
||||
.value === 'expandIcon'
|
||||
) {
|
||||
this.iconWidth = tabButtons[
|
||||
tabButtons.length - 1
|
||||
].getBoundingClientRect().width;
|
||||
|
@ -73,12 +100,12 @@ export default class TabsHeader extends Component {
|
|||
}
|
||||
|
||||
enableResizeEvents() {
|
||||
this.resizeDetector = observeResize(this.tabsWrapperRef, this.collapse);
|
||||
this.resizeDetector = observeResize(this.tabsWrapperRef!, this.collapse);
|
||||
window.addEventListener('mousedown', this.hideSubmenu);
|
||||
}
|
||||
|
||||
disableResizeEvents() {
|
||||
this.resizeDetector.remove();
|
||||
this.resizeDetector!.remove();
|
||||
window.removeEventListener('mousedown', this.hideSubmenu);
|
||||
}
|
||||
|
||||
|
@ -88,13 +115,13 @@ export default class TabsHeader extends Component {
|
|||
const { selected, tabs } = this.props;
|
||||
const tabsWrapperRef = this.tabsWrapperRef;
|
||||
const tabsRef = this.tabsRef;
|
||||
const tabButtons = this.tabsRef.children;
|
||||
const tabButtons = this.tabsRef!.children;
|
||||
const visibleTabs = this.state.visibleTabs;
|
||||
const hiddenTabs = this.state.hiddenTabs;
|
||||
let tabsWrapperRight = tabsWrapperRef.getBoundingClientRect().right;
|
||||
let tabsWrapperRight = tabsWrapperRef!.getBoundingClientRect().right;
|
||||
if (!tabsWrapperRight) return; // tabs are hidden
|
||||
|
||||
const tabsRefRight = tabsRef.getBoundingClientRect().right;
|
||||
const tabsRefRight = tabsRef!.getBoundingClientRect().right;
|
||||
let i = visibleTabs.length - 1;
|
||||
let hiddenTab;
|
||||
|
||||
|
@ -102,16 +129,16 @@ export default class TabsHeader extends Component {
|
|||
if (
|
||||
this.props.position === 'right' &&
|
||||
hiddenTabs.length > 0 &&
|
||||
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
|
||||
tabsWrapperRef.getBoundingClientRect().width
|
||||
tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
|
||||
tabsWrapperRef!.getBoundingClientRect().width
|
||||
) {
|
||||
while (
|
||||
i < tabs.length - 1 &&
|
||||
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
|
||||
tabsWrapperRef.getBoundingClientRect().width
|
||||
tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
|
||||
tabsWrapperRef!.getBoundingClientRect().width
|
||||
) {
|
||||
hiddenTab = hiddenTabs.shift();
|
||||
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
|
||||
visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
|
@ -121,7 +148,7 @@ export default class TabsHeader extends Component {
|
|||
tabButtons[i].getBoundingClientRect().right >=
|
||||
tabsWrapperRight - this.iconWidth
|
||||
) {
|
||||
if (tabButtons[i].value !== selected) {
|
||||
if ((tabButtons[i] as HTMLButtonElement).value !== selected) {
|
||||
hiddenTabs.unshift(...visibleTabs.splice(i, 1));
|
||||
this.hiddenTabsWidth.unshift(
|
||||
tabButtons[i].getBoundingClientRect().width
|
||||
|
@ -140,7 +167,7 @@ export default class TabsHeader extends Component {
|
|||
tabsWrapperRight - this.iconWidth
|
||||
) {
|
||||
hiddenTab = hiddenTabs.shift();
|
||||
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
|
||||
visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!);
|
||||
this.hiddenTabsWidth.shift();
|
||||
i++;
|
||||
}
|
||||
|
@ -152,15 +179,15 @@ export default class TabsHeader extends Component {
|
|||
this.setState({ subMenuOpened: false, contextMenu: undefined });
|
||||
};
|
||||
|
||||
getTabsWrapperRef = node => {
|
||||
getTabsWrapperRef: React.RefCallback<HTMLDivElement> = node => {
|
||||
this.tabsWrapperRef = node;
|
||||
};
|
||||
|
||||
getTabsRef = node => {
|
||||
getTabsRef: React.RefCallback<HTMLDivElement> = node => {
|
||||
this.tabsRef = node;
|
||||
};
|
||||
|
||||
expandMenu = e => {
|
||||
expandMenu: React.MouseEventHandler<HTMLButtonElement> = e => {
|
||||
const rect = e.currentTarget.children[0].getBoundingClientRect();
|
||||
this.setState({
|
||||
contextMenu: {
|
||||
|
@ -174,11 +201,7 @@ export default class TabsHeader extends Component {
|
|||
render() {
|
||||
const { visibleTabs, hiddenTabs, contextMenu } = this.state;
|
||||
return (
|
||||
<TabsWrapper
|
||||
innerRef={this.getTabsWrapperRef}
|
||||
main={this.props.main}
|
||||
position={this.props.position}
|
||||
>
|
||||
<TabsWrapper innerRef={this.getTabsWrapperRef} main={this.props.main}>
|
||||
<div ref={this.getTabsRef}>
|
||||
{visibleTabs}
|
||||
{this.props.collapsible &&
|
||||
|
@ -200,9 +223,8 @@ export default class TabsHeader extends Component {
|
|||
</TabsWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TabsHeader.propTypes = {
|
||||
static propTypes = {
|
||||
tabs: PropTypes.array.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
main: PropTypes.bool,
|
||||
|
@ -210,4 +232,5 @@ TabsHeader.propTypes = {
|
|||
position: PropTypes.string,
|
||||
collapsible: PropTypes.bool,
|
||||
selected: PropTypes.string
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export default from './Tabs';
|
1
packages/devui/src/Tabs/index.ts
Normal file
1
packages/devui/src/Tabs/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './Tabs';
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
/* eslint-disable react/prop-types */
|
||||
const Component = ({ selected }) => (
|
||||
const Component = ({ selected }: { selected: string }) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
|
@ -17,7 +17,9 @@ const Component = ({ selected }) => (
|
|||
);
|
||||
/* eslint-enable react/prop-types */
|
||||
|
||||
const selector = tab => ({ selected: tab.name });
|
||||
const selector = (tab: { name: string; value?: string }) => ({
|
||||
selected: tab.name
|
||||
});
|
||||
|
||||
export const tabs = [
|
||||
{
|
||||
|
@ -37,6 +39,6 @@ export const tabs = [
|
|||
}
|
||||
];
|
||||
|
||||
export const simple10Tabs = [];
|
||||
export const simple10Tabs: { name: string; value: string }[] = [];
|
||||
for (let i = 1; i <= 10; i++)
|
||||
simple10Tabs.push({ name: `Tab${i}`, value: `${i}` });
|
13
packages/devui/src/Tabs/stories/index.js → packages/devui/src/Tabs/stories/index.tsx
Executable file → Normal file
13
packages/devui/src/Tabs/stories/index.js → packages/devui/src/Tabs/stories/index.tsx
Executable file → Normal file
|
@ -5,6 +5,7 @@ import { withKnobs, text, boolean, select } from '@storybook/addon-knobs';
|
|||
import styled from 'styled-components';
|
||||
import Tabs from '../';
|
||||
import { tabs, simple10Tabs } from './data';
|
||||
import { Position } from '../Tabs';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
|
@ -24,7 +25,11 @@ storiesOf('Tabs', module)
|
|||
main={boolean('main', true)}
|
||||
onClick={action('tab selected')}
|
||||
collapsible={boolean('collapsible', true)}
|
||||
position={select('position', ['left', 'right', 'center'], 'left')}
|
||||
position={select<Position>(
|
||||
'position',
|
||||
['left', 'right', 'center'],
|
||||
'left'
|
||||
)}
|
||||
/>
|
||||
</Container>
|
||||
))
|
||||
|
@ -35,6 +40,10 @@ storiesOf('Tabs', module)
|
|||
main={boolean('main', false)}
|
||||
onClick={action('tab selected')}
|
||||
collapsible={boolean('collapsible', false)}
|
||||
position={select('position', ['left', 'right', 'center'], 'left')}
|
||||
position={select<Position>(
|
||||
'position',
|
||||
['left', 'right', 'center'],
|
||||
'left'
|
||||
)}
|
||||
/>
|
||||
));
|
|
@ -1,6 +1,11 @@
|
|||
import styled from 'styled-components';
|
||||
import { Position } from '../Tabs';
|
||||
|
||||
export const TabsContainer = styled.div`
|
||||
interface StyleProps {
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export const TabsContainer = styled.div<StyleProps>`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -12,15 +17,17 @@ export const TabsContainer = styled.div`
|
|||
|
||||
> div > div:first-child {
|
||||
${props =>
|
||||
props.position !== 'left' &&
|
||||
`
|
||||
props.position !== 'left'
|
||||
? `
|
||||
margin-left: auto !important;
|
||||
`}
|
||||
${props =>
|
||||
props.position === 'center' &&
|
||||
`
|
||||
: ''}
|
||||
${props =>
|
||||
props.position === 'center'
|
||||
? `
|
||||
margin-right: auto !important;
|
||||
`}
|
||||
`
|
||||
: ''}
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
|
@ -1,17 +1,28 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, main }) => css`
|
||||
export interface StyleProps {
|
||||
main: boolean | undefined;
|
||||
}
|
||||
|
||||
export const style = ({
|
||||
theme,
|
||||
main
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: flex;
|
||||
flex: 0 0 1;
|
||||
padding-left: 1px;
|
||||
background-color: ${theme.base01};
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
${!main &&
|
||||
`
|
||||
${
|
||||
!main
|
||||
? `
|
||||
border-top: 1px solid ${theme.base01};
|
||||
border-bottom: 1px solid ${theme.base02};
|
||||
`}
|
||||
`
|
||||
: ''
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
|
@ -1,18 +1,24 @@
|
|||
import { css } from 'styled-components';
|
||||
import { css, ThemedStyledProps } from 'styled-components';
|
||||
import { ripple } from '../../utils/animations';
|
||||
import { StyleProps } from './default';
|
||||
import { Theme } from '../../themes/default';
|
||||
|
||||
export const style = ({ theme, main }) => css`
|
||||
export const style = ({
|
||||
theme,
|
||||
main
|
||||
}: ThemedStyledProps<StyleProps, Theme>) => css`
|
||||
display: flex;
|
||||
flex: 0 0 1;
|
||||
padding-left: 1px;
|
||||
background-color: ${theme.base01};
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
${!main &&
|
||||
`
|
||||
${!main
|
||||
? `
|
||||
border-top: 1px solid ${theme.base01};
|
||||
border-bottom: 1px solid ${theme.base02};
|
||||
`}
|
||||
`
|
||||
: ''}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
|
@ -24,7 +30,7 @@ export const style = ({ theme, main }) => css`
|
|||
color: ${theme.base07};
|
||||
min-height: 30px;
|
||||
padding: 0 2em;
|
||||
${main && 'text-transform: uppercase;'}
|
||||
${main ? 'text-transform: uppercase;' : ''}
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
|
@ -1,3 +0,0 @@
|
|||
export Toolbar from './styles/Toolbar';
|
||||
export Divider from './styles/Divider';
|
||||
export Spacer from './styles/Spacer';
|
3
packages/devui/src/Toolbar/index.ts
Normal file
3
packages/devui/src/Toolbar/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export { default as Toolbar } from './styles/Toolbar';
|
||||
export { default as Divider } from './styles/Divider';
|
||||
export { default as Spacer } from './styles/Spacer';
|
73
packages/devui/src/Toolbar/stories/index.js → packages/devui/src/Toolbar/stories/index.tsx
Executable file → Normal file
73
packages/devui/src/Toolbar/stories/index.js → packages/devui/src/Toolbar/stories/index.tsx
Executable file → Normal file
|
@ -25,6 +25,9 @@ import {
|
|||
} from '../../';
|
||||
import { options } from '../../Select/stories/options';
|
||||
import { simple10Tabs } from '../../Tabs/stories/data';
|
||||
import { TooltipPosition } from '../../Button/Button';
|
||||
import { BorderPosition } from '../styles/Toolbar';
|
||||
import { Position } from '../../Tabs/Tabs';
|
||||
|
||||
export const Container = styled.div`
|
||||
display: flex;
|
||||
|
@ -43,10 +46,18 @@ storiesOf('Toolbar', module)
|
|||
.addDecorator(withKnobs)
|
||||
.add('default', () => (
|
||||
<Container>
|
||||
<Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}>
|
||||
<Toolbar
|
||||
borderPosition={select<BorderPosition>(
|
||||
'borderPosition',
|
||||
['top', 'bottom'],
|
||||
'top'
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
title={text('Title', 'Hello Tooltip')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -55,7 +66,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
|
@ -64,7 +77,9 @@ storiesOf('Toolbar', module)
|
|||
<Divider />
|
||||
<Button
|
||||
title={text('Title', 'Hello Tooltip')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -73,7 +88,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
|
@ -90,7 +107,9 @@ storiesOf('Toolbar', module)
|
|||
<Toolbar>
|
||||
<Button
|
||||
title={text('Title', 'Hello Tooltip')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -99,7 +118,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
|
@ -111,11 +132,17 @@ storiesOf('Toolbar', module)
|
|||
main={boolean('main', true)}
|
||||
onClick={action('tab selected')}
|
||||
collapsible={boolean('collapsible', true)}
|
||||
position={select('position', ['left', 'right', 'center'], 'center')}
|
||||
position={select<Position>(
|
||||
'position',
|
||||
['left', 'right', 'center'],
|
||||
'center'
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
title={text('Title', 'Hello Tooltip')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -124,7 +151,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
|
@ -139,7 +168,9 @@ storiesOf('Toolbar', module)
|
|||
<Toolbar noBorder fullHeight compact>
|
||||
<Button
|
||||
title={text('play title', 'Play')}
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -148,7 +179,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
onClick={action('button clicked')}
|
||||
>
|
||||
<PlayIcon />
|
||||
|
@ -163,7 +196,9 @@ storiesOf('Toolbar', module)
|
|||
/>
|
||||
<Button
|
||||
title="Previous state"
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -172,7 +207,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
disabled
|
||||
onClick={action('previous state clicked')}
|
||||
>
|
||||
|
@ -180,7 +217,9 @@ storiesOf('Toolbar', module)
|
|||
</Button>
|
||||
<Button
|
||||
title="Next state"
|
||||
tooltipPosition={select('tooltipPosition', [
|
||||
tooltipPosition={select<TooltipPosition>(
|
||||
'tooltipPosition',
|
||||
[
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
|
@ -189,7 +228,9 @@ storiesOf('Toolbar', module)
|
|||
'bottom-right',
|
||||
'top-left',
|
||||
'top-right'
|
||||
])}
|
||||
],
|
||||
'top'
|
||||
)}
|
||||
onClick={action('next state clicked')}
|
||||
>
|
||||
<RightIcon />
|
|
@ -1,6 +1,15 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
const Toolbar = styled.div`
|
||||
export type BorderPosition = 'top' | 'bottom';
|
||||
|
||||
interface Props {
|
||||
fullHeight?: boolean;
|
||||
compact?: boolean;
|
||||
borderPosition?: BorderPosition;
|
||||
noBorder?: boolean;
|
||||
}
|
||||
|
||||
const Toolbar = styled.div<Props>`
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
|
@ -8,13 +17,15 @@ const Toolbar = styled.div`
|
|||
font-family: ${props => props.theme.fontFamily || 'monospace'};
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
${props => props.fullHeight && 'height: 100%;'}
|
||||
${props => (props.fullHeight ? 'height: 100%;' : '')}
|
||||
padding: ${props => (props.compact ? '0' : '5px')} 5px;
|
||||
background-color: ${props => props.theme.base01};
|
||||
text-align: center;
|
||||
position: relative;
|
||||
${({ borderPosition, theme }) =>
|
||||
borderPosition && `border-${borderPosition}: 1px solid ${theme.base02};`}
|
||||
borderPosition
|
||||
? `border-${borderPosition}: 1px solid ${theme.base02};`
|
||||
: ''}
|
||||
|
||||
& > div {
|
||||
margin: auto ${props => (props.noBorder ? '0' : '1px;')};
|
||||
|
@ -22,7 +33,7 @@ const Toolbar = styled.div`
|
|||
|
||||
& button {
|
||||
border-radius: 0;
|
||||
${props => props.noBorder && 'border-color: transparent;'}
|
||||
${props => (props.noBorder ? 'border-color: transparent;' : '')}
|
||||
white-space: nowrap;
|
||||
box-shadow: none !important;
|
||||
}
|
61
packages/devui/src/base16.ts
Normal file
61
packages/devui/src/base16.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
declare module 'base16' {
|
||||
export interface Base16Theme {
|
||||
scheme?: string;
|
||||
author?: string;
|
||||
base00: string;
|
||||
base01: string;
|
||||
base02: string;
|
||||
base03: string;
|
||||
base04: string;
|
||||
base05: string;
|
||||
base06: string;
|
||||
base07: string;
|
||||
base08: string;
|
||||
base09: string;
|
||||
base0A: string;
|
||||
base0B: string;
|
||||
base0C: string;
|
||||
base0D: string;
|
||||
base0E: string;
|
||||
base0F: string;
|
||||
}
|
||||
|
||||
export const threezerotwofour: Base16Theme;
|
||||
export const apathy: Base16Theme;
|
||||
export const ashes: Base16Theme;
|
||||
export const atelierDune: Base16Theme;
|
||||
export const atelierForest: Base16Theme;
|
||||
export const atelierHeath: Base16Theme;
|
||||
export const atelierLakeside: Base16Theme;
|
||||
export const atelierSeaside: Base16Theme;
|
||||
export const bespin: Base16Theme;
|
||||
export const brewer: Base16Theme;
|
||||
export const bright: Base16Theme;
|
||||
export const chalk: Base16Theme;
|
||||
export const codeschool: Base16Theme;
|
||||
export const colors: Base16Theme;
|
||||
const _default: Base16Theme;
|
||||
export default _default;
|
||||
export const eighties: Base16Theme;
|
||||
export const embers: Base16Theme;
|
||||
export const flat: Base16Theme;
|
||||
export const google: Base16Theme;
|
||||
export const grayscale: Base16Theme;
|
||||
export const greenscreen: Base16Theme;
|
||||
export const harmonic: Base16Theme;
|
||||
export const hopscotch: Base16Theme;
|
||||
export const isotope: Base16Theme;
|
||||
export const marrakesh: Base16Theme;
|
||||
export const mocha: Base16Theme;
|
||||
export const monokai: Base16Theme;
|
||||
export const ocean: Base16Theme;
|
||||
export const paraiso: Base16Theme;
|
||||
export const pop: Base16Theme;
|
||||
export const railscasts: Base16Theme;
|
||||
export const shapeshifter: Base16Theme;
|
||||
export const solarized: Base16Theme;
|
||||
export const summerfruit: Base16Theme;
|
||||
export const tomorrow: Base16Theme;
|
||||
export const tube: Base16Theme;
|
||||
export const twilight: Base16Theme;
|
||||
}
|
2
packages/devui/src/index.js → packages/devui/src/index.ts
Executable file → Normal file
2
packages/devui/src/index.js → packages/devui/src/index.ts
Executable file → Normal file
|
@ -13,4 +13,4 @@ export * from './Toolbar';
|
|||
|
||||
import color from './utils/color';
|
||||
export const effects = { color };
|
||||
export createStyledComponent from './utils/createStyledComponent';
|
||||
export { default as createStyledComponent } from './utils/createStyledComponent';
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user