Merge branch 'dev' into theme-tab-sub-components

old
Ron Derksen 2016-08-23 14:53:10 +02:00
commit 908ad72533
173 changed files with 8440 additions and 1016 deletions

View File

@ -221,9 +221,7 @@
"react/no-unknown-property": 1,
"react/prop-types": [2, {"ignore": ["onMouseDown", "onTouchStart"]}],
"react/react-in-jsx-scope": 1,
"react/require-extension": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1
"react/sort-comp": 1
}
}

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ build
node_modules
npm-debug.log
.idea
.DS_Store

View File

@ -49,7 +49,6 @@ rules:
convention: hyphenatedlowercase
mixins-before-declarations: 0
nesting-depth: 0
no-color-keyword: 1
no-color-literals: 0
no-css-comments: 1
no-debug: 1
@ -73,9 +72,6 @@ rules:
no-trailing-zero: 1
no-url-protocols: 1
placeholder-in-extend: 0
placeholder-name-format:
- 0
- convention: hyphenatedlowercase
property-sort-order:
- 1
- ignore-custom-properties: false

View File

@ -110,7 +110,7 @@ $color-primary: $palette-blue-500;
$color-primary-dark: $palette-blue-700;
```
This file should be prepended to each stylesheet compilation which be achieved in multiple ways.
This file should be prepended to each stylesheet compilation which can be achieved in multiple ways.
### Using SASS Loader
@ -122,7 +122,7 @@ sassLoader: {
}
```
In this case we have are prepending the theme import to each SASS compilation so the primary color will be changed in every single stylesheet. If you are not using webpack maybe your loader still has a similar option, otherwise don't worry, there are solutions.
In this case we are prepending the theme import to each SASS compilation so the primary color will be changed in every single stylesheet. If you are not using webpack maybe your loader still has a similar option, otherwise don't worry, there are solutions.
### Using SASS imports and props
@ -179,7 +179,7 @@ A couple of things here. First you need to use raw components to get this styles
## Roboto Font and Material Design Icons
React Toolbox assumes that you are importing [Roboto Font](https://www.google.com/fonts/specimen/Roboto) and [Material Design Icons](https://www.google.com/design/icons/).
React Toolbox assumes that you are importing [Roboto Font](https://fonts.google.com/specimen/Roboto) and [Material Design Icons](https://www.google.com/design/icons/).
In order to import the fonts for you, we'd need to include them in the CSS which is considered a bad practice. If you are not including them in your app, go to the linked sites and follow the instructions.

View File

@ -7,7 +7,7 @@
@mixin typo-display-4($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 11.2rem;
font-size: $unit * 11.2;
font-weight: 300;
line-height: 1;
letter-spacing: -.04em;
@ -19,7 +19,7 @@
@mixin typo-display-3($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 5.6rem;
font-size: $unit * 5.6;
font-weight: 400;
line-height: 1.35;
letter-spacing: -.02em;
@ -31,9 +31,9 @@
@mixin typo-display-2($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 4.5rem;
font-size: $unit * 4.5;
font-weight: 400;
line-height: 4.8rem;
line-height: $unit * 4.8;
@if $color-contrast {
opacity: .54;
@ -42,9 +42,9 @@
@mixin typo-display-1($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 3.4rem;
font-size: $unit * 3.4;
font-weight: 400;
line-height: 4rem;
line-height: $unit * 4;
@if $color-contrast {
opacity: .54;
@ -53,9 +53,9 @@
@mixin typo-headline($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 2.4rem;
font-size: $unit * 2.4;
font-weight: 400;
line-height: 3.2rem;
line-height: $unit * 3.2;
-moz-osx-font-smoothing: grayscale;
@if $color-contrast {
@ -65,7 +65,7 @@
@mixin typo-title($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 2rem;
font-size: $unit * 2;
font-weight: 500;
line-height: 1;
letter-spacing: .02em;
@ -77,9 +77,9 @@
@mixin typo-subhead($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.6rem;
font-size: $unit * 1.6;
font-weight: 400;
line-height: 2.4rem;
line-height: $unit * 2.4;
letter-spacing: .04em;
@if $color-contrast {
@ -89,9 +89,9 @@
@mixin typo-subhead-2($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.6rem;
font-size: $unit * 1.6;
font-weight: 400;
line-height: 2.8rem;
line-height: $unit * 2.8;
letter-spacing: .04em;
@if $color-contrast {
@ -101,8 +101,8 @@
@mixin typo-body-2($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
line-height: 2.4rem;
font-size: $unit * 1.4;
line-height: $unit * 2.4;
letter-spacing: 0;
@if $use-preferred {
@ -118,9 +118,9 @@
@mixin typo-body-1($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 400;
line-height: 2.4rem;
line-height: $unit * 2.4;
letter-spacing: 0;
@if $color-contrast {
@ -130,7 +130,7 @@
@mixin typo-caption($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.2rem;
font-size: $unit * 1.2;
font-weight: 400;
line-height: 1;
letter-spacing: 0;
@ -143,7 +143,7 @@
@mixin typo-blockquote($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
position: relative;
font-size: 2.4rem;
font-size: $unit * 2.4;
font-style: italic;
font-weight: 300;
line-height: 1.35;
@ -167,7 +167,7 @@
@mixin typo-menu($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 500;
line-height: 1;
letter-spacing: 0;
@ -179,7 +179,7 @@
@mixin typo-button($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 500;
line-height: 1;
text-transform: uppercase;

41
components/app_bar/index.d.ts vendored Normal file
View File

@ -0,0 +1,41 @@
import __ReactToolbox from "../index.d.ts";
export interface AppBarTheme {
/**
* Used for the component root element.
*/
appBar?: string;
/**
* Added to the root element when the app bar is fixed.
*/
fixed?: string;
/**
* Added to the root element when the app bar is flat.
*/
flat?: string;
}
interface AppBarProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Determine if the bar should have position fixed or relative.
* @default false
*/
fixed?: boolean;
/**
* If true, the AppBar shows a shadow.
* @default false
*/
flat?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: AppBarTheme;
}
export class AppBar extends __React.Component<AppBarProps, {}> { }
export default AppBar;

View File

@ -16,6 +16,7 @@ const POSITION = {
const factory = (Chip, Input) => {
class Autocomplete extends Component {
static propTypes = {
allowCreate: PropTypes.bool,
className: PropTypes.string,
direction: PropTypes.oneOf(['auto', 'up', 'down']),
disabled: PropTypes.bool,
@ -43,6 +44,7 @@ const factory = (Chip, Input) => {
};
static defaultProps = {
allowCreate: false,
className: '',
direction: 'auto',
selectedPosition: 'above',
@ -72,7 +74,6 @@ const factory = (Chip, Input) => {
const direction = this.calculateDirection();
if (this.state.direction !== direction) {
this.setState({ direction });
return false;
}
}
return true;
@ -118,9 +119,12 @@ const factory = (Chip, Input) => {
let target = this.state.active;
if (!target) {
target = [...this.suggestions().keys()][0];
if (!target && this.props.allowCreate) {
target = this.state.query;
}
this.setState({active: target});
}
this.select(target, event);
this.select(event, target);
}
if (event.which === 27) ReactDOM.findDOMNode(this).querySelector('input').blur();
@ -134,8 +138,8 @@ const factory = (Chip, Input) => {
}
};
handleSuggestionHover = (key) => {
this.setState({active: key});
handleSuggestionHover = (event) => {
this.setState({active: event.target.id});
};
calculateDirection () {
@ -150,12 +154,18 @@ const factory = (Chip, Input) => {
}
query (key) {
return !this.props.multiple && key ? this.source().get(key) : '';
}
let query_value = '';
if (!this.props.multiple && key) {
const source_value = this.source().get(key);
query_value = source_value ? source_value : key;
}
return query_value;
}
suggestions () {
let suggest = new Map();
const query = this.state.query.toLowerCase().trim() || '';
const rawQuery = this.state.query || this.props.multiple ? '' : this.props.value;
const query = (rawQuery || '').toLowerCase().trim();
const values = this.values();
const source = this.source();
@ -216,11 +226,12 @@ const factory = (Chip, Input) => {
return valueMap;
}
select (key, event) {
select = (event, target) => {
events.pauseEvent(event);
const values = this.values(this.props.value);
this.handleChange([key, ...values.keys()], event);
}
const newValue = target === void 0 ? event.target.id : target;
this.handleChange([newValue, ...values.keys()], event);
};
unselect (key, event) {
if (!this.props.disabled) {
@ -255,10 +266,11 @@ const factory = (Chip, Input) => {
const className = classnames(theme.suggestion, {[theme.active]: this.state.active === key});
return (
<li
id={key}
key={key}
className={className}
onMouseDown={this.select.bind(this, key)}
onMouseOver={this.handleSuggestionHover.bind(this, key)}
onMouseDown={this.select}
onMouseOver={this.handleSuggestionHover}
>
{value}
</li>
@ -271,8 +283,8 @@ const factory = (Chip, Input) => {
render () {
const {
error, label, source, suggestionMatch, //eslint-disable-line no-unused-vars
selectedPosition, showSuggestionsWhenValueIsSet, //eslint-disable-line no-unused-vars
allowCreate, error, label, source, suggestionMatch, //eslint-disable-line no-unused-vars
selectedPosition, showSuggestionsWhenValueIsSet, //eslint-disable-line no-unused-vars
theme, ...other
} = this.props;
const className = classnames(theme.autocomplete, {

105
components/autocomplete/index.d.ts vendored Normal file
View File

@ -0,0 +1,105 @@
import __ReactToolbox from "../index.d.ts";
export interface AutocompleteTheme {
/**
* Used for a suggestion when it's active.
*/
active?: string;
/**
* Used for the root element.
*/
autocomplete?: string;
/**
* Used when the input is focused.
*/
focus?: string;
/**
* Used to style the Input component.
*/
input?: string;
/**
* Used for the label.
*/
label?: string;
/**
* Used to style each suggestion.
*/
suggestion?: string;
/**
* Used to style the suggestions container.
*/
suggestions?: string;
/**
* Used for the suggestions when it's opening to the top.
*/
up?: string;
/**
* Classname used for a single value.
*/
value?: string;
/**
* Classname used for the values container.
*/
values?: string;
}
interface AutocompleteProps extends __ReactToolbox.Props {
/**
* Determines the opening direction. It can be auto, up or down.
* @default auto
*/
direction?: "auto" | "up" | "down";
/**
* If true, component will be disabled.
*/
disabled?: boolean;
/**
* Sets the error string for the internal input element.
* @default false
*/
error?: string;
/**
* The text string to use for the floating label element.
*/
label?: string;
/**
* If true, component can hold multiple values.
*/
multiple?: boolean;
/**
* Callback function that is fired when the components's value changes.
* @default auto
*/
onChange?: Function;
/**
* Determines if the selected list is shown above or below input. It can be above or below.
* @default above
*/
selectedPosition?: "above" | "below";
/**
* If true, the list of suggestions will not be filtered when a value is selected.
* @default false
*/
showSuggestionsWHenValueIsSet?: boolean;
/**
* Object of key/values or array representing all items suggested.
*/
source?: any;
/**
* Determines how suggestions are supplied.
* @default start
*/
suggestionMatch?: "start" | "anywhere" | "word";
/**
* Classnames object defining the component style.
*/
theme?: AutocompleteTheme;
/**
* Value or array of values currently selected component.
*/
value?: any;
}
export class Autocomplete extends __React.Component<AutocompleteProps, {}> { }
export default Autocomplete;

View File

@ -43,6 +43,7 @@ If you want to provide a theme via context, the component key is `RTAutocomplete
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `allowCreate` | `Bool` | `false` | Determines if user can create a new option with the current typed value |
| `className` | `String` | `''` | Sets a class to style of the Component.|
| `direction` | `String` | `auto` | Determines the opening direction. It can be `auto`, `top` or `bottom`.|
| `disabled` | `Bool` | `false` | If true, component will be disabled.|

47
components/avatar/index.d.ts vendored Normal file
View File

@ -0,0 +1,47 @@
import __ReactToolbox from "../index.d.ts";
export interface AvatarTheme {
/**
* Used for the root class of the element.
*/
avatar?: string;
/**
* Added to the root element when the component has image.
*/
image?: string;
/**
* Used for the root element if the component shows the letter.
*/
letter?: string;
}
interface AvatarProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Set to true if your image is not squared so it will be used as a cover for the element.
*/
cover?: boolean;
/**
* A key to identify an Icon from Material Design Icons or a custom Icon Element.
*/
icon?: __React.ReactNode | string;
/**
* An image source or an image element.
*/
image?: __React.ReactNode | string;
/**
* Classnames object defining the component style.
*/
theme?: AvatarTheme;
/**
* A title for the image. If no image is provided, the first letter will be displayed as the avatar.
*/
title?: string;
}
export class Avatar extends __React.Component<AvatarProps, {}> { }
export default Avatar;

211
components/button/index.d.ts vendored Normal file
View File

@ -0,0 +1,211 @@
import __ReactToolbox from "../index.d.ts";
export interface ButtonTheme {
/**
* Used for the root in case button is accent.
*/
accent?: string;
/**
* Used for the root element in any button.
*/
button?: string;
/**
* Used when the button is flat for the root element.
*/
flat?: string;
/**
* Used when the button is floating for the root element.
*/
floating?: string;
/**
* For the icon inside a button.
*/
icon?: string;
/**
* Used when colors are inverted.
*/
inverse?: string;
/**
* Used for mini floating buttons.
*/
mini?: string;
/**
* Used for neutral colored buttons.
*/
neutral?: string;
/**
* Used for primary buttons when button is primary.
*/
primary?: string;
/**
* Used when the button is raised for root element.
*/
raised?: string;
/**
* Used for the ripple element.
*/
rippleWrapper?: string;
/**
* Used for toggle buttons in the root element.
*/
toggle?: string;
}
interface ButtonProps extends __ReactToolbox.Props {
/**
* Indicates if the button should have accent color.
* @default false
*/
accent?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, component will be disabled.
* @default false
*/
disabled?: boolean;
/**
* If true, the button will have a flat look.
* @default false
*/
flat?: boolean;
/**
* If true, the button will have a floating look.
* @default false
*/
floating?: boolean;
/**
* Creates a link for the button.
*/
href?: string;
/**
* Value of the icon (See Font Icon Component).
*/
icon?: __React.ReactNode | string;
/**
* If true, the neutral colors are inverted. Useful to put a button over a dark background.
*/
inverse?: boolean;
/**
* The text string to use for the name of the button.
*/
label?: string;
/**
* To be used with floating button. If true, the button will be smaller.
* @default false
*/
mini?: boolean;
/**
* Set it to false if you don't want the neutral styles to be included.
* @default true
*/
neutral?: boolean;
/**
* Indicates if the button should have primary color.
* @default false
*/
primary?: boolean;
/**
* If true, the button will have a raised look.
* @default false
*/
raised?: boolean;
/**
* If true, component will have a ripple effect on click.
* @default true
*/
ripple?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: ButtonTheme;
}
export class Button extends __React.Component<ButtonProps, {}> { }
export interface IconButtonTheme {
/**
* Used for the root in case button is accent.
*/
accent?: string;
/**
* Used for the root element in any button.
*/
button?: string;
/**
* For the icon inside a button.
*/
icon?: string;
/**
* Used when colors are inverted.
*/
inverse?: string;
/**
* Used for neutral colored buttons.
*/
neutral?: string;
/**
* Used for primary buttons when button is primary.
*/
primary?: string;
/**
* Used for the ripple element.
*/
rippleWrapper?: string;
/**
* Used for toggle buttons in the root element.
*/
toggle?: string;
}
interface IconButtonProps extends __ReactToolbox.Props {
/**
* Indicates if the button should have accent color.
* @default false
*/
accent?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, component will be disabled.
* @default false
*/
disabled?: boolean;
/**
* Creates a link for the button.
*/
href?: string;
/**
* Value of the icon (See Font Icon Component).
*/
icon?: __React.ReactNode | string;
/**
* If true, the neutral colors are inverted. Useful to put a button over a dark background.
*/
inverse?: boolean;
/**
* Set it to false if you don't want the neutral styles to be included.
* @default true
*/
neutral?: boolean;
/**
* Indicates if the button should have primary color.
* @default false
*/
primary?: boolean;
/**
* If true, component will have a ripple effect on click.
* @default true
*/
ripple?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: IconButtonTheme;
}
export class IconButton extends __React.Component<IconButtonProps, {}> { }

View File

@ -17,6 +17,7 @@
align-content: center;
align-items: center;
justify-content: center;
line-height: $button-height;
text-align: center;
text-decoration: none;
white-space: nowrap;
@ -30,14 +31,14 @@
> span:not([data-react-toolbox="tooltip"]) {
display: inline-block;
line-height: $button-height;
vertical-align: middle;
vertical-align: top;
}
> svg {
display: inline-block;
width: 1em;
height: $button-height;
font-size: 120%;
vertical-align: middle;
vertical-align: top;
fill: currentColor;
}
> * {
@ -125,7 +126,7 @@
> .icon, svg {
font-size: $button-toggle-font-size;
line-height: $button-height;
vertical-align: middle;
vertical-align: top;
}
> .rippleWrapper {
border-radius: 50%;

102
components/card/index.d.ts vendored Normal file
View File

@ -0,0 +1,102 @@
import __ReactToolbox from "../index.d.ts";
export interface CardTheme {
card?: string;
raised?: string;
}
interface CardProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
raised?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: CardTheme;
}
export class Card extends __React.Component<CardProps, {}> { }
export interface CardActionsTheme {
cardActions?: string;
}
interface CardActionsProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Classnames object defining the component style.
*/
theme?: CardActionsTheme;
}
export class CardActions extends __React.Component<CardActionsProps, {}> { }
export interface CardMediaTheme {
cardMedia?: string;
content?: string;
contentOverlay?: string;
square?: string;
wide?: string;
}
interface CardMediaProps extends __ReactToolbox.Props {
aspectRatio?: "wide" | "square";
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
color?: string;
contentOverlay?: boolean;
image?: __React.ReactNode | string;
/**
* Classnames object defining the component style.
*/
theme?: CardMediaTheme;
}
export class CardMedia extends __React.Component<CardMediaProps, {}> { }
export interface CardTextTheme {
cardText?: string;
}
interface CardTextProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Classnames object defining the component style.
*/
theme?: CardTextTheme;
}
export class CardText extends __React.Component<CardTextProps, {}> { }
export interface CardTitleTheme {
large?: string;
title?: string;
small?: string;
subtitle?: string;
}
interface CardTitleProps extends __ReactToolbox.Props {
avatar?: __React.ReactNode | string;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
subtitle?: __React.ReactNode | string;
/**
* Classnames object defining the component style.
*/
theme?: CardTitleTheme;
title?: __React.ReactNode | string;
}
export class CardTitle extends __React.Component<CardTitleProps, {}> { }

View File

@ -73,10 +73,11 @@ A versatile title block that can be used in various places on the card, includin
| Name | Description|
|:---------|:-----------|
| `cardTitle` | Class used for the root element.|
| `large` | Added to the root element when the card has avatar.|
| `small` | Added to the root element when the card has no avatar.|
| `subtitle` | Added to the root element for subtitle.|
| `title` | Used in for the root element.|
| `subtitle` | Added to the subtitle element.|
| `title` | Added to the title element.|
## CardMedia

69
components/checkbox/index.d.ts vendored Normal file
View File

@ -0,0 +1,69 @@
import __ReactToolbox from "../index.d.ts";
export interface CheckboxTheme {
/**
* Used as root in the check element.
*/
check?: string;
/**
* Used for the check element when it's checked.
*/
checked?: string;
/**
* Used when the component is disabled.
*/
disabled?: string;
/**
* Used as the root class of the component.
*/
field?: string;
/**
* Used for the input element.
*/
input?: string;
/**
* Used for the ripple component.
*/
ripple?: string;
/**
* Used for the text label.
*/
text?: string;
}
interface CheckboxProps extends __ReactToolbox.Props {
/**
* Value for the checkbox, can be true or false.
* @default false
*/
checked?: boolean;
/**
* If true, the checkbox shown as disabled and cannot be modified.
* @default false
*/
disabled?: boolean;
/**
* Text label to attach next to the checkbox element.
*/
label?: __React.ReactNode | string;
/**
* The name of the field to set in the input checkbox.
*/
name?: string;
/**
* Callback called when the checkbox is blurred.
*/
onBlur?: Function;
/**
* Callback called when the checkbox value is changed.
*/
onChange?: Function;
/**
* Classnames object defining the component style.
*/
theme?: CheckboxTheme;
}
export class Checkbox extends __React.Component<CheckboxProps, {}> { }
export default Checkbox;

52
components/chip/index.d.ts vendored Normal file
View File

@ -0,0 +1,52 @@
import __ReactToolbox from "../index.d.ts";
export interface ChipTheme {
/**
* Added to the root element when the component includes an avatar.
*/
avatar?: string;
/**
* Used for the root element.
*/
chip?: string;
/**
* Added to the root element when the component is deletable.
*/
deletable?: string;
/**
* Used for the delete element wrapper.
*/
delete?: string;
/**
* Used for the delete icon.
*/
deleteIcon?: string;
/**
* Used for the delete svg inner layer.
*/
deleteX?: string;
}
interface ChipProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the chip will be rendered with a delete icon.
* @default false
*/
deletable?: boolean;
/**
* Callback to be invoked when the delete icon is clicked.
*/
onDeleteClick?: Function;
/**
* Classnames object defining the component style.
*/
theme?: ChipTheme;
}
export class Chip extends __React.Component<ChipProps, {}> { }
export default Chip;

View File

@ -5,14 +5,21 @@ import time from '../utils/time.js';
import utils from '../utils/utils.js';
import CalendarMonth from './CalendarMonth.js';
const DIRECTION_STEPS = { left: -1, right: 1 };
const factory = (IconButton) => {
class Calendar extends Component {
static propTypes = {
display: PropTypes.oneOf(['months', 'years']),
locale: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object
]),
maxDate: PropTypes.object,
minDate: PropTypes.object,
onChange: PropTypes.func,
selectedDate: PropTypes.object,
sundayFirstDayOfWeek: React.PropTypes.bool,
theme: PropTypes.shape({
active: PropTypes.string,
calendar: PropTypes.string,
@ -48,37 +55,34 @@ const factory = (IconButton) => {
this.props.onChange(time.setDay(this.state.viewDate, day), true);
};
handleYearClick = (year) => {
handleYearClick = (event) => {
const year = parseInt(event.target.id);
const viewDate = time.setYear(this.props.selectedDate, year);
this.setState({viewDate});
this.props.onChange(viewDate, false);
};
changeViewMonth = (direction, step) => {
changeViewMonth = (event) => {
const direction = event.target.id;
this.setState({
direction,
viewDate: time.addMonths(this.state.viewDate, step)
viewDate: time.addMonths(this.state.viewDate, DIRECTION_STEPS[direction])
});
};
renderYear (year) {
const props = {
className: year === this.state.viewDate.getFullYear() ? this.props.theme.active : '',
key: year,
onClick: this.handleYearClick.bind(this, year)
};
if (year === this.state.viewDate.getFullYear()) {
props.ref = 'activeYear';
}
return <li {...props}>{year}</li>;
}
renderYears () {
return (
<ul data-react-toolbox='years' ref="years" className={this.props.theme.years}>
{utils.range(1900, 2100).map((i) => { return this.renderYear(i); })}
{utils.range(1900, 2100).map(year => (
<li
children={year}
className={year === this.state.viewDate.getFullYear() ? this.props.theme.active : ''}
id={year}
key={year}
onClick={this.handleYearClick}
ref={year === this.state.viewDate.getFullYear() ? 'activeYear' : undefined}
/>
))}
</ul>
);
}
@ -88,18 +92,20 @@ const factory = (IconButton) => {
const animation = this.state.direction === 'left' ? SlideLeft : SlideRight;
return (
<div data-react-toolbox='calendar'>
<IconButton className={theme.prev} icon='chevron_left' onClick={this.changeViewMonth.bind(this, 'left', -1)} />
<IconButton className={theme.next} icon='chevron_right' onClick={this.changeViewMonth.bind(this, 'right', 1)} />
<IconButton id='left' className={theme.prev} icon='chevron_left' onClick={this.changeViewMonth} />
<IconButton id='right' className={theme.next} icon='chevron_right' onClick={this.changeViewMonth} />
<CssTransitionGroup transitionName={animation} transitionEnterTimeout={350} transitionLeaveTimeout={350}>
<CalendarMonth
key={this.state.viewDate.getMonth()}
locale={this.props.locale}
maxDate={this.props.maxDate}
minDate={this.props.minDate}
onDayClick={this.handleDayClick}
selectedDate={this.props.selectedDate}
sundayFirstDayOfWeek={this.props.sundayFirstDayOfWeek}
theme={this.props.theme}
viewDate={this.state.viewDate}
/>
/>
</CssTransitionGroup>
</div>
);

View File

@ -8,6 +8,7 @@ class Day extends Component {
disabled: PropTypes.bool,
onClick: PropTypes.func,
selectedDate: PropTypes.object,
sundayFirstDayOfWeek: PropTypes.bool,
theme: PropTypes.shape({
active: PropTypes.string,
day: PropTypes.string,
@ -18,8 +19,9 @@ class Day extends Component {
dayStyle () {
if (this.props.day === 1) {
const e = (this.props.sundayFirstDayOfWeek) ? 0 : 1;
return {
marginLeft: `${time.getFirstWeekDay(this.props.viewDate) * 100 / 7}%`
marginLeft: `${(time.getFirstWeekDay(this.props.viewDate) - e) * 100 / 7}%`
};
}
}
@ -31,6 +33,12 @@ class Day extends Component {
return sameYear && sameMonth && sameDay;
}
handleClick = () => {
if (!this.props.disabled && this.props.onClick) {
this.props.onClick(this.props.day);
}
};
render () {
const className = classnames(this.props.theme.day, {
[this.props.theme.active]: this.isSelected(),
@ -39,7 +47,7 @@ class Day extends Component {
return (
<div data-react-toolbox='day' className={className} style={this.dayStyle()}>
<span onClick={this.props.onClick}>
<span onClick={this.handleClick}>
{this.props.day}
</span>
</div>

View File

@ -5,10 +5,15 @@ import CalendarDay from './CalendarDay.js';
class Month extends Component {
static propTypes = {
locale: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object
]),
maxDate: PropTypes.object,
minDate: PropTypes.object,
onDayClick: PropTypes.func,
selectedDate: PropTypes.object,
sundayFirstDayOfWeek: React.PropTypes.bool,
theme: PropTypes.shape({
days: PropTypes.string,
month: PropTypes.string,
@ -23,9 +28,9 @@ class Month extends Component {
};
renderWeeks () {
return utils.range(0, 7).map(i => {
return <span key={i}>{time.getFullDayOfWeek(i).charAt(0)}</span>;
});
const days = utils.range(0, 7).map(d => time.getDayOfWeekLetter(d, this.props.locale));
const source = (this.props.sundayFirstDayOfWeek) ? days : [...days.slice(1), days[0]];
return source.map((d, i) => (<span key={i}>{d}</span>));
}
renderDays () {
@ -38,10 +43,11 @@ class Month extends Component {
key={i}
day={i}
disabled={disabled}
onClick={!disabled ? this.handleDayClick.bind(this, i) : null}
onClick={this.handleDayClick}
selectedDate={this.props.selectedDate}
theme={this.props.theme}
viewDate={this.props.viewDate}
sundayFirstDayOfWeek={this.props.sundayFirstDayOfWeek}
/>
);
});
@ -51,7 +57,7 @@ class Month extends Component {
return (
<div data-react-toolbox='month' className={this.props.theme.month}>
<span className={this.props.theme.title}>
{time.getFullMonth(this.props.viewDate)} {this.props.viewDate.getFullYear()}
{time.getFullMonth(this.props.viewDate, this.props.locale)} {this.props.viewDate.getFullYear()}
</span>
<div className={this.props.theme.week}>{this.renderWeeks()}</div>
<div className={this.props.theme.days}>{this.renderDays()}</div>

View File

@ -14,6 +14,7 @@ import datePickerDialogFactory from './DatePickerDialog.js';
const factory = (Input, DatePickerDialog) => {
class DatePicker extends Component {
static propTypes = {
active: PropTypes.bool,
autoOk: PropTypes.bool,
className: PropTypes.string,
error: PropTypes.string,
@ -24,6 +25,10 @@ const factory = (Input, DatePickerDialog) => {
inputClassName: PropTypes.string,
inputFormat: PropTypes.func,
label: PropTypes.string,
locale: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object
]),
maxDate: PropTypes.object,
minDate: PropTypes.object,
name: PropTypes.string,
@ -31,6 +36,8 @@ const factory = (Input, DatePickerDialog) => {
onEscKeyDown: PropTypes.func,
onKeyPress: PropTypes.func,
onOverlayClick: PropTypes.func,
readonly: PropTypes.bool,
sundayFirstDayOfWeek: React.PropTypes.bool,
theme: PropTypes.shape({
input: PropTypes.string
}),
@ -40,14 +47,36 @@ const factory = (Input, DatePickerDialog) => {
])
};
state = {
active: false
static defaultProps = {
active: false,
locale: 'en',
sundayFirstDayOfWeek: false
};
state = {
active: this.props.active
};
componentWillReceiveProps (nextProps) {
if (this.state.active !== nextProps.active) {
this.setState({ active: nextProps.active });
}
}
handleDismiss = () => {
this.setState({active: false});
};
handleInputFocus = (event) => {
events.pauseEvent(event);
this.setState({active: true});
};
handleInputBlur = (event) => {
events.pauseEvent(event);
this.setState({active: false});
};
handleInputMouseDown = (event) => {
events.pauseEvent(event);
this.setState({active: true});
@ -67,31 +96,36 @@ const factory = (Input, DatePickerDialog) => {
};
render () {
const { autoOk, inputClassName, inputFormat, maxDate, minDate,
onEscKeyDown, onOverlayClick, value, ...others } = this.props;
const { active, // eslint-disable-line
autoOk, inputClassName, inputFormat, locale, maxDate, minDate,
onEscKeyDown, onOverlayClick, readonly, sundayFirstDayOfWeek, value,
...others } = this.props;
const finalInputFormat = inputFormat || time.formatDate;
const date = Object.prototype.toString.call(value) === '[object Date]' ? value : undefined;
const formattedDate = date === undefined ? '' : finalInputFormat(value);
const formattedDate = date === undefined ? '' : finalInputFormat(value, locale);
return (
<div data-react-toolbox='date-picker'>
<Input
{...others}
className={classnames(this.props.theme.input, {[inputClassName]: inputClassName })}
disabled={readonly}
error={this.props.error}
onMouseDown={this.handleInputMouseDown}
onKeyPress={this.handleInputKeyPress}
name={this.props.name}
icon={this.props.icon}
label={this.props.label}
name={this.props.name}
onFocus={this.handleInputFocus}
onKeyPress={this.handleInputKeyPress}
onMouseDown={this.handleInputMouseDown}
readOnly
type='text'
icon={this.props.icon}
value={formattedDate}
/>
<DatePickerDialog
active={this.state.active}
autoOk={autoOk}
className={this.props.className}
locale={locale}
maxDate={maxDate}
minDate={minDate}
name={this.props.name}
@ -99,6 +133,7 @@ const factory = (Input, DatePickerDialog) => {
onEscKeyDown={onEscKeyDown}
onOverlayClick={onOverlayClick}
onSelect={this.handleSelect}
sundayFirstDayOfWeek={sundayFirstDayOfWeek}
theme={this.props.theme}
value={date}
/>
@ -115,5 +150,8 @@ const DatePickerDialog = datePickerDialogFactory(InjectDialog, Calendar);
const DatePicker = factory(InjectInput, DatePickerDialog);
export default themr(DATE_PICKER)(DatePicker);
export { factory as datePickerFactory };
export {
DatePickerDialog as DatePickerDialog,
factory as datePickerFactory
};
export { DatePicker };

View File

@ -8,6 +8,10 @@ const factory = (Dialog, Calendar) => {
active: PropTypes.bool,
autoOk: PropTypes.bool,
className: PropTypes.string,
locale: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object
]),
maxDate: PropTypes.object,
minDate: PropTypes.object,
name: PropTypes.string,
@ -15,6 +19,7 @@ const factory = (Dialog, Calendar) => {
onEscKeyDown: PropTypes.func,
onOverlayClick: PropTypes.func,
onSelect: PropTypes.func,
sundayFirstDayOfWeek: React.PropTypes.bool,
theme: PropTypes.shape({
button: PropTypes.string,
calendarWrapper: PropTypes.string,
@ -41,17 +46,20 @@ const factory = (Dialog, Calendar) => {
componentWillMount () {
this.updateStateDate(this.props.value);
}
componentWillReceiveProps (nextProps) {
this.updateStateDate(nextProps.value);
}
handleCalendarChange = (value, dayClick) => {
handleNewDate = (value, dayClick) => {
const state = {display: 'months', date: value};
if (time.dateOutOfRange(value, this.props.minDate, this.props.maxDate)) {
state.date = this.props.maxDate || this.props.minDate;
if (this.props.maxDate && this.props.minDate) {
state.date = time.closestDate(value, this.props.maxDate, this.props.minDate);
} else {
state.date = this.props.maxDate || this.props.minDate;
}
}
this.setState(state);
if (dayClick && this.props.autoOk && this.props.onSelect) {
@ -63,17 +71,15 @@ const factory = (Dialog, Calendar) => {
if (this.props.onSelect) this.props.onSelect(this.state.date, event);
};
handleSwitchDisplay = (display) => {
this.setState({ display });
handleSwitchDisplay = (event) => {
this.setState({ display: event.target.id });
};
updateStateDate = (date) => {
if (Object.prototype.toString.call(date) === '[object Date]') {
this.setState({
date
});
this.handleNewDate(date, false);
}
}
};
actions = [
{ label: 'Cancel', className: this.props.theme.button, onClick: this.props.onDismiss },
@ -85,6 +91,9 @@ const factory = (Dialog, Calendar) => {
const display = `${this.state.display}Display`;
const className = classnames(theme.dialog, this.props.className);
const headerClassName = classnames(theme.header, theme[display]);
const shortDayOfWeek = time.getShortDayOfWeek(this.state.date.getDay(), this.props.locale);
const shortMonth = time.getShortMonth(this.state.date, this.props.locale);
const date = this.state.date.getDate();
return (
<Dialog
@ -96,11 +105,11 @@ const factory = (Dialog, Calendar) => {
type="custom"
>
<header className={headerClassName}>
<span className={theme.year} onClick={this.handleSwitchDisplay.bind(this, 'years')}>
<span id='years' className={theme.year} onClick={this.handleSwitchDisplay}>
{this.state.date.getFullYear()}
</span>
<h3 className={theme.date} onClick={this.handleSwitchDisplay.bind(this, 'months')}>
{time.getShortDayOfWeek(this.state.date.getDay())}, {time.getShortMonth(this.state.date)} {this.state.date.getDate()}
<h3 id='months' className={theme.date} onClick={this.handleSwitchDisplay}>
{shortDayOfWeek}, {shortMonth} {date}
</h3>
</header>
@ -109,9 +118,11 @@ const factory = (Dialog, Calendar) => {
display={this.state.display}
maxDate={this.props.maxDate}
minDate={this.props.minDate}
onChange={this.handleCalendarChange}
onChange={this.handleNewDate}
selectedDate={this.state.date}
theme={this.props.theme} />
theme={this.props.theme}
locale={this.props.locale}
sundayFirstDayOfWeek={this.props.sundayFirstDayOfWeek} />
</div>
</Dialog>
);

View File

@ -0,0 +1,83 @@
import expect from 'expect';
import theme from '../theme.scss';
import { DatePickerDialog } from '../DatePicker';
import utils from '../../utils/testing';
describe('DatePickerDialog', function () {
describe('#on mount', function () {
it('passes value through to calendar if no maxDate/minDate specified', function () {
const value = new Date(2016, 1, 1);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value});
expect(getDatePassedToCalendar(wrapper)).toBe(value);
});
describe('when minDate but not maxDate specified', function () {
const minDate = new Date(2016, 1, 2);
it('passes through a value after minDate', function () {
const value = new Date(2016, 1, 3);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, minDate});
expect(getDatePassedToCalendar(wrapper)).toBe(value);
});
it('sanitises a value before minDate to minDate', function () {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
theme, value: new Date(2016, 1, 1), minDate
});
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
});
});
describe('when maxDate but not minDate specified', function () {
const maxDate = new Date(2016, 1, 2);
it('passes through a value before maxDate', function () {
const value = new Date(2016, 1, 1);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, maxDate});
expect(getDatePassedToCalendar(wrapper)).toBe(value);
});
it('sanitises a value after maxDate to maxDate', function () {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
theme, value: new Date(2016, 1, 3), maxDate
});
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
});
});
describe('if both minDate and maxDate are set', function () {
const minDate = new Date(2016, 1, 2);
const maxDate = new Date(2016, 1, 4);
it('sanitises value to minDate if value is before minDate', function () {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
theme,
value: new Date(2016, 1, 1),
minDate,
maxDate
});
expect(getDatePassedToCalendar(wrapper)).toBe(minDate);
});
it('sanitises value to maxDate if value is after maxDate', function () {
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {
theme,
value: new Date(2016, 1, 5),
minDate,
maxDate
});
expect(getDatePassedToCalendar(wrapper)).toBe(maxDate);
});
it('doesn\'t sanitise when value is between maxDate/minDate', function () {
const value = new Date(2016, 1, 3);
const wrapper = utils.shallowRenderComponent(DatePickerDialog, {theme, value, minDate, maxDate});
expect(getDatePassedToCalendar(wrapper)).toBe(value);
});
});
function getDatePassedToCalendar (wrapper) {
return wrapper.props.children[1].props.children.props.selectedDate;
}
});
});

148
components/date_picker/index.d.ts vendored Normal file
View File

@ -0,0 +1,148 @@
import __ReactToolbox from "../index.d.ts";
export interface DatePickerTheme {
/**
* Used for the active day and year.
*/
active?: string;
/**
* Used for the buttons in the dialog.
*/
button?: string;
/**
* Used for the calendar root element.
*/
calendar?: string;
/**
* Used as wrapper for the calendar component inside dialog.
*/
calendarWrapper?: string;
/**
* Used for the date element inside header.
*/
date?: string;
/**
* Used for the day element inside the calendar.
*/
day?: string;
/**
* Used for the list of days inside a month.
*/
days?: string;
/**
* Used for the dialog component.
*/
dialog?: string;
/**
* Added to day element when day is disabled.
*/
disabled?: string;
/**
* Used for the dialog header,.
*/
header?: string;
/**
* Used for Input element that opens the picker.
*/
input?: string;
/**
* Used for the month root element.
*/
month?: string;
/**
* Added to the root dialog when months are displayed.
*/
monthsDisplay?: string;
/**
* Used for the next month icon.
*/
next?: string;
/**
* Used for the prev month icon.
*/
prev?: string;
/**
* Used for the month title element.
*/
title?: string;
/**
* Used for the weekdays wrapper.
*/
week?: string;
/**
* Used for the year element inside header.
*/
year?: string;
/**
* Used for the years list in years view.
*/
years?: string;
/**
* Added to the root dialog when years are displayed.
*/
yearsDisplay?: string;
}
interface DatePickerProps extends __ReactToolbox.Props {
/**
* Automatically selects a date upon clicking on a day
* @default false
*/
autoOk?: boolean;
/**
* Give an error node to display under the field.
*/
error?: string;
/**
* A key to identify an Icon from Material Design Icons or a custom Icon Element.
*/
icon?: __React.ReactNode | string;
/**
* This class will be applied to Input component of DatePicker.
*/
inputClassName?: string;
/**
* Function to format the date displayed on the input.
*/
inputFormat?: Function;
/**
* The text string to use for the floating label element in the input component.
*/
label?: string;
/**
* Date object with the maximum selectable date.
*/
maxDate?: Date;
/**
* Date object with the minimum selectable date.
*/
minDate?: Date;
/**
* Name for the input field.
*/
name?: string;
/**
* Callback called when the picker value is changed.
*/
onChange?: Function;
/**
* Callback called when the ESC key is pressed with the overlay active.
*/
onEscKeyDown?: Function;
/**
* Callback to be invoked when the dialog overlay is clicked.
*/
onOverlayClick?: Function;
/**
* Classnames object defining the component style.
*/
theme?: DatePickerTheme;
/**
* Date object with the currently selected date.
*/
value?: Date | string;
}
export class DatePicker extends __React.Component<DatePickerProps, {}> { }
export default DatePicker;

View File

@ -11,6 +11,14 @@ const min_datetime = new Date(new Date(datetime).setDate(8));
datetime.setHours(17);
datetime.setMinutes(28);
const localeExample = {
months: 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'),
monthsShort: 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'),
weekdays: 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'),
weekdaysShort: 'ig._al._ar._az._og._ol._lr.'.split('_'),
weekdaysLetter: 'ig_al_ar_az_og_ol_lr'.split('_')
}
class DatePickerTest extends React.Component {
state = {date2: datetime};
@ -21,9 +29,11 @@ class DatePickerTest extends React.Component {
render () {
return (
<section>
<DatePicker label='Birthdate' onChange={this.handleChange.bind(this, 'date1')} value={this.state.date1} />
<DatePicker label='Expiration date' minDate={min_datetime} onChange={this.handleChange.bind(this, 'date2')} value={this.state.date2} />
<DatePicker label='Formatted date' inputFormat={(value) => `${value.getDate()}/${value.getMonth() + 1}/${value.getFullYear()}`} onChange={this.handleChange.bind(this, 'date3')} value={this.state.date3} />
<DatePicker label='Birthdate' sundayFirstDayOfWeek onChange={this.handleChange.bind(this, 'date1')} value={this.state.date1} />
<DatePicker label='Locale (String) - Spanish' locale='es' onChange={this.handleChange.bind(this, 'date1')} value={this.state.date1} />
<DatePicker label='Locale (Object) - Basque' locale={localeExample} onChange={this.handleChange.bind(this, 'date1')} value={this.state.date1} />
<DatePicker label='Expiration date' sundayFirstDayOfWeek minDate={min_datetime} onChange={this.handleChange.bind(this, 'date2')} value={this.state.date2} />
<DatePicker label='Formatted date' sundayFirstDayOfWeek inputFormat={(value) => `${value.getDate()}/${value.getMonth() + 1}/${value.getFullYear()}`} onChange={this.handleChange.bind(this, 'date3')} value={this.state.date3} />
</section>
);
}
@ -34,18 +44,22 @@ If you want to provide a theme via context, the component key is `RTDatePicker`.
## Properties
| Name | Type | Default | Description|
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| `autoOk` | `Boolean` | `false` | Automatically selects a date upon clicking on a day|
| `active` | `Boolean` | `false` | Allows to control if the picker should be shown from outside. Beware you should update the prop when the Dialog is closed. |
| `autoOk` | `Boolean` | `false` | Automatically selects a date upon clicking on a day. |
| `className` | `String` | | This class will be placed at the top of the `DatePickerDialog` component so you can provide custom styles.|
| `inputClassName`| `String` | | This class will be applied to `Input` component of `DatePicker`. |
| `inputFormat` | `Function` | | Function to format the date displayed on the input. |
| `label` | `String` | | The text string to use for the floating label element in the input component.|
| `locale` | `String` or `Object` | `'en'` | Set the locale for the date picker dialog ('en','es','af','ar','be','bg','bn','bo','br','bs','ca','gl','eu','pt','it',fr'). Object is supported too (see example above). |
| `maxDate` | `Date` | | Date object with the maximum selectable date. |
| `minDate` | `Date` | | Date object with the minimum selectable date. |
| `onChange` | `Function` | | Callback called when the picker value is changed.|
| `onEscKeyDown` | `Function` | | Callback called when the ESC key is pressed with the overlay active. |
| `onOverlayClick` | `Function` | | Callback to be invoked when the dialog overlay is clicked.|
| `onEscKeyDown` | `Function` | | Callback called when the ESC key is pressed with the overlay active. |
| `onOverlayClick`| `Function` | | Callback to be invoked when the dialog overlay is clicked.|
| `readonly` | `Boolean` | | The input element will be readonly and look like disabled.|
| `sundayFirstDayOfWeek` | `Boolean`| `false` | Set week's first day to Sunday. Default week's first day is Monday ([ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Week_dates)). |
| `value` | `Date` | | Date object with the currently selected date. |
## Theme

View File

@ -3,7 +3,7 @@
@import "../mixins";
@import "./config";
.input > [role="input"] {
.input:not(.disabled) > .inputElement {
cursor: pointer;
}

92
components/dialog/index.d.ts vendored Normal file
View File

@ -0,0 +1,92 @@
import __ReactToolbox from "../index.d.ts";
export interface DialogTheme {
/**
* Used for the root when the dialog is active.
*/
active?: string;
/**
* Used to wrap the dialog body.
*/
body?: string;
/**
* Used in buttons when the dialog implements actions.
*/
button?: string;
/**
* Used for the root element.
*/
dialog?: string;
/**
* Used for the navigation element when it implements actions.
*/
navigation?: string;
/**
* Used for the title element of the dialog.
*/
title?: string;
}
interface DialogActionProps {
/**
* The text string to use for the name of the button.
*/
label?: string;
/**
* Callback called when the component is clicked.
*/
onClick?: Function;
}
interface DialogProps extends __ReactToolbox.Props {
/**
* A array of objects representing the buttons for the dialog navigation area. The properties will be transferred to the buttons.
*/
actions?: DialogActionProps[];
/**
* If true, the dialog will be active.
* @default false
*/
active?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Callback called when the ESC key is pressed with the overlay active.
*/
onEscKeyDown?: Function;
/**
* Callback to be invoked when the dialog overlay is clicked.
*/
onOverlayClick?: Function;
/**
* Callback called when the mouse button is pressed on the overlay.
*/
onOverlayMouseDown?: Function;
/**
* Callback called when the mouse is moving over the overlay.
*/
onOverlayMouseMove?: Function;
/**
* Callback called when the mouse button is released over the overlay.
*/
onOverlayMouseUp?: Function;
/**
* Classnames object defining the component style.
*/
theme?: DialogTheme;
/**
* The text string to use as standar title of the dialog.
*/
title?: string;
/**
* Used to determine the size of the dialog. It can be small, normal or large.
* @default normal
*/
type?: string;
}
export class Dialog extends __React.Component<DialogProps, {}> { }
export default Dialog;

53
components/drawer/index.d.ts vendored Normal file
View File

@ -0,0 +1,53 @@
import __ReactToolbox from "../index.d.ts";
export interface DrawerTheme {
/**
* Used for the root class when the drawer is active.
*/
active?: string;
/**
* Used for the drawer content.
*/
content?: string;
/**
* Root class.
*/
drawer?: string;
/**
* Added to the root class when drawer is to the left.
*/
left?: string;
/**
* Added to the root class when drawer is to the right.
*/
right?: string;
}
interface DrawerProps extends __ReactToolbox.Props {
/**
* If true, the drawer will be visible.
* @default false
*/
active?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Callback function to be invoked when the overlay is clicked.
*/
onOverlayClick?: Function;
/**
* Classnames object defining the component style.
*/
theme?: DrawerTheme;
/**
* Type of drawer. It can be left or right to display the drawer on the left or right side of the screen.
* @default left
*/
type?: "left" | "right";
}
export class Drawer extends __React.Component<DrawerProps, {}> { }
export default Drawer;

View File

@ -130,7 +130,7 @@ const factory = (Input) => {
);
}
renderValue (item, idx) {
renderValue = (item, idx) => {
const { theme } = this.props;
const className = item.value === this.props.value ? theme.selected : null;
return (
@ -138,7 +138,7 @@ const factory = (Input) => {
{this.props.template ? this.props.template(item) : item.label}
</li>
);
}
};
render () {
const {template, theme, source, allowBlank, auto, ...others} = this.props; //eslint-disable-line no-unused-vars
@ -161,7 +161,7 @@ const factory = (Input) => {
/>
{template && selected ? this.renderTemplateValue(selected) : null}
<ul className={theme.values} ref='values'>
{source.map(this.renderValue.bind(this))}
{source.map(this.renderValue)}
</ul>
</div>
);

114
components/dropdown/index.d.ts vendored Normal file
View File

@ -0,0 +1,114 @@
import __ReactToolbox from "../index.d.ts";
export interface DropdownTheme {
/**
* Added to the root element when the dropdown is active.
*/
active?: string;
/**
* Added to the root element when it's disabled.
*/
disabled?: string;
/**
* Root element class.
*/
dropdown?: string;
/**
* Used for the error element.
*/
error?: string;
/**
* Added to the inner wrapper if it's errored.
*/
errored?: string;
/**
* Used for the inner wrapper of the component.
*/
field?: string;
/**
* Used for the the label element.
*/
label?: string;
/**
* Used to highlight the selected value.
*/
selected?: string;
/**
* Used as a wrapper for the given template value.
*/
templateValue?: string;
/**
* Added to the root element when it's opening up.
*/
up?: string;
/**
* Used for each value in the dropdown component.
*/
value?: string;
/**
* Used for the list of values.
*/
values?: string;
}
interface DropdownProps extends __ReactToolbox.Props {
/**
* If true the dropdown will preselect the first item if the supplied value matches none of the options' values.
* @default true
*/
allowBlank?: boolean;
/**
* If true, the dropdown will open up or down depending on the position in the screen.
* @default true
*/
auto?: boolean;
/**
* Set the component as disabled.
* @default false
*/
disabled?: boolean;
/**
* Give an error string to display under the field.
*/
error?: string;
/**
* The text string to use for the floating label element.
*/
label?: string;
/**
* Name for the input field.
*/
name?: string;
/**
* Callback function that is fired when the component is blurred.
*/
onBlur?: Function;
/**
* Callback function that is fired when the component's value changes.
*/
onChange?: Function;
/**
* Callback function that is fired when the component is focused.
*/
onFocus?: Function;
/**
* Array of data objects with the data to represent in the dropdown.
*/
source: any[];
/**
* Callback function that returns a JSX template to represent the element.
*/
template?: Function;
/**
* Classnames object defining the component style.
*/
theme?: DropdownTheme;
/**
* Default value using JSON data.
*/
value?: string | number;
}
export class Dropdown extends __React.Component<DropdownProps, {}> { }
export default Dropdown;

View File

@ -37,7 +37,7 @@
}
.value {
> input {
> .input {
cursor: pointer;
}
&:after {
@ -56,6 +56,7 @@
transition: transform $animation-duration $animation-curve-default;
}
}
.field {
position: relative;
padding: $input-padding 0;

16
components/font_icon/index.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
import __ReactToolbox from "../index.d.ts";
interface FontIconProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* The key string for the icon you want be displayed.
*/
value?: __React.ReactNode | string;
}
export class FontIcon extends __React.Component<FontIconProps, {}> { }
export default FontIcon;

View File

@ -24,4 +24,4 @@ export const SWITCH = 'RTSwitch';
export const TABLE = 'RTTable';
export const TABS = 'RTTabs';
export const TOOLTIP = 'RTTooltip';
export const TIMEPICKER = 'RTTimePicker';
export const TIME_PICKER = 'RTTimePicker';

55
components/index.d.ts vendored Normal file
View File

@ -0,0 +1,55 @@
export declare namespace __ReactToolbox {
interface Props {
/**
* Set a class for the root component.
*/
className?: string;
/**
* Key used to uniquely identify the element within an Array.
*/
key?: string | number;
/**
* Callback called when the component is clicked.
*/
onClick?: Function;
/**
* Fires after the mouse is released from the Component.
*/
onMouseUp?: Function;
/**
* Callback called when the mouse enters the Component.
*/
onMouseEnter?: Function;
/**
* Callback called when the mouse leaves the Component.
*/
onMouseLeave?: Function;
/**
* Callback called when the mouse press the Component.
*/
onMouseDown?: Function;
onContextMenu?: Function;
onDoubleClick?: Function;
onDrag?: Function;
onDragEnd?: Function;
onDragEnter?: Function;
onDragExit?: Function;
onDragLeave?: Function;
onDragOver?: Function;
onDragStart?: Function;
onDrop?: Function;
onMouseMove?: Function;
onMouseOut?: Function;
onMouseOver?: Function;
onTouchCancel?: Function;
onTouchEnd?: Function;
uchMove?: Function;
onTouchStart?: Function;
/**
* Set inline style for the root component.
*/
style?: __React.CSSProperties;
}
}
export default __ReactToolbox;

View File

@ -55,7 +55,10 @@ const factory = (FontIcon) => {
};
componentDidMount () {
window.addEventListener('resize', this.handleAutoresize);
if (this.props.multiline) {
window.addEventListener('resize', this.handleAutoresize);
this.handleAutoresize();
}
}
componentWillReceiveProps (nextProps) {
@ -66,15 +69,27 @@ const factory = (FontIcon) => {
}
}
componentDidUpdate () {
// resize the textarea, if nessesary
if (this.props.multiline) this.handleAutoresize();
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleAutoresize);
if (this.props.multiline) window.removeEventListener('resize', this.handleAutoresize);
}
handleChange = (event) => {
if (this.props.multiline) {
this.handleAutoresize();
}
if (this.props.onChange) this.props.onChange(event.target.value, event);
const { onChange, multiline, maxLength } = this.props;
const valueFromEvent = event.target.value;
// Trim value to maxLength if that exists (only on multiline inputs).
// Note that this is still required even tho we have the onKeyPress filter
// because the user could paste smt in the textarea.
const haveToTrim = (multiline && maxLength && event.target.value.length > maxLength);
const value = haveToTrim ? valueFromEvent.substr(0, maxLength) : valueFromEvent;
// propagate to to store and therefore to the input
if (onChange) onChange(value, event);
};
handleAutoresize = () => {
@ -90,6 +105,27 @@ const factory = (FontIcon) => {
element.style.height = `${element.scrollHeight + heightOffset}px`;
}
handleKeyPress = (event) => {
// prevent insertion of more characters if we're a multiline input
// and maxLength exists
const { multiline, maxLength, onKeyPress } = this.props;
if (multiline && maxLength) {
// check if smt is selected, in which case the newly added charcter would
// replace the selected characters, so the length of value doesn't actually
// increase.
const isReplacing = event.target.selectionEnd - event.target.selectionStart;
const value = event.target.value;
if (!isReplacing && value.length === maxLength) {
event.preventDefault();
event.stopPropagation();
return;
}
}
if (onKeyPress) onKeyPress(event);
};
blur () {
this.refs.input.blur();
}
@ -101,7 +137,7 @@ const factory = (FontIcon) => {
render () {
const { children, disabled, error, floating, hint, icon,
name, label: labelText, maxLength, multiline, required,
theme, type, value, ...others} = this.props;
theme, type, value, onKeyPress, ...others} = this.props;
const length = maxLength && value ? value.length : 0;
const labelClassName = classnames(theme.label, {[theme.fixed]: !floating});
@ -117,7 +153,7 @@ const factory = (FontIcon) => {
&& value !== ''
&& !(typeof value === Number && isNaN(value));
const InputElement = React.createElement(multiline ? 'textarea' : 'input', {
const inputElementProps = {
...others,
className: classnames(theme.inputElement, {[theme.filled]: valuePresent}),
onChange: this.handleChange,
@ -127,15 +163,21 @@ const factory = (FontIcon) => {
disabled,
required,
type,
value,
maxLength
});
value
};
if (!multiline) {
inputElementProps.maxLength = maxLength;
inputElementProps.onKeyPress = onKeyPress;
} else {
inputElementProps.rows = 1;
inputElementProps.onKeyPress = this.handleKeyPress;
}
return (
<div data-react-toolbox='input' className={className}>
{InputElement}
{React.createElement(multiline ? 'textarea' : 'input', inputElementProps)}
{icon ? <FontIcon className={theme.icon} value={icon} /> : null}
<span className={theme.bar}></span>
<span className={theme.bar} />
{labelText
? <label className={labelClassName}>
{labelText}
@ -155,6 +197,6 @@ const factory = (FontIcon) => {
};
const Input = factory(InjectedFontIcon);
export default themr(INPUT)(Input);
export default themr(INPUT, null, { withRef: true })(Input);
export { factory as inputFactory };
export { Input };

135
components/input/index.d.ts vendored Normal file
View File

@ -0,0 +1,135 @@
import __ReactToolbox from "../index.d.ts";
export interface InputTheme {
/**
* Used for the bar under the input.
*/
bar?: string;
/**
* Used for the counter element.
*/
counter?: string;
/**
* Added to the root class when input is disabled.
*/
disabled?: string;
/**
* Used for the text error.
*/
error?: string;
/**
* Added to the root class when input is errored.
*/
errored?: string;
/**
* Used when the input is hidden.
*/
hidden?: string;
/**
* Used for the hint text.
*/
hint?: string;
/**
* Used for the icon in case the input has icon.
*/
icon?: string;
/**
* Used as root class for the component.
*/
input?: string;
/**
* Used for the HTML input element.
*/
inputElement?: string;
/**
* Used in case the input is required.
*/
required?: string;
/**
* Added to the root class if the input has icon.
*/
withIcon?: string;
}
interface InputProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, component will be disabled.
* @default false
*/
disabled?: boolean;
/**
* Give an error node to display under the field.
*/
error?: string;
/**
* Indicates if the label is floating in the input field or not.
* @default true
*/
floating?: boolean;
/**
* The text string to use for hint text element.
*/
hint?: string;
/**
* Name of an icon to use as a label for the input.
*/
icon?: __React.ReactNode | string;
/**
* The text string to use for the floating label element.
*/
label?: string;
/**
* Specifies the maximum number of characters allowed in the component
*/
maxLength?: number;
/**
* If true, a textarea element will be rendered. The textarea also grows and shrinks according to the number of lines.
* @default false
*/
multiline?: boolean;
/**
* Name for the input field.
*/
name?: string;
/**
* Callback function that is fired when component is blurred.
*/
onBlur?: Function;
/**
* Callback function that is fired when the component's value changes
*/
onChange?: Function;
/**
* Callback function that is fired when component is focused.
*/
onFocus?: Function;
/**
* Callback function that is fired when a key is pressed.
*/
onKeyPress?: Function;
/**
* If true, the html input has a required attribute.
* @default false
*/
required?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: InputTheme;
/**
* Type of the input element. It can be a valid HTML5 input type
*/
type?: string;
/**
* Current value of the input element.
*/
value?: any;
}
export class Input extends __React.Component<InputProps, {}> { }
export default Input;

View File

@ -5,7 +5,7 @@ import FontIcon from '../font_icon/FontIcon.js';
import theme from './theme.scss';
const Input = inputFactory(FontIcon);
const ThemedInput = themr(INPUT, theme)(Input);
const ThemedInput = themr(INPUT, theme, { withRef: true })(Input);
export default ThemedInput;
export { ThemedInput as Input };

View File

@ -64,5 +64,13 @@ If you want to provide a theme via context, the component key is `RTInput`.
| `icon` | Used for the icon in case the input has icon.|
| `input` | Used as root class for the component.|
| `inputElement` | Used for the HTML input element.|
| `label` | Used for the label when the input has a label.|
| `required` | Used in case the input is required.|
| `withIcon` | Added to the root class if the input has icon.|
## Methods
The `Input` component has some imperative methods that are used as a bypass to the native rendered DOM element. To call this methods you will need to retrieve the instance of the component. Check the [Install](http://react-toolbox.com/#/install) section for details on how to do this. The methods included for the `Input` are:
- `blur` used to blur the `input` element.
- `focus` used to focus the `input` element.

View File

@ -35,7 +35,7 @@
border: 0;
border-bottom: 1px solid $input-text-bottom-border-color;
outline: none;
&:focus {
&:focus:not([disabled]):not([readonly]) {
~ .bar:before, ~ .bar:after {
width: 50%;
}
@ -52,7 +52,7 @@
color: $input-text-highlight-color;
}
}
&:focus, &.filled, &[type="date"], &[type="time"] {
&:focus:not([disabled]):not([readonly]), &.filled, &[type="date"], &[type="time"] {
~ .label:not(.fixed) {
top: $input-focus-label-top;
font-size: $input-label-font-size;

View File

@ -5,7 +5,7 @@ import { LAYOUT } from '../identifiers.js';
const Layout = ({ className, children, theme }) => (
<div data-react-toolbox='layout' className={classnames(theme.layout, className)}>
{children}
{React.Children.map(children, (child) => React.cloneElement(child, { theme }))}
</div>
);

193
components/layout/index.d.ts vendored Normal file
View File

@ -0,0 +1,193 @@
import __ReactToolbox from "../index.d.ts";
export interface LayoutTheme {
/**
* Class used in the container to position and align inner items.
*/
layout?: string;
}
interface LayoutProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: [NavDrawer | Panel | Sidebar];
/**
* Classnames object defining the component style.
*/
theme?: LayoutTheme;
}
export class Layout extends __React.Component<LayoutProps, {}> { }
export interface NavDrawerTheme {
/**
* Used when the drawer is active.
*/
active?: string;
/**
* Used for the content of the drawer.
*/
drawerContent?: string;
/**
* Added to the root class for large drawer.
*/
lgPermangent?: string;
/**
* Added to the root class for medium drawer.
*/
mdPermangent?: string;
/**
* Root class for the drawer.
*/
navDrawer?: string;
/**
* Added to the root class if positioning is pinned.
*/
pinned?: string;
/**
* Used as a wrapper for the drawer content.
*/
scrim?: string;
/**
* Added to the drawer content if its scrollable.
*/
scrollY?: string;
/**
* Added to the root class for small drawer.
*/
smPermanent?: string;
/**
* Added to the root class if width is wide.
*/
wide?: string;
/**
* Added to the root class for extra big drawer.
*/
xlPermanent?: string;
/**
* Added to the root class for super big drawer.
*/
xxlPermangent?: string;
/**
* Added to the root class for largest possible drawer.
*/
xxxlPermangent?: string;
}
interface NavDrawerProps extends __ReactToolbox.Props {
/**
* If true, the drawer will be shown as an overlay.
* @default false
*/
active?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Callback function to be invoked when the overlay is clicked.
*/
onOverlayClick?: Function;
/**
* The breakpoint at which the drawer is automatically pinned.
*/
permanentAt?: "sm" | "md" | "lg" | "xl" | "xxl" | "xxxl";
/**
* If true, the drawer will be pinned open. pinned takes precedence over active.
* @default false
*/
pinned?: boolean;
/**
* If true, the drawer will vertically scroll all content.
* @default false
*/
scrollY?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: NavDrawerTheme;
/**
* 320px or 400px. Only applicable above the sm breakpoint.
* @default normal
*/
width?: "normal" | "wide";
}
export class NavDrawer extends __React.Component<NavDrawerProps, {}> { }
export interface PanelTheme {
/**
* Used as the root class of the panel component.
*/
panel?: string;
/**
* Used in case the panel is scrollable.
*/
scrollY?: string;
}
interface PanelProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the panel will vertically scroll all content.
* @default false
*/
scrollY?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: PanelTheme;
}
export class Panel extends __React.Component<PanelProps, {}> { }
export interface SidebarTheme {
/**
* Added to the root class if sidebar is pinned.
*/
pinned?: string;
/**
* Add to the content of sidebar if its scrollable.
*/
scrollY?: string;
/**
* Root class of the sidebar.
*/
sidebar?: string;
/**
* Used in for the content element of the sidebar.
*/
sidebarContent?: string;
}
interface SidebarProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the sidebar will be pinned open.
* @default false
*/
pinned?: boolean;
/**
* If true, the sidebar will vertically scroll all content
* @default false
*/
scrollY?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: SidebarTheme;
/**
* Width in standard increments (1-12) or percentage (25, 33, 50, 66, 75, 100)
* @default 5
*/
width?: number; // 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 25 | 33 | 50 | 66 | 75 | 100;
}
export class Sidebar extends __React.Component<SidebarProps, {}> { }

50
components/link/index.d.ts vendored Normal file
View File

@ -0,0 +1,50 @@
import __ReactToolbox from "../index.d.ts";
export interface LinkTheme {
/**
* Added to the root element if the Link is active.
*/
active?: string;
/**
* Used for the icon element if it's present.
*/
icon?: string;
/**
* Used for the root element.
*/
link?: string;
}
interface LinkProps extends __ReactToolbox.Props {
/**
* If true, adds active style to link.
* @default false
*/
active?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Sets a count number.
*/
count?: number;
/**
* Sets the anchor link.
*/
href?: string;
/**
* An icon key string to include a FontIcon component in front of the text.
*/
icon?: __React.ReactNode | string;
/**
* The text string used for the text content of the link.
*/
label?: string;
/**
* Classnames object defining the component style.
*/
theme?: LinkTheme;
}
export class Link extends __React.Component<LinkProps, {}> { }

View File

@ -11,7 +11,7 @@ const factory = (ListItemAction) => {
return (
<span className={theme[type]}>
{validChildren.map((action, i) => <ListItemAction key={i} action={action} />)}
{validChildren.map((action, i) => <ListItemAction key={i} theme={theme} action={action} />)}
</span>
);
};

View File

@ -37,8 +37,8 @@ const factory = (ListItemText) => {
return (
<span className={className}>
{caption && <ListItemText primary>{caption}</ListItemText>}
{legend && <ListItemText>{legend}</ListItemText>}
{caption && <ListItemText theme={theme} primary>{caption}</ListItemText>}
{legend && <ListItemText theme={theme}>{legend}</ListItemText>}
{children}
</span>
);

View File

@ -23,14 +23,14 @@ const factory = (Avatar, ListItemContent, ListItemActions) => {
props.rightIcon && <FontIcon value={props.rightIcon} key='rightIcon'/>,
...props.rightActions
];
const content = props.itemContent || <ListItemContent caption={props.caption} legend={props.legend} />;
const content = props.itemContent || <ListItemContent theme={props.theme} caption={props.caption} legend={props.legend} />;
const emptyActions = (item) => !item[0] && !item[1] && !item[2];
return (
<span className={className}>
{!emptyActions(leftActions) > 0 && <ListItemActions type='left'>{leftActions}</ListItemActions>}
{!emptyActions(leftActions) > 0 && <ListItemActions type='left' theme={props.theme}>{leftActions}</ListItemActions>}
{content}
{!emptyActions(rightActions) > 0 && <ListItemActions type='right'>{rightActions}</ListItemActions>}
{!emptyActions(rightActions) > 0 && <ListItemActions type='right' theme={props.theme}>{rightActions}</ListItemActions>}
</span>
);
};

264
components/list/index.d.ts vendored Normal file
View File

@ -0,0 +1,264 @@
import __ReactToolbox from "../index.d.ts";
export interface ListTheme {
/**
* Used for the root element of the list.
*/
list?: string;
}
interface ListProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, each element in the list will have a ripple effect on click
* @default false
*/
ripple?: boolean;
/**
* If true, the elements in the list will display a hover effect and a pointer cursor.
* @default false
*/
selectable?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: ListTheme;
}
export class List extends __React.Component<ListProps, {}> { }
export interface ListCheckboxTheme {
/**
* Used as a wrapper class for the subheader element.
*/
checkbox?: string;
/**
* Added to the checkbox element.
*/
checkboxItem?: string;
/**
* Added to the inner content if its a disabled item.
*/
disabled?: string;
/**
* Used for the inner content of a list item.
*/
item?: string;
/**
* Used for the content wrapper element in list item.
*/
itemContentRoot?: string;
/**
* Added to the text inside of the list item.
*/
itemText?: string;
/**
* Added to the content wrapper element if size is large.
*/
large?: string;
/**
* Added to the text inside of the list item if its primary.
*/
primary?: string;
}
interface ListCheckboxProps extends __ReactToolbox.Props {
/**
* Main text of the item. Required.
*/
caption?: string;
/**
* If true the checkbox appears checked by default.
* @default false
*/
checked?: boolean;
/**
* If true, the item is displayed as disabled and it's not clickable.
* @default false
*/
disabled?: boolean;
/**
* Secondary text to display under the caption.
*/
legend?: string;
/**
* Name for the checkbox input item.
*/
name?: string;
/**
* Callback called when the input element is blurred.
*/
onBlur?: Function;
/**
* Callback called when the input element is changed.
*/
onChange?: Function;
/**
* Callback called when the input element is focused.
*/
onFocus?: Function;
/**
* Classnames object defining the component style.
*/
theme?: ListCheckboxTheme;
}
export class ListCheckbox extends __React.Component<ListCheckboxProps, {}> { }
export interface ListDividerTheme {
/**
* Added to the root element.
*/
divider?: string;
/**
* Added to root element if inset is true.
*/
inset?: string;
}
interface ListDividerProps extends __ReactToolbox.Props {
/**
* If true, will leave a space at the left side.
*/
inset?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: ListDividerTheme;
}
export class ListDivider extends __React.Component<ListDivider, {}> { }
export interface ListItemTheme {
/**
* Added to the inner content if its a disabled item.
*/
disabled?: string;
/**
* Used for the inner content of a list item.
*/
item?: string;
/**
* Used for each action element (left/right).
*/
itemAction?: string;
/**
* Used for the content wrapper element in list item.
*/
itemContentRoot?: string;
/**
* Added to the text inside of the list item.
*/
itemText?: string;
/**
* Added to the content wrapper element if size is large.
*/
large?: string;
/**
* Added for the element that wraps left actions.
*/
left?: string;
/**
* Used for the root element of the list.
*/
listItem?: string;
/**
* Added to the text inside of the list item if its primary.
*/
primary?: string;
/**
* Added for the element that wraps right actions.
*/
right?: string;
/**
* Added to the inner content if its a selectable item.
*/
selectable?: string;
}
interface ListItemProps extends __ReactToolbox.Props {
/**
* A string URL to specify an avatar in the left side of the item.
*/
avatar?: __React.ReactNode | string;
/**
* Main text of the item.
*/
caption?: string;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the item is displayed as disabled and is not clickable.
* @default false
*/
disabled?: boolean;
/**
* An element that will be displayed as the item. If set, this will override caption and legend.
*/
itemContent?: __React.ReactNode;
/**
* A list of elements that are placed on the left side of the item and after the avatar attribute.
*/
leftActions?: __React.ReactNode;
/**
* A string key of a font icon or element to display an icon in the left side of the item.
*/
leftIcon?: __React.ReactNode | string;
/**
* Secondary text to display under the caption.
*/
legend?: string;
/**
* A list of elements that are placed on the right side of the item and after the rightIcon attribute.
*/
rightActions?: __React.ReactNode;
/**
* The same as the leftIcon but in this case the icon is displayed in the right side.
*/
rightIcon?: __React.ReactNode | string;
/**
* If true, the item displays a ripple effect on click. By default it's inherited from the parent element.
*/
ripple?: boolean;
/**
* If true, the elements in the list will display a hover effect and a pointer cursor. Inherited from the parent.
* @default false
*/
selectable?: boolean;
/**
* Classnames object defining the component style.
* @default false
*/
theme?: ListItemTheme;
/**
* In case you want to provide the item as a link, you can pass this property to specify the href.
*/
to?: string;
}
export class ListItem extends __React.Component<ListItemProps, {}> { }
export interface ListSubHeaderTheme {
/**
* Used as a wrapper class for the subheader element.
*/
subheader?: string;
}
interface ListSubHeaderProps extends __ReactToolbox.Props {
/**
* Text that will be displayed.
*/
caption?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: ListSubHeaderTheme;
}
export class ListSubHeader extends __React.Component<ListSubHeaderProps, {}> { }

View File

@ -59,39 +59,50 @@ const factory = (MenuItem) => {
componentDidMount () {
this.positionTimeoutHandle = setTimeout(() => {
const { width, height } = this.refs.menu.getBoundingClientRect();
const position = this.props.position === POSITION.AUTO ? this.calculatePosition() : this.props.position;
const position = this.props.position === POSITION.AUTO
? this.calculatePosition()
: this.props.position;
this.setState({ position, width, height });
});
}
componentWillReceiveProps (nextProps) {
if (this.props.position !== nextProps.position) {
const position = nextProps.position === POSITION.AUTO ? this.calculatePosition() : nextProps.position;
const position = nextProps.position === POSITION.AUTO
? this.calculatePosition()
: nextProps.position;
this.setState({ position });
}
/**
* If the menu is going to be activated via props and its not active, verify
* the position is appropriated and then show it recalculating position if its
* wrong. It should be shown in two consecutive setState.
*/
if (!this.props.active && nextProps.active && !this.state.active) {
this.show();
if (nextProps.position === POSITION.AUTO) {
const position = this.calculatePosition();
if (this.state.position !== position) {
this.setState({ position, active: false }, () => {
this.activateTimeoutHandle = setTimeout(() => { this.show(); }, 20);
});
} else {
this.show();
}
} else {
this.show();
}
}
/**
* If the menu is being deactivated via props and the current state is
* active, it should be hid.
*/
if (this.props.active && !nextProps.active && this.state.active) {
this.hide();
}
}
shouldComponentUpdate (nextProps, nextState) {
if (!this.state.active && nextState.active && this.props.position === POSITION.AUTO) {
const position = this.calculatePosition();
if (this.state.position !== position) {
this.setState({ position, active: false }, () => {
this.activateTimeoutHandle = setTimeout(() => {this.setState({active: true}); }, 20);
});
return false;
}
}
return true;
}
componentWillUpdate (nextProps, nextState) {
if (!this.state.active && nextState.active) {
events.addEventsToDocument({click: this.handleDocumentClick});
@ -108,9 +119,7 @@ const factory = (MenuItem) => {
}
componentWillUnmount () {
if (this.state.active) {
events.removeEventsFromDocument({click: this.handleDocumentClick});
}
if (this.state.active) events.removeEventsFromDocument({click: this.handleDocumentClick});
clearTimeout(this.positionTimeoutHandle);
clearTimeout(this.activateTimeoutHandle);
}
@ -197,7 +206,7 @@ const factory = (MenuItem) => {
return (
<div data-react-toolbox='menu' className={className} style={this.getRootStyle()}>
{this.props.outline ? <div className={theme.outline} style={outlineStyle}></div> : null}
{this.props.outline ? <div className={theme.outline} style={outlineStyle} /> : null}
<ul ref='menu' className={theme.menuInner} style={this.getMenuStyle()}>
{this.renderItems()}
</ul>

241
components/menu/index.d.ts vendored Normal file
View File

@ -0,0 +1,241 @@
import __ReactToolbox from "../index.d.ts";
export interface MenuTheme {
/**
* Added to the root element when menu is active.
*/
active?: string;
/**
* Added to the root when position is bottom left.
*/
bottomLeft?: string;
/**
* Added to the root when position is bottom right.
*/
bottomRight?: string;
/**
* Used for the root element of the menu.
*/
menu?: string;
/**
* Used for the inner wrapper.
*/
menuInner?: string;
/**
* Used to draw the outline.
*/
outline?: string;
/**
* Added to the menu in case if should have ripple.
*/
rippled?: string;
/**
* Added to the root in case its static.
*/
static?: string;
/**
* Added to the root when position is top left.
*/
topLeft?: string;
/**
* Added to the root when position is top right.
*/
topRight?: string;
}
interface MenuProps extends __ReactToolbox.Props {
/**
* If true, the menu will be displayed as opened by default.
* @default false
*/
active?: boolean;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Callback that will be called when the menu is being hidden.
*/
onHide?: Function;
/**
* Callback that will be invoked when a menu item is selected.
*/
onSelect?: Function;
/**
* Callback that will be invoked when the menu is being shown.
*/
onShow?: Function;
/**
* If true the menu wrapper will show an outline with a soft shadow.
* @default false
*/
outline?: boolean;
/**
* Determine the position of the menu. With static value the menu will be always shown, auto means that the it will decide the opening direction based on the current position. To force a position use topLeft, topRight, bottomLeft, bottomRight.
* @default static
*/
position?: "auto" | "static" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
/**
* If true, the menu items will show a ripple effect on click.
* @default true
*/
ripple?: boolean;
/**
* If true, the menu will keep a value to highlight the active child item.
* @default false
*/
selectable?: boolean;
/**
* Used for selectable menus. Indicates the current selected value so the child item with this value can be highlighted.
*/
selected?: any;
/**
* Classnames object defining the component style.
*/
theme?: MenuTheme;
}
export class Menu extends __React.Component<MenuProps, {}> { }
export interface IconMenuTheme {
/**
* Used for the icon element.
*/
icon?: string;
/**
* Used for the root element of the icon menu.
*/
iconMenu?: string;
}
interface IconMenuProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Icon font key string or Element to display the opener icon.
* @default more_vert
*/
icon?: __React.ReactNode | string;
/**
* If true, the icon will show a ripple when is clicked.
* @default true
*/
iconRipple?: boolean;
/**
* Transferred to the Menu component.
* @default true
*/
menuRipple?: boolean;
/**
* Callback that will be called when the menu is being hidden.
*/
onHide?: Function;
/**
* Callback that will be invoked when a menu item is selected.
*/
onSelect?: Function;
/**
* Callback that will be invoked when the menu is being shown.
*/
onShow?: Function;
/**
* Determines the position of the menu. This property is transferred to the inner Menu component.
* @default auto
*/
position?: "auto" | "static" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
/**
* If true, the menu will keep a value to highlight the active child item.
* @default false
*/
selectable?: boolean;
/**
* Used for selectable menus. Indicates the current selected value so the child item with this value can be highlighted.
*/
selected?: any;
/**
* Classnames object defining the component style.
*/
theme?: IconMenuTheme;
}
export class IconMenu extends __React.Component<IconMenuProps, {}> { }
export interface MenuDividerTheme {
/**
*
*/
menuDivider?: string;
}
interface MenuDividerProps extends __ReactToolbox.Props {
/**
* Classnames object defining the component style.
*/
theme?: MenuDividerTheme;
}
export class MenuDivider extends __React.Component<MenuDividerProps, {}> { }
export interface MenuItemTheme {
/**
* Used for the caption inside the item.
*/
caption?: string;
/**
* Added to the root element if it's disabled.
*/
disabled?: string;
/**
* Used for the icon element if exists.
*/
icon?: string;
/**
* Used as the root class for the component.
*/
menuItem?: string;
/**
* Added to the root element in case it's selected.
*/
selected?: string;
/**
* Used for the shortcut element if exists.
*/
shortcut?: string;
}
interface MenuItemProps extends __ReactToolbox.Props {
/**
* The text to include in the menu item. Required.
*/
caption: string;
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the item will be displayed as disabled and is not selectable.
* @default false
*/
disabled?: boolean;
/**
* Icon font key string or Element to display in the right side of the option.
*/
icon?: __React.ReactNode | string;
/**
* Transferred from the Menu component for selectable menus. Indicates if it's the current active option.
* @default false
*/
selected?: boolean;
/**
* Displays shortcut text on the right side of the caption attribute.
*/
shortcut?: string;
/**
* Classnames object defining the component style.
*/
theme?: MenuItemTheme;
}
export class MenuItem extends __React.Component<MenuItemProps, {}> { }

46
components/navigation/index.d.ts vendored Normal file
View File

@ -0,0 +1,46 @@
import __ReactToolbox from "../index.d.ts";
export interface NavigationTheme {
/**
* Used for buttons provided in the component.
*/
button?: string;
/**
* Used for the root element if the layout is horizontal.
*/
horizontal?: string;
/**
* Used for links provided in the component.
*/
link?: string;
/**
* Used for the root element if the layout is vertical.
*/
vertical?: string;
}
interface NavigationProps extends __ReactToolbox.Props {
/**
* Array of objects that will be represented as <Button/> so the keys will be transferred as properties the Button Component.
*/
actions?: any[];
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Array of objects similar to actions but that will be rendered as <Link/> component definition.
*/
routes?: any[];
/**
* Classnames object defining the component style.
*/
theme?: NavigationTheme;
/**
* Type of the navigation, it can be vertical or horizontal.
* @default horizontal
*/
type?: "vertical" | "horizontal";
}
export class Navigation extends __React.Component<NavigationProps, {}> { }

View File

@ -62,7 +62,7 @@ class ProgressBar extends Component {
renderCircular () {
return (
<svg className={this.props.theme.circle}>
<svg className={this.props.theme.circle} viewBox="0 0 60 60">
<circle className={this.props.theme.path} style={this.circularStyle()} cx='30' cy='30' r='25' />
</svg>
);
@ -72,8 +72,8 @@ class ProgressBar extends Component {
const {buffer, value} = this.linearStyle();
return (
<div>
<span ref='buffer' data-ref='buffer' className={this.props.theme.buffer} style={buffer}></span>
<span ref='value' data-ref='value' className={this.props.theme.value} style={value}></span>
<span ref='buffer' data-ref='buffer' className={this.props.theme.buffer} style={buffer}/>
<span ref='value' data-ref='value' className={this.props.theme.value} style={value}/>
</div>
);
}

82
components/progress_bar/index.d.ts vendored Normal file
View File

@ -0,0 +1,82 @@
import __ReactToolbox from "../index.d.ts";
export interface ProgressBarTheme {
/**
* Used to style the buffer element in the linear progress.
*/
buffer?: string;
/**
* Used for the circle element in the circular progress.
*/
circle?: string;
/**
* Used for the root element when the type is circular.
*/
circular?: string;
/**
* Added to the root element if mode is indeterminate.
*/
indeterminate?: string;
/**
* Used for the root element when the type is linear.
*/
linear?: string;
/**
* Added to the root if the component is multicolor (circular).
*/
multicolor?: string;
/**
* Used for the inner path in the circular progress.
*/
path?: string;
/**
* Used to style the value element in the linear progress.
*/
value?: string;
}
interface ProgressBarProps extends __ReactToolbox.Props {
/**
* Value of a secondary progress bar useful for buffering.
* @default 0
*/
buffer?: number;
/**
* Maximum value permitted.
* @default 100
*/
max?: number;
/**
* Minimum value permitted.
* @default 0
*/
min?: number;
/**
* Mode of the progress bar, it can be determinate or indeterminate.
* @default indeterminate
*/
mode?: "determinate" | "indeterminate";
/**
* If true, the circular progress bar will be changing its color.
* @default false
*/
multicolor?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: ProgressBarTheme;
/**
* Type of the progress bar, it can be circular or linear.
* @default linear
*/
type?: "linear" | "circular";
/**
* Value of the current progress.
* @default 0
*/
value?: number;
}
export class ProgressBar extends __React.Component<ProgressBarProps, {}> { }
export default ProgressBar;

101
components/radio/index.d.ts vendored Normal file
View File

@ -0,0 +1,101 @@
import __ReactToolbox from "../index.d.ts";
interface RadioGroupProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* If true, the group will be displayed as disabled.
* @default false
*/
disabled?: boolean;
/**
* Name for the input element group.
*/
name?: string;
/**
* Callback function that will be invoked when the value changes.
*/
onChange?: Function;
/**
* Default value selected in the radio group.
*/
value?: any;
}
export class RadioGroup extends __React.Component<RadioGroupProps, {}> { }
export interface RadioButtonTheme {
/**
* Used to for the radio element.
*/
radio?: string;
/**
* Used for the radio element when it's checked.
*/
radioChecked?: string;
/**
* To provide styles for the ripple.
*/
ripple?: string;
/**
* Added to the root of the Radio in case it's disabled.
*/
disabled?: string;
/**
* Used as the root class of the component.
*/
field?: string;
/**
* Used for the input element.
*/
input?: string;
/**
* Used to style the text label element.
*/
text?: string;
}
interface RadioButtonProps extends __ReactToolbox.Props {
/**
* If true, the input element will be selected by default. Transferred from the parent.
* @default false
*/
checked?: boolean;
/**
* If true, the item will be displayed as disabled.
* @default false
*/
disabled?: boolean;
/**
* Label for the radio button.
*/
label?: __React.ReactNode | string;
/**
* Name for the input element.
*/
name?: string;
/**
* Callback function that will be invoked when the input is blurred.
*/
onBlur?: Function;
/**
* Callback function that will be invoked when the value changes.
*/
onChange?: Function;
/**
* Callback function that will be invoked when the input is focused.
*/
onFocus?: Function;
/**
* Classnames object defining the component style.
*/
theme?: RadioButtonTheme;
/**
* Value for the radio button.
*/
value?: any;
}
export class RadioButton extends __React.Component<RadioButtonProps, {}> { }

View File

@ -1,14 +1,17 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import classnames from 'classnames';
import update from 'react-addons-update';
import { themr } from 'react-css-themr';
import { RIPPLE } from '../identifiers.js';
import events from '../utils/events';
import prefixer from '../utils/prefixer';
import utils from '../utils/utils';
const defaults = {
centered: false,
className: '',
multiple: true,
spread: 2,
theme: {}
};
@ -17,6 +20,7 @@ const rippleFactory = (options = {}) => {
const {
centered: defaultCentered,
className: defaultClassName,
multiple: defaultMultiple,
spread: defaultSpread,
theme: defaultTheme,
...props
@ -31,6 +35,7 @@ const rippleFactory = (options = {}) => {
ripple: PropTypes.bool,
rippleCentered: PropTypes.bool,
rippleClassName: PropTypes.string,
rippleMultiple: PropTypes.bool,
rippleSpread: PropTypes.number,
theme: PropTypes.shape({
ripple: PropTypes.string,
@ -45,98 +50,208 @@ const rippleFactory = (options = {}) => {
ripple: true,
rippleCentered: defaultCentered,
rippleClassName: defaultClassName,
rippleMultiple: defaultMultiple,
rippleSpread: defaultSpread
};
state = {
active: false,
left: null,
restarting: false,
top: null,
width: null
ripples: {}
};
componentDidMount () {
if (this.props.onRippleEnded) {
events.addEventListenerOnTransitionEnded(this.refs.ripple, (evt) => {
if (evt.propertyName === 'transform') this.props.onRippleEnded(evt);
});
componentDidUpdate (prevProps, prevState) {
// If a new ripple was just added, add a remove event listener to its animation
if (Object.keys(prevState.ripples).length < Object.keys(this.state.ripples).length) {
this.addRippleRemoveEventListener(this.getLastKey());
}
}
componentWillUnmount () {
if (this.props.onRippleEnded) {
events.removeEventListenerOnTransitionEnded(this.refs.ripple);
}
// Remove document event listeners for ripple if they still exists
Object.keys(this.state.ripples).forEach(key => {
this.state.ripples[key].endRipple();
});
}
handleEnd = () => {
document.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
this.setState({active: false});
};
/**
* Add an event listener to the reference with given key so when the animation transition
* ends we can be sure that it finished and it can be safely removed from the state.
* This function is called whenever a new ripple is added to the component.
*
* @param {String} rippleKey Is the key of the ripple to add the event.
*/
addRippleRemoveEventListener (rippleKey) {
const self = this;
events.addEventListenerOnTransitionEnded(this.refs[rippleKey], function onOpacityEnd (e) {
if (e.propertyName === 'opacity') {
if (self.props.onRippleEnded) self.props.onRippleEnded(e);
events.removeEventListenerOnTransitionEnded(self.refs[rippleKey], onOpacityEnd);
self.setState({ ripples: utils.removeObjectKey(rippleKey, self.state.ripples) });
}
});
}
start = ({pageX, pageY}, touch = false) => {
if (!this._isTouchRippleReceivingMouseEvent(touch)) {
this.touch = touch;
document.addEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd);
const {top, left, width} = this._getDescriptor(pageX, pageY);
this.setState({active: false, restarting: true, top, left, width}, () => {
this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions
this.setState({active: true, restarting: false});
/**
* Start a ripple animation on an specific point with touch or mouse events. First
* decides if the animation should trigger. If the ripple is multiple or there is no
* ripple present, it creates a new key. If it's a simple ripple and already exists,
* it just restarts the current ripple. The animation happens in two state changes
* to allow triggering via css.
*
* @param {Number} x Coordinate X on the screen where animation should start
* @param {Number} y Coordinate Y on the screen where animation should start
* @param {Boolean} isTouch Use events from touch or mouse.
*/
animateRipple (x, y, isTouch) {
if (this.rippleShouldTrigger(isTouch)) {
const { top, left, width } = this.getDescriptor(x, y);
const noRipplesActive = Object.keys(this.state.ripples).length === 0;
const key = this.props.rippleMultiple || noRipplesActive ? this.getNextKey() : this.getLastKey();
const endRipple = this.addRippleDeactivateEventListener(isTouch, key);
const initialState = { active: false, restarting: true, top, left, width, endRipple };
const runningState = { active: true, restarting: false };
this.setState(update(this.state, { ripples: { [key]: { $set: initialState } } }), () => {
this.refs[key].offsetWidth; //eslint-disable-line no-unused-expressions
this.setState(update(this.state, { ripples: { [key]: { $merge: runningState } } }));
});
}
};
_isTouchRippleReceivingMouseEvent (touch) {
return this.touch && !touch;
}
_getDescriptor (pageX, pageY) {
const {left, top, height, width} = ReactDOM.findDOMNode(this).getBoundingClientRect();
const {rippleCentered: centered, rippleSpread: spread} = this.props;
/**
* Determine if a ripple should start depending if its a touch event. For mobile both
* touchStart and mouseDown are launched so in case is touch we should always trigger
* but if its not we should check if a touch was already triggered to decide.
*
* @param {Boolean} isTouch True in case a touch event triggered the ripple false otherwise.
* @return {Boolean} True in case the ripple should trigger or false if it shouldn't.
*/
rippleShouldTrigger (isTouch) {
const shouldStart = isTouch ? true : !this.touchCache;
this.touchCache = isTouch;
return shouldStart;
}
/**
* Find out a descriptor object for the ripple element being created depending on
* the position where the it was triggered and the component's dimensions.
*
* @param {Number} x Coordinate x in the viewport where ripple was triggered
* @param {Number} y Coordinate y in the viewport where ripple was triggered
* @return {Object} Descriptor element including position and size of the element
*/
getDescriptor (x, y) {
const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect();
const { rippleCentered: centered, rippleSpread: spread } = this.props;
return {
left: centered ? 0 : pageX - left - width / 2 - window.scrollX,
top: centered ? 0 : pageY - top - height / 2 - window.scrollY,
left: centered ? 0 : x - left - width / 2,
top: centered ? 0 : y - top - height / 2,
width: width * spread
};
}
/**
* Increments and internal counter and returns the next value as a string. It
* is used to assign key references to new ripple elements.
*
* @return {String} Key to be assigned to a ripple.
*/
getNextKey () {
this.currentCount = this.currentCount ? this.currentCount + 1 : 1;
return `ripple${this.currentCount}`;
}
/**
* Return the last generated key for a ripple element. When there is only one ripple
* and to get the reference when a ripple was just created.
*
* @return {String} The last generated ripple key.
*/
getLastKey () {
return `ripple${this.currentCount}`;
}
/**
* Add an event listener to the document needed to deactivate a ripple and make it dissappear.
* Deactivation can happen with a touchend or mouseup depending on the trigger type. The
* ending function is created from a factory function and returned.
*
* @param {Boolean} isTouch True in case the trigger was a touch event false otherwise.
* @param {String} rippleKey It's a key to identify the ripple that should be deactivated.
* @return {Function} Callback function that deactivates the ripple and removes the event listener
*/
addRippleDeactivateEventListener (isTouch, rippleKey) {
const eventType = isTouch ? 'touchend' : 'mouseup';
const endRipple = this.createRippleDeactivateCallback(eventType, rippleKey);
document.addEventListener(eventType, endRipple);
return endRipple;
}
/**
* Generates a function that can be called to deactivate a given ripple and remove its finishing
* event listener. If is generated because we need to store it to be called on unmount in case
* the ripple is still running.
*
* @param {String} eventType Is the event type that can be touchend or mouseup
* @param {String} rippleKey Is the key representing the ripple
* @return {Function} Callback function that deactivates the ripple and removes the listener
*/
createRippleDeactivateCallback (eventType, rippleKey) {
const self = this;
return function endRipple () {
document.removeEventListener(eventType, endRipple);
self.setState({ ripples: update(self.state.ripples, {
[rippleKey]: { $merge: { active: false } }
}) });
};
}
handleMouseDown = (event) => {
if (!this.props.disabled) this.start(event);
if (this.props.onMouseDown) this.props.onMouseDown(event);
if (!this.props.disabled) {
const { x, y } = events.getMousePosition(event);
this.animateRipple(x, y, false);
}
};
handleTouchStart = (event) => {
if (this.props.onTouchStart) this.props.onTouchStart(event);
if (!this.props.disabled) {
const { x, y } = events.getTouchPosition(event);
this.animateRipple(x, y, true);
}
};
renderRipple (key, className, { active, left, restarting, top, width }) {
const scale = restarting ? 0 : 1;
const transform = `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`;
const _className = classnames(this.props.theme.ripple, {
[this.props.theme.rippleActive]: active,
[this.props.theme.rippleRestarting]: restarting
}, className);
return (
<span key={key} data-react-toolbox='ripple' className={this.props.theme.rippleWrapper} {...props}>
<span
role='ripple'
ref={key}
className={_className}
style={prefixer({ transform }, {width, height: width})}
/>
</span>
);
}
render () {
if (!this.props.ripple) {
return <ComposedComponent {...this.props} />;
} else {
const { ripples } = this.state;
const {
children,
ripple, //eslint-disable-line no-unused-vars
onRippleEnded, //eslint-disable-line no-unused-vars
rippleClassName: className,
rippleCentered: centered, //eslint-disable-line no-unused-vars
rippleSpread: spread, //eslint-disable-line no-unused-vars
...other
ripple, onRippleEnded, rippleCentered, rippleMultiple, rippleSpread, //eslint-disable-line no-unused-vars
children, rippleClassName: className, ...other
} = this.props;
const rippleClassName = classnames(this.props.theme.ripple, {
[this.props.theme.rippleActive]: this.state.active,
[this.props.theme.rippleRestarting]: this.state.restarting
}, className);
const { left, top, width } = this.state;
const scale = this.state.restarting ? 0 : 1;
const rippleStyle = prefixer({
transform: `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})`
}, {width, height: width});
return (
<ComposedComponent {...other} onMouseDown={this.handleMouseDown}>
<ComposedComponent {...other} onMouseDown={this.handleMouseDown} onTouchStart={this.handleTouchStart}>
{children ? children : null}
<span data-react-toolbox='ripple' className={this.props.theme.rippleWrapper} {...props}>
<span ref='ripple' role='ripple' className={rippleClassName} style={rippleStyle} />
</span>
{Object.keys(ripples).map(key => this.renderRipple(key, className, ripples[key]))}
</ComposedComponent>
);
}

49
components/ripple/index.d.ts vendored Normal file
View File

@ -0,0 +1,49 @@
import __ReactToolbox from "../index.d.ts";
export interface RippleTheme {
/**
* Root classname for the ripple.
*/
ripple?: string;
/**
* Applied when the ripple is active.
*/
rippleActive?: string;
/**
* Applied when the ripple is restarting.
*/
rippleRestarting?: string;
/**
* Wrapper class to fit to the parent element.
*/
rippleWrapper?: string;
}
interface RippleProps {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* True in case you want a centered ripple.
* @default false
*/
disabled?: boolean;
/**
* Function that will be called when the ripple animation ends.
*/
onRippleEnded?: Function;
/**
* Factor to indicate how much should the ripple spread under the component.
* @default 2
*/
spread?: number;
/**
* Classnames object defining the component style.
*/
theme?: RippleTheme;
}
export class Ripple extends __React.Component<RippleProps, {}> { }
export default Ripple;

View File

@ -1,6 +1,6 @@
# Ripple
The ripple is a surface reaction that happens when the user interacts with the component. It's useful to provide feedback about a click or touch action. In React Toolbox it's implemented as an higher order component (HOC) being a requirement for the child to implement `children` and `onMouseDown` props. Also it should be placed as relative. Hiding the overflow is up to you.
The ripple is a surface reaction that happens when the user interacts with the component. It's useful to provide feedback about a click or touch action. In React Toolbox it's implemented as an higher order component (HOC) being a requirement for the child to implement `children`, `onMouseDown` and `onTouchStart` (in case you want to support touch) props. Also it should be placed as relative. Hiding the overflow is up to you.
<!-- example -->
```jsx
@ -17,18 +17,32 @@ const RippleLink = Ripple({spread: 3})(Link);
const RippleTest = () => <RippleLink href='#' theme={theme}>Test</RippleLink>;
```
## Properties
## Options
In any component you decorate with the Ripple you'd get some additional props:
You can pass some options to configure the default props for the ripple when decorating a component:
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `centered` | `Boolean` | `false` | True in case you want a centered ripple.|
| `className` | `String` | `''` | String to customize appearance (color and opacity for example).|
| `onRippleEnded` | `Function` | | Function that will be called when the ripple animation ends. |
| `multiple` | `Boolean` | `true` | If true each touch produces a different wave. If false the same wave is restarted. |
| `spread` | `Number` | `2` | Factor to indicate how much should the ripple spread under the component.|
| `theme` | `Object` | `null` | Classnames object defining the ripple style.|
## Properties
In any component you decorate with the Ripple you'd get some additional props namespaced with `ripple` prefix. Some of them will get the default from the options given to the ripple factory function:
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `onRippleEnded` | `Function` | | Function that will be called when the ripple animation ends. Beware if your animation supports multiple waves this function will be called for each ended ripple. |
| `ripple` | `Boolean` | `true` | False in case you want to deactivate the ripple.|
| `rippleCentered` | `Boolean` | `options.centered` | True in case you want a centered ripple.|
| `rippleClassName` | `String` | `options.className` | String to customize appearance (color and opacity for example).|
| `rippleMultiple` | `Boolean` | `options.multiple` | If true each touch produces a different wave. If false the same wave is restarted. |
| `rippleSpread` | `Number` | `options.spread` | Factor to indicate how much should the ripple spread under the component.|
| `theme` | `Object` | `options.theme` | Classnames object defining the ripple style.|
## Theming
You can take a look to the `_config.scss` variables. The themed key for this component is `ToolboxRipple`, it should implement the following interface:

View File

@ -61,13 +61,14 @@ const factory = (ProgressBar, Input) => {
this.handleResize();
}
shouldComponentUpdate (nextProps, nextState) {
if (!this.state.inputFocused && nextState.inputFocused) return false;
componentWillReceiveProps (nextProps) {
if (this.state.inputFocused && this.props.value !== nextProps.value) {
this.setState({inputValue: this.valueForInput(nextProps.value)});
return false;
}
return true;
}
shouldComponentUpdate (nextProps, nextState) {
return this.state.inputFocused || !nextState.inputFocused;
}
componentWillUnmount () {
@ -96,16 +97,13 @@ const factory = (ProgressBar, Input) => {
};
handleKeyDown = (event) => {
if ([13, 27].indexOf(event.keyCode) !== -1) {
this.refs.input.blur();
ReactDOM.findDOMNode(this).blur();
}
if ([13, 27].indexOf(event.keyCode) !== -1) this.getInput().blur();
if (event.keyCode === 38) this.addToValue(this.props.step);
if (event.keyCode === 40) this.addToValue(-this.props.step);
};
handleMouseDown = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
if (this.state.inputFocused) this.getInput().blur();
events.addEventsToDocument(this.getMouseEventMap());
this.start(events.getMousePosition(event));
events.pauseEvent(event);
@ -143,7 +141,7 @@ const factory = (ProgressBar, Input) => {
};
handleTouchStart = (event) => {
if (this.state.inputFocused) this.refs.input.blur();
if (this.state.inputFocused) this.getInput().blur();
this.start(events.getTouchPosition(event));
events.addEventsToDocument(this.getTouchEventMap());
events.pauseEvent(event);
@ -155,6 +153,12 @@ const factory = (ProgressBar, Input) => {
if (value !== this.props.value) this.props.onChange(value);
}
getInput () {
return this.refs.input && this.refs.input.getWrappedInstance
? this.refs.input.getWrappedInstance()
: this.refs.input;
}
getKeyboardEvents () {
return {
keydown: this.handleKeyDown
@ -277,7 +281,7 @@ const factory = (ProgressBar, Input) => {
onTouchStart={this.handleTouchStart}
style={knobStyles}
>
<div className={theme.innerknob} data-value={parseInt(this.props.value)}></div>
<div className={theme.innerknob} data-value={parseInt(this.props.value)}/>
</div>
<div className={theme.progress}>

106
components/slider/index.d.ts vendored Normal file
View File

@ -0,0 +1,106 @@
import __ReactToolbox from "../index.d.ts";
export interface SliderTheme {
/**
* Used as an inner container of the root component.
*/
container?: string;
/**
* Added to the root of in case the Slider is editable.
*/
editable?: string;
/**
* Used to style the inner element of the knob.
*/
innerknob?: string;
/**
* Provided to the ProgressBar component.
*/
innerprogress?: string;
/**
* Provided to the Input element in case it's editable.
*/
input?: string;
/**
* Used to style the outer layer of the knob.
*/
knob?: string;
/**
* Added to the root in case the Slider is pinned.
*/
pinned?: string;
/**
* Added to the root in case the state is pressed.
*/
pressed?: string;
/**
* Used as an outer wrapper for the ProgressBar.
*/
progress?: string;
/**
* Used in the root when the knob should be a ring.
*/
ring?: string;
/**
* Class used for the root element.
*/
slider?: string;
/**
* Used for every individual snap element.
*/
snap?: string;
/**
* Used as a wrapper for the group of snaps when it's snapped.
*/
snaps?: string;
}
interface SliderProps extends __ReactToolbox.Props {
/**
* If true, an input is shown and the user can set the slider from keyboard value.
* @default false
*/
editable?: boolean;
/**
* Maximum value permitted.
* @default 100
*/
max?: number;
/**
* Minimum value permitted.
* @default 0
*/
min?: number;
/**
* Callback function that will be invoked when the slider value changes.
*/
onChange?: Function;
/**
* If true, a pin with numeric value label is shown when the slider thumb is pressed. Use for settings for which users need to know the exact value of the setting.
* @default false
*/
pinned?: boolean;
/**
* If true, the slider thumb snaps to tick marks evenly spaced based on the step property value.
* @default false
*/
snaps?: boolean;
/**
* Amount to vary the value when the knob is moved or increase/decrease is called.
* @default 0.01
*/
step?: number;
/**
* Classnames object defining the component style.
*/
theme?: SliderTheme;
/**
* Current value of the slider.
* @default 0
*/
value?: number;
}
export class Slider extends __React.Component<SliderProps, {}> { }
export default Slider;

76
components/snackbar/index.d.ts vendored Normal file
View File

@ -0,0 +1,76 @@
import __ReactToolbox from "../index.d.ts";
export interface SnackbarTheme {
/**
* Added to the root element in case it's accept type.
*/
accept?: string;
/**
* Added to the root element when its active.
*/
active?: string;
/**
* Used for the button inside the component.
*/
button?: string;
/**
* Added to the root element in case it's cancel type.
*/
cancel?: string;
/**
* Used for the icon element.
*/
icon?: string;
/**
* Used for the label element.
*/
label?: string;
/**
* Used as the className for the root element of the component.
*/
snackbar?: string;
/**
* Added to the root element in case it's warning type.
*/
warning?: string;
}
interface SnackbarProps extends __ReactToolbox.Props {
/**
* Label for the action component inside the Snackbar.
*/
action?: string;
/**
* If true, the snackbar will be active.
* @default true
*/
active?: boolean;
/**
* String key for an icon or Element which will be displayed in left side of the snackbar.
*/
icon?: __React.ReactNode | string;
/**
* Text to display in the content. Required.
*/
label: string;
/**
* Callback function when finish the set timeout.
*/
onTimeout?: Function;
/**
* Classnames object defining the component style.
*/
theme?: SnackbarTheme;
/**
* Amount of time in milliseconds after the Snackbar will be automatically hidden.
*/
timeout?: number;
/**
* Indicates the action type. Can be accept, warning or cancel
*/
type?: "accept" | "cancel" | "warning";
}
export class Snackbar extends __React.Component<SnackbarProps, {}> { }
export default Snackbar;

77
components/switch/index.d.ts vendored Normal file
View File

@ -0,0 +1,77 @@
import __ReactToolbox from "../index.d.ts";
export interface SwitchTheme {
/**
* Used for the root element if the component is disabled.
*/
disabled?: string;
/**
* Used for the root element if the component is not disabled.
*/
field?: string;
/**
* Used for the input element.
*/
input?: string;
/**
* Used for a wrapper around the thumb if checked is false.
*/
off?: string;
/**
* Used for a wrapper around the thumb if checked is true.
*/
on?: string;
/**
* Used for the ripple inside the switch.
*/
ripple?: string;
/**
* Used for the text label element.
*/
text?: string;
/**
* Used for the thumb element.
*/
thumb?: string;
}
interface SwitchProps extends __ReactToolbox.Props {
/**
* If true, the switch will be enabled.
* @default false
*/
checked?: boolean;
/**
* If true, component will be disabled.
* @default false
*/
disabled?: boolean;
/**
* The text string to use for the floating label element.
*/
label?: string;
/**
* The text string used as name of the input.
*/
name?: string;
/**
* Callback function that is fired when when the switch is blurred.
*/
onBlur?: Function;
/**
* Callback function that is fired when the component's value changes.
*/
onChange?: Function;
/**
* Callback function that is fired when the switch is focused.
*/
onFocus?: Function;
/**
* Classnames object defining the component style.
*/
theme?: SwitchTheme;
}
export class Switch extends __React.Component<SwitchProps, {}> { }
export default Switch;

View File

@ -42,15 +42,16 @@ const factory = (TableHead, TableRow) => {
handleRowSelect = (index) => {
if (this.props.onSelect) {
const position = this.props.selected.indexOf(index);
let newSelected = [...this.props.selected];
if (position !== -1) { newSelected.splice(position, 1); }
if (position !== -1 && this.props.multiSelectable) {
newSelected.push(index);
let newSelection = [];
if (this.props.multiSelectable) {
const position = this.props.selected.indexOf(index);
newSelection = this.props.selected.indexOf(index) !== -1
? this.props.selected.filter((el, idx) => idx !== position)
: newSelection.concat([index]);
} else {
newSelected = [index];
newSelection = [index];
}
this.props.onSelect(newSelected);
this.props.onSelect(newSelection);
}
};

View File

@ -16,7 +16,7 @@ const factory = (Checkbox) => {
);
} else if (selectable) {
selectCell = (
<th key='select' className={theme.selectable}></th>
<th key='select' className={theme.selectable}/>
);
}
return (

70
components/table/index.d.ts vendored Normal file
View File

@ -0,0 +1,70 @@
import __ReactToolbox from "../index.d.ts";
export interface TableTheme {
/**
* It will be added to a row in case it is editable.
*/
editable?: string;
/**
* Used for the row element.
*/
row?: string;
/**
* It will be added to a row in case it is selectable.
*/
selectable?: string;
/**
* Added to a row in case it is selected.
*/
selected?: string;
/**
* Classname used for the root element.
*/
table?: string;
}
interface TableProps extends __ReactToolbox.Props {
/**
* If true, component will show a heading using model field names.
* @default true
*/
heading?: boolean;
/**
* Object describing the data model that represents each object in the source.
*/
model?: any;
/**
* Callback function that is fired when an item in a row changes. If set, rows are editable.
*/
onChange?: Function;
/**
* Callback function invoked when the row selection changes.
*/
onSelect?: Function;
/**
* If true, each row will display a checkbox to allow the user to select that one row.
* @default true
*/
selectable?: boolean;
/**
* If true, the header and each row will display a checkbox to allow the user to select multiple rows.
* @default true
*/
multiSelectable?: boolean;
/**
* Array of indexes of the items in the source that should appear as selected.
*/
selected?: any[];
/**
* Array of objects representing each item to show.
*/
source?: any[];
/**
* Classnames object defining the component style.
*/
theme?: TableTheme;
}
export class Table extends __React.Component<TableProps, {}> { }
export default Table;

View File

@ -41,7 +41,10 @@ class Tab extends Component {
};
render () {
const { active, activeClassName, hidden, disabled, className, theme } = this.props;
const {
onActive, // eslint-disable-line
active, activeClassName, className, disabled, hidden, theme, ...other
} = this.props;
const _className = classnames(theme.label, {
[theme.active]: active,
[theme.hidden]: hidden,
@ -50,7 +53,7 @@ class Tab extends Component {
}, className);
return (
<label data-react-toolbox='tab' className={_className} onClick={this.handleClick}>
<label {...other} data-react-toolbox='tab' className={_className} onClick={this.handleClick}>
{this.props.label}
</label>
);

View File

@ -11,9 +11,13 @@ const factory = (Tab, TabContent) => {
children: PropTypes.node,
className: PropTypes.string,
disableAnimatedBottomBorder: PropTypes.bool,
fixed: PropTypes.bool,
index: PropTypes.number,
inverse: PropTypes.bool,
onChange: PropTypes.func,
theme: PropTypes.shape({
fixed: PropTypes.string,
inverse: PropTypes.string,
navigation: PropTypes.string,
pointer: PropTypes.string,
tabs: PropTypes.string
@ -21,7 +25,9 @@ const factory = (Tab, TabContent) => {
};
static defaultProps = {
index: 0
index: 0,
fixed: false,
inverse: false
};
state = {
@ -30,6 +36,8 @@ const factory = (Tab, TabContent) => {
componentDidMount () {
!this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index);
window.addEventListener('resize', this.handleResize);
this.handleResize();
}
componentWillReceiveProps (nextProps) {
@ -37,13 +45,31 @@ const factory = (Tab, TabContent) => {
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize);
clearTimeout(this.resizeTimeout);
clearTimeout(this.pointerTimeout);
}
handleHeaderClick = (idx) => {
handleHeaderClick = (event) => {
const idx = parseInt(event.currentTarget.id);
if (this.props.onChange) this.props.onChange(idx);
};
handleResize = () => {
if (!this.props.fixed) {
return;
}
if (this.resizeTimeout) {
clearTimeout(this.resizeTimeout);
}
this.resizeTimeout = setTimeout(this.handleResizeEnd, 50);
};
handleResizeEnd = () => {
this.updatePointer(this.props.index);
};
parseChildren () {
const headers = [];
const contents = [];
@ -80,10 +106,14 @@ const factory = (Tab, TabContent) => {
renderHeaders (headers) {
return headers.map((item, idx) => {
return React.cloneElement(item, {
id: idx,
key: idx,
theme: this.props.theme,
active: this.props.index === idx,
onClick: this.handleHeaderClick.bind(this, idx, item)
onClick: event => {
this.handleHeaderClick(event);
item.props.onClick && item.props.onClick(event);
}
});
});
}
@ -104,10 +134,18 @@ const factory = (Tab, TabContent) => {
}
render () {
const { className, theme } = this.props;
const { className, theme, fixed, inverse } = this.props;
const { headers, contents } = this.parseChildren();
const classes = classnames(
theme.tabs,
className,
{
[theme.fixed]: fixed,
[theme.inverse]: inverse
}
);
return (
<div ref='tabs' data-react-toolbox='tabs' className={classnames(theme.tabs, className)}>
<div ref='tabs' data-react-toolbox='tabs' className={classes}>
<nav className={theme.navigation} ref='navigation'>
{this.renderHeaders(headers)}
</nav>

View File

@ -3,10 +3,17 @@ import utils from '../../utils/testing';
import ReactTestUtils from 'react-addons-test-utils';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Tabs from '../Tabs';
import Tabs, { Tabs as RawTabs } from '../Tabs';
import Tab from '../Tab';
import TabContent from '../TabContent';
import theme from '../theme.scss';
const getRenderedClassName = (tree, TargetComponent) => {
const rendered = ReactTestUtils.findRenderedComponentWithType(tree, TargetComponent);
return ReactDOM.findDOMNode(rendered).getAttribute('class');
};
describe('Tabs', function () {
let tabContents, composition;
@ -48,4 +55,29 @@ describe('Tabs', function () {
expect(tabContents[0].props.tabIndex).toEqual(1);
});
describe('#render', function () {
it('does not use fixed by default', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toNotContain(theme.fixed);
});
it('uses fixed when set', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} fixed />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toContain(theme.fixed);
});
it('does not use inverse by default', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toNotContain(theme.inverse);
});
it('uses inverse when set', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} inverse />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toContain(theme.inverse);
});
});
});

View File

@ -9,3 +9,10 @@ $tab-pointer-height: .2 * $unit !default;
$tab-text: $color-black !default;
$tab-text-color: $tab-text !default;
$tab-text-inactive-color: rgba($tab-text, .7) !default;
// Inverse
$tab-inverse-bar-color: $color-primary !default;
$tab-inverse-pointer-color: $color-accent !default;
$tab-inverse-text: $color-primary-contrast !default;
$tab-inverse-text-color: $tab-inverse-text !default;
$tab-inverse-text-inactive-color: rgba($tab-inverse-text, .7) !default;

105
components/tabs/index.d.ts vendored Normal file
View File

@ -0,0 +1,105 @@
import __ReactToolbox from "../index.d.ts";
export interface TabsTheme {
/**
* Added to the active tab content and header.
*/
active?: string;
/**
* Used for the navigation element.
*/
navigation?: string;
/**
* Used for the moving underline element.
*/
pointer?: string;
/**
* Used as a root classname for the component.
*/
tabs?: string;
/**
* Used for the tab content element.
*/
tab?: string;
}
interface TabsProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Disable the animation below the active tab.
* @default false
*/
disableAnimatedBottomBorder?: boolean;
/**
* Current
* @default 0
*/
index?: number;
/**
* Callback function that is fired when the tab changes.
*/
onChange?: Function;
/**
* Classnames object defining the component style.
*/
theme?: TabsTheme;
}
export class Tabs extends __React.Component<TabsProps, {}> { }
export interface TabTheme {
/**
* Added to the navigation tab element in case it's active.
*/
active?: string;
/**
* Added to the navigation tab element in case it's disabled.
*/
disabled?: string;
/**
* Added to the navigation tab element in case it's hidden.
*/
hidden?: string;
/**
* Added to the navigation tab element in case it's active.
*/
label?: string;
}
interface TabProps extends __ReactToolbox.Props {
/**
* If true, the current component is visible.
*/
active?: boolean;
/**
* Additional class name to provide custom styling for the active tab.
*/
activeClassName?: string;
/**
* If true, the current component is not clickable.
* @default false
*/
disabled?: boolean;
/**
* If true, the current component is not visible.
* @default false
*/
hidden?: boolean;
/**
* Label text for navigation header. Required.
*/
label: string;
/**
* Callback function that is fired when the tab is activated.
*/
onActive?: Function;
/**
* Classnames object defining the component style.
*/
theme?: TabTheme;
}
export class Tab extends __React.Component<TabProps, {}> { }

View File

@ -8,26 +8,51 @@ import {Tab, Tabs} from 'react-toolbox';
class TabsTest extends React.Component {
state = {
index: 1
index: 1,
fixedIndex: 1,
inverseIndex: 1
};
handleTabChange = (index) => {
this.setState({index});
};
handleFixedTabChange = (index) => {
this.setState({fixedIndex: index});
};
handleInverseTabChange = (index) => {
this.setState({inverseIndex: index});
};
handleActive = () => {
console.log('Special one activated');
};
render () {
return (
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<section>
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<h5>Fixed Tabs</h5>
<Tabs index={this.state.fixedIndex} onChange={this.handleFixedTabChange} fixed>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
</Tabs>
<h5>Inverse Tabs</h5>
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
</section>
);
}
}
@ -45,7 +70,9 @@ This component acts as the wrapper and the main controller of the content that i
|:-----|:-----|:-----|:-----|
| `className` | `String` | `''` | Additional class name to provide custom styling.|
| `disableAnimatedBottomBorder` | `Boolean` | `false` | Disable the animation below the active tab.|
| `fixed` | `Boolean` | `false` | If True, the tabs will be 'fixed tabs'.|
| `index` | `Number` | `0` | Current <Tab> |
| `inverse` | `Boolean` | `false` | If True, the tabs will have an inverse style.|
| `onChange` | `Function` | | Callback function that is fired when the tab changes.|
### Theming
@ -53,6 +80,8 @@ This component acts as the wrapper and the main controller of the content that i
| Name | Description|
|:---------|:-----------|
| `active` | Added to the active tab content and header.|
| `fixed` | Used to make the tabs 'fixed tabs'.|
| `inverse` | Used to invert the colors.|
| `navigation` | Used for the navigation element.|
| `pointer` | Used for the moving underline element.|
| `tabs` |Used as a root classname for the component.|

View File

@ -61,3 +61,27 @@
display: block;
}
}
.fixed {
.label {
flex: 1;
text-align: center;
}
}
.inverse {
.navigation {
background-color: $tab-inverse-bar-color;
}
.label {
color: $tab-inverse-text-inactive-color;
&.active {
color: $tab-inverse-text-color;
}
}
.pointer {
background-color: $tab-inverse-pointer-color;
}
}

View File

@ -58,7 +58,7 @@ class Clock extends Component {
handleCalculateShape = () => {
const { top, left, width } = this.refs.placeholder.getBoundingClientRect();
this.setState({
center: { x: left + width / 2 - window.scrollX, y: top + width / 2 - window.scrollX },
center: { x: left + width / 2 - window.pageXOffset, y: top + width / 2 - window.pageXOffset },
radius: width / 2
});
};

View File

@ -37,7 +37,7 @@ class Face extends Component {
};
}
renderNumber (number, idx) {
renderNumber = (number, idx) => {
const { active, radius, spacing, theme, twoDigits } = this.props;
return (
<span
@ -60,7 +60,7 @@ class Face extends Component {
onMouseDown={onMouseDown}
style={this.faceStyle()}
>
{numbers.map(this.renderNumber.bind(this))}
{numbers.map(this.renderNumber)}
</div>
);
}

View File

@ -116,7 +116,7 @@ class Hand extends Component {
return (
<div className={className} style={handStyle}>
<div ref='knob' className={theme.knob}></div>
<div ref='knob' className={theme.knob}/>
</div>
);
}

View File

@ -11,6 +11,7 @@ import timePickerDialogFactory from './TimePickerDialog.js';
const factory = (TimePickerDialog, Input) => {
class TimePicker extends Component {
static propTypes = {
active: PropTypes.bool,
className: PropTypes.string,
error: PropTypes.string,
format: PropTypes.oneOf(['24hr', 'ampm']),
@ -21,6 +22,7 @@ const factory = (TimePickerDialog, Input) => {
onEscKeyDown: PropTypes.func,
onKeyPress: PropTypes.func,
onOverlayClick: PropTypes.func,
readonly: PropTypes.bool,
theme: PropTypes.shape({
input: PropTypes.string
}),
@ -28,18 +30,35 @@ const factory = (TimePickerDialog, Input) => {
};
static defaultProps = {
active: false,
className: '',
format: '24hr'
};
state = {
active: false
active: this.props.active
};
componentWillReceiveProps (nextProps) {
if (this.state.active !== nextProps.active) {
this.setState({ active: nextProps.active });
}
}
handleDismiss = () => {
this.setState({active: false});
};
handleInputFocus = (event) => {
events.pauseEvent(event);
this.setState({active: true});
};
handleInputBlur = (event) => {
events.pauseEvent(event);
this.setState({active: false});
};
handleInputMouseDown = (event) => {
events.pauseEvent(event);
this.setState({active: true});
@ -59,18 +78,22 @@ const factory = (TimePickerDialog, Input) => {
};
render () {
const { value, format, inputClassName, onEscKeyDown, onOverlayClick, theme, ...others } = this.props;
const {
active, // eslint-disable-line
format, inputClassName, onEscKeyDown, onOverlayClick, readonly, value, ...others
} = this.props;
const formattedTime = value ? time.formatTime(value, format) : '';
return (
<div data-react-toolbox='time-picker'>
<Input
{...others}
className={classnames(theme.input, {[inputClassName]: inputClassName })}
className={classnames(this.props.theme.input, {[inputClassName]: inputClassName })}
disabled={readonly}
error={this.props.error}
name={this.props.name}
label={this.props.label}
onMouseDown={this.handleInputMouseDown}
name={this.props.name}
onKeyPress={this.handleInputKeyPress}
onMouseDown={this.handleInputMouseDown}
readOnly
type='text'
value={formattedTime}

View File

@ -65,8 +65,8 @@ const factory = (Dialog) => {
if (this.state.display === 'hours') this.setState({display: 'minutes'});
};
switchDisplay = (display) => {
this.setState({display});
switchDisplay = (event) => {
this.setState({display: event.target.id});
};
actions = [
@ -108,11 +108,11 @@ const factory = (Dialog) => {
onOverlayClick={this.props.onOverlayClick}
>
<header className={theme.header}>
<span className={theme.hours} onClick={this.switchDisplay.bind(this, 'hours')}>
<span id='hours' className={theme.hours} onClick={this.switchDisplay}>
{('0' + this.formatHours()).slice(-2)}
</span>
<span className={theme.separator}>:</span>
<span className={theme.minutes} onClick={this.switchDisplay.bind(this, 'minutes')}>
<span id='minutes' className={theme.minutes} onClick={this.switchDisplay}>
{('0' + this.state.displayTime.getMinutes()).slice(-2)}
</span>
{this.renderAMPMLabels()}

136
components/time_picker/index.d.ts vendored Normal file
View File

@ -0,0 +1,136 @@
import __ReactToolbox from "../index.d.ts";
export interface TimePickerTheme {
/**
* Added to the number which is active in clock face.
*/
active?: string;
/**
* AM label in dialog header when mode is AM/PM.
*/
am?: string;
/**
* Added to the dialog when the selected format is AM.
*/
amFormat?: string;
/**
* Wrapper for AM and PM labels in header when mode is AM/PM.
*/
ampm?: string;
/**
* Used for buttons inside the dialog of the picker.
*/
button?: string;
/**
* Clock root class element.
*/
clock?: string;
/**
* Wrapper for the proper positioning of the clock.
*/
clockWrapper?: string;
/**
* Used for the dialog component.
*/
dialog?: string;
/**
* Used to style the clock face.
*/
face?: string;
/**
* Used for the clock's hand.
*/
hand?: string;
/**
* Dialog header wrapper class.
*/
header?: string;
/**
* Used for hours in dialog header.
*/
hours?: string;
/**
* Added to the dialog hours are displayed.
*/
hoursDisplay?: string;
/**
* Used for Input element that opens the picker.
*/
input?: string;
/**
* Used for the knob of the hand.
*/
knob?: string;
/**
* Used for minutes in dialog header.
*/
minutes?: string;
/**
* Added to the dialog minutes are displayed.
*/
minutesDisplay?: string;
/**
* Each of the numbers in the clock's face.
*/
number?: string;
/**
* Placeholder for the clock inside the dialog (inner wrapper).
*/
placeholder?: string;
/**
* PM label in dialog header when mode is AM/PM.
*/
pm?: string;
/**
* Added to the dialog when the selected format is PM.
*/
pmFormat?: string;
/**
* Is the : separator between hours and minutes in dialog header.
*/
separator?: string;
/**
* Added to the knob when no round number is selected.
*/
small?: string;
}
interface TimePickerProps {
/**
* Provide error text which will be displayed under the field.
*/
error?: string;
/**
* A key to identify an Icon from Material Design Icons or a custom Icon Element.
*/
icon?: __React.ReactNode | string;
/**
* This class will be applied to Input component of TimePicker.
*/
inputClassName?: string;
/**
* Format to display the clock. It can be 24hr or ampm.
* @default false
*/
format?: "24hr" | "ampm";
/**
* The text string to use for the floating label element in the input component.
*/
label?: string;
/**
* Callback called when the picker value is changed.
*/
onChange?: Function;
/**
* Classnames object defining the component style.
*/
theme?: TimePickerTheme;
/**
* Datetime object with currrently selected time.
*/
value?: Date;
}
export class TimePicker extends __React.Component<TimePickerProps, {}> { }
export default TimePicker;

View File

@ -35,12 +35,14 @@ If you want to provide a theme via context, the component key is `RTTimePicker`.
| Name | Type | Default | Description|
|:-----|:-----|:-----|:-----|
| `active` | `Boolean` | `false` | Allows to control if the picker should be shown from outside. Beware you should update the prop when the Dialog is closed. |
| `className` | `String` | | This class will be placed at the top of the `TimePickerDialog` component so you can provide custom styles.|
| `error` | `String` | | Provide error text which will be displayed under the field.|
| `inputClassName`| `String` | | This class will be applied to `Input` component of `TimePicker`. |
| `format` | `String` | `24hr` | Format to display the clock. It can be `24hr` or `ampm`.|
| `label` | `String` | | The text string to use for the floating label element in the input component.|
| `onChange` | `Function` | | Callback called when the picker value is changed.|
| `readonly` | `Boolean` | | The input element will be readonly and look like disabled.|
| `value` | `Date` | | Datetime object with currrently selected time. |
## Theme

View File

@ -3,7 +3,7 @@
@import "../mixins";
@import "./config";
.input > [role="input"] {
.input:not(.disabled) > .inputElement {
cursor: pointer;
}

View File

@ -1,10 +1,38 @@
import React, { Component, PropTypes } from 'react';
import Portal from '../hoc/Portal';
import classnames from 'classnames';
import { themr } from 'react-css-themr';
import { TOOLTIP } from '../identifiers.js';
import events from '../utils/events';
import utils from '../utils/utils';
const factory = (defaultTheme = {}) => {
const Tooltip = (ComposedComponent) => {
const POSITION = {
BOTTOM: 'bottom',
HORIZONTAL: 'horizontal',
LEFT: 'left',
RIGHT: 'right',
TOP: 'top',
VERTICAL: 'vertical'
};
const defaults = {
className: '',
delay: 0,
hideOnClick: true,
position: POSITION.VERTICAL,
theme: {}
};
const tooltipFactory = (options = {}) => {
const {
className: defaultClassName,
delay: defaultDelay,
hideOnClick: defaultHideOnClick,
position: defaultPosition,
theme: defaultTheme
} = {...defaults, ...options};
return ComposedComponent => {
class TooltippedComponent extends Component {
static propTypes = {
children: PropTypes.any,
@ -19,55 +47,154 @@ const factory = (defaultTheme = {}) => {
}),
tooltip: PropTypes.string,
tooltipDelay: PropTypes.number,
tooltipHideOnClick: PropTypes.bool
tooltipHideOnClick: PropTypes.bool,
tooltipPosition: PropTypes.oneOf(Object.keys(POSITION).map(key => POSITION[key]))
};
static defaultProps = {
className: '',
tooltipDelay: 0,
tooltipHideOnClick: true
className: defaultClassName,
tooltipDelay: defaultDelay,
tooltipHideOnClick: defaultHideOnClick,
tooltipPosition: defaultPosition
};
state = {
active: false
active: false,
position: this.props.tooltipPosition,
visible: false
};
componentWillUnmount () {
if (this.refs.tooltip) {
events.removeEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
}
}
activate ({ top, left, position }) {
if (this.timeout) clearTimeout(this.timeout);
this.setState({ visible: true, position });
this.timeout = setTimeout(() => {
this.setState({ active: true, top, left });
}, this.props.tooltipDelay);
}
deactivate () {
if (this.timeout) clearTimeout(this.timeout);
if (this.state.active) {
events.addEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
this.setState({ active: false });
} else if (this.state.visible) {
this.setState({ visible: false });
}
}
getPosition (element) {
const { tooltipPosition } = this.props;
if (tooltipPosition === POSITION.HORIZONTAL) {
const origin = element.getBoundingClientRect();
const { width: ww } = utils.getViewport();
const toRight = origin.left < ((ww / 2) - origin.width / 2);
return toRight ? POSITION.RIGHT : POSITION.LEFT;
} else if (tooltipPosition === POSITION.VERTICAL) {
const origin = element.getBoundingClientRect();
const { height: wh } = utils.getViewport();
const toBottom = origin.top < ((wh / 2) - origin.height / 2);
return toBottom ? POSITION.BOTTOM : POSITION.TOP;
} else {
return tooltipPosition;
}
}
calculatePosition (element) {
const position = this.getPosition(element);
const { top, left, height, width } = element.getBoundingClientRect();
const xOffset = window.scrollX || window.pageXOffset;
const yOffset = window.scrollY || window.pageYOffset;
if (position === POSITION.BOTTOM) {
return {
top: top + height + yOffset,
left: left + (width / 2) + xOffset,
position
};
} else if (position === POSITION.TOP) {
return {
top: top + yOffset,
left: left + (width / 2) + xOffset,
position
};
} else if (position === POSITION.LEFT) {
return {
top: top + (height / 2) + yOffset,
left: left + xOffset,
position
};
} else if (position === POSITION.RIGHT) {
return {
top: top + (height / 2) + yOffset,
left: left + width + xOffset,
position
};
}
}
onTransformEnd = (e) => {
if (e.propertyName === 'transform') {
events.removeEventListenerOnTransitionEnded(this.refs.tooltip, this.onTransformEnd);
this.setState({ visible: false });
}
};
handleMouseEnter = (event) => {
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(() =>this.setState({active: true}), this.props.tooltipDelay);
this.activate(this.calculatePosition(event.target));
if (this.props.onMouseEnter) this.props.onMouseEnter(event);
};
handleMouseLeave = (event) => {
if (this.timeout) clearTimeout(this.timeout);
if (this.state.active) this.setState({active: false});
this.deactivate();
if (this.props.onMouseLeave) this.props.onMouseLeave(event);
};
handleClick = (event) => {
if (this.timeout) clearTimeout(this.timeout);
if (this.props.tooltipHideOnClick) this.setState({active: false});
if (this.props.tooltipHideOnClick) this.deactivate();
if (this.props.onClick) this.props.onClick(event);
};
render () {
const {children, className, tooltip,
tooltipDelay, tooltipHideOnClick, theme, ...other} = this.props; //eslint-disable-line no-unused-vars
const composedClassName = classnames(this.props.theme.tooltipWrapper, className);
const tooltipClassName = classnames(this.props.theme.tooltip, {
[this.props.theme.tooltipActive]: this.state.active
const { active, left, top, position, visible } = this.state;
const positionClass = `tooltip${position.charAt(0).toUpperCase() + position.slice(1)}`;
const {
children,
className,
theme,
tooltip,
tooltipDelay, //eslint-disable-line no-unused-vars
tooltipHideOnClick, //eslint-disable-line no-unused-vars
tooltipPosition, //eslint-disable-line no-unused-vars
...other
} = this.props;
const _className = classnames(theme.tooltip, {
[theme.tooltipActive]: active,
[theme[positionClass]]: theme[positionClass]
});
return (
<ComposedComponent
{...other}
className={composedClassName}
className={className}
onClick={this.handleClick}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
theme={theme}
>
{children ? children : null}
<span data-react-toolbox="tooltip" className={tooltipClassName}>{tooltip}</span>
{visible && (
<Portal>
<span ref="tooltip" className={_className} data-react-toolbox="tooltip" style={{top, left}}>
<span className={theme.tooltipInner}>{tooltip}</span>
</span>
</Portal>
)}
</ComposedComponent>
);
}
@ -75,9 +202,6 @@ const factory = (defaultTheme = {}) => {
return themr(TOOLTIP, defaultTheme)(TooltippedComponent);
};
return Tooltip;
};
export default factory();
export { factory as tooltipFactory };
export default tooltipFactory;

46
components/tooltip/index.d.ts vendored Normal file
View File

@ -0,0 +1,46 @@
export interface TooltipTheme {
/**
* Added to the tooltip element.
*/
tooltip?: string;
/**
* Added to the root when the tooltip is active.
*/
tooltipActive?: string;
/**
* Wrapper for the root element used to position the tooltip.
*/
tooltipWrapper?: string;
}
interface TooltipProps {
/**
* Classnames object defining the component style.
*/
theme?: TooltipTheme;
/**
* The text string to use for the tooltip.
*/
tooltip?: string;
/**
* Amount of time in miliseconds spent before the tooltip is visible.
*/
tooltipDelay?: number;
/**
* If true, the Tooltip hides after a click in the host component.
* @default true
*/
tooltipHideOnClick?: boolean;
}
declare class TooltipComponent<P, S> extends __React.Component<P, S> {
props: P & TooltipProps;
}
interface TooltippedComponentClass<P> extends TooltipProps {
new (props?: P, context?: any): TooltipComponent<P, any>;
}
export function Tooltip<P>(componentClass: __React.ComponentClass<P>): TooltippedComponentClass<P>;
export default Tooltip;

View File

@ -1,4 +1,6 @@
import { tooltipFactory } from './Tooltip.js';
import tooltipFactory from './Tooltip.js';
import theme from './theme.scss';
export default tooltipFactory(theme);
const themedTooltipFactory = (options) => tooltipFactory({ ...options, theme });
export default tooltipFactory({ theme });
export { themedTooltipFactory as tooltipFactory };

View File

@ -1,6 +1,6 @@
# Tooltip
A Tooltip is useful to show information on hover in any kind of component. We have a component that can be used as a **decorator** for any kind of component. You just have to take into account that the overflow in the component should be visible.
A Tooltip is useful to show information on hover in any kind of component. We have a component that can be used as a **decorator** for any kind of component. Also, it's factory function is exposed so you can create your own decorator with specific properties.
<!-- example -->
```jsx
@ -37,11 +37,16 @@ In any component you decorate with the Tooltip you'd get some additional props:
| `tooltip` | `String` | | The text string to use for the tooltip.|
| `tooltipDelay` | `Number` | | Amount of time in miliseconds spent before the tooltip is visible.|
| `tooltipHideOnClick` | `Boolean` | `true` | If true, the Tooltip hides after a click in the host component.|
| `tooltipPosition` | `String` | `vertical` | Determines the position of the tooltip. It can be automatic with `vertical` and `horizontal` values or forced with `bottom`, `top`, `left` or `right`.|
## Theming
| Name | Description|
|:---------|:-----------|
| `tooltip` | Added to the tooltip element.|
| `tooltip` | Added to the tooltip element wrapper.|
| `tooltipActive` | Added to the root when the tooltip is active.|
| `tooltipWrapper` | Wrapper for the root element used to position the tooltip.|
| `tooltipBottom` | Added to the root in case the tooltip is being positioned at bottom.|
| `tooltipInner` | Added to the inner element which sets the background, font and rounded borders.|
| `tooltipLeft` | Added to the root in case the tooltip is being positioned at left.|
| `tooltipRight` | Added to the root in case the tooltip is being positioned at right.|
| `tooltipTop` | Added to the root in case the tooltip is being positioned at top.|

View File

@ -3,32 +3,53 @@
@import "../mixins";
@import "./config";
.tooltipWrapper {
position: relative;
}
.tooltip {
position: absolute;
top: 100%;
left: 50%;
z-index: $z-index-higher;
display: block;
max-width: $tooltip-max-width;
padding: $tooltip-padding;
margin: $tooltip-margin 0;
padding: $tooltip-margin;
font-family: Roboto, sans-serif;
font-size: $tooltip-font-size;
font-weight: $font-weight-bold;
line-height: $font-size-small;
color: $tooltip-color;
text-align: center;
text-transform: none;
background: $tooltip-background;
border-radius: $tooltip-border-radius;
pointer-events: none;
transition: $animation-curve-default $tooltip-animation-duration transform;
transform: scale(0) translateX(-50%);
transform-origin: top left;
&.tooltipActive {
transform: scale(1) translateX(-50%);
}
&.tooltipTop {
transform: scale(0) translateX(-50%) translateY(-100%);
&.tooltipActive {
transform: scale(1) translateX(-50%) translateY(-100%);
}
}
&.tooltipLeft {
transform: scale(0) translateX(-100%) translateY(-50%);
&.tooltipActive {
transform: scale(1) translateX(-100%) translateY(-50%);
}
}
&.tooltipRight {
transform: scale(0) translateX(0) translateY(-50%);
&.tooltipActive {
transform: scale(1) translateX(0) translateY(-50%);
}
}
}
.tooltipInner {
display: block;
padding: $tooltip-padding;
color: $tooltip-color;
background: $tooltip-background;
border-radius: $tooltip-border-radius;
}

View File

@ -46,10 +46,10 @@ export default {
return true;
},
removeEventListenerOnTransitionEnded (element) {
removeEventListenerOnTransitionEnded (element, fn) {
const eventName = transitionEventNamesFor(element);
if (!eventName) return false;
element.removeEventListener(eventName);
element.removeEventListener(eventName, fn);
return true;
}
};

View File

@ -1,5 +1,7 @@
import 'core-js/fn/array/from';
import 'core-js/fn/array/iterator';
import 'core-js/fn/array/find-index';
import 'core-js/fn/map';
import 'core-js/fn/string/starts-with';
import 'core-js/fn/string/includes';
import 'core-js/fn/symbol';

View File

@ -1,3 +1,117 @@
const dateLocales = {
en: {
months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
monthsShort: 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
weekdaysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
weekdaysLetter: []
},
es: {
months: 'enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre'.split('_'),
monthsShort: 'ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic'.split('_'),
weekdays: 'domingo_lunes_martes_miércoles_jueves_viernes_sábado'.split('_'),
weekdaysShort: 'dom._lun._mar._mié._jue._vie._sáb.'.split('_'),
weekdaysLetter: 'D_L_M_X_J_V_S'.split('_')
},
af: {
months: 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split('_'),
monthsShort: 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'),
weekdays: 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split('_'),
weekdaysShort: 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'),
weekdaysLetter: []
},
ar: {
months: ['كانون الثاني يناير', 'شباط فبراير', 'آذار مارس', 'نيسان أبريل', 'أيار مايو', 'حزيران يونيو', 'تموز يوليو', 'آب أغسطس', 'أيلول سبتمبر', 'تشرين الأول أكتوبر', 'تشرين الثاني نوفمبر', 'كانون الأول ديسمبر'],
monthsShort: ['كانون الثاني يناير', 'شباط فبراير', 'آذار مارس', 'نيسان أبريل', 'أيار مايو', 'حزيران يونيو', 'تموز يوليو', 'آب أغسطس', 'أيلول سبتمبر', 'تشرين الأول أكتوبر', 'تشرين الثاني نوفمبر', 'كانون الأول ديسمبر'],
weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
weekdaysLetter: []
},
be: {
months: 'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split('_'),
monthsShort: 'студ_лют_сак_красрав_чэрв_ліп_жнів_вераст_ліст_снеж'.split('_'),
weekdays: 'нядзеля_панядзелак_аўторак_серадаацвер_пятніца_субота'.split('_'),
weekdaysShort: 'нд_пн_ат_ср_чц_пт_сб'.split('_'),
weekdaysLetter: []
},
bg: {
months: 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split('_'),
monthsShort: 'янрев_мар_апрай_юни_юли_авг_сеп_окт_ноеек'.split('_'),
weekdays: еделя_понеделник_вторник_срядаетвъртък_петък_събота'.split('_'),
weekdaysShort: ед_пон_вто_сря_чет_пет_съб'.split('_'),
weekdaysLetter: []
},
bn: {
months: 'জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split('_'),
monthsShort: 'জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্'.split('_'),
weekdays: 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রবার_শনিবার'.split('_'),
weekdaysShort: 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্র_শনি'.split('_'),
weekdaysLetter: []
},
bo: {
months: 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'),
monthsShort: 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split('_'),
weekdays: 'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split('_'),
weekdaysShort: 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split('_'),
weekdaysLetter: []
},
br: {
months: 'Genver_C\'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split('_'),
monthsShort: 'Gen_C\'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'),
weekdays: 'Sul_Lun_Meurzh_Merc\'her_Yaou_Gwener_Sadorn'.split('_'),
weekdaysShort: 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'),
weekdaysLetter: []
},
bs: {
months: 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split('_'),
monthsShort: 'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split('_'),
weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split('_'),
weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'),
weekdaysLetter: []
},
ca: {
months: 'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split('_'),
monthsShort: 'gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.'.split('_'),
weekdays: 'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split('_'),
weekdaysShort: 'dg._dl._dt._dc._dj._dv._ds.'.split('_'),
weekdaysLetter: 'Dg_Dl_Dt_Dc_Dj_Dv_Ds'.split('_')
},
gl: {
months: 'Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro'.split('_'),
monthsShort: 'Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.'.split('_'),
weekdays: 'Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado'.split('_'),
weekdaysShort: 'Dom._Lun._Mar._Mér._Xov._Ven._Sáb.'.split('_'),
weekdaysLetter: 'Do_Lu_Ma_Mé_Xo_Ve_Sá'.split('_')
},
eu: {
months: 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'),
monthsShort: 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'),
weekdays: 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'),
weekdaysShort: 'ig._al._ar._az._og._ol._lr.'.split('_'),
weekdaysLetter: 'ig_al_ar_az_og_ol_lr'.split('_')
},
pt: {
months: 'Janeiro_Fevereiro_Março_Abril_Maio_Junho_Julho_Agosto_Setembro_Outubro_Novembro_Dezembro'.split('_'),
monthsShort: 'Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez'.split('_'),
weekdays: 'Domingo_Segunda-Feira_Terça-Feira_Quarta-Feira_Quinta-Feira_Sexta-Feira_Sábado'.split('_'),
weekdaysShort: 'Dom_Seg_Ter_Qua_Qui_Sex_Sáb'.split('_'),
weekdaysLetter: []
},
it: {
months: 'gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre'.split('_'),
monthsShort: 'gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic'.split('_'),
weekdays: 'Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato'.split('_'),
weekdaysShort: 'Dom_Lun_Mar_Mer_Gio_Ven_Sab'.split('_'),
weekdaysLetter: []
},
fr: {
months: 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
monthsShort: 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
weekdays: 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
weekdaysShort: 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
weekdaysLetter: []
}
};
const time = {
getDaysInMonth (d) {
const resultDate = this.getFirstDayOfMonth(d);
@ -18,68 +132,31 @@ const time = {
return d.getHours() >= 12 ? 'pm' : 'am';
},
getFullMonth (d) {
getFullMonth (d, locale = 'en') {
const month = d.getMonth();
switch (month) {
default: return 'Unknown';
case 0: return 'January';
case 1: return 'February';
case 2: return 'March';
case 3: return 'April';
case 4: return 'May';
case 5: return 'June';
case 6: return 'July';
case 7: return 'August';
case 8: return 'September';
case 9: return 'October';
case 10: return 'November';
case 11: return 'December';
}
const l = ((typeof locale === 'string') ? dateLocales[locale] : locale) || dateLocales.en;
return (l.hasOwnProperty('months')) ? l.months[month] || 'Unknown' : 'Unknown';
},
getShortMonth (d) {
getShortMonth (d, locale = 'en') {
const month = d.getMonth();
switch (month) {
default: return 'Unknown';
case 0: return 'Jan';
case 1: return 'Feb';
case 2: return 'Mar';
case 3: return 'Apr';
case 4: return 'May';
case 5: return 'Jun';
case 6: return 'Jul';
case 7: return 'Aug';
case 8: return 'Sep';
case 9: return 'Oct';
case 10: return 'Nov';
case 11: return 'Dec';
}
const l = ((typeof locale === 'string') ? dateLocales[locale] : locale) || dateLocales.en;
return (l.hasOwnProperty('monthsShort')) ? l.monthsShort[month] || 'Unknown' : 'Unknown';
},
getFullDayOfWeek (day) {
switch (day) {
default: return 'Unknown';
case 0: return 'Sunday';
case 1: return 'Monday';
case 2: return 'Tuesday';
case 3: return 'Wednesday';
case 4: return 'Thursday';
case 5: return 'Friday';
case 6: return 'Saturday';
}
getFullDayOfWeek (day, locale = 'en') {
const l = ((typeof locale === 'string') ? dateLocales[locale] : locale) || dateLocales.en;
return (l.hasOwnProperty('weekdays')) ? l.weekdays[day] || 'Unknown' : 'Unknown';
},
getShortDayOfWeek (day) {
switch (day) {
default: return 'Unknown';
case 0: return 'Sun';
case 1: return 'Mon';
case 2: return 'Tue';
case 3: return 'Wed';
case 4: return 'Thu';
case 5: return 'Fri';
case 6: return 'Sat';
}
getShortDayOfWeek (day, locale = 'en') {
const l = ((typeof locale === 'string') ? dateLocales[locale] : locale) || dateLocales.en;
return (l.hasOwnProperty('weekdaysShort')) ? l.weekdaysShort[day] || 'Unknown' : 'Unknown';
},
getDayOfWeekLetter (day, locale = 'en') {
const l = ((typeof locale === 'string') ? dateLocales[locale] : locale) || dateLocales.en;
return (l.hasOwnProperty('weekdaysLetter')) ? l.weekdaysLetter[day] || this.getFullDayOfWeek(day, locale).charAt(0) : 'Unknown';
},
clone (d) {
@ -177,8 +254,21 @@ const time = {
return ((minDate && !(date >= minDate)) || (maxDate && !(date <= maxDate)));
},
formatDate (date) {
return `${date.getDate()} ${time.getFullMonth(date)} ${date.getFullYear()}`;
closestDate (to, date1, date2) {
const toTime = to.getTime();
const diff1 = Math.abs(toTime - date1.getTime());
const diff2 = Math.abs(toTime - date2.getTime());
return diff1 < diff2 ? date1 : date2;
},
formatDate (date, locale = 'en') {
if (locale === 'en') {
return `${date.getDate()} ${time.getFullMonth(date, locale)} ${date.getFullYear()}`;
} else {
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`;
}
}
};

View File

@ -56,5 +56,13 @@ export default {
return value ? 'on' : '';
}
return value;
},
removeObjectKey (key, object) {
const newObject = {};
Object.keys(object)
.filter(k => k !== key)
.forEach(k => { newObject[k] = object[k]; });
return newObject;
}
};

View File

@ -14,7 +14,6 @@
.logo {
width: $appbar-logo-size;
height: $appbar-logo-size;
margin-top: ($appbar-height - $appbar-logo-size) / 2;
fill: $color-primary-contrast;
}

View File

@ -94,7 +94,7 @@ $content-width: 740px;
margin: 0 $unit;
}
.twitter {
color: $twitter-color;
color: $twitter-color !important;
}
}
> p {

View File

@ -63,7 +63,7 @@ import { AppBar } from 'react-toolbox/lib/app_bar';
You import from the component definition so the imported component is bundled with its dependencies but it does not require any style for you. This means that no CSS will be bundled and the component markup will **not** include any classname. It's your responsibility to provide a theme to the component to be properly style and you can do it via properties or context. For example:
```js
import { AppBar } from 'react-toolbox/lib/app_bar/AppBar.js';
import AppBar from 'react-toolbox/lib/app_bar/AppBar.js';
```
## Customizing components
@ -134,7 +134,7 @@ Remember that you can import components without styles and provide those styles
Then, when you use a button you can inject the appropriated theme:
```js
import { Button } from 'react-toolbox/lib/button/Button';
import Button from 'react-toolbox/lib/button/Button';
import buttonTheme from './theme/button.scss';
const ThemedButton = (props) => (
@ -181,6 +181,12 @@ React Toolbox assumes that you are importing [Roboto Font](https://www.google.co
In order to import the fonts for you, we'd need to include them in the CSS which is considered a bad practice. If you are not including them in your app, go to the linked sites and follow the instructions.
## References to Components
As mentioned in the theming section, you'd usually import a component with a theme already injected. To do this we use a [Higher Order Component](http://github.com/javivelasco/react-css-themr). If you add a `ref` property to a React Toolbox component, what you will get is the **HOC** instead of the **Wrapped** component.
For components with imperative methods such as the `Input`, you can also get its real instance instead of the HOC. To do this, the HOC includes a `getWrappedInstance` method that returns the wrapped component instance. This way you can call methods directly on the component's instance.
## Server Side Rendering
The only requirement for SSR is to be able to require ES6 and CSS Modules in the backend. To make it possible you can check projects like [CSS Modules register hook](https://github.com/css-modules/css-modules-require-hook) or [Webpack Isomorphic tools](https://github.com/halt-hammerzeit/webpack-isomorphic-tools). Also, make sure you can import from `node_modules`.

View File

@ -5,7 +5,7 @@ class TestCheckbox extends React.Component {
};
handleChange = (field, value) => {
this.setState({...this.state, [field]: value});
this.setState({[field]: value});
};
render () {

View File

@ -4,6 +4,14 @@ const max_datetime = new Date(new Date(datetime).setDate(24));
datetime.setHours(17);
datetime.setMinutes(28);
const localeExample = {
months: 'urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua'.split('_'),
monthsShort: 'urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.'.split('_'),
weekdays: 'igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata'.split('_'),
weekdaysShort: 'ig._al._ar._az._og._ol._lr.'.split('_'),
weekdaysLetter: 'ig_al_ar_az_og_ol_lr'.split('_')
}
class DatePickerTest extends React.Component {
state = {date2: datetime};
@ -18,6 +26,21 @@ class DatePickerTest extends React.Component {
label='Birthdate'
onChange={this.handleChange.bind(this, 'date1')}
value={this.state.date1}
sundayFirstDayOfWeek
/>
<DatePicker
label='Locale (String) - Spanish'
locale='es'
onChange={this.handleChange.bind(this, 'date1')}
value={this.state.date1}
/>
<DatePicker
label='Locale (Object) - Basque'
locale={localeExample}
onChange={this.handleChange.bind(this, 'date1')}
value={this.state.date1}
/>
<DatePicker
@ -25,6 +48,7 @@ class DatePickerTest extends React.Component {
minDate={min_datetime}
onChange={this.handleChange.bind(this, 'date2')}
value={this.state.date2}
sundayFirstDayOfWeek
/>
<DatePicker
@ -33,6 +57,7 @@ class DatePickerTest extends React.Component {
inputFormat={(value) => `${value.getDate()}/${value.getMonth() + 1}/${value.getFullYear()}`}
onChange={this.handleChange.bind(this, 'date3')}
value={this.state.date3}
sundayFirstDayOfWeek
/>
</section>
);

View File

@ -8,12 +8,12 @@ class InputTest extends React.Component {
render () {
return (
<section>
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16 } />
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16} />
<Input type='text' label='Disabled field' disabled />
<Input type='text' multiline label='Multiline' onChange={this.handleChange.bind(this, 'multiline')} />
<Input type='text' multiline label='Multiline' maxLength={20} value={this.state.multiline} onChange={this.handleChange.bind(this, 'multiline')} />
<Input type='email' label='Email address' icon='email' value={this.state.email} onChange={this.handleChange.bind(this, 'email')} />
<Input type='tel' label='Phone' name='phone' icon='phone' value={this.state.phone} onChange={this.handleChange.bind(this, 'phone')} />
<Input type='text' value={this.state.hint} label='Required Field' hint='With Hint' required onChange={this.handleChange.bind(this, 'hint')} icon={<span>J</span>} />
<Input type='text' value={this.state.hint} label='Required Field' hint='With Hint' required onChange={this.handleChange.bind(this, 'hint')} icon='share' />
</section>
);
}

View File

@ -1,21 +1,50 @@
class TabsExample extends React.Component {
state = {
index: 1
index: 1,
fixedIndex: 1,
inverseIndex: 1
};
handleTabChange = (index) => {
this.setState({index});
};
handleFixedTabChange = (index) => {
this.setState({fixedIndex: index});
};
handleInverseTabChange = (index) => {
this.setState({inverseIndex: index});
};
handleActive = () => {
console.log('Special one activated');
};
render () {
return (
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary'><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<section>
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<h5>Fixed Tabs</h5>
<Tabs index={this.state.fixedIndex} onChange={this.handleFixedTabChange} fixed>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
</Tabs>
<h5>Inverse Tabs</h5>
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
</section>
);
}
}

View File

@ -8,8 +8,8 @@ const Logo = (props) => {
return (
<svg className={className} viewBox="0 0 144 131">
<g>
<path d="M118.954,87.235 C117.784,80.465 115.755,73.277 112.881,65.938 C115.756,58.592 117.784,51.398 118.952,44.624 C130.693,50.324 137.999,57.982 138,65.909 C138,73.901 130.708,81.552 118.954,87.235 L118.954,87.235 Z M105,123.11 C102.829,124.364 100.281,125 97.428,125 C91.361,125 84.259,122.231 76.981,117.308 C82.313,112.871 87.579,107.457 92.545,101.223 C100.313,100.042 107.527,98.206 113.955,95.844 C114.897,108.916 111.902,119.125 105,123.11 L105,123.11 Z M46.568,124.997 C43.717,124.997 41.171,124.362 39.001,123.108 C32.104,119.127 29.109,108.918 30.052,95.846 C36.472,98.205 43.677,100.037 51.435,101.218 C56.402,107.454 61.671,112.871 67.005,117.309 C59.731,122.23 52.633,124.997 46.568,124.997 L46.568,124.997 Z M6,65.909 C6,57.957 13.296,50.305 25.036,44.612 C26.204,51.397 28.236,58.604 31.117,65.963 C28.25,73.288 26.224,80.464 25.055,87.221 C13.301,81.532 6,73.877 6,65.909 L6,65.909 Z M46.881,37.496 C34.717,58.616 46.806,37.626 34.785,58.498 C32.879,52.829 31.528,47.322 30.729,42.143 C35.601,40.255 41.033,38.677 46.881,37.496 L46.881,37.496 Z M72.005,18.28 C76.117,21.584 80.221,25.531 84.168,30.008 C60.223,29.96 84.478,30.009 59.886,29.959 C63.82,25.502 67.909,21.571 72.005,18.28 L72.005,18.28 Z M109.214,58.468 C97.071,37.445 109.179,58.408 97.101,37.496 C102.949,38.678 108.383,40.259 113.26,42.152 C112.461,47.319 111.113,52.813 109.214,58.468 L109.214,58.468 Z M109.235,73.475 C111.119,79.096 112.457,84.56 113.251,89.702 C108.396,91.576 102.987,93.144 97.16,94.319 C109.053,73.79 96.994,94.605 109.235,73.475 L109.235,73.475 Z M34.764,73.5 C47.04,94.67 34.992,73.892 46.834,94.313 C41.014,93.136 35.61,91.567 30.758,89.691 C31.551,84.56 32.886,79.107 34.764,73.5 L34.764,73.5 Z M84.108,101.947 C80.175,106.402 76.088,110.332 71.993,113.623 C67.883,110.321 63.78,106.375 59.835,101.902 C83.777,101.946 59.54,101.901 84.108,101.947 L84.108,101.947 Z M86.649,95.951 L71.75,95.924 L56.853,95.896 C56.122,95.812 55.399,95.718 54.677,95.622 C54.474,95.357 54.27,95.096 54.068,94.829 L38.235,67.525 C38.011,67.004 37.8,66.484 37.584,65.963 C37.731,65.609 37.871,65.255 38.022,64.9 L53.936,37.268 C54.21,36.903 54.487,36.545 54.764,36.185 C55.365,36.105 55.965,36.024 56.572,35.953 L72.262,35.984 L87.951,36.016 C88.387,36.069 88.816,36.131 89.248,36.188 C89.475,36.483 89.702,36.775 89.927,37.073 L105.949,64.813 C106.109,65.189 106.259,65.564 106.414,65.939 C106.2,66.457 105.989,66.975 105.766,67.494 L89.983,94.737 C89.757,95.038 89.529,95.331 89.301,95.628 C88.424,95.744 87.54,95.853 86.649,95.951 L86.649,95.951 Z M144,65.909 C143.999,54.951 134.647,45.112 119.808,38.364 C121.377,22.123 117.514,9.088 108.001,3.596 C104.843,1.774 101.279,0.905 97.437,0.905 L97.438,6.905 C100.286,6.905 102.831,7.54 105.001,8.793 C111.904,12.778 114.898,22.945 113.965,35.978 C107.519,33.608 100.279,31.77 92.485,30.588 C87.54,24.392 82.299,19.008 76.992,14.593 C84.266,9.672 91.365,6.906 97.435,6.905 L97.437,0.905 L97.434,0.905 C89.71,0.906 80.873,4.42 72.008,10.742 C63.141,4.418 54.301,0.904 46.573,0.904 L46.573,6.904 C52.639,6.904 59.74,9.672 67.017,14.594 C61.711,19.009 56.47,24.391 51.525,30.586 C43.724,31.768 36.477,33.608 30.026,35.981 C29.095,22.949 32.093,12.782 39,8.794 C41.172,7.54 43.72,6.904 46.573,6.904 L46.573,0.904 C42.729,0.904 39.16,1.773 36,3.597 C26.486,9.091 22.619,22.126 24.184,38.366 C9.349,45.114 0,54.953 0,65.909 C0,76.869 9.357,86.709 24.202,93.457 C22.615,109.734 26.472,122.804 36.001,128.304 C39.16,130.128 42.725,130.997 46.568,130.997 C54.292,130.997 63.128,127.484 71.992,121.162 C80.859,127.486 89.7,131 97.428,131 C101.271,131 104.84,130.131 108,128.306 C117.53,122.803 121.391,109.73 119.804,93.451 C134.643,86.702 144,76.866 144,65.909 L144,65.909 Z"></path>
<path d="M71.5482968,65.9688173 C69.154234,70.0775221 66.7769144,74.1965782 64.47,78.356 C63.271,80.497 62.097,82.654 60.931,84.815 C59.794,86.993 58.615,89.146 57.518,91.348 C58.905,89.316 60.21,87.237 61.558,85.183 C62.876,83.111 64.187,81.035 65.472,78.944 C67.9377029,74.9691498 70.3353266,70.9543598 72.7167607,66.929899 L99.741,65.952 L72.7137677,64.9739927 C70.3372065,60.9888441 67.9401733,57.0159615 65.476,53.083 C64.19,51.014 62.88,48.96 61.562,46.909 C60.212,44.879 58.907,42.821 57.518,40.813 C58.613,42.995 59.792,45.128 60.926,47.286 C62.093,49.426 63.268,51.561 64.466,53.682 C66.7739163,57.7980908 69.150305,61.8738486 71.5470195,65.9374346 L71.5387573,65.952 L71.5482968,65.9688173 Z M56.341,93.07 L40.684,65.952 L56.344,38.834 L87.657,38.831 L103.315,65.952 L87.659,93.073 L56.341,93.07 Z"></path>
<path d="M118.954,87.235 C117.784,80.465 115.755,73.277 112.881,65.938 C115.756,58.592 117.784,51.398 118.952,44.624 C130.693,50.324 137.999,57.982 138,65.909 C138,73.901 130.708,81.552 118.954,87.235 L118.954,87.235 Z M105,123.11 C102.829,124.364 100.281,125 97.428,125 C91.361,125 84.259,122.231 76.981,117.308 C82.313,112.871 87.579,107.457 92.545,101.223 C100.313,100.042 107.527,98.206 113.955,95.844 C114.897,108.916 111.902,119.125 105,123.11 L105,123.11 Z M46.568,124.997 C43.717,124.997 41.171,124.362 39.001,123.108 C32.104,119.127 29.109,108.918 30.052,95.846 C36.472,98.205 43.677,100.037 51.435,101.218 C56.402,107.454 61.671,112.871 67.005,117.309 C59.731,122.23 52.633,124.997 46.568,124.997 L46.568,124.997 Z M6,65.909 C6,57.957 13.296,50.305 25.036,44.612 C26.204,51.397 28.236,58.604 31.117,65.963 C28.25,73.288 26.224,80.464 25.055,87.221 C13.301,81.532 6,73.877 6,65.909 L6,65.909 Z M46.881,37.496 C34.717,58.616 46.806,37.626 34.785,58.498 C32.879,52.829 31.528,47.322 30.729,42.143 C35.601,40.255 41.033,38.677 46.881,37.496 L46.881,37.496 Z M72.005,18.28 C76.117,21.584 80.221,25.531 84.168,30.008 C60.223,29.96 84.478,30.009 59.886,29.959 C63.82,25.502 67.909,21.571 72.005,18.28 L72.005,18.28 Z M109.214,58.468 C97.071,37.445 109.179,58.408 97.101,37.496 C102.949,38.678 108.383,40.259 113.26,42.152 C112.461,47.319 111.113,52.813 109.214,58.468 L109.214,58.468 Z M109.235,73.475 C111.119,79.096 112.457,84.56 113.251,89.702 C108.396,91.576 102.987,93.144 97.16,94.319 C109.053,73.79 96.994,94.605 109.235,73.475 L109.235,73.475 Z M34.764,73.5 C47.04,94.67 34.992,73.892 46.834,94.313 C41.014,93.136 35.61,91.567 30.758,89.691 C31.551,84.56 32.886,79.107 34.764,73.5 L34.764,73.5 Z M84.108,101.947 C80.175,106.402 76.088,110.332 71.993,113.623 C67.883,110.321 63.78,106.375 59.835,101.902 C83.777,101.946 59.54,101.901 84.108,101.947 L84.108,101.947 Z M86.649,95.951 L71.75,95.924 L56.853,95.896 C56.122,95.812 55.399,95.718 54.677,95.622 C54.474,95.357 54.27,95.096 54.068,94.829 L38.235,67.525 C38.011,67.004 37.8,66.484 37.584,65.963 C37.731,65.609 37.871,65.255 38.022,64.9 L53.936,37.268 C54.21,36.903 54.487,36.545 54.764,36.185 C55.365,36.105 55.965,36.024 56.572,35.953 L72.262,35.984 L87.951,36.016 C88.387,36.069 88.816,36.131 89.248,36.188 C89.475,36.483 89.702,36.775 89.927,37.073 L105.949,64.813 C106.109,65.189 106.259,65.564 106.414,65.939 C106.2,66.457 105.989,66.975 105.766,67.494 L89.983,94.737 C89.757,95.038 89.529,95.331 89.301,95.628 C88.424,95.744 87.54,95.853 86.649,95.951 L86.649,95.951 Z M144,65.909 C143.999,54.951 134.647,45.112 119.808,38.364 C121.377,22.123 117.514,9.088 108.001,3.596 C104.843,1.774 101.279,0.905 97.437,0.905 L97.438,6.905 C100.286,6.905 102.831,7.54 105.001,8.793 C111.904,12.778 114.898,22.945 113.965,35.978 C107.519,33.608 100.279,31.77 92.485,30.588 C87.54,24.392 82.299,19.008 76.992,14.593 C84.266,9.672 91.365,6.906 97.435,6.905 L97.437,0.905 L97.434,0.905 C89.71,0.906 80.873,4.42 72.008,10.742 C63.141,4.418 54.301,0.904 46.573,0.904 L46.573,6.904 C52.639,6.904 59.74,9.672 67.017,14.594 C61.711,19.009 56.47,24.391 51.525,30.586 C43.724,31.768 36.477,33.608 30.026,35.981 C29.095,22.949 32.093,12.782 39,8.794 C41.172,7.54 43.72,6.904 46.573,6.904 L46.573,0.904 C42.729,0.904 39.16,1.773 36,3.597 C26.486,9.091 22.619,22.126 24.184,38.366 C9.349,45.114 0,54.953 0,65.909 C0,76.869 9.357,86.709 24.202,93.457 C22.615,109.734 26.472,122.804 36.001,128.304 C39.16,130.128 42.725,130.997 46.568,130.997 C54.292,130.997 63.128,127.484 71.992,121.162 C80.859,127.486 89.7,131 97.428,131 C101.271,131 104.84,130.131 108,128.306 C117.53,122.803 121.391,109.73 119.804,93.451 C134.643,86.702 144,76.866 144,65.909 L144,65.909 Z"/>
<path d="M71.5482968,65.9688173 C69.154234,70.0775221 66.7769144,74.1965782 64.47,78.356 C63.271,80.497 62.097,82.654 60.931,84.815 C59.794,86.993 58.615,89.146 57.518,91.348 C58.905,89.316 60.21,87.237 61.558,85.183 C62.876,83.111 64.187,81.035 65.472,78.944 C67.9377029,74.9691498 70.3353266,70.9543598 72.7167607,66.929899 L99.741,65.952 L72.7137677,64.9739927 C70.3372065,60.9888441 67.9401733,57.0159615 65.476,53.083 C64.19,51.014 62.88,48.96 61.562,46.909 C60.212,44.879 58.907,42.821 57.518,40.813 C58.613,42.995 59.792,45.128 60.926,47.286 C62.093,49.426 63.268,51.561 64.466,53.682 C66.7739163,57.7980908 69.150305,61.8738486 71.5470195,65.9374346 L71.5387573,65.952 L71.5482968,65.9688173 Z M56.341,93.07 L40.684,65.952 L56.344,38.834 L87.657,38.831 L103.315,65.952 L87.659,93.073 L56.341,93.07 Z"/>
</g>
</svg>
);

View File

@ -14,21 +14,21 @@
"codemirror": "^5.14.2",
"history": "^2.1.1",
"react": "^15.2.0",
"react-css-themr": "^1.1.2",
"react-css-themr": "^1.2.0",
"react-dom": "^15.2.0",
"react-router": "^2.4.0"
},
"devDependencies": {
"autoprefixer": "^6.3.6",
"babel-core": "^6.8.0",
"autoprefixer": "^6.4.0",
"babel-core": "^6.13.2",
"babel-eslint": "^6.0.4",
"babel-loader": "^6.2.4",
"babel-plugin-react-transform": "^2.0.2",
"babel-preset-es2015": "^6.6.0",
"babel-preset-es2015": "^6.13.2",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"babel-preset-stage-2": "^6.5.0",
"cross-env": "^1.0.7",
"babel-preset-stage-2": "^6.13.0",
"cross-env": "^2.0.0",
"css-loader": "^0.23.1",
"express": "^4.13.4",
"extract-text-webpack-plugin": "^1.0.1",
@ -44,7 +44,7 @@
"react-transform-catch-errors": "^1.0.2",
"react-transform-hmr": "^1.0.4",
"redbox-react": "^1.2.4",
"sass-loader": "^3.2.0",
"sass-loader": "^4.0.0",
"style-loader": "^0.13.1",
"transfer-webpack-plugin": "^0.1.4",
"webpack": "^1.13.0",

168
index.d.ts vendored
View File

@ -1,57 +1,115 @@
// Type definitions for react-toolbox 0.16.2
// Project: http://react-toolbox.com/
// Definitions by: @xogeny (Michael M. Tiller), @hsrobflavorus (Robert Parker), @ixrock (Roman Karlov)
/* CHANGES
* 26/05/2015 Export components by names (not only defaults), added missing components exports
* 06/05/2016: Refactor into external module declarations (no more 'declare module ...')
* 04/27/2016: Updates for 0.16.2, added <Chip>, <Overlay>, and ActivableRendererFactory definitions, misc. tweaks and fixes.
* 02/03/2016:
* Fixed for TypeScript 1.8.0 stricter var declaration requirements (move `declare var ...` inside each individual module).
* Removed triple-slash reference to React to fix npm install compatibility (you'll need to make sure you're referencing react.d.ts somewhere in your project!).
* Hopefully fixed the default exports where applicable
* 01/13/2016: Minor changes, add a few missing props, add IconButton to react-toolbox/lib/button
* 12/21/2015: Fix "import * as Input from 'react-toolbox/lib/input'" style imports, which now correctly import only the necessary component(s).
* NOTE that you must use "import * as {Component Name}" not just "import {Component Name}" for this to work.
* 12/20/2015: Should be compatible with 0.14.0. Refactor modules into 'react-toolbox/lib/*' format.
Unfortunately importing them directly in that manner doesn't seem to work
i.e. "import Input from 'react-toolbox/lib/input'" resolves to undefined, whereas "import { Input } from 'react-toolbox'" works fine!
... Any ideas welcome!
* 12/20/2015: Add AppBar, Avatar, and refactor Card and its child Components to match the documentation.
* 12/18/2015: Update to react-toolbox 0.13.1 (from 0.12.11)
* 12/18/2015: Use JSDoc-style comments to provide Intellisense where supported
*/
/*
MISSING COMPONENTS (Contributions welcome)
* Ripple HOC
*/
// Type definitions for react-toolbox
// Project: https://github.com/react-toolbox/react-toolbox
// Definitions by: Per Bergqwist <https://github.com/normano64>
export { default as ActivableRendererFactory } from 'react-toolbox/lib/hoc/ActivableRenderer';
import { AppBar } from "react-toolbox/lib/app_bar";
import { Autocomplete } from "react-toolbox/lib/autocomplete";
import { Avatar } from "react-toolbox/lib/avatar";
import { Button, IconButton } from "react-toolbox/lib/button";
import { Card, CardTitle, CardMedia, CardText, CardActions } from "react-toolbox/lib/card";
import { Checkbox } from "react-toolbox/lib/checkbox";
import { Chip } from "react-toolbox/lib/chip";
import { DatePicker } from "react-toolbox/lib/date_picker";
import { Dialog } from "react-toolbox/lib/dialog";
import { Drawer } from "react-toolbox/lib/drawer";
import { Dropdown } from "react-toolbox/lib/dropdown";
import { FontIcon } from "react-toolbox/lib/font_icon";
import { Input } from "react-toolbox/lib/input";
import { Layout, Panel, NavDrawer, Sidebar } from "react-toolbox/lib/layout";
import { Link } from "react-toolbox/lib/link";
import { List, ListCheckbox, ListItem, ListDivider, ListSubHeader } from "react-toolbox/lib/list";
import { Menu, MenuDivider, MenuItem, IconMenu } from "react-toolbox/lib/menu";
import { Navigation } from "react-toolbox/lib/navigation";
import { ProgressBar } from "react-toolbox/lib/progress_bar";
import { RadioGroup, RadioButton } from "react-toolbox/lib/radio";
import { Ripple } from "react-toolbox/lib/ripple";
import { Slider } from "react-toolbox/lib/slider";
import { Snackbar } from "react-toolbox/lib/snackbar";
import { Switch } from "react-toolbox/lib/switch";
import { Tabs, Tab } from "react-toolbox/lib/tabs";
import { TimePicker } from "react-toolbox/lib/time_picker";
import { Tooltip } from "react-toolbox/lib/tooltip";
export { AppBar } from 'react-toolbox/lib/app_bar'
export { Autocomplete } from 'react-toolbox/lib/autocomplete'
export { Avatar } from 'react-toolbox/lib/avatar'
export { Button, IconButton } from 'react-toolbox/lib/button'
export { Card, CardTitle, CardMedia, CardText, CardActions } from 'react-toolbox/lib/card'
export { Checkbox } from 'react-toolbox/lib/checkbox'
export { Chip } from 'react-toolbox/lib/chip'
export { DatePicker } from 'react-toolbox/lib/date_picker'
export { Dialog } from 'react-toolbox/lib/dialog'
export { Drawer } from 'react-toolbox/lib/drawer'
export { Dropdown } from 'react-toolbox/lib/dropdown'
export { FontIcon } from 'react-toolbox/lib/font_icon'
export { Input } from 'react-toolbox/lib/input'
export { Layout, Panel, NavDrawer, Sidebar } from 'react-toolbox/lib/layout'
export { Link } from 'react-toolbox/lib/link'
export { List, ListCheckbox, ListItem, ListDivider, ListSubHeader } from 'react-toolbox/lib/list'
export { Menu, MenuDivider, MenuItem, IconMenu } from 'react-toolbox/lib/menu'
export { Navigation } from 'react-toolbox/lib/navigation'
export { Overlay } from 'react-toolbox/lib/overlay'
export { ProgressBar } from 'react-toolbox/lib/progress_bar'
export { RadioButton, RadioGroup } from 'react-toolbox/lib/radio'
export { Ripple } from 'react-toolbox/lib/ripple'
export { Slider } from 'react-toolbox/lib/slider'
export { Snackbar } from 'react-toolbox/lib/snackbar'
export { Switch } from 'react-toolbox/lib/switch'
export { Tabs, Tab } from 'react-toolbox/lib/tabs'
export { TimePicker } from 'react-toolbox/lib/time_picker'
export { default as Tooltip } from 'react-toolbox/lib/tooltip'
import { AppBarTheme } from "react-toolbox/lib/app_bar";
import { AutocompleteTheme } from "react-toolbox/lib/autocomplete";
import { AvatarTheme } from "react-toolbox/lib/avatar";
import { ButtonTheme, IconButtonTheme } from "react-toolbox/lib/button";
import { CardTheme, CardTitleTheme, CardMediaTheme, CardTextTheme, CardActionsTheme } from "react-toolbox/lib/card";
import { CheckboxTheme } from "react-toolbox/lib/checkbox";
import { ChipTheme } from "react-toolbox/lib/chip";
import { DatePickerTheme } from "react-toolbox/lib/date_picker";
import { DialogTheme } from "react-toolbox/lib/dialog";
import { DrawerTheme } from "react-toolbox/lib/drawer";
import { DropdownTheme } from "react-toolbox/lib/dropdown";
import { InputTheme } from "react-toolbox/lib/input";
import { LayoutTheme, PanelTheme, NavDrawerTheme, SidebarTheme } from "react-toolbox/lib/layout";
import { LinkTheme } from "react-toolbox/lib/link";
import { ListTheme, ListCheckboxTheme, ListItemTheme, ListDividerTheme, ListSubHeaderTheme } from "react-toolbox/lib/list";
import { MenuTheme, MenuDividerTheme, MenuItemTheme, IconMenuTheme } from "react-toolbox/lib/menu";
import { NavigationTheme } from "react-toolbox/lib/navigation";
import { ProgressBarTheme } from "react-toolbox/lib/progress_bar";
import { RadioButtonTheme } from "react-toolbox/lib/radio";
import { RippleTheme } from "react-toolbox/lib/ripple";
import { SliderTheme } from "react-toolbox/lib/slider";
import { SnackbarTheme } from "react-toolbox/lib/snackbar";
import { SwitchTheme } from "react-toolbox/lib/switch";
import { TabsTheme, TabTheme } from "react-toolbox/lib/tabs";
import { TimePickerTheme } from "react-toolbox/lib/time_picker";
import { TooltipTheme } from "react-toolbox/lib/tooltip";
export {
AppBar,
Autocomplete,
Avatar,
Button, IconButton,
Card, CardTitle, CardMedia, CardText, CardActions,
Checkbox,
Chip,
DatePicker,
Dialog,
Drawer,
Dropdown,
FontIcon,
Input,
Layout, Panel, NavDrawer, Sidebar,
Link,
List, ListCheckbox, ListItem, ListDivider, ListSubHeader,
Menu, MenuDivider, MenuItem, IconMenu,
Navigation,
ProgressBar,
RadioGroup, RadioButton,
Ripple,
Slider,
Snackbar,
Switch,
Tabs, Tab,
TimePicker,
Tooltip,
AppBarTheme,
AutocompleteTheme,
AvatarTheme,
ButtonTheme, IconButtonTheme,
CardTheme, CardTitleTheme, CardMediaTheme, CardTextTheme, CardActionsTheme,
CheckboxTheme,
ChipTheme,
DatePickerTheme,
DialogTheme,
DrawerTheme,
DropdownTheme,
InputTheme,
LayoutTheme, PanelTheme, NavDrawerTheme, SidebarTheme,
LinkTheme,
ListTheme, ListCheckboxTheme, ListItemTheme, ListDividerTheme, ListSubHeaderTheme,
MenuTheme, MenuDividerTheme, MenuItemTheme, IconMenuTheme,
NavigationTheme,
ProgressBarTheme,
RadioButtonTheme,
RippleTheme,
SliderTheme,
SnackbarTheme,
SwitchTheme,
TabsTheme, TabTheme,
TimePickerTheme,
TooltipTheme
}

View File

@ -7,7 +7,7 @@
@mixin typo-display-4($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 11.2rem;
font-size: $unit * 11.2;
font-weight: 300;
line-height: 1;
letter-spacing: -.04em;
@ -19,7 +19,7 @@
@mixin typo-display-3($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 5.6rem;
font-size: $unit * 5.6;
font-weight: 400;
line-height: 1.35;
letter-spacing: -.02em;
@ -31,9 +31,9 @@
@mixin typo-display-2($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 4.5rem;
font-size: $unit * 4.5;
font-weight: 400;
line-height: 4.8rem;
line-height: $unit * 4.8;
@if $color-contrast {
opacity: .54;
@ -42,9 +42,9 @@
@mixin typo-display-1($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 3.4rem;
font-size: $unit * 3.4;
font-weight: 400;
line-height: 4rem;
line-height: $unit * 4;
@if $color-contrast {
opacity: .54;
@ -53,9 +53,9 @@
@mixin typo-headline($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 2.4rem;
font-size: $unit * 2.4;
font-weight: 400;
line-height: 3.2rem;
line-height: $unit * 3.2;
-moz-osx-font-smoothing: grayscale;
@if $color-contrast {
@ -65,7 +65,7 @@
@mixin typo-title($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 2rem;
font-size: $unit * 2;
font-weight: 500;
line-height: 1;
letter-spacing: .02em;
@ -77,9 +77,9 @@
@mixin typo-subhead($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.6rem;
font-size: $unit * 1.6;
font-weight: 400;
line-height: 2.4rem;
line-height: $unit * 2.4;
letter-spacing: .04em;
@if $color-contrast {
@ -89,9 +89,9 @@
@mixin typo-subhead-2($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.6rem;
font-size: $unit * 1.6;
font-weight: 400;
line-height: 2.8rem;
line-height: $unit * 2.8;
letter-spacing: .04em;
@if $color-contrast {
@ -101,8 +101,8 @@
@mixin typo-body-2($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
line-height: 2.4rem;
font-size: $unit * 1.4;
line-height: $unit * 2.4;
letter-spacing: 0;
@if $use-preferred {
@ -118,9 +118,9 @@
@mixin typo-body-1($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 400;
line-height: 2.4rem;
line-height: $unit * 2.4;
letter-spacing: 0;
@if $color-contrast {
@ -130,7 +130,7 @@
@mixin typo-caption($color-contrast: false, $use-preferred: false) {
@include typo-preferred-font($use-preferred);
font-size: 1.2rem;
font-size: $unit * 1.2;
font-weight: 400;
line-height: 1;
letter-spacing: 0;
@ -143,7 +143,7 @@
@mixin typo-blockquote($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
position: relative;
font-size: 2.4rem;
font-size: $unit * 2.4;
font-style: italic;
font-weight: 300;
line-height: 1.35;
@ -167,7 +167,7 @@
@mixin typo-menu($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 500;
line-height: 1;
letter-spacing: 0;
@ -179,7 +179,7 @@
@mixin typo-button($color-contrast: false, $use-preferred: true) {
@include typo-preferred-font($use-preferred);
font-size: 1.4rem;
font-size: $unit * 1.4;
font-weight: 500;
line-height: 1;
text-transform: uppercase;

41
lib/app_bar/index.d.ts vendored Normal file
View File

@ -0,0 +1,41 @@
import __ReactToolbox from "../index.d.ts";
export interface AppBarTheme {
/**
* Used for the component root element.
*/
appBar?: string;
/**
* Added to the root element when the app bar is fixed.
*/
fixed?: string;
/**
* Added to the root element when the app bar is flat.
*/
flat?: string;
}
interface AppBarProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Determine if the bar should have position fixed or relative.
* @default false
*/
fixed?: boolean;
/**
* If true, the AppBar shows a shadow.
* @default false
*/
flat?: boolean;
/**
* Classnames object defining the component style.
*/
theme?: AppBarTheme;
}
export class AppBar extends __React.Component<AppBarProps, {}> { }
export default AppBar;

View File

@ -104,6 +104,9 @@ var factory = function factory(Chip, Input) {
var target = _this.state.active;
if (!target) {
target = [].concat(_toConsumableArray(_this.suggestions().keys()))[0];
if (!target && _this.props.allowCreate) {
target = _this.state.query;
}
_this.setState({ active: target });
}
_this.select(target, event);
@ -118,8 +121,12 @@ var factory = function factory(Chip, Input) {
if (index >= suggestionsKeys.length) index = 0;
_this.setState({ active: suggestionsKeys[index] });
}
}, _this.handleSuggestionHover = function (key) {
_this.setState({ active: key });
}, _this.handleSuggestionHover = function (event) {
_this.setState({ active: event.target.id });
}, _this.select = function (event) {
_events2.default.pauseEvent(event);
var values = _this.values(_this.props.value);
_this.handleChange([event.target.id].concat(_toConsumableArray(values.keys())), event);
}, _temp), _possibleConstructorReturn(_this, _ret);
}
@ -139,7 +146,6 @@ var factory = function factory(Chip, Input) {
var direction = this.calculateDirection();
if (this.state.direction !== direction) {
this.setState({ direction: direction });
return false;
}
}
return true;
@ -159,13 +165,18 @@ var factory = function factory(Chip, Input) {
}, {
key: 'query',
value: function query(key) {
return !this.props.multiple && key ? this.source().get(key) : '';
var query_value = '';
if (!this.props.multiple && key) {
var source_value = this.source().get(key);
query_value = source_value ? source_value : key;
}
return query_value;
}
}, {
key: 'suggestions',
value: function suggestions() {
var suggest = new Map();
var query = this.state.query.toLowerCase().trim() || '';
var query = (this.state.query || (!this.props.multiple ? this.props.value : '')).toLowerCase().trim() || '';
var values = this.values();
var source = this.source();
@ -203,40 +214,40 @@ var factory = function factory(Chip, Input) {
}
}
} else if (query && !this.state.showAllSuggestions) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = source[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _step2$value = _slicedToArray(_step2.value, 2);
var key = _step2$value[0];
var value = _step2$value[1];
if (this.matches(value.toLowerCase().trim(), query)) {
suggest.set(key, value);
}
}
// When multiple is false, suggest all values when showAllSuggestions is true
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
for (var _iterator2 = source[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _step2$value = _slicedToArray(_step2.value, 2);
var key = _step2$value[0];
var value = _step2$value[1];
if (this.matches(value.toLowerCase().trim(), query)) {
suggest.set(key, value);
}
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
// When multiple is false, suggest all values when showAllSuggestions is true
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
if (_didIteratorError2) {
throw _iteratorError2;
}
}
} else {
suggest = source;
}
}
} else {
suggest = source;
}
return suggest;
}
@ -307,13 +318,6 @@ var factory = function factory(Chip, Input) {
return valueMap;
}
}, {
key: 'select',
value: function select(key, event) {
_events2.default.pauseEvent(event);
var values = this.values(this.props.value);
this.handleChange([key].concat(_toConsumableArray(values.keys())), event);
}
}, {
key: 'unselect',
value: function unselect(key, event) {
@ -371,10 +375,11 @@ var factory = function factory(Chip, Input) {
return _react2.default.createElement(
'li',
{
id: key,
key: key,
className: className,
onMouseDown: _this3.select.bind(_this3, key),
onMouseOver: _this3.handleSuggestionHover.bind(_this3, key)
onMouseDown: _this3.select,
onMouseOver: _this3.handleSuggestionHover
},
value
);
@ -391,17 +396,16 @@ var factory = function factory(Chip, Input) {
key: 'render',
value: function render() {
var _props = this.props;
var allowCreate = _props.allowCreate;
var error = _props.error;
var label = _props.label;
var source = _props.source;
var suggestionMatch = _props.suggestionMatch;
var //eslint-disable-line no-unused-vars
selectedPosition = _props.selectedPosition;
var selectedPosition = _props.selectedPosition;
var showSuggestionsWhenValueIsSet = _props.showSuggestionsWhenValueIsSet;
var //eslint-disable-line no-unused-vars
theme = _props.theme;
var theme = _props.theme;
var other = _objectWithoutProperties(_props, ['error', 'label', 'source', 'suggestionMatch', 'selectedPosition', 'showSuggestionsWhenValueIsSet', 'theme']);
var other = _objectWithoutProperties(_props, ['allowCreate', 'error', 'label', 'source', 'suggestionMatch', 'selectedPosition', 'showSuggestionsWhenValueIsSet', 'theme']);
var className = (0, _classnames5.default)(theme.autocomplete, _defineProperty({}, theme.focus, this.state.focus), this.props.className);
@ -431,6 +435,7 @@ var factory = function factory(Chip, Input) {
}(_react.Component);
Autocomplete.propTypes = {
allowCreate: _react.PropTypes.bool,
className: _react.PropTypes.string,
direction: _react.PropTypes.oneOf(['auto', 'up', 'down']),
disabled: _react.PropTypes.bool,
@ -457,6 +462,7 @@ var factory = function factory(Chip, Input) {
value: _react.PropTypes.any
};
Autocomplete.defaultProps = {
allowCreate: false,
className: '',
direction: 'auto',
selectedPosition: 'above',

105
lib/autocomplete/index.d.ts vendored Normal file
View File

@ -0,0 +1,105 @@
import __ReactToolbox from "../index.d.ts";
export interface AutocompleteTheme {
/**
* Used for a suggestion when it's active.
*/
active?: string;
/**
* Used for the root element.
*/
autocomplete?: string;
/**
* Used when the input is focused.
*/
focus?: string;
/**
* Used to style the Input component.
*/
input?: string;
/**
* Used for the label.
*/
label?: string;
/**
* Used to style each suggestion.
*/
suggestion?: string;
/**
* Used to style the suggestions container.
*/
suggestions?: string;
/**
* Used for the suggestions when it's opening to the top.
*/
up?: string;
/**
* Classname used for a single value.
*/
value?: string;
/**
* Classname used for the values container.
*/
values?: string;
}
interface AutocompleteProps extends __ReactToolbox.Props {
/**
* Determines the opening direction. It can be auto, up or down.
* @default auto
*/
direction?: "auto" | "up" | "down";
/**
* If true, component will be disabled.
*/
disabled?: boolean;
/**
* Sets the error string for the internal input element.
* @default false
*/
error?: string;
/**
* The text string to use for the floating label element.
*/
label?: string;
/**
* If true, component can hold multiple values.
*/
multiple?: boolean;
/**
* Callback function that is fired when the components's value changes.
* @default auto
*/
onChange?: Function;
/**
* Determines if the selected list is shown above or below input. It can be above or below.
* @default above
*/
selectedPosition?: "above" | "below";
/**
* If true, the list of suggestions will not be filtered when a value is selected.
* @default false
*/
showSuggestionsWHenValueIsSet?: boolean;
/**
* Object of key/values or array representing all items suggested.
*/
source?: any;
/**
* Determines how suggestions are supplied.
* @default start
*/
suggestionMatch?: "start" | "anywhere" | "word";
/**
* Classnames object defining the component style.
*/
theme?: AutocompleteTheme;
/**
* Value or array of values currently selected component.
*/
value?: any;
}
export class Autocomplete extends __React.Component<AutocompleteProps, {}> { }
export default Autocomplete;

47
lib/avatar/index.d.ts vendored Normal file
View File

@ -0,0 +1,47 @@
import __ReactToolbox from "../index.d.ts";
export interface AvatarTheme {
/**
* Used for the root class of the element.
*/
avatar?: string;
/**
* Added to the root element when the component has image.
*/
image?: string;
/**
* Used for the root element if the component shows the letter.
*/
letter?: string;
}
interface AvatarProps extends __ReactToolbox.Props {
/**
* Children to pass through the component.
*/
children?: __React.ReactNode;
/**
* Set to true if your image is not squared so it will be used as a cover for the element.
*/
cover?: boolean;
/**
* A key to identify an Icon from Material Design Icons or a custom Icon Element.
*/
icon?: __React.ReactNode | string;
/**
* An image source or an image element.
*/
image?: __React.ReactNode | string;
/**
* Classnames object defining the component style.
*/
theme?: AvatarTheme;
/**
* A title for the image. If no image is provided, the first letter will be displayed as the avatar.
*/
title?: string;
}
export class Avatar extends __React.Component<AvatarProps, {}> { }
export default Avatar;

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