Last active
January 25, 2018 19:28
-
-
Save jfbrennan/8fd5c0272660cad4e2d23e93863983ea 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
<dom-module id="poly-alert"> | |
<template> | |
<div> | |
<i id="icon"></i> | |
<div> | |
<h6 dom-if="title" class="alert-title sub-header">{{title}}</h6> | |
<p>{{msg}}</p> | |
</div> | |
<button dom-if="!preventClose" id="dismissBtn" class="btn-close alert-remove"><span class="icon icon-close"></span></button> | |
</div> | |
</template> | |
<script> | |
class PolyAlert extends Polymer.Element { | |
static get is() { return 'poly-alert'; } | |
static get properties() { | |
return { | |
type: String, | |
title: String, | |
msg: String, | |
autodismiss: Object, | |
preventClose: { | |
type: Boolean, | |
value: false | |
} | |
}; | |
} | |
constructor() { | |
super(); | |
} | |
ready() { | |
super.ready(); | |
// Create class lists | |
this.shadowRoot.firstElementChild.classList.add('alert'); | |
this.shadowRoot.firstElementChild.classList.add('alert-' + this.type); | |
this.$.icon.classList.add('icon'); | |
this.$.icon.classList.add('icon-' + (this.type === 'success' ? 'check' : this.type === 'error' ? 'info' : 'warn') + 'alt'); | |
// on-click approach means the callback's context is the target not this element | |
this.$.dismissBtn.addEventListener('click', this.dismiss.bind(this)); | |
if (this.autodismiss) { | |
var seconds = (typeof this.autodismiss === 'number' ? this.autodismiss : 4) * 1000; | |
setTimeout(this.close.bind(this), seconds); | |
} | |
} | |
// Removes Alert after CSS transition finishes. Publishes UITK event. | |
dismiss(e) { | |
var element = this.shadowRoot.firstElementChild; // firstElementChild is hacky, no find('.alert')??? | |
this.addEventListener('transitionend', function(e) { | |
this.remove(); | |
uitk.publish('alert.remove', this); | |
}.bind(this), {once: true}); | |
element.classList.add('remove-animated'); // IE11 can't take multiple args :( | |
element.classList.add('animated-fade'); // IE11 can't take multiple args :( | |
} | |
} | |
customElements.define(PolyAlert.is, PolyAlert); | |
</script> | |
<style> | |
// Shadow DOM! Will have to redo all the existing Alert styles and any helpers like animation classes | |
// Would be ~100 more loc | |
</style> | |
</dom-module> |
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, {Component} from 'react' | |
import PropTypes from 'prop-types' | |
class ReactAlert extends Component { | |
static defaultProps = { | |
icon: null, | |
msg: '', | |
type: 'info', | |
autodismiss: 0, | |
onRemoveAlert: () => {} | |
}; | |
static propTypes = { | |
icon: PropTypes.element, | |
msg: PropTypes.PropTypes.string, | |
type: PropTypes.oneOf(['info', 'success', 'error', 'warn']), | |
autodismiss: PropTypes.oneOfType([ | |
PropTypes.number, | |
PropTypes.boolean | |
]), | |
onRemoveAlert: PropTypes.func | |
}; | |
_removeItself = () => { | |
const {onRemoveAlert, id} = this.props; | |
onRemoveAlert(id) | |
}; | |
componentDidMount () { | |
const {autodismiss} = this.props; | |
if (autodismiss) { | |
let seconds = (typeof autodismiss === 'number' ? autodismiss : 4) * 1000; | |
setTimeout(() => { this._removeItself() }, seconds) | |
} | |
} | |
render () { | |
const {msg, icon, type} = this.props; | |
return ( | |
<div> | |
<div className={alert alert-{type}> | |
<i className={icon icon-{icon}></i> | |
<div> | |
<h6 className="alert-title sub-header">{title}</h6> | |
<p>{msg}</p> | |
</div> | |
<button onClick={this._removeItself} className="btn-close alert-remove"></button> | |
</div> | |
</div> | |
) | |
} | |
} | |
export default ReactAlert |
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
<riot-alert class="alert alert-{opts.type} {remove-animated: removing, animated-fade: removing}"> | |
<i class="icon icon-{icon}alt"></i> | |
<div> | |
<h6 if="{opts.title}" class="alert-title sub-header">{opts.title}</h6> | |
<p>{opts.msg}</p> | |
</div> | |
<button if="{!opts.preventclose}" onclick="{dismiss}" class="btn-close alert-remove"><span class="icon icon-close"></span></button> | |
<script> | |
// Determine Icon based on type | |
this.icon = this.opts.type === 'success' ? 'check' : this.opts.type === 'error' ? 'info' : 'warn'; | |
// Removes Alert after CSS transition finishes. Publishes UITK event. | |
dismiss(e) { | |
this.root.addEventListener('transitionend', function(e) { | |
this.unmount(); | |
uitk.publish('alert.remove'); | |
}.bind(this), {once: true}); | |
this.removing = true; // Transition event callback will do the unmount | |
} | |
this.on('mount', function() { | |
if (this.opts.autodismiss) { | |
var seconds = (typeof this.opts.autodismiss === 'number' ? this.opts.autodismiss : 4) * 1000; | |
setTimeout(this.remove.bind(this), seconds); | |
} | |
}) | |
</script> | |
</riot-alert> |
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
Slim.tag('slim-alert', | |
` | |
<div bind:class="alertClass"> | |
<i bind:class="iconClass"></i> | |
<div> | |
<h6 s:if="title" class="alert-title sub-header" bind>{{title}}</h6> | |
<p bind>{{msg}}</p> | |
</div> | |
<button s:if="!preventClose" click="dismiss" class="btn-close alert-remove"><span class="icon icon-close"></span></button> | |
</div> | |
`, | |
class SlimAlert extends Slim { | |
onBeforeCreated() { | |
this.type = this.getAttribute('type'); | |
this.msg = this.getAttribute('msg') || ''; | |
this.preventClose = this.hasAttribute('preventclose'); | |
this.autodismiss = this.getAttribute('dismiss'); | |
// Create class lists | |
this.alertClass = 'alert alert-' + this.type; | |
this.iconClass = 'icon icon-' + (this.type === 'success' ? 'check' : this.type === 'error' ? 'info' : 'warn') + 'alt'; | |
// Removes SlimAlert after CSS transition finishes. Publishes UITK event. | |
this.dismiss = function() { | |
this.addEventListener('transitionend', (e) => { | |
this.remove(); | |
uitk.publish('alert.remove'); | |
}, {once: true}); | |
this.find('.alert').classList.add('remove-animated'); // IE11 can't take multiple args :( | |
this.find('.alert').classList.add('animated-fade'); // IE11 can't take multiple args :( | |
} | |
} | |
onCreated() { | |
if (this.autodismiss) { | |
var seconds = (typeof this.autodismiss === 'number' ? this.autodismiss : 4) * 1000; // The given secs or default to 4 secs | |
setTimeout(this.unmount.bind(this), seconds); | |
} | |
} | |
}); |
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
Vue.component('vue-alert', {template: | |
`<div v-bind:class="'alert alert-' + type"> | |
<i v-bind:class="'icon icon-' + icon"></i> | |
<div> | |
<h6 v-if="title" class="alert-title sub-header">{{title}}</h6> | |
<p>{{msg}}</p> | |
</div> | |
<button v-if="preventClose" v-on:click="dismiss" class="btn-close alert-remove"><span class="icon icon-close"></span></button> | |
</div> | |
`, | |
props: ['type', 'title', 'msg', 'autodismiss', 'preventClose'], | |
data: function () { | |
return { | |
counter: 0 | |
} | |
}, | |
computed: { | |
icon: function () { | |
return this.type === 'success' ? 'check' : this.type === 'error' ? 'info' : 'warn' | |
} | |
}, | |
methods: { | |
dismiss: function() { | |
this.root.addEventListener('transitionend', function(e) { | |
this.unmount(); | |
uitk.publish('alert.remove'); | |
}.bind(this), {once: true}); | |
this.removing = true; // Transition event callback will do the unmount | |
} | |
} | |
}); |
The above components implement the same requirements. These are:
- Block-level element with a colored border and white background (existing stylesheet was able to be reused except in shadow DOM cases)
- Required
type
(one ofinfo
,success
,warn
, orerror
; determines colors) - Optional
title
- Required
message
- Optional
autodismiss
(passtrue
for the default duration, or passNumber
of seconds for custom duration) - Dismiss button when clicked removes Alert and 'alert.dismissed' is published
- Optional
disabled
(set it to hide the dismiss button)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Trying all the View libs. Who's the easiest?
So far Riot (29 loc of src, just 1 line to use). Slim and Vue are not far behind. React and Polymer are up to double the loc.
Interesting to note how Riot, Slim, Polymer usage is identical:
Vue is similar, but requires ugly "root" elements:
React is not like Custom Elements and also requires container elements like Vue:
Riot is also the smallest of the libs I've tried so far. It's 10kb min+gz.