Replace Ember app with Mithril app

This commit is contained in:
Toby Zerner
2015-04-25 22:28:39 +09:30
parent 6f67b8c247
commit b68a4711dc
377 changed files with 5641 additions and 7330 deletions

View File

@ -0,0 +1,21 @@
import Component from 'flarum/component';
import icon from 'flarum/helpers/icon';
export default class ActionButton extends Component {
view() {
var attrs = {};
for (var i in this.props) { attrs[i] = this.props[i]; }
var iconName = attrs.icon;
delete attrs.icon;
var label = attrs.label;
delete attrs.label;
attrs.href = attrs.href || 'javascript:;';
return m('a', attrs, [
iconName ? icon(iconName+' icon-glyph') : '',
m('span.label', label)
]);
}
}

View File

@ -0,0 +1,33 @@
import Component from 'flarum/component';
import ActionButton from 'flarum/components/action-button';
import listItems from 'flarum/helpers/list-items';
export default class Alert extends Component {
view() {
var attrs = {};
for (var i in this.props) { attrs[i] = this.props[i]; }
attrs.className = (attrs.className || '') + ' alert-'+attrs.type;
delete attrs.type;
var message = attrs.message;
delete attrs.message;
var controlItems = attrs.controls.slice() || [];
delete attrs.controls;
if (attrs.dismissible || attrs.dismissible === undefined) {
controlItems.push(ActionButton.component({
icon: 'times',
className: 'btn btn-icon btn-link',
onclick: attrs.ondismiss.bind(this)
}));
}
delete attrs.dismissible;
return m('div.alert', attrs, [
m('span.alert-text', message),
m('ul.alert-controls', listItems(controlItems))
]);
}
}

View File

@ -0,0 +1,34 @@
import Component from 'flarum/component';
export default class Alerts extends Component {
constructor(props) {
super(props);
this.components = [];
}
view() {
return m('div.alerts', this.components.map((component) => {
component.props.ondismiss = this.dismiss.bind(this, component);
return m('div.alert-wrapper', component);
}));
}
show(component) {
this.components.push(component);
m.redraw();
}
dismiss(component) {
var index = this.components.indexOf(component);
if (index !== -1) {
this.components.splice(index, 1);
}
m.redraw();
}
clear() {
this.components = [];
m.redraw();
}
}

View File

@ -0,0 +1,32 @@
import Component from 'flarum/component';
import icon from 'flarum/helpers/icon';
/**
The back/pin button group in the top-left corner of Flarum's interface.
*/
export default class BackButton extends Component {
view() {
var history = app.history;
var pane = app.pane;
return m('div.back-button', {
className: this.props.className || '',
onmouseenter: pane && pane.show.bind(pane),
onmouseleave: pane && pane.onmouseleave.bind(pane),
config: this.onload.bind(this)
}, history.canGoBack() ? m('div.btn-group', [
m('button.btn.btn-default.btn-icon.back', {onclick: history.back.bind(history)}, icon('chevron-left icon-glyph')),
pane && pane.active ? m('button.btn.btn-default.btn-icon.pin'+(pane.active ? '.active' : ''), {onclick: pane.togglePinned.bind(pane)}, icon('thumb-tack icon-glyph')) : '',
]) : (this.props.drawer ? [
m('button.btn.btn-default.btn-icon.drawer-toggle', {onclick: this.toggleDrawer.bind(this)}, icon('reorder icon-glyph'))
] : ''));
}
onload(element, isInitialized, context) {
context.retain = true;
}
toggleDrawer() {
$('body').toggleClass('drawer-open');
}
}

View File

@ -0,0 +1,19 @@
import Component from 'flarum/component';
import icon from 'flarum/helpers/icon';
export default class Badge extends Component {
view(ctrl) {
var iconName = this.props.icon;
var label = this.props.title = this.props.label;
delete this.props.icon, this.props.label;
this.props.config = function(element) {
$(element).tooltip();
};
this.props.className = 'badge '+(this.props.className || '');
return m('span', this.props, [
icon(iconName+' icon-glyph'),
m('span.label', label)
]);
}
}

View File

