<section>
<div class="row">
<div class="col _span-12">
<form action="#" class="Form js-snackbar-open">
<label>
<span>Message</span>
<input
name="message"
type="text"
placeholder="Message"
value="What a tasty snack."
required
>
</label>
<p><button class="button">Grab a snack</button></p>
</form>
</div>
</div>
</section>
<!--<div class="js-snackbar"></div>-->
<section>
<div class="row">
<div class="col _span-12">
<form action="#" class="Form js-snackbar-open">
<label>
<span>Message</span>
<input
name="message"
type="text"
placeholder="Message"
value="What a tasty snack."
required
>
</label>
<p><button class="button">Grab a snack</button></p>
</form>
</div>
</div>
</section>
<!--<div class="js-snackbar"></div>-->
/* No context defined for this component. */
import PropTypes from 'prop-types';
import React from 'react';
/**
* @typedef {Object} Message - A message for the Snackbar to display.
* @property {string} text - The message inner HTML content.
* @property {string} [action] - The text for the message action button.
* @property {string} [callback] - The function to run when the action button is pressed.
*/
/**
* A message that is displayed in a Snackbar.
*/
class SnackMessage extends React.Component {
static propTypes = {
text: PropTypes.string.isRequired,
action: PropTypes.string,
callback: PropTypes.func
};
/**
* @type Message
*/
static defaultProps = {
text: '',
action: '',
callback: () => true
};
render() {
const action = this.props.action ? (
<button
className="Snackbar-action"
onClick={this.props.callback}
>
{this.props.action}
</button>
) : '';
/*eslint-disable*/
return (
<div className="Snackbar">
<p dangerouslySetInnerHTML={{__html: this.props.text}} />
{action}
</div>
);
/*eslint-enable*/
}
}
export default SnackMessage;
import PropTypes from 'prop-types';
import React from 'react';
import { CSSTransitionGroup } from 'react-transition-group';
import SnackMessage from './SnackMessage';
/**
* Opens a small window to notify a user of an event.
*/
class Snackbar extends React.Component {
static propTypes = {
message: PropTypes.object,
ttl: PropTypes.number
};
/**
* @type {{message: Message, ttl: number}}
*/
static defaultProps = {
message: {},
ttl: 8000
};
/**
* Creates a Snackbar with a given message, clearing any previous ones.
* @param {Object} props
* @param {Message} props.message - A message to be displayed.
* @param {number} props.ttl - The time until a displayed message disappears after no interaction.
*/
constructor(props) {
super(props);
const message = this.props.message;
if (message) {
message.id = new Date().getTime();
this.state = {
messages: [message]
};
this.timedRemove(message);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.message) {
nextProps.message.id = new Date().getTime();
this.setState({
messages: [nextProps.message]
});
}
}
/**
* Removes an existing message from the Snackbar.
* @param {Message} message
*/
removeMessage(message) {
this.setState({
messages: this.state.messages
.filter((m) => m.id != message.id)
});
}
/**
* Removes an existing message after the set TTL.
* @param {Message} message
*/
timedRemove(message) {
setTimeout(this.removeMessage.bind(this, message), this.props.ttl);
}
render() {
const message = this.state.messages[0];
let callback;
if (message) {
this.timedRemove(message);
callback = message.action ? () => {
this.removeMessage(message);
if (message.callback) message.callback();
} : undefined;
}
return (
<div className="Snackbar-container">
<div className="Snackbar-group">
<CSSTransitionGroup
transitionName={{enter: 'enter', leave: 'leave', appear: 'enter'}}
transitionAppear={true}
transitionAppearTimeout={200}
transitionEnterTimeout={200}
transitionLeaveTimeout={200}
>
{message &&
<SnackMessage
key={message.id}
text={message.text}
action={message.action}
callback={callback}
/>
}
</CSSTransitionGroup>
</div>
</div>
);
}
}
export default Snackbar;
// Snackbar
//
// OM NOM NOM.
.Snackbar {
@include Elevation("Snackbar");
position: relative;
display: flex;
max-height: 200px;
min-width: 240px;
padding: 1rem 1.5rem;
margin-top: .75rem;
justify-content: space-between;
align-items: flex-end;
color: get-color(neutral, 0);
background-color: get-color(neutral, 1000);
opacity: 1;
border-radius: $Border-radius--500;
transition: transform $transition-time-default $transition-in-default;
transform: translate3d(0, 0, 0);
pointer-events: auto;
h4 {
color: get-color(neutral, 0);
}
p {
@include font-size-small;
margin-top: 0;
}
&-action {
@include spaced-out;
@include font-size-small;
display: block;
padding: 0;
margin-left: 20px;
color: theme-color(link);
background: none;
border: 0;
}
&.enter,
&.leave {
max-height: 0;
opacity: 0;
transform: translate3d(0, 20px, 0);
}
}
.Snackbar-group {
display: inline-flex;
max-width: 780px;
flex-direction: column;
}
.Snackbar-container {
@include ZIndex("Snackbar");
position: fixed;
right: 0;
bottom: 0;
left: 0;
text-align: center;
pointer-events: none;
}
@media #{$mobile-media} {
.Snackbar-group,
.Snackbar {
width: 100%;
border-radius: 0;
}
}
There are no notes for this item.