mirror of
https://github.com/flarum/framework.git
synced 2025-02-27 19:15:41 +08:00
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:
parent
bf223071f2
commit
f61b5201d1
@ -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')));
|
||||
});
|
||||
}
|
||||
|
@ -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>);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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')}
|
||||
|
@ -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"
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
@ -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>);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 (
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user