@ -0,0 +1,20 @@
import Component from 'flarum/component';
import icon from 'flarum/helpers/icon';
import listItems from 'flarum/helpers/list-items';
export default class DropdownButton extends Component {
view() {
return m('div', {className: 'dropdown btn-group '+(this.props.items ? 'item-count-'+this.props.items.length : '')+' '+(this.props.className || '')}, [
m('a[href=javascript:;]', {
className: 'dropdown-toggle '+(this.props.buttonClass || 'btn btn-default'),
'data-toggle': 'dropdown',
onclick: this.props.buttonClick
}, this.props.buttonContent || [
icon((this.props.icon || 'ellipsis-v')+' icon-glyph'),
m('span.label', this.props.label || 'Controls'),
icon('caret-down icon-caret')
]),
m(this.props.menuContent ? 'div' : 'ul', {className: 'dropdown-menu '+(this.props.menuClass || '')}, this.props.menuContent || listItems(this.props.items))
]);
}
}

View File

@ -0,0 +1,18 @@
import Component from 'flarum/component'
import icon from 'flarum/helpers/icon'
import listItems from 'flarum/helpers/list-items';
export default class DropdownSelect extends Component {
view() {
var activeItem = this.props.items.filter((item) => item.component.active && item.component.active(item.props))[0];
var label = activeItem && activeItem.props.label;
return m('div', {className: 'dropdown dropdown-select btn-group item-count-'+this.props.items.length+' '+this.props.className}, [
m('a[href=javascript:;]', {className: 'dropdown-toggle '+(this.props.buttonClass || 'btn btn-default'), 'data-toggle': 'dropdown'}, [
m('span.label', label), ' ',
icon('sort icon-caret')
]),
m('ul', {className: 'dropdown-menu '+this.props.menuClass}, listItems(this.props.items, true))
])
}
}

View File

@ -0,0 +1,30 @@
import Component from 'flarum/component';
import icon from 'flarum/helpers/icon';
import listItems from 'flarum/helpers/list-items';
import ActionButton from 'flarum/components/action-button';
/**
Given a list of items, this component displays a split button: the left side
is the first item in the list, while the right side is a dropdown-toggle
which shows a dropdown menu containing all of the items.
*/
export default class DropdownSplit extends Component {
view() {
var firstItem = this.props.items[0];
var items = listItems(this.props.items);
var buttonProps = { className: this.props.buttonClass || 'btn btn-default' };
for (var i in firstItem.props) {
buttonProps[i] = firstItem.props[i];
}
return m('div', {className: 'dropdown dropdown-split btn-group item-count-'+(items.length)+' '+this.props.className}, [
ActionButton.component(buttonProps),
m('a[href=javascript:;]', {className: 'dropdown-toggle '+this.props.buttonClass, 'data-toggle': 'dropdown'}, [
icon('caret-down icon-caret'),
icon((this.props.icon || 'ellipsis-v')+' icon-glyph'),
]),
m('ul', {className: 'dropdown-menu '+(this.props.menuClass || 'pull-right')}, items)
])
}
}

View File

@ -0,0 +1,11 @@
import Component from 'flarum/component';
import listItems from 'flarum/helpers/list-items';
export default class FieldSet extends Component {
view() {
return m('fieldset', {className: this.props.className}, [
m('legend', this.props.label),
m('ul', listItems(this.props.fields))
]);
}
}

View File

@ -0,0 +1,15 @@
import Component from 'flarum/component';
export default class LoadingIndicator extends Component {
view() {
var size = this.props.size || 'small';
delete this.props.size;
this.props.config = function(element) {
$.fn.spin.presets[size].zIndex = 'auto';
$(element).spin(size);
};
return m('div.loading-indicator', this.props, m.trust(' '));
}
}

View File

@ -0,0 +1,35 @@
import Component from 'flarum/component';
export default class Modal extends Component {
view() {
return m('div.modal.fade', {config: this.onload.bind(this)}, this.component && this.component.view())
}
onload(element, isInitialized) {
if (isInitialized) { return; }
this.element(element);
this.$()
.on('hidden.bs.modal', this.destroy.bind(this))
.on('shown.bs.modal', this.ready.bind(this));
}
show(component) {
this.component = component;
m.redraw(true);
this.$().modal('show');
}
close() {
this.$().modal('hide');
}
destroy() {
this.component = null;
}
ready() {
this.component && this.component.ready && this.component.ready(this.$());
}
}

View File

