diff --git a/extensions/tags/composer.json b/extensions/tags/composer.json index 29fe795f1..d5e85690b 100644 --- a/extensions/tags/composer.json +++ b/extensions/tags/composer.json @@ -22,6 +22,12 @@ }, "flarum-extension": { "title": "Tags", + "category": "discussion", + "info": { + "donate": "https://flarum.org/donate/", + "website": "https://flarum.org", + "support": "https://discuss.flarum.org" + }, "icon": { "name": "fas fa-tags", "backgroundColor": "#F28326", diff --git a/extensions/tags/js/src/admin/addTagPermission.js b/extensions/tags/js/src/admin/addTagPermission.js index 9278ec133..6188830d6 100644 --- a/extensions/tags/js/src/admin/addTagPermission.js +++ b/extensions/tags/js/src/admin/addTagPermission.js @@ -1,12 +1,9 @@ -import { extend } from 'flarum/extend'; -import PermissionGrid from 'flarum/components/PermissionGrid'; - -export default function() { - extend(PermissionGrid.prototype, 'moderateItems', items => { - items.add('tag', { +export default function () { + app.extensionData + .for('flarum-tags') + .registerPermission({ icon: 'fas fa-tag', label: app.translator.trans('flarum-tags.admin.permissions.tag_discussions_label'), - permission: 'discussion.tag' - }, 95); - }); + permission: 'discussion.tag', + }, 'moderate', 95); } diff --git a/extensions/tags/js/src/admin/addTagsPane.js b/extensions/tags/js/src/admin/addTagsPane.js deleted file mode 100644 index 12bf1c606..000000000 --- a/extensions/tags/js/src/admin/addTagsPane.js +++ /dev/null @@ -1,19 +0,0 @@ -import { extend } from 'flarum/extend'; -import AdminNav from 'flarum/components/AdminNav'; -import AdminLinkButton from 'flarum/components/AdminLinkButton'; - -import TagsPage from './components/TagsPage'; - -export default function() { - app.routes.tags = {path: '/tags', component: TagsPage}; - - 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', - description: app.translator.trans('flarum-tags.admin.nav.tags_text') - }, app.translator.trans('flarum-tags.admin.nav.tags_button'))); - }); -} diff --git a/extensions/tags/js/src/admin/compat.js b/extensions/tags/js/src/admin/compat.js index 2b948615a..025584d94 100644 --- a/extensions/tags/js/src/admin/compat.js +++ b/extensions/tags/js/src/admin/compat.js @@ -2,8 +2,6 @@ import compat from '../common/compat'; import addTagsHomePageOption from './addTagsHomePageOption'; import addTagChangePermission from './addTagChangePermission'; -import addTagsPane from './addTagsPane'; -import TagSettingsModal from './components/TagSettingsModal'; import TagsPage from './components/TagsPage'; import EditTagModal from './components/EditTagModal'; import addTagPermission from './addTagPermission'; @@ -12,8 +10,6 @@ import addTagsPermissionScope from './addTagsPermissionScope'; export default Object.assign(compat, { 'tags/addTagsHomePageOption': addTagsHomePageOption, 'tags/addTagChangePermission': addTagChangePermission, - 'tags/addTagsPane': addTagsPane, - 'tags/components/TagSettingsModal': TagSettingsModal, 'tags/components/TagsPage': TagsPage, 'tags/components/EditTagModal': EditTagModal, 'tags/addTagPermission': addTagPermission, diff --git a/extensions/tags/js/src/admin/components/EditTagModal.js b/extensions/tags/js/src/admin/components/EditTagModal.js index c1fa5dd18..acd2cd3f4 100644 --- a/extensions/tags/js/src/admin/components/EditTagModal.js +++ b/extensions/tags/js/src/admin/components/EditTagModal.js @@ -22,6 +22,7 @@ export default class EditTagModal extends Modal { this.color = Stream(this.tag.color() || ''); this.icon = Stream(this.tag.icon() || ''); this.isHidden = Stream(this.tag.isHidden() || false); + this.primary = Stream(this.attrs.primary || false); } className() { @@ -114,7 +115,8 @@ export default class EditTagModal extends Modal { description: this.description(), color: this.color(), icon: this.icon(), - isHidden: this.isHidden() + isHidden: this.isHidden(), + primary: this.primary(), }; } diff --git a/extensions/tags/js/src/admin/components/TagSettingsModal.js b/extensions/tags/js/src/admin/components/TagSettingsModal.js deleted file mode 100644 index b73675298..000000000 --- a/extensions/tags/js/src/admin/components/TagSettingsModal.js +++ /dev/null @@ -1,65 +0,0 @@ -import SettingsModal from 'flarum/components/SettingsModal'; -import withAttr from 'flarum/utils/withAttr'; - -export default class TagSettingsModal extends SettingsModal { - setMinTags(minTags, maxTags, value) { - minTags(value); - maxTags(Math.max(value, maxTags())); - } - - className() { - return 'TagSettingsModal Modal--small'; - } - - title() { - return app.translator.trans('flarum-tags.admin.tag_settings.title'); - } - - form() { - const minPrimaryTags = this.setting('flarum-tags.min_primary_tags', 0); - const maxPrimaryTags = this.setting('flarum-tags.max_primary_tags', 0); - - const minSecondaryTags = this.setting('flarum-tags.min_secondary_tags', 0); - const maxSecondaryTags = this.setting('flarum-tags.max_secondary_tags', 0); - - return [ -
- -
- {app.translator.trans('flarum-tags.admin.tag_settings.required_primary_text')} -
-
- - {app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')} - -
-
, - -
- -
- {app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_text')} -
-
- - {app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')} - -
-
- ]; - } -} diff --git a/extensions/tags/js/src/admin/components/TagsPage.js b/extensions/tags/js/src/admin/components/TagsPage.js index 7d7b9b7e6..630b026e9 100644 --- a/extensions/tags/js/src/admin/components/TagsPage.js +++ b/extensions/tags/js/src/admin/components/TagsPage.js @@ -1,10 +1,10 @@ import sortable from 'sortablejs'; -import Page from 'flarum/components/Page'; +import ExtensionPage from 'flarum/components/ExtensionPage'; import Button from 'flarum/components/Button'; +import withAttr from 'flarum/utils/withAttr'; import EditTagModal from './EditTagModal'; -import TagSettingsModal from './TagSettingsModal'; import tagIcon from '../../common/helpers/tagIcon'; import sortTags from '../../common/utils/sortTags'; @@ -31,7 +31,7 @@ function tagItem(tag) { ); } -export default class TagsPage extends Page { +export default class TagsPage extends ExtensionPage { oninit(vnode) { super.oninit(vnode); @@ -42,44 +42,89 @@ export default class TagsPage extends Page { this.forcedRefreshKey = 0; } - view() { + content() { + const minPrimaryTags = this.setting('flarum-tags.min_primary_tags', 0); + const maxPrimaryTags = this.setting('flarum-tags.max_primary_tags', 0); + + const minSecondaryTags = this.setting('flarum-tags.min_secondary_tags', 0); + const maxSecondaryTags = this.setting('flarum-tags.max_secondary_tags', 0); + return ( -
-
-
-

- {app.translator.trans('flarum-tags.admin.tags.about_tags_text')} -

- {Button.component({ - className: 'Button Button--primary', - icon: 'fas fa-plus', - onclick: () => app.modal.show(EditTagModal) - }, app.translator.trans('flarum-tags.admin.tags.create_tag_button'))} - {Button.component({ - className: 'Button', - onclick: () => app.modal.show(TagSettingsModal) - }, app.translator.trans('flarum-tags.admin.tags.settings_button'))} -
-
-
-
+
+
+
    {sortTags(app.store.all('tags')) - .filter(tag => tag.position() !== null && !tag.isChild()) + .filter((tag) => tag.position() !== null && !tag.isChild()) .map(tagItem)}
+ {Button.component( + { + className: 'Button TagList-button', + icon: 'fas fa-plus', + onclick: () => app.modal.show(EditTagModal, { primary: true }), + }, + app.translator.trans('flarum-tags.admin.tags.create_primary_tag_button') + )}
-
+
    - {app.store.all('tags') - .filter(tag => tag.position() === null) + {app.store + .all('tags') + .filter((tag) => tag.position() === null) .sort((a, b) => a.name().localeCompare(b.name())) .map(tagItem)}
+ {Button.component( + { + className: 'Button TagList-button', + icon: 'fas fa-plus', + onclick: () => app.modal.show(EditTagModal, { primary: false }), + }, + app.translator.trans('flarum-tags.admin.tags.create_secondary_tag_button') + )} +
+
+ +
+ +
{app.translator.trans('flarum-tags.admin.tag_settings.required_primary_text')}
+
+ + {app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')} + +
+
+
+ +
{app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_text')}
+
+ + {app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')} + +
+
+
{this.submitButton()}
+
+
+
+

{app.translator.trans('flarum-tags.admin.tags.about_tags_text')}

@@ -100,6 +145,11 @@ export default class TagsPage extends Page { }); } + setMinTags(minTags, maxTags, value) { + minTags(value); + maxTags(Math.max(value, maxTags())); + } + 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 diff --git a/extensions/tags/js/src/admin/index.js b/extensions/tags/js/src/admin/index.js index db8aaf45d..4c146ae18 100644 --- a/extensions/tags/js/src/admin/index.js +++ b/extensions/tags/js/src/admin/index.js @@ -1,16 +1,17 @@ import Tag from '../common/models/Tag'; import addTagsPermissionScope from './addTagsPermissionScope'; import addTagPermission from './addTagPermission'; -import addTagsPane from './addTagsPane'; import addTagsHomePageOption from './addTagsHomePageOption'; import addTagChangePermission from './addTagChangePermission'; +import TagsPage from './components/TagsPage'; app.initializers.add('flarum-tags', app => { app.store.models.tags = Tag; + app.extensionData.for('flarum-tags').registerPage(TagsPage); + addTagsPermissionScope(); addTagPermission(); - addTagsPane(); addTagsHomePageOption(); addTagChangePermission(); }); diff --git a/extensions/tags/less/admin.less b/extensions/tags/less/admin.less index 13df8f305..0aeeefb87 100644 --- a/extensions/tags/less/admin.less +++ b/extensions/tags/less/admin.less @@ -3,7 +3,6 @@ @import "admin/TagsPage"; @import "admin/EditTagModal"; -@import "admin/TagSettingsModal"; .Dropdown--restrictByTag .Dropdown-menu { max-height: 400px; diff --git a/extensions/tags/less/admin/TagSettingsModal.less b/extensions/tags/less/admin/TagSettingsModal.less deleted file mode 100644 index 6522257c6..000000000 --- a/extensions/tags/less/admin/TagSettingsModal.less +++ /dev/null @@ -1,16 +0,0 @@ -.TagSettingsModal { - .Form-group:not(:last-child) { - margin-bottom: 30px; - } -} -.TagSettingsModal-rangeInput { - input { - width: 80px; - display: inline; - margin: 0 5px; - - &:first-child { - margin-left: 0; - } - } -} diff --git a/extensions/tags/less/admin/TagsPage.less b/extensions/tags/less/admin/TagsPage.less index 06c2197b0..843851603 100644 --- a/extensions/tags/less/admin/TagsPage.less +++ b/extensions/tags/less/admin/TagsPage.less @@ -1,43 +1,45 @@ -.TagsPage-header { - background: @control-bg; +.flarum-tags-Page { + padding-bottom: 140px; +} + +.TagsContent-footer { color: @control-color; padding: 20px 0; - .container { - max-width: 600px; - } p { - margin-bottom: 20px; - } - .Button { - margin-right: 10px; + margin-top: 10px; } } -.TagsPage-list { - padding: 20px 0; - .container { - max-width: 600px; - } +.TagsContent-list { + padding: 20px 0 0; + } -.TagList, .TagList ol { + +.TagList, +.TagList ol { list-style: none; - padding: 10px 0; - margin: 0; + padding: 0; color: @muted-color; font-size: 13px; - > li { + >li { + display: inline-block; + max-height: 40px; cursor: move; + width: 100%; } - .TagIcon, .icon { + .TagIcon, + .icon { margin-right: 10px; } } + .TagListItem-info { - padding: 5px 10px; border-radius: @border-radius; + padding: 5px; + max-width: max-content; &:hover { background: @control-bg; @@ -46,45 +48,110 @@ .Button { float: right; visibility: hidden; - margin: -8px -10px -8px 10px; + margin: -8px -16px -8px 16px; } } -li:not(.sortable-dragging) > .TagListItem-info:hover > .Button { + +li:not(.sortable-dragging)>.TagListItem-info:hover>.Button { visibility: visible; } + .TagList--primary { font-size: 16px; - > .sortable-placeholder { - height: 34px; + >.sortable-placeholder { + height: 38px; margin-bottom: 10px; } } + .TagList ol { margin-left: 27px; min-height: 10px; padding: 0; - & > :last-child { + &> :last-child { margin-bottom: 10px; } } + .sortable-placeholder { border: 2px dashed @control-bg; border-radius: @border-radius; - height: 29px; + height: 34px; + max-width: max-content; } -.TagGroup { - padding-left: 150px; +.SettingsGroups { + display: flex; + column-count: 3; + column-gap: 30px; + flex-wrap: wrap; - &:first-child { - border-bottom: 2px solid @control-bg; + @media (@tablet-up) { + .TagGroup--secondary { + max-width: 250px !important; + } } - > label { - margin-left: -150px; - float: left; - font-weight: bold; - margin-top: 14px; + + .Form { + min-width: 300px; + + >label { + margin-bottom: 10px; + } + + .TagSettings-rangeInput { + input { + width: 80px; + display: inline; + margin: 0 5px; + + &:first-child { + margin-left: 0; + } + } + } + } + + .TagGroup, + .Form { + display: inline-grid; + padding: 10px 20px; + min-height: 20vh; + max-width: 400px; + grid-template-rows: min-content; + border: 1px solid @control-bg; + border-radius: @border-radius; + flex: 1 1 160px; + + @media (max-width: 1209px) { + margin-bottom: 20px; + } + + >ol { + >li { + margin-top: 8px; + + .Button { + float: right; + visibility: hidden; + margin: -8px -16px -8px 16px; + } + } + } + + .TagList-button { + background: none; + border: 1px dashed @control-bg; + height: 40px; + margin: auto auto 0 0; + } + + >label { + float: left; + font-weight: bold; + color: @muted-color; + } } } diff --git a/extensions/tags/src/Command/CreateTagHandler.php b/extensions/tags/src/Command/CreateTagHandler.php index 08680531e..1349d2f09 100644 --- a/extensions/tags/src/Command/CreateTagHandler.php +++ b/extensions/tags/src/Command/CreateTagHandler.php @@ -50,11 +50,12 @@ class CreateTagHandler ); $parentId = Arr::get($data, 'relationships.parent.data.id'); + $primary = Arr::get($data, 'attributes.primary'); - if ($parentId !== null) { + if ($parentId !== null || $primary) { $rootTags = Tag::whereNull('parent_id')->whereNotNull('position'); - if ($parentId === 0) { + if ($parentId === 0 || $primary) { $tag->position = $rootTags->max('position') + 1; } elseif ($rootTags->find($parentId)) { $position = Tag::where('parent_id', $parentId)->max('position');