Mithril 2 Update (#93)

Update for Mithril 2

- TagLinkButtons now have children passed in, even though those children are not directly shown. This is because those children are used if that TagLinkButton is the active element in a dropdown.
- Since `m.redraw.strategy('all')` is no longer an option, we use keys to force a full-page rerender after rearranging tag order in the admin dashboard
This commit is contained in:
Alexander Skvortsov 2020-09-23 23:00:15 -04:00 committed by GitHub
parent bf223071f2
commit f61b5201d1
17 changed files with 207 additions and 209 deletions

View File

@ -5,16 +5,15 @@ import AdminLinkButton from 'flarum/components/AdminLinkButton';
import TagsPage from './components/TagsPage';
export default function() {
app.routes.tags = {path: '/tags', component: TagsPage.component()};
app.routes.tags = {path: '/tags', component: TagsPage};
app.extensionSettings['flarum-tags'] = () => m.route(app.route('tags'));
app.extensionSettings['flarum-tags'] = () => m.route.set(app.route('tags'));
extend(AdminNav.prototype, 'items', items => {
items.add('tags', AdminLinkButton.component({
href: app.route('tags'),
icon: 'fas fa-tags',
children: app.translator.trans('flarum-tags.admin.nav.tags_button'),
description: app.translator.trans('flarum-tags.admin.nav.tags_text')
}));
}, app.translator.trans('flarum-tags.admin.nav.tags_button')));
});
}

View File

@ -48,18 +48,11 @@ export default function() {
const tags = sortTags(app.store.all('tags').filter(tag => !tag.isRestricted()));
if (tags.length) {
items.add('tag', Dropdown.component({
className: 'Dropdown--restrictByTag',
buttonClassName: 'Button Button--text',
label: app.translator.trans('flarum-tags.admin.permissions.restrict_by_tag_heading'),
icon: 'fas fa-plus',
caretIcon: null,
children: tags.map(tag => Button.component({
icon: true,
children: [tagIcon(tag, {className: 'Button-icon'}), ' ', tag.name()],
onclick: () => tag.save({isRestricted: true})
}))
}));
items.add('tag', <Dropdown className='Dropdown--restrictByTag' buttonClassName='Button Button--text' label={app.translator.trans('flarum-tags.admin.permissions.restrict_by_tag_heading')} icon='fas fa-plus' caretIcon={null}>
{tags.map(tag => <Button icon={true} onclick={() => tag.save({ isRestricted: true })}>
{[tagIcon(tag, { className: 'Button-icon' }), ' ', tag.name()]}
</Button>)}
</Dropdown>);
}
});
}

View File

