-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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