FEATURE: Add filter + alpha & numeric sort to categories and tags in docs sidebar (#62)
* UX: Docs sidebar changes Co-authored-by: Jarek Radosz <jradosz@gmail.com> Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
parent
0f78bcc9db
commit
d8c48e6218
|
@ -1,10 +1,12 @@
|
|||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
import { alias, equal, readOnly } from "@ember/object/computed";
|
||||
import { alias, equal, gt, readOnly } from "@ember/object/computed";
|
||||
import Docs from "discourse/plugins/discourse-docs/discourse/models/docs";
|
||||
import { getOwner } from "@ember/application";
|
||||
|
||||
const SHOW_FILTER_AT = 10;
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
ascending: "ascending",
|
||||
|
@ -31,6 +33,14 @@ export default Controller.extend({
|
|||
ascending: null,
|
||||
orderColumn: null,
|
||||
|
||||
showCategoryFilter: gt("categories.length", SHOW_FILTER_AT),
|
||||
categoryFilter: "",
|
||||
categorySort: {},
|
||||
|
||||
showTagFilter: gt("tags.length", SHOW_FILTER_AT),
|
||||
tagFilter: "",
|
||||
tagSort: {},
|
||||
|
||||
loadMoreUrl: alias("model.topics.load_more_url"),
|
||||
categories: readOnly("model.categories"),
|
||||
topics: alias("model.topics.topic_list.topics"),
|
||||
|
@ -43,6 +53,106 @@ export default Controller.extend({
|
|||
if (!this.site.mobileView) {
|
||||
this.set("expandedFilters", true);
|
||||
}
|
||||
this.setProperties({
|
||||
categorySort: {
|
||||
type: "numeric", // or alpha
|
||||
direction: "desc", // or asc
|
||||
},
|
||||
tagSort: {
|
||||
type: "numeric", // or alpha
|
||||
direction: "desc", // or asc
|
||||
},
|
||||
});
|
||||
},
|
||||
@discourseComputed("categories", "categorySort", "categoryFilter")
|
||||
sortedCategories(categories, categorySort, filter) {
|
||||
let { type, direction } = categorySort;
|
||||
if (type === "numeric") {
|
||||
categories = categories.sort((a, b) => a.count - b.count);
|
||||
} else {
|
||||
categories = categories.sort((a, b) => {
|
||||
const first = this.site.categories
|
||||
.findBy("id", a.id)
|
||||
.name.toLowerCase(),
|
||||
second = this.site.categories.findBy("id", b.id).name.toLowerCase();
|
||||
return first.localeCompare(second);
|
||||
});
|
||||
}
|
||||
|
||||
if (direction === "desc") {
|
||||
categories = categories.reverse();
|
||||
}
|
||||
|
||||
if (this.showCategoryFilter) {
|
||||
return categories.filter((category) => {
|
||||
let categoryData = this.site.categories.findBy("id", category.id);
|
||||
return (
|
||||
categoryData.name.toLowerCase().indexOf(filter.toLowerCase()) > -1 ||
|
||||
(categoryData.description_excerpt &&
|
||||
categoryData.description_excerpt
|
||||
.toLowerCase()
|
||||
.indexOf(filter.toLowerCase()) > -1)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return categories;
|
||||
},
|
||||
|
||||
@discourseComputed("categorySort")
|
||||
categorySortNumericIcon(catSort) {
|
||||
if (catSort.type === "numeric" && catSort.direction === "asc") {
|
||||
return "sort-numeric-down";
|
||||
}
|
||||
return "sort-numeric-up";
|
||||
},
|
||||
|
||||
@discourseComputed("categorySort")
|
||||
categorySortAlphaIcon(catSort) {
|
||||
if (catSort.type === "alpha" && catSort.direction === "asc") {
|
||||
return "sort-alpha-down";
|
||||
}
|
||||
return "sort-alpha-up";
|
||||
},
|
||||
|
||||
@discourseComputed("tags", "tagSort", "tagFilter")
|
||||
sortedTags(tags, tagSort, filter) {
|
||||
let { type, direction } = tagSort;
|
||||
if (type === "numeric") {
|
||||
tags = tags.sort((a, b) => a.count - b.count);
|
||||
} else {
|
||||
tags = tags.sort((a, b) => {
|
||||
return a.id.toLowerCase().localeCompare(b.id.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
if (direction === "desc") {
|
||||
tags = tags.reverse();
|
||||
}
|
||||
|
||||
if (this.showTagFilter) {
|
||||
return tags.filter((tag) => {
|
||||
return tag.id.toLowerCase().indexOf(filter.toLowerCase()) > -1;
|
||||
});
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
|
||||
@discourseComputed("tagSort")
|
||||
tagSortNumericIcon(tagSort) {
|
||||
if (tagSort.type === "numeric" && tagSort.direction === "asc") {
|
||||
return "sort-numeric-down";
|
||||
}
|
||||
return "sort-numeric-up";
|
||||
},
|
||||
|
||||
@discourseComputed("tagSort")
|
||||
tagSortAlphaIcon(tagSort) {
|
||||
if (tagSort.type === "alpha" && tagSort.direction === "asc") {
|
||||
return "sort-alpha-down";
|
||||
}
|
||||
return "sort-alpha-up";
|
||||
},
|
||||
|
||||
@discourseComputed("topics", "isSearching", "filterSolved")
|
||||
|
@ -79,6 +189,31 @@ export default Controller.extend({
|
|||
return !!filterTags;
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
shouldShowTags() {
|
||||
return this.siteSettings.tagging_enabled;
|
||||
},
|
||||
|
||||
@action
|
||||
toggleCategorySort(newType) {
|
||||
let { type, direction } = this.categorySort;
|
||||
this.set("categorySort", {
|
||||
type: newType,
|
||||
direction:
|
||||
type === newType ? (direction === "asc" ? "desc" : "asc") : "asc",
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
toggleTagSort(newType) {
|
||||
let { type, direction } = this.tagSort;
|
||||
this.set("tagSort", {
|
||||
type: newType,
|
||||
direction:
|
||||
type === newType ? (direction === "asc" ? "desc" : "asc") : "asc",
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
setSelectedTopic(topicId) {
|
||||
this.set("selectedTopic", topicId);
|
||||
|
|
|
@ -26,25 +26,59 @@
|
|||
|
||||
{{#if categories}}
|
||||
<div class="docs-items docs-categories">
|
||||
<h3>{{i18n "docs.categories"}}</h3>
|
||||
{{#each categories as |category|}}
|
||||
{{docs-category
|
||||
category=category
|
||||
selectCategory=(action "updateSelectedCategories" category)
|
||||
<section class="item-controls">
|
||||
<h3>{{i18n "docs.categories"}}</h3>
|
||||
<div class="item-controls-buttons">
|
||||
{{d-button class=(if (eq categorySort.type "alpha") "categories-alphabet active" "categories-alphabet") icon=categorySortAlphaIcon action=toggleCategorySort actionParam="alpha"}}
|
||||
{{d-button class=(if (eq categorySort.type "numeric") "categories-amount active" "categories-amount") icon=categorySortNumericIcon action=toggleCategorySort actionParam="numeric"}}
|
||||
</div>
|
||||
</section>
|
||||
{{#if showCategoryFilter}}
|
||||
{{input
|
||||
value=categoryFilter
|
||||
class="filter"
|
||||
placeholderKey="docs.categories_filter_placeholder"
|
||||
}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
<ul>
|
||||
{{#each sortedCategories as |category|}}
|
||||
<li>
|
||||
{{docs-category
|
||||
category=category
|
||||
selectCategory=(action "updateSelectedCategories" category)
|
||||
}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if tags}}
|
||||
{{#if (and tags shouldShowTags)}}
|
||||
<div class="docs-items docs-tags">
|
||||
<h3>{{i18n "docs.tags"}}</h3>
|
||||
{{#each tags as |tag|}}
|
||||
{{docs-tag
|
||||
tag=tag
|
||||
selectTag=(action "updateSelectedTags" tag)
|
||||
<section class="item-controls">
|
||||
<h3>{{i18n "docs.tags"}}</h3>
|
||||
<div class="item-controls-buttons">
|
||||
{{d-button class=(if (eq tagSort.type "alpha") "tags-alphabet active" "tags-alphabet") icon=tagSortAlphaIcon action=toggleTagSort actionParam="alpha"}}
|
||||
{{d-button class=(if (eq tagSort.type "numeric") "tags-amount active" "tags-amount") icon=tagSortNumericIcon action=toggleTagSort actionParam="numeric"}}
|
||||
</div>
|
||||
</section>
|
||||
{{#if showTagFilter}}
|
||||
{{input
|
||||
value=tagFilter
|
||||
class="filter"
|
||||
placeholderKey="docs.tags_filter_placeholder"
|
||||
}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
<ul>
|
||||
{{#each sortedTags as |tag|}}
|
||||
<li>
|
||||
{{docs-tag
|
||||
tag=tag
|
||||
selectTag=(action "updateSelectedTags" tag)
|
||||
}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -75,7 +75,6 @@
|
|||
}
|
||||
|
||||
h3 {
|
||||
padding-left: 0.35em;
|
||||
font-size: $font-up-1;
|
||||
}
|
||||
|
||||
|
@ -203,3 +202,35 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.docs-items {
|
||||
.item-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.btn {
|
||||
background-color: transparent;
|
||||
padding: 0.25em;
|
||||
svg {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: var(--secondary-very-high);
|
||||
svg {
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.docs-items {
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ en:
|
|||
activity: "Activity"
|
||||
no_topics: "No topics in Docs."
|
||||
categories: "Categories"
|
||||
categories_filter_placeholder: "Filter categories"
|
||||
tags_filter_placeholder: "Filter tags"
|
||||
tags: "Tags"
|
||||
search:
|
||||
results:
|
||||
|
|
|
@ -11,6 +11,11 @@ enabled_site_setting :docs_enabled
|
|||
register_asset 'stylesheets/common/docs.scss'
|
||||
register_asset 'stylesheets/mobile/docs.scss'
|
||||
|
||||
register_svg_icon 'sort-alpha-down'
|
||||
register_svg_icon 'sort-alpha-up'
|
||||
register_svg_icon 'sort-numeric-up'
|
||||
register_svg_icon 'sort-numeric-down'
|
||||
|
||||
load File.expand_path('lib/docs/engine.rb', __dir__)
|
||||
load File.expand_path('lib/docs/query.rb', __dir__)
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ acceptance("Docs", function (needs) {
|
|||
});
|
||||
|
||||
test("index page", async function (assert) {
|
||||
this.siteSettings.tagging_enabled = true;
|
||||
|
||||
await visit("/");
|
||||
await click("#toggle-hamburger-menu");
|
||||
await click(".docs-link");
|
||||
|
|
Loading…
Reference in New Issue