@ -10,17 +10,17 @@ import tagLabel from '../../common/helpers/tagLabel';
* to create or edit a tag.
*/
export default class EditTagModal extends Modal {
init() {
super.init();
oninit(vnode) {
super.oninit(vnode);
this.tag = this.props.tag || app.store.createRecord('tags');
this.tag = this.attrs.model || app.store.createRecord('tags');
this.name = m.prop(this.tag.name() || '');
this.slug = m.prop(this.tag.slug() || '');
this.description = m.prop(this.tag.description() || '');
this.color = m.prop(this.tag.color() || '');
this.icon = m.prop(this.tag.icon() || '');
this.isHidden = m.prop(this.tag.isHidden() || false);
this.name = m.stream(this.tag.name() || '');
this.slug = m.stream(this.tag.slug() || '');
this.description = m.stream(this.tag.description() || '');
this.color = m.stream(this.tag.color() || '');
this.icon = m.stream(this.tag.icon() || '');
this.isHidden = m.stream(this.tag.isHidden() || false);
}
className() {
@ -60,17 +60,17 @@ export default class EditTagModal extends Modal {
items.add('slug', <div className="Form-group">
<label>{app.translator.trans('flarum-tags.admin.edit_tag.slug_label')}</label>
<input className="FormControl" value={this.slug()} oninput={m.withAttr('value', this.slug)}/>
<input className="FormControl" bidi={this.slug}/>
</div>, 40);
items.add('description', <div className="Form-group">
<label>{app.translator.trans('flarum-tags.admin.edit_tag.description_label')}</label>
<textarea className="FormControl" value={this.description()} oninput={m.withAttr('value', this.description)}/>
<textarea className="FormControl" bidi={this.description}/>
</div>, 30);
items.add('color', <div className="Form-group">
<label>{app.translator.trans('flarum-tags.admin.edit_tag.color_label')}</label>
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)}/>
<input className="FormControl" placeholder="#aaaaaa" bidi={this.color}/>
</div>, 20);
items.add('icon', <div className="Form-group">
@ -78,13 +78,13 @@ export default class EditTagModal extends Modal {
<div className="helpText">
{app.translator.trans('flarum-tags.admin.edit_tag.icon_text', {a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1"/>})}
</div>
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)}/>
<input className="FormControl" placeholder="fas fa-bolt" bidi={this.icon}/>
</div>, 10);
items.add('hidden', <div className="Form-group">
<div>
<label className="checkbox">
<input type="checkbox" value="1" checked={this.isHidden()} onchange={m.withAttr('checked', this.isHidden)}/>
<input type="checkbox" bidi={this.isHidden()}/>
{app.translator.trans('flarum-tags.admin.edit_tag.hide_label')}
</label>
</div>
@ -95,8 +95,7 @@ export default class EditTagModal extends Modal {
type: 'submit',
className: 'Button Button--primary EditTagModal-save',
loading: this.loading,
children: app.translator.trans('flarum-tags.admin.edit_tag.submit_button')
})}
}, app.translator.trans('flarum-tags.admin.edit_tag.submit_button'))}
{this.tag.exists ? (
<button type="button" className="Button EditTagModal-delete" onclick={this.delete.bind(this)}>
{app.translator.trans('flarum-tags.admin.edit_tag.delete_tag_button')}

View File

@ -1,4 +1,5 @@
import SettingsModal from 'flarum/components/SettingsModal';
import withAttr from 'flarum/utils/withAttr';
export default class TagSettingsModal extends SettingsModal {
setMinTags(minTags, maxTags, value) {
@ -32,7 +33,7 @@ export default class TagSettingsModal extends SettingsModal {
type="number"
min="0"
value={minPrimaryTags()}
oninput={m.withAttr('value', this.setMinTags.bind(this, minPrimaryTags, maxPrimaryTags))} />
oninput={withAttr('value', this.setMinTags.bind(this, minPrimaryTags, maxPrimaryTags))} />
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
<input className="FormControl"
type="number"
@ -51,7 +52,7 @@ export default class TagSettingsModal extends SettingsModal {
type="number"
min="0"
value={minSecondaryTags()}
oninput={m.withAttr('value', this.setMinTags.bind(this, minSecondaryTags, maxSecondaryTags))} />
oninput={withAttr('value', this.setMinTags.bind(this, minSecondaryTags, maxSecondaryTags))} />
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
<input className="FormControl"
type="number"

View File

@ -10,14 +10,14 @@ import sortTags from '../../common/utils/sortTags';
function tagItem(tag) {
return (
<li data-id={tag.id()} style={{color: tag.color()}}>
<li data-id={tag.id()} style={{ color: tag.color() }}>
<div className="TagListItem-info">
{tagIcon(tag)}
<span className="TagListItem-name">{tag.name()}</span>
{Button.component({
className: 'Button Button--link',
icon: 'fas fa-pencil-alt',
onclick: () => app.modal.show(EditTagModal, {tag})
onclick: () => app.modal.show(EditTagModal, { model: tag })
})}
</div>
{!tag.isChild() && tag.position() !== null ? (
@ -32,6 +32,16 @@ function tagItem(tag) {
}
export default class TagsPage extends Page {
oninit(vnode) {
super.oninit(vnode);
// A regular redraw won't work here, because sortable has mucked around
// with the DOM which will confuse Mithril's diffing algorithm. Instead
// we force a full reconstruction of the DOM by changing the key, which
// makes mithril completely re-render the component on redraw.
this.forcedRefreshKey = 0;
}
view() {
return (
<div className="TagsPage">
@ -43,18 +53,16 @@ export default class TagsPage extends Page {
{Button.component({
className: 'Button Button--primary',
icon: 'fas fa-plus',
children: app.translator.trans('flarum-tags.admin.tags.create_tag_button'),
onclick: () => app.modal.show(EditTagModal)
})}
}, app.translator.trans('flarum-tags.admin.tags.create_tag_button'))}
{Button.component({
className: 'Button',
children: app.translator.trans('flarum-tags.admin.tags.settings_button'),
onclick: () => app.modal.show(TagSettingsModal)
})}
}, app.translator.trans('flarum-tags.admin.tags.settings_button'))}
</div>
</div>
<div className="TagsPage-list">
<div className="container">
<div className="container" key={this.forcedRefreshKey} oncreate={this.onListOnCreate.bind(this)}>
<div className="TagGroup">
<label>{app.translator.trans('flarum-tags.admin.tags.primary_heading')}</label>
<ol className="TagList TagList--primary">
@ -79,80 +87,77 @@ export default class TagsPage extends Page {
);
}
config() {
this.$('.TagList').get().map(e => {
sortable.create(e, {
group: 'tags',
animation: 150,
swapThreshold: 0.65,
dragClass: 'sortable-dragging',
ghostClass: 'sortable-placeholder',
onSort: (e) => this.onSortUpdate(e)
})
});
onListOnCreate(vnode) {
this.$('.TagList').get().map(e => {
sortable.create(e, {
group: 'tags',
animation: 150,
swapThreshold: 0.65,
dragClass: 'sortable-dragging',
ghostClass: 'sortable-placeholder',
onSort: (e) => this.onSortUpdate(e)
})
});
}
onSortUpdate(e) {
// If we've moved a tag from 'primary' to 'secondary', then we'll update
// its attributes in our local store so that when we redraw the change
// will be made.
if (e.from instanceof HTMLOListElement && e.to instanceof HTMLUListElement) {
app.store.getById('tags', e.item.getAttribute('data-id')).pushData({
attributes: {
position: null,
isChild: false
},
relationships: {parent: null}
});
}
// If we've moved a tag from 'primary' to 'secondary', then we'll update
// its attributes in our local store so that when we redraw the change
// will be made.
if (e.from instanceof HTMLOListElement && e.to instanceof HTMLUListElement) {
app.store.getById('tags', e.item.getAttribute('data-id')).pushData({
attributes: {
position: null,
isChild: false
},
relationships: { parent: null }
});
}
// Construct an array of primary tag IDs and their children, in the same
// order that they have been arranged in.
const order = this.$('.TagList--primary > li')
.map(function() {
return {
id: $(this).data('id'),
children: $(this).find('li')
.map(function() {
return $(this).data('id');
}).get()
};
}).get();
// Construct an array of primary tag IDs and their children, in the same
// order that they have been arranged in.
const order = this.$('.TagList--primary > li')
.map(function () {
return {
id: $(this).data('id'),
children: $(this).find('li')
.map(function () {
return $(this).data('id');
}).get()
};
}).get();
// Now that we have an accurate representation of the order which the
// primary tags are in, we will update the tag attributes in our local
// store to reflect this order.
order.forEach((tag, i) => {
const parent = app.store.getById('tags', tag.id);
parent.pushData({
attributes: {
position: i,
isChild: false
},
relationships: {parent: null}
});
tag.children.forEach((child, j) => {
app.store.getById('tags', child).pushData({
attributes: {
position: j,
isChild: true
},
relationships: {parent}
});
});
// Now that we have an accurate representation of the order which the
// primary tags are in, we will update the tag attributes in our local
// store to reflect this order.
order.forEach((tag, i) => {
const parent = app.store.getById('tags', tag.id);
parent.pushData({
attributes: {
position: i,
isChild: false
},
relationships: { parent: null }
});
app.request({
url: app.forum.attribute('apiUrl') + '/tags/order',
method: 'POST',
data: {order}
tag.children.forEach((child, j) => {
app.store.getById('tags', child).pushData({
attributes: {
position: j,
isChild: true
},
relationships: { parent }
});
});
});
// A diff redraw won't work here, because sortable has mucked around
// with the DOM which will confuse Mithril's diffing algorithm. Instead
// we force a full reconstruction of the DOM.
m.redraw.strategy('all');
m.redraw();
app.request({
url: app.forum.attribute('apiUrl') + '/tags/order',
method: 'POST',
body: { order }
});
this.forcedRefreshKey++;
m.redraw();
}
}

View File

@ -17,8 +17,7 @@ export default function tagLabel(tag, attrs = {}) {
if (link) {
attrs.title = tag.description() || '';
attrs.href = app.route('tag', {tags: tag.slug()});
attrs.config = m.route;
attrs.route = app.route('tag', {tags: tag.slug()});
}
} else {
attrs.className += ' untagged';

View File

@ -12,7 +12,9 @@ export default function () {
if (tag) {
const parent = tag.parent();
const tags = parent ? [parent, tag] : [tag];
promise.then(component => component.tags = tags);
promise.then(composer => composer.fields.tags = tags);
} else {
app.composer.fields.tags = [];
}
});
@ -20,9 +22,9 @@ export default function () {
DiscussionComposer.prototype.tags = [];
DiscussionComposer.prototype.chooseTags = function () {
app.modal.show(TagDiscussionModal, {
selectedTags: this.tags.slice(0),
selectedTags: (this.composer.fields.tags || []).slice(0),
onsubmit: tags => {
this.tags = tags;
this.composer.fields.tags = tags;
this.$('textarea').focus();
}
});
@ -31,17 +33,19 @@ export default function () {
// Add a tag-selection menu to the discussion composer's header, after the
// title.
extend(DiscussionComposer.prototype, 'headerItems', function (items) {
const tags = this.composer.fields.tags || [];
items.add('tags', (
<a className="DiscussionComposer-changeTags" onclick={this.chooseTags.bind(this)}>
{this.tags.length
? tagsLabel(this.tags)
{tags.length
? tagsLabel(tags)
: <span className="TagLabel untagged">{app.translator.trans('flarum-tags.forum.composer_discussion.choose_tags_link')}</span>}
</a>
), 10);
});
override(DiscussionComposer.prototype, 'onsubmit', function (original) {
const chosenTags = this.tags;
const chosenTags = this.composer.fields.tags || [];
const chosenPrimaryTags = chosenTags.filter(tag => tag.position() !== null && !tag.isChild());
const chosenSecondaryTags = chosenTags.filter(tag => tag.position() === null);
if (!chosenTags.length
@ -50,7 +54,7 @@ export default function () {
app.modal.show(TagDiscussionModal, {
selectedTags: chosenTags,
onsubmit: tags => {
this.tags = tags;
this.composer.fields.tags = tags;
original();
}
});
@ -62,6 +66,6 @@ export default function () {
// Add the selected tags as data to submit to the server.
extend(DiscussionComposer.prototype, 'data', function (data) {
data.relationships = data.relationships || {};
data.relationships.tags = this.tags;
data.relationships.tags = this.composer.fields.tags;
});
}

View File

@ -8,11 +8,9 @@ export default function() {
// Add a control allowing the discussion to be moved to another category.
extend(DiscussionControls, 'moderationControls', function(items, discussion) {
if (discussion.canTag()) {
items.add('tags', Button.component({
children: app.translator.trans('flarum-tags.forum.discussion_controls.edit_tags_button'),
icon: 'fas fa-tag',
onclick: () => app.modal.show(TagDiscussionModal, {discussion})
}));
items.add('tags', <Button icon="fas fa-tag" onclick={() => app.modal.show(TagDiscussionModal, { discussion })}>
{app.translator.trans('flarum-tags.forum.discussion_controls.edit_tags_button')}
</Button>);
}
});
}

View File

@ -16,7 +16,7 @@ export default function() {
override(IndexPage.prototype, 'hero', function(original) {
const tag = this.currentTag();
if (tag) return TagHero.component({tag});
if (tag) return <TagHero model={tag} />;
return original();
});
@ -27,7 +27,7 @@ export default function() {
if (tag) vdom.attrs.className += ' IndexPage--tag'+tag.id();
});
extend(IndexPage.prototype, 'config', function() {
extend(IndexPage.prototype, 'setTitle', function() {
const tag = this.currentTag();
if (tag) {
@ -45,11 +45,11 @@ export default function() {
const canStartDiscussion = tag.canStartDiscussion();
if (color) {
items.get('newDiscussion').props.style = {backgroundColor: color};
items.get('newDiscussion').attrs.style = {backgroundColor: color};
}
items.get('newDiscussion').props.disabled = !canStartDiscussion;
items.get('newDiscussion').props.children = app.translator.trans(canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button');
items.get('newDiscussion').attrs.disabled = !canStartDiscussion;
items.get('newDiscussion').children = app.translator.trans(canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button');
}
});

View File

@ -8,7 +8,7 @@ import sortTags from '../common/utils/sortTags';
export default function() {
// Add tag labels to each discussion in the discussion list.
extend(DiscussionListItem.prototype, 'infoItems', function(items) {
const tags = this.props.discussion.tags();
const tags = this.attrs.discussion.tags();
if (tags && tags.length) {
items.add('tags', tagsLabel(tags), 10);
@ -17,7 +17,7 @@ export default function() {
// Restyle a discussion's hero to use its first tag's color.
extend(DiscussionHero.prototype, 'view', function(view) {
const tags = sortTags(this.props.discussion.tags());
const tags = sortTags(this.attrs.discussion.tags());
if (tags && tags.length) {
const color = tags[0].color();
@ -31,7 +31,7 @@ export default function() {
// Add a list of a discussion's tags to the discussion hero, displayed
// before the title. Put the title on its own line.
extend(DiscussionHero.prototype, 'items', function(items) {
const tags = this.props.discussion.tags();
const tags = this.attrs.discussion.tags();
if (tags && tags.length) {
items.add('tags', tagsLabel(tags, {link: true}), 5);

View File

@ -10,12 +10,11 @@ import sortTags from '../common/utils/sortTags';
export default function() {
// Add a link to the tags page, as well as a list of all the tags,
// to the index page's sidebar.
extend(IndexPage.prototype, 'navItems', function(items) {
items.add('tags', LinkButton.component({
icon: 'fas fa-th-large',
children: app.translator.trans('flarum-tags.forum.index.tags_link'),
href: app.route('tags')
}), -10);
extend(IndexPage.prototype, 'navItems', function (items) {
items.add('tags', <LinkButton icon="fas fa-th-large" href={app.route('tags')}>
{app.translator.trans('flarum-tags.forum.index.tags_link')}
</LinkButton>
, -10);
if (app.current.matches(TagsPage)) return;
@ -32,7 +31,12 @@ export default function() {
active = currentTag.parent() === tag;
}
items.add('tag' + tag.id(), TagLinkButton.component({tag, params, active}), -14);
// tag.name() is passed here as children even though it isn't used directly
// because when we need to get the active child in SelectDropdown, we need to
// use its children to populate the dropdown. The problem here is that `view`
// on TagLinkButton is only called AFTER SelectDropdown, so no children are available
// for SelectDropdown to use at the time.
items.add('tag' + tag.id(), TagLinkButton.component({model: tag, params, active}, tag?.name()), -14);
};
sortTags(tags)
@ -46,10 +50,9 @@ export default function() {
more.splice(0, 3).forEach(addTag);
if (more.length) {
items.add('moreTags', LinkButton.component({
children: app.translator.trans('flarum-tags.forum.index.more_link'),
href: app.route('tags')
}), -16);
items.add('moreTags', <LinkButton href={app.route('tags')}>
{app.translator.trans('flarum-tags.forum.index.more_link')}
</LinkButton>, -16)
}
});
}

View File

@ -2,11 +2,11 @@ import EventPost from 'flarum/components/EventPost';
import tagsLabel from '../../common/helpers/tagsLabel';
export default class DiscussionTaggedPost extends EventPost {
static initProps(props) {
super.initProps(props);
static initAttrs(attrs) {
super.initAttrs(attrs);
const oldTags = props.post.content()[0];
const newTags = props.post.content()[1];
const oldTags = attrs.post.content()[0];
const newTags = attrs.post.content()[1];
function diffTags(tags1, tags2) {
return tags1
@ -14,8 +14,8 @@ export default class DiscussionTaggedPost extends EventPost {
.map(id => app.store.getById('tags', id));
}
props.tagsAdded = diffTags(newTags, oldTags);
props.tagsRemoved = diffTags(oldTags, newTags);
attrs.tagsAdded = diffTags(newTags, oldTags);
attrs.tagsRemoved = diffTags(oldTags, newTags);
}
icon() {
@ -23,8 +23,8 @@ export default class DiscussionTaggedPost extends EventPost {
}
descriptionKey() {
if (this.props.tagsAdded.length) {
if (this.props.tagsRemoved.length) {
if (this.attrs.tagsAdded.length) {
if (this.attrs.tagsRemoved.length) {
return 'flarum-tags.forum.post_stream.added_and_removed_tags_text';
}
@ -37,17 +37,17 @@ export default class DiscussionTaggedPost extends EventPost {
descriptionData() {
const data = {};
if (this.props.tagsAdded.length) {
data.tagsAdded = app.translator.transChoice('flarum-tags.forum.post_stream.tags_text', this.props.tagsAdded.length, {
tags: tagsLabel(this.props.tagsAdded, {link: true}),
count: this.props.tagsAdded.length
if (this.attrs.tagsAdded.length) {
data.tagsAdded = app.translator.transChoice('flarum-tags.forum.post_stream.tags_text', this.attrs.tagsAdded.length, {
tags: tagsLabel(this.attrs.tagsAdded, {link: true}),
count: this.attrs.tagsAdded.length
});
}
if (this.props.tagsRemoved.length) {
data.tagsRemoved = app.translator.transChoice('flarum-tags.forum.post_stream.tags_text', this.props.tagsRemoved.length, {
tags: tagsLabel(this.props.tagsRemoved, {link: true}),
count: this.props.tagsRemoved.length
if (this.attrs.tagsRemoved.length) {
data.tagsRemoved = app.translator.transChoice('flarum-tags.forum.post_stream.tags_text', this.attrs.tagsRemoved.length, {
tags: tagsLabel(this.attrs.tagsRemoved, {link: true}),
count: this.attrs.tagsRemoved.length
});
}

View File

@ -11,13 +11,13 @@ import tagIcon from '../../common/helpers/tagIcon';
import sortTags from '../../common/utils/sortTags';
export default class TagDiscussionModal extends Modal {
init() {
super.init();
oninit(vnode) {
super.oninit(vnode);
this.tags = app.store.all('tags');
if (this.props.discussion) {
this.tags = this.tags.filter(tag => tag.canAddToDiscussion() || this.props.discussion.tags().indexOf(tag) !== -1);
if (this.attrs.discussion) {
this.tags = this.tags.filter(tag => tag.canAddToDiscussion() || this.attrs.discussion.tags().indexOf(tag) !== -1);
} else {
this.tags = this.tags.filter(tag => tag.canStartDiscussion());
}
@ -25,14 +25,14 @@ export default class TagDiscussionModal extends Modal {
this.tags = sortTags(this.tags);
this.selected = [];
this.filter = m.prop('');
this.filter = m.stream('');
this.index = this.tags[0].id();
this.focused = false;
if (this.props.selectedTags) {
this.props.selectedTags.map(this.addTag.bind(this));
} else if (this.props.discussion) {
this.props.discussion.tags().map(this.addTag.bind(this));
if (this.attrs.selectedTags) {
this.attrs.selectedTags.map(this.addTag.bind(this));
} else if (this.attrs.discussion) {
this.attrs.discussion.tags().map(this.addTag.bind(this));
}
this.minPrimary = app.forum.attribute('minPrimaryTags');
@ -100,8 +100,8 @@ export default class TagDiscussionModal extends Modal {
}
title() {
return this.props.discussion
? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', {title: <em>{this.props.discussion.title()}</em>})
return this.attrs.discussion
? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', {title: <em>{this.attrs.discussion.title()}</em>})
: app.translator.trans('flarum-tags.forum.choose_tags.title');
}
@ -169,22 +169,17 @@ export default class TagDiscussionModal extends Modal {
</span>
<input className="FormControl"
placeholder={extractText(this.getInstruction(primaryCount, secondaryCount))}
value={this.filter()}
bidi={this.filter}
style={{ width: inputWidth + 'ch' }}
oninput={m.withAttr('value', this.filter)}
onkeydown={this.navigator.navigate.bind(this.navigator)}
onfocus={() => this.focused = true}
onblur={() => this.focused = false}/>
</div>
</div>
<div className="TagDiscussionModal-form-submit App-primaryControl">
{Button.component({
type: 'submit',
className: 'Button Button--primary',
disabled: primaryCount < this.minPrimary || secondaryCount < this.minSecondary,
icon: 'fas fa-check',
children: app.translator.trans('flarum-tags.forum.choose_tags.submit_button')
})}
<Button type="submit" className="Button Button--primary" disabled={primaryCount < this.minPrimary || secondaryCount < this.minSecondary} icon="fas fa-check">
{app.translator.trans('flarum-tags.forum.choose_tags.submit_button')}
</Button>
</div>
</div>
</div>,
@ -304,7 +299,7 @@ export default class TagDiscussionModal extends Modal {
onsubmit(e) {
e.preventDefault();
const discussion = this.props.discussion;
const discussion = this.attrs.discussion;
const tags = this.selected;
if (discussion) {
@ -317,10 +312,8 @@ export default class TagDiscussionModal extends Modal {
});
}
if (this.props.onsubmit) this.props.onsubmit(tags);
if (this.attrs.onsubmit) this.attrs.onsubmit(tags);
app.modal.close();
m.redraw.strategy('none');
this.hide();
}
}

View File

@ -3,7 +3,7 @@ import tagIcon from '../../common/helpers/tagIcon';
export default class TagHero extends Component {
view() {
const tag = this.props.tag;
const tag = this.attrs.model;
const color = tag.color();
return (

View File

@ -1,27 +1,35 @@
import LinkButton from 'flarum/components/LinkButton';
import classList from 'flarum/utils/classList';
import tagIcon from '../../common/helpers/tagIcon';
export default class TagLinkButton extends LinkButton {
view() {
const tag = this.props.tag;
const active = this.constructor.isActive(this.props);
view(vnode) {
const tag = this.attrs.model;
const active = this.constructor.isActive(this.attrs);
const description = tag && tag.description();
const className = classList([
'TagLinkButton',
'hasIcon',
this.attrs.className,
tag.isChild() && 'child',
]);
return (
<a className={'TagLinkButton hasIcon ' + (tag.isChild() ? 'child' : '')} href={this.props.href} config={m.route}
<a className={className} route={this.attrs.route}
style={active && tag ? {color: tag.color()} : ''}
title={description || ''}>
{tagIcon(tag, {className: 'Button-icon'})}
{this.props.children}
{tag ? tag.name() : app.translator.trans('flarum-tags.forum.index.untagged_link')}
</a>
);
}
static initProps(props) {
const tag = props.tag;
static initAttrs(attrs) {
super.initAttrs(attrs);
props.params.tags = tag ? tag.slug() : 'untagged';
props.href = app.route('tag', props.params);
props.children = tag ? tag.name() : app.translator.trans('flarum-tags.forum.index.untagged_link');
const tag = attrs.model;
attrs.params.tags = tag ? tag.slug() : 'untagged';
attrs.route = app.route('tag', attrs.params);
}
}

View File

@ -7,8 +7,8 @@ import tagLabel from '../../common/helpers/tagLabel';
import sortTags from '../../common/utils/sortTags';
export default class TagsPage extends Page {
init() {
super.init();
oninit(vnode) {
super.oninit(vnode);
this.tags = sortTags(app.store.all('tags').filter(tag => !tag.parent()));
@ -23,7 +23,7 @@ export default class TagsPage extends Page {
<div className="TagsPage">
{IndexPage.prototype.hero()}
<div className="container">
<nav className="TagsPage-nav IndexPage-nav sideNav" config={IndexPage.prototype.affixSidebar}>
<nav className="TagsPage-nav IndexPage-nav sideNav">
<ul>{listItems(IndexPage.prototype.sidebarItems().toArray())}</ul>
</nav>
@ -36,18 +36,14 @@ export default class TagsPage extends Page {
return (
<li className={'TagTile ' + (tag.color() ? 'colored' : '')}
style={{backgroundColor: tag.color()}}>
<a className="TagTile-info" href={app.route.tag(tag)} config={m.route}>
<a className="TagTile-info" route={app.route.tag(tag)}>
<h3 className="TagTile-name">{tag.name()}</h3>
<p className="TagTile-description">{tag.description()}</p>
{children
? (
<div className="TagTile-children">
{children.map(child => [
<a href={app.route.tag(child)} config={function(element, isInitialized) {
if (isInitialized) return;
$(element).on('click', e => e.stopPropagation());
m.route.apply(this, arguments);
}}>
<a route={app.route.tag(child)}>
{child.name()}
</a>,
' '
@ -58,8 +54,8 @@ export default class TagsPage extends Page {
{lastPostedDiscussion
? (
<a className="TagTile-lastPostedDiscussion"
href={app.route.discussion(lastPostedDiscussion, lastPostedDiscussion.lastPostNumber())}
config={m.route}>
route={app.route.discussion(lastPostedDiscussion, lastPostedDiscussion.lastPostNumber())}
>
<span className="TagTile-lastPostedDiscussion-title">{lastPostedDiscussion.title()}</span>
{humanTime(lastPostedDiscussion.lastPostedAt())}
</a>
@ -85,8 +81,8 @@ export default class TagsPage extends Page {
);
}
config(...args) {
super.config(...args);
oncreate(vnode) {
super.oncreate(vnode);
app.setTitle(app.translator.trans('flarum-tags.forum.all_tags.meta_title_text'));
app.setTitleCount(0);

View File

@ -13,8 +13,8 @@ import addTagControl from './addTagControl';
import addTagComposer from './addTagComposer';
app.initializers.add('flarum-tags', function(app) {
app.routes.tags = {path: '/tags', component: TagsPage.component()};
app.routes.tag = {path: '/t/:tags', component: IndexPage.component()};
app.routes.tags = {path: '/tags', component: TagsPage };
app.routes.tag = {path: '/t/:tags', component: IndexPage };
app.route.tag = tag => app.route('tag', {tags: tag.slug()});
@ -37,4 +37,4 @@ app.initializers.add('flarum-tags', function(app) {
import tagsCompat from './compat';
import { compat } from '@flarum/core/forum';
Object.assign(compat, tagsCompat);
Object.assign(compat, tagsCompat);