@ -0,0 +1,17 @@
import Component from 'flarum/component'
import icon from 'flarum/helpers/icon'
export default class NavItem extends Component {
view() {
var active = NavItem.active(this.props);
return m('li'+(active ? '.active' : ''), m('a', {href: this.props.href, config: m.route}, [
icon(this.props.icon),
this.props.label, ' ',
m('span.count', this.props.badge)
]))
}
static active(props) {
return typeof props.active !== 'undefined' ? props.active : m.route() === props.href;
}
}

View File

@ -0,0 +1,13 @@
import Component from 'flarum/component'
import icon from 'flarum/helpers/icon';
export default class SelectInput extends Component {
view(ctrl) {
return m('span.select-input', [
m('select.form-control', {onchange: m.withAttr('value', this.props.onchange.bind(ctrl)), value: this.props.value}, [
this.props.options.map(function(option) { return m('option', {value: option.key}, option.value) })
]),
icon('sort')
])
}
}

View File

@ -0,0 +1,14 @@
import Component from 'flarum/component';
/**
*/
class Separator extends Component {
view() {
return m('span');
}
}
Separator.wrapperClass = 'divider';
export default Separator;

View File

@ -0,0 +1,30 @@
import Component from 'flarum/component';
import LoadingIndicator from 'flarum/components/loading-indicator';
export default class SwitchInput extends Component {
constructor(props) {
super(props);
this.loading = m.prop(false);
}
view() {
return m('div.checkbox.checkbox-switch', [
m('label', [
m('div.switch-control', [
m('input[type=checkbox]', {
checked: this.props.state,
onchange: m.withAttr('checked', this.onchange.bind(this))
}),
m('div.switch', {className: this.loading() && 'loading'})
]),
this.props.label, ' ',
this.loading() ? LoadingIndicator.component({size: 'tiny'}) : ''
])
])
}
onchange(checked) {
this.props.onchange && this.props.onchange(checked, this);
}
}

View File

@ -0,0 +1,65 @@
import Component from 'flarum/component';
import ItemList from 'flarum/utils/item-list';
import listItems from 'flarum/helpers/list-items';
import ActionButton from 'flarum/components/action-button';
/**
A text editor. Contains a textarea and an item list of `controls`, including
a submit button.
*/
export default class TextEditor extends Component {
constructor(props) {
props.submitLabel = props.submitLabel || 'Submit';
super(props);
this.value = m.prop(this.props.value || '');
}
view() {
return m('div.text-editor', {config: this.element}, [
m('textarea.form-control.flexible-height', {
config: this.configTextarea.bind(this),
onkeyup: m.withAttr('value', this.onkeyup.bind(this)),
placeholder: this.props.placeholder || '',
disabled: !!this.props.disabled,
value: this.props.value || ''
}),
m('ul.text-editor-controls.fade', listItems(this.controlItems().toArray()))
]);
}
configTextarea(element, isInitialized) {
if (isInitialized) { return; }
$(element).bind('keydown', 'meta+return', this.onsubmit.bind(this));
}
controlItems() {
var items = new ItemList();
items.add('submit',
ActionButton.component({
label: this.props.submitLabel,
icon: 'check',
className: 'btn btn-primary',
wrapperClass: 'primary-control',
onclick: this.onsubmit.bind(this)
})
);
return items;
}
onkeyup(value) {
this.value(value);
this.props.onchange(this.value());
this.$('.text-editor-controls').toggleClass('in', !!value);
m.redraw.strategy('none');
}
onsubmit() {
this.props.onsubmit(this.value());
}
}

View File

@ -0,0 +1,35 @@
import Component from 'flarum/component';
import LoadingIndicator from 'flarum/components/loading-indicator';
import classList from 'flarum/utils/class-list';
import icon from 'flarum/helpers/icon';
export default class YesNoInput extends Component {
constructor(props) {
super(props);
this.loading = m.prop(false);
}
view() {
return m('label.yesno-control', [
m('input[type=checkbox]', {
checked: this.props.state,
disabled: this.props.disabled,
onchange: m.withAttr('checked', this.onchange.bind(this))
}),
m('div.yesno', {className: classList({
loading: this.loading(),
disabled: this.props.disabled,
state: this.props.state ? 'yes' : 'no'
})}, [
this.loading()
? LoadingIndicator.component({size: 'tiny'})
: icon(this.props.state ? 'check' : 'times')
])
]);
}
onchange(checked) {
this.props.onchange && this.props.onchange(checked, this);
}
}