@vcnkit/decorators v1.1.0
VCNKit/Decorators
@vcnkit/decorators
provides decorators for common functionality, like generating unique id=
's and managing hover, focused and collapsed state.
Installation
NPM
$ npm install --save @vcnkit/decorators
Yarn
$ yarn add @vcnkit/decorators
Usage
uniqueId
Passes a getId
function through props to the decorated component.getId(identifier: string)
returns the provided argument concatenated with a unique identifier for the component instance.
Subsequent calls to getId()
with the same identifier
within the same component instance will return the same generated id.
import * as React from 'react';
import { uniqueId } from '@vcnkit/decorators';
@uniqueId()
class SomeClassComponent extends React.Component {
render() {
const { getId, ...rest } = this.props;
return (
<div { ...rest }>
<label htmlFor={ getId('input') }>Label</label>
<input
id={ getId('input') }
type="text"
/>
</div>
);
}
}
Stateless components can use the decorator by wrapping itself.
import { uniqueId } from '@vcnkit/decorators';
const SomeComponent = ({ getId, ...rest ) => (
<div { ...rest }>
<label htmlFor={ getId('input') }>Label</label>
<input
id={ getId('input') }
type="text"
/>
</div>
);
export default uniqueId()(SomeComponent);
hoverable
Provides decorated components with a hovering
-prop. This prop tells the component if the user is currently hovering over the element.
import * as React from 'react';
import { hoverable } from '@vcnkit/decorators';
@hoverable()
class SomeClassComponent extends React.Component {
render() {
const { hovering, onMouseEnter, onMouseOut, ...rest } = this.props;
return (
<div
onMouseEnter={ onMouseEnter }
onMouseOut={ onMouseOut }
{ ...rest }
>
{ hovering ? 'User is hovering over this div!' : 'User is not hovering over this div' }
</div>
);
}
}
Stateless components can use the decorator by wrapping itself.
import { hoverable } from '@vcnkit/decorators';
const SomeComponent = ({ hovering, onMouseEnter, onMouseOut, ...rest ) => (
<div
onMouseEnter={ onMouseEnter }
onMouseOut={ onMouseOut }
{ ...rest }
>
{ hovering ? 'User is hovering over this element!' : 'User is not hovering over this element' }
</div>
);
export default hoverable()(SomeComponent);
focusable
Provides decorated components with a focused
-prop. This prop tells the component if the component currently has focus, or if focusOnChildFocus
is true if a child component has focus.
focusable(focusOnChildFocus: bool)
This decorator will not provide a tabIndex
prop, the component itself is responsible for giving itself or it's children a tabIndex
prop.
import * as React from 'react';
import { focusable } from '@vcnkit/decorators';
@focusable()
class SomeClassComponent extends React.Component {
render() {
const { focused, onFocus, onBlur, ...rest } = this.props;
return (
<div
onFocus={ onFocus }
onBlur={ onBlur }
tabIndex={ 0 }
{ ...rest }
>
{ focused ? 'Element has focus' : 'Element does not have focus' }
</div>
);
}
}
Stateless components can use the decorator by wrapping itself.
import { focusable } from '@vcnkit/decorators';
const SomeComponent = ({ hovering, onMouseEnter, onMouseOut, ...rest ) => (
<div
onFocus={ onFocus }
onBlur={ onBlur }
tabIndex={ 0 }
{ ...rest }
>
{ focused ? 'Element has focus' : 'Element does not have focus' }
</div>
);
export default focusable()(SomeComponent);
expandable
Provides decorated components with a collapsed
-prop. This can either by controlled by internal state or managed by parent components by attaching an onChange
-prop to the decorated component. The current and initial state can be defined by attaching an collapsed
-prop to the decorated component.
import * as React from 'react';
import { expandable } from '@vcnkit/decorators';
@expandale()
class SomeClassComponent extends React.Component {
render() {
const { collapsed, onChange, ...rest } = this.props;
return (
<div
{ ...rest }
>
<h1 onClick={ onChange }>Click here to toggle</h1>
{ !collapsed && <p>Expanded</p> }
</div>
)
}
}
Stateless components can use the decorator by wrapping itself.
import { expandable } from '@vcnkit/decorators';
const SomeComponent = ({ collapsed, onChange, ...rest ) => (
<div
{ ...rest }
>
<h1 onClick={ onChange }>Click here to toggle</h1>
{ !collapsed && <p>Expanded</p> }
</div>
);
export default expandable()(SomeComponent);
Decorating a class or stateless component with multiple decorators
If you want a component to have both the hoverable
and focusable
decorators, you can simply chain them.
import * as React from 'react';
import { hoverable, focusable } from '@vcnkit/decorators';
@focusable()
@hoverable()
class SomeClassComponent extends React.Component {
render() {
const { hovering, focused, ...rest } = this.props;
return (
<div
tabIndex={ 0 }
{ ...rest }
>
{ hovering ? 'User is hovering over this element!' : 'User is not hovering over this element' }
{ focused ? 'Element has focus' : 'Element does not have focus' }
</div>
)
}
}
And for stateless components
import { hoverable, focusable } from '@vcnkit/decorators';
/*
* ...rest will contain all the necessary event handlers. onMouseEnter, onMouseOut, onFocus and onBlur
*/
const SomeComponent = ({ hovering, focused, ...rest }) => (
<div
tabIndex={ 0 }
{ ...rest }
>
{ hovering ? 'User is hovering over this element!' : 'User is not hovering over this element' }
{ focused ? 'Element has focus' : 'Element does not have focus' }
</div>
);
export default focusable()(hoverable()(SomeComponent))
Attaching refs
to decorated components
The decorators in @vcnkit/decorators
utilize React's forwardRef API to pass refs to the decorated components.
Using React's createRef API:
import * as React from 'react';
import { hoverable } from '@vcnkit/decorators';
@hoverable()
class MyDecoratedComponent extends React.Component {
...
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
// This will hold a ref to 'MyDecoratedComponent' instead of the decorator itself.
this.ref = React.createRef();
}
render() {
return (
<MyDecoratedComponent
ref={ this.ref }
/>
)
}
}
Or, using a callback ref:
import * as React from 'react';
import { hoverable } from '@vcnkit/decorators';
@hoverable()
class MyDecoratedComponent extends React.Component {
...
}
class MyComponent extends React.Component {
render() {
return (
<MyDecoratedComponent
ref={ ref => { this.ref = ref; } }
/>
)
}
}