Skip to content

Instantly share code, notes, and snippets.

@ndesmic
Forked from jfbrennan/ReactAlert.js
Last active December 1, 2017 22:40
Show Gist options
  • Save ndesmic/6578ab95f9ab969ff175fee7cd8b2401 to your computer and use it in GitHub Desktop.
Save ndesmic/6578ab95f9ab969ff175fee7cd8b2401 to your computer and use it in GitHub Desktop.
Alert UI component rebuilt to compare Riot vs Slim vs Polymer vs Vue vs React and more
import React from "react";
import ReactDOM from "react-dom";
class Alert extends React.Component {
constructor(props){
super();
this.state = {
dismissed : false,
transitionRef : null
};
if(props.dismiss){
setTimeout(() => this.setState({dismissed: true}), 4000);
}
}
componentDidMount(){
const ref = this.refs.self.addEventListener("transitionend", () => {
if(this.state.dismissed){
this.dismiss();
this.refs.self.removeEventListener("transitionend", this.state.transitionRef);
}
});
this.setState({ transitionRef: ref });
}
startDismiss(){
this.setState({ dismissed : true });
}
dismiss(){
this.props.onRemove();
}
render(){
const click = () => this.startDismiss();
const klass = `alert alert-${this.props.type} ${this.state.dismissed ? "remove-animated animated-fade" : "" }`;
const iconClass = `icon icon-${this.props.type}alt`;
return (
<div className={klass} onClick={click} ref="self">
<i className={iconClass}></i>
<div className="alert-message">
<p>{this.props.children}</p>
</div>
</div>
);
}
}
class Controller extends React.Component {
constructor(props){
super();
this.state = { renderChild: true };
}
dismiss(){
this.setState({renderChild: false });
uitk.publish("alert.remove", this);
}
handleRemove(){
if(!this.props.preventClose){
this.dismiss();
}
}
render() {
return this.state.renderChild
? <Alert
onRemove={() => this.handleRemove()}
onDismiss={() => this.dismiss()}
type="error">
Could not get such and such
</Alert>
: null;
}
}
//for testing
ReactDOM.render(<Controller />, document.querySelector("#react-root"));
import { Component, Prop, Event, EventEmitter, Element, State } from '@stencil/core';
@Component({
tag: "my-alert",
styleUrl: "my-alert.scss"
})
export class MyAlert {
@Prop() type: string;
@Prop() preventClose: boolean;
@Prop() dismiss: boolean | number;
@Event() remove: EventEmitter;
@Element() root: HTMLElement;
@State() state: any;
componentWillLoad(){
this.state = { dismissed : false };
if(this.dismiss){
setTimeout(() => this.state = { ...this.state, ...{dismissed: true}}, 4000);
}
this.root.classList.add("alert");
}
startDismiss() {
this.state = { ...this.state, ...{ dismissed : true } };
this.root.addEventListener("transitionend", this.dismissed.bind(this));
}
dismissed() {
//is this leaky?
this.root.parentElement.removeChild(this.root);
}
render(){
const iconClass = `icon icon-${this.type}alt`;
const klass = `alert alert-${this.type} ${this.state.dismissed ? "remove-animated animated-fade" : "" }`;
return (
<div class={klass} onClick={e => this.startDismiss()}>
<i class={iconClass}></i>
<div class="alert-message">
<p><slot></slot></p>
</div>
</div>
);
}
}
customElements.define("my-alert",
class extends HTMLElement {
static get observedAttributes(){
return ["type", "dismiss", "preventClose"];
}
static camelCase(text){
return text.replace(/-([a-z])/g, g => g[1].toUpperCase());
}
constructor(){
super();
this.bind(this);
this.attrs = new Proxy({}, { set : this.handleSet });
}
bind(){
this.render = this.render.bind(this);
this.startDismiss = this.startDismiss.bind(this);
this.dismissed = this.dismissed.bind(this);
this.handleSet = this.handleSet.bind(this);
}
handleSet(target, propertyName, value, reciever){
if(this.constructor.observedAttributes.map(this.constructor.camelCase).includes(propertyName)){
this.render();
}
return Reflect.set(target, propertyName, value, reciever);
}
connectedCallback(){
this.classList.add("alert"); //for css compat
this.createShadowDom();
this.attachEvents();
this.render();
if(this.dismiss){
setTimeout(() => this.startDismiss(), 4000);
}
}
createShadowDom(){
this.attachShadow({ mode : "open", delegatesFocus: true });
this.shadowRoot.innerHTML = `
<style>
:host { display: block; }
:host { contains: all; }
.icon {
position: absolute;
top: 1.33em;
}
.icon:before {
font-size: 1rem;
}
.icon + .alert-message {
left: 2.13em;
}
.alert-message {
width: 90%;
font-size: 0.87rem;
font-weight: 400;
display: inline-block;
color: #5E778B;
position: relative;
}
.alert-message .alert-title {
margin-top: 1.11em;
margin-bottom: 0;
color: #485F71;
}
.alert-message > p {
margin-top: 1.54em;
margin-bottom: 1.54em;
}
.alert-message .alert-title + p {
margin-top: 0;
margin-bottom: 0.77em;
}
.btn-close {
position: absolute;
right: 1.2em;
top: 1.33em;
}
.btn-close > .icon {
color: #dbdfe3;
}
.btn-close:before {
display: none;
}
a {
text-decoration: underline;
}
:host(.alert-error):before {
background-color: #e75c5c;
}
:host(.alert-error) a,
:host(.alert-error) a:link,
:host(.alert-error) a:visited {
color: #5E778B;
}
:host(.alert-error) > .icon {
color: #e75c5c;
}
:host(.alert-success):before {
background-color: #7dcd75;
}
:host(.alert-success) a,
:host(.alert-success) a:link,
:host(.alert-success) a:visited {
color: #5E778B;
}
:host(.alert-success) > .icon {
color: #7dcd75;
}
:host(.alert-info):before {
background-color: #3799cf;
}
:host(.alert-info) a,
:host(.alert-info) a:link,
:host(.alert-info) a:visited {
color: #5E778B;
}
:host(.alert-info) > .icon {
color: #3799cf;
}
:host(.alert-warn):before {
background-color: #ffc72c;
}
:host(.alert-warn) a,
:host(.alert-warn) a:link,
:host(.alert-warn) a:visited {
color: #5E778B;
}
:host(.alert-warn) > .icon {
color: #ffc72c;
}
.alert-text-error,
.alert-text-error a:link,
.alert-text-error .link,
.alert-text-error a:visited,
.alert-text-error.icon {
color: #e75c5c;
}
.alert-text-success,
.alert-text-success a:link,
.alert-text-success .link,
.alert-text-success a:visited,
.alert-text-success.icon {
color: #7dcd75;
}
.alert-text-info,
.alert-text-info a:link,
.alert-text-info .link,
.alert-text-info a:visited,
.alert-text-info.icon {
color: #3799cf;
}
.alert-text-warn,
.alert-text-warn a:link,
.alert-text-warn .link,
.alert-text-warn a:visited,
.alert-text-warn.icon {
color: #ffc72c;
}
.alert-text-error a,
.alert-text-info a,
.alert-text-success a,
.alert-text-warn a {
text-decoration: underline;
}
i {
font-family: "EgenciaIcons";
font-style: normal;
}
.icon-warnalt:before,
.icon-erroralt:before {
content: "\\e057";
}
.icon-infoalt:before {
content: "\\e05b";
}
.icon-successalt:before {
content: "\\e0aa";
}
</style>
<i class="icon icon-${this.type}alt"></i>
<div class="alert-message">
<p><slot></slot></p>
</div>
`;
}
attachEvents(){
this.addEventListener("click", this.startDismiss);
}
startDismiss(){
if(this.preventClose) return;
this.classList.add("animated-fade");
this.classList.add("remove-animated");
this.addEventListener("transitionend", this.endDissmiss, { once : true });
}
dismissed(){
this.parentElement.removeChild(this);
}
render(){
if(this.type){
this.classList.add(`alert-${this.type}`);
}
}
attributeChangedCallback(name, oldValue, newValue){
this[this.constructor.camelCase(name)] = newValue;
}
get type(){
return this.attrs.type;
}
set type(value){
this.attrs.type = value;
}
get preventClose(){
return this.attrs.preventClose;
}
set preventClose(value){
this.attrs.preventClose = value;
}
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment