mirror of
https://github.com/flarum/framework.git
synced 2025-05-23 23:29:57 +08:00
Replace Ember app with Mithril app
This commit is contained in:
21
js/lib/components/action-button.js
Normal file
21
js/lib/components/action-button.js
Normal 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)
|
||||
]);
|
||||
}
|
||||
}
|
33
js/lib/components/alert.js
Normal file
33
js/lib/components/alert.js
Normal 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))
|
||||
]);
|
||||
}
|
||||
}
|
34
js/lib/components/alerts.js
Normal file
34
js/lib/components/alerts.js
Normal 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();
|
||||
}
|
||||
}
|
32
js/lib/components/back-button.js
Normal file
32
js/lib/components/back-button.js
Normal 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');
|
||||
}
|
||||
}
|
19
js/lib/components/badge.js
Normal file
19
js/lib/components/badge.js
Normal 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)
|
||||
]);
|
||||
}
|
||||
}
|
20
js/lib/components/dropdown-button.js
Normal file
20
js/lib/components/dropdown-button.js
Normal 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))
|
||||
]);
|
||||
}
|
||||
}
|
18
js/lib/components/dropdown-select.js
Normal file
18
js/lib/components/dropdown-select.js
Normal 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))
|
||||
])
|
||||
}
|
||||
}
|
30
js/lib/components/dropdown-split.js
Normal file
30
js/lib/components/dropdown-split.js
Normal 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)
|
||||
])
|
||||
}
|
||||
}
|
11
js/lib/components/field-set.js
Normal file
11
js/lib/components/field-set.js
Normal 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))
|
||||
]);
|
||||
}
|
||||
}
|
15
js/lib/components/loading-indicator.js
Normal file
15
js/lib/components/loading-indicator.js
Normal 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(' '));
|
||||
}
|
||||
}
|
35
js/lib/components/modal.js
Normal file
35
js/lib/components/modal.js
Normal 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.$());
|
||||
}
|
||||
}
|
17
js/lib/components/nav-item.js
Normal file
17
js/lib/components/nav-item.js
Normal 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;
|
||||
}
|
||||
}
|
13
js/lib/components/select-input.js
Normal file
13
js/lib/components/select-input.js
Normal 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')
|
||||
])
|
||||
}
|
||||
}
|
14
js/lib/components/separator.js
Normal file
14
js/lib/components/separator.js
Normal file
@ -0,0 +1,14 @@
|
||||
import Component from 'flarum/component';
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
class Separator extends Component {
|
||||
view() {
|
||||
return m('span');
|
||||
}
|
||||
}
|
||||
|
||||
Separator.wrapperClass = 'divider';
|
||||
|
||||
export default Separator;
|
30
js/lib/components/switch-input.js
Normal file
30
js/lib/components/switch-input.js
Normal 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);
|
||||
}
|
||||
}
|
65
js/lib/components/text-editor.js
Normal file
65
js/lib/components/text-editor.js
Normal 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());
|
||||
}
|
||||
}
|
35
js/lib/components/yesno-input.js
Normal file
35
js/lib/components/yesno-input.js
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user