@project-r/styleguide
Advanced tools
Comparing version 0.1.7 to 0.2.0
{ | ||
"name": "@project-r/styleguide", | ||
"version": "0.1.7", | ||
"version": "0.2.0", | ||
"dependencies": { | ||
"react-autocomplete": "^1.4.1" | ||
}, | ||
"peerDependencies": { | ||
@@ -18,2 +21,3 @@ "glamor": "^2.20.24", | ||
"react-dom": "^15.4.2", | ||
"react-maskedinput": "^3.3.4", | ||
"react-scripts": "0.9.3", | ||
@@ -20,0 +24,0 @@ "rimraf": "^2.6.1" |
@@ -17,2 +17,96 @@ ## Felder | ||
### Change and Validation | ||
`onChange` gets called with the following arguments: | ||
- `event: SyntheticEvent` | ||
- `value: String` | ||
- `shouldValidate: Boolean` | ||
`shouldValidate` is a hint to run any necessary validations, if they fail an `error` should be set on the field. | ||
`shouldValidate` becomes `true` after on the first blur if the field has been changed. An additional `onChange` is triggered when is happens. | ||
```react | ||
state: { | ||
value: '' | ||
} | ||
--- | ||
<Field | ||
label='Name' | ||
value={state.value} | ||
error={state.error} | ||
onChange={(event, value, shouldValidate) => { | ||
setState({ | ||
error: ( | ||
shouldValidate && | ||
!value.trim().length && | ||
'Geben sie Ihren Namen an' | ||
), | ||
value: value | ||
}) | ||
}} /> | ||
``` | ||
### Autocomplete | ||
You can provide a `items` array which will be suggested to the user. Powered by `react-autocomplete`. | ||
```react|span-3 | ||
<AutocompleteField | ||
label='Land' | ||
items={[ | ||
'Schweiz', | ||
'Deutschland', | ||
'Österreich' | ||
]} /> | ||
``` | ||
```react|span-3 | ||
state: { | ||
value: '' | ||
} | ||
--- | ||
<AutocompleteField | ||
label='Monat' | ||
items={[ | ||
'01', '02', '03', '04', '05', '06', | ||
'07', '08', '09', '10', '11', '12' | ||
]} | ||
value={state.value} | ||
error={state.error} | ||
onChange={(event, value, shouldValidate) => { | ||
setState({ | ||
error: ( | ||
shouldValidate && | ||
!value.trim().length && | ||
'Monat fehlt' | ||
), | ||
value: value | ||
}) | ||
}} /> | ||
``` | ||
### Integration with Third-Party | ||
Integration is possible with any input component which support `value`, `onChange`, `onFocus`, `onBlur` and `className`. To do so use a custom `renderInput`, see example below. | ||
#### Example with `react-maskedinput` | ||
```react | ||
state: { | ||
value: '4242424242424242' | ||
} | ||
--- | ||
<Field | ||
value={state.value} | ||
onChange={(_, value) => setState({value})} | ||
label='Kreditkarten-Nummer' | ||
renderInput={props => <MaskedInput {...props} placeholderChar=" " mask="1111 1111 1111 1111" />} /> | ||
``` | ||
`npm i react-maskedinput --save` | ||
`import MaskedInput from 'react-maskedinput'` | ||
## Zahlungsmethoden | ||
@@ -19,0 +113,0 @@ |
@@ -1,2 +0,2 @@ | ||
import React, {Component} from 'react' | ||
import React, {Component, PropTypes} from 'react' | ||
import {css, merge, simulate} from 'glamor' | ||
@@ -9,2 +9,3 @@ import * as colors from '../../theme/colors' | ||
const lineHeight = 20 | ||
export const fieldHeight = 40 | ||
@@ -18,3 +19,3 @@ const fieldStyle = css({ | ||
textDecoration: 'none', | ||
height: 40, | ||
height: fieldHeight, | ||
fontSize: 22, | ||
@@ -25,2 +26,3 @@ boxSizing: 'border-box', | ||
borderBottom: `solid ${colors.disabled} ${borderWidth}px`, | ||
borderRadius: 0, | ||
color: colors.text, | ||
@@ -37,8 +39,2 @@ ':focus': { | ||
}) | ||
const errorMessageStyle = css({ | ||
display: 'inline-block', | ||
color: colors.error, | ||
margin: '5px 0', | ||
fontSize: 14 | ||
}) | ||
@@ -57,8 +53,8 @@ const containerStyle = css({ | ||
left: xPadding, | ||
top: yPadding + lineHeight + borderWidth, | ||
bottom: yPadding + borderWidth, | ||
color: colors.disabled, | ||
transition: 'top 200ms, font-size 200ms' | ||
transition: 'bottom 200ms, font-size 200ms' | ||
}) | ||
const labelTextTopStyle = css({ | ||
top: 0, | ||
bottom: fieldHeight, | ||
fontSize: 14 | ||
@@ -69,2 +65,5 @@ }) | ||
}) | ||
const labelTextErrorStyle = css({ | ||
color: colors.error | ||
}) | ||
@@ -75,3 +74,5 @@ class Field extends Component { | ||
this.state = { | ||
focused: false, | ||
isFocused: false, | ||
isValidating: false, | ||
isDirty: false, | ||
value: '' | ||
@@ -82,10 +83,16 @@ } | ||
render () { | ||
const {onChange, name, type, simulate: sim, label, error} = this.props | ||
const { | ||
onChange, | ||
name, type, simulate: sim, | ||
label, error, | ||
renderInput | ||
} = this.props | ||
let simulations = {} | ||
let {focused} = this.state | ||
let simulationClassName | ||
let {isFocused} = this.state | ||
if (sim) { | ||
focused = sim.indexOf('focus') !== -1 | ||
simulations = simulate(sim) | ||
isFocused = sim.indexOf('focus') !== -1 | ||
simulationClassName = simulate(sim).toString() | ||
} | ||
const {isValidating, isDirty} = this.state | ||
@@ -95,4 +102,8 @@ const value = this.props.value || this.state.value | ||
const hasError = !!error | ||
const labelStyle = (focused || value) | ||
? merge(labelTextStyle, labelTextTopStyle, focused && labelTextFocusedStyle) | ||
const labelStyle = (isFocused || value || hasError) | ||
? merge( | ||
labelTextStyle, labelTextTopStyle, | ||
isFocused && labelTextFocusedStyle, | ||
hasError && labelTextErrorStyle | ||
) | ||
: labelTextStyle | ||
@@ -105,14 +116,33 @@ const fStyle = hasError | ||
<label {...containerStyle}> | ||
<input name={name} type={type} ref={this.inputRef} | ||
onChange={onChange || ((event) => { | ||
{renderInput({ | ||
name, | ||
type, | ||
ref: this.inputRef, | ||
onChange: (event) => { | ||
let v = event.target.value | ||
if (onChange) { | ||
onChange(event, v, isValidating) | ||
this.setState(() => ({isDirty: true})) | ||
} else { | ||
this.setState(() => ({isDirty: true, value: v})) | ||
} | ||
}, | ||
value, | ||
onFocus: () => this.setState(() => ({isFocused: true})), | ||
onBlur: (event) => { | ||
const v = event.target.value | ||
this.setState(() => ({value: v})) | ||
})} | ||
value={value} | ||
onFocus={() => this.setState(() => ({focused: true}))} | ||
onBlur={() => this.setState(() => ({focused: false}))} | ||
{...fStyle} | ||
{...simulations} /> | ||
<span {...labelStyle}>{label}</span> | ||
{hasError && <span {...errorMessageStyle}>{error}</span>} | ||
if (!isValidating && onChange && isDirty) { | ||
onChange(event, v, true) | ||
} | ||
this.setState((state) => ({ | ||
isFocused: false, | ||
isValidating: state.isDirty | ||
})) | ||
}, | ||
className: [ | ||
fStyle.toString(), | ||
simulationClassName | ||
].filter(Boolean).join(' ') | ||
})} | ||
<span {...labelStyle}>{error || label}</span> | ||
</label> | ||
@@ -123,2 +153,13 @@ ) | ||
Field.propTypes = { | ||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), | ||
renderInput: PropTypes.func.isRequired | ||
} | ||
Field.defaultProps = { | ||
renderInput: props => ( | ||
<input {...props} /> | ||
) | ||
} | ||
export default Field |
@@ -5,3 +5,3 @@ import React from 'react' | ||
const linkRule = css(styles.link) | ||
export const linkRule = css(styles.link) | ||
export const A = ({children, ...props}) => ( | ||
@@ -8,0 +8,0 @@ <a {...props} {...linkRule}>{children}</a> |
@@ -87,3 +87,5 @@ import React from 'react'; | ||
Button: require('./components/Button'), | ||
Field: require('./components/Form/Field.js') | ||
Field: require('./components/Form/Field.js'), | ||
AutocompleteField: require('./components/Form/AutocompleteField.js'), | ||
MaskedInput: require('react-maskedinput') | ||
}, | ||
@@ -90,0 +92,0 @@ src: require('./components/Form/docs.md') |
@@ -10,3 +10,4 @@ import * as allColors from './theme/colors' | ||
export {default as Field} from './components/Form/Field' | ||
export {default as AutocompleteField} from './components/Form/AutocompleteField' | ||
export * from './components/Grid' | ||
export * from './components/Typography' |
9430484
139
1666
3
12
+ Addedreact-autocomplete@^1.4.1
+ Addeddom-scroll-into-view@1.0.1(transitive)
+ Addedreact@16.14.0(transitive)
+ Addedreact-autocomplete@1.8.1(transitive)
+ Addedreact-dom@16.14.0(transitive)
+ Addedscheduler@0.19.1(transitive)