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 Controller, { inject as controller } from "@ember/controller";
|
||||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||||
import { action } from "@ember/object";
|
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 Docs from "discourse/plugins/discourse-docs/discourse/models/docs";
|
||||||
import { getOwner } from "@ember/application";
|
import { getOwner } from "@ember/application";
|
||||||
|
|
||||||
|
const SHOW_FILTER_AT = 10;
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
ascending: "ascending",
|
ascending: "ascending",
|
||||||
|
@ -31,6 +33,14 @@ export default Controller.extend({
|
||||||
ascending: null,
|
ascending: null,
|
||||||
orderColumn: 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"),
|
loadMoreUrl: alias("model.topics.load_more_url"),
|
||||||
categories: readOnly("model.categories"),
|
categories: readOnly("model.categories"),
|
||||||
topics: alias("model.topics.topic_list.topics"),
|
topics: alias("model.topics.topic_list.topics"),
|
||||||
|
@ -43,6 +53,106 @@ export default Controller.extend({
|
||||||
if (!this.site.mobileView) {
|
if (!this.site.mobileView) {
|
||||||
this.set("expandedFilters", true);
|
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")
|
@discourseComputed("topics", "isSearching", "filterSolved")
|
||||||
|
@ -79,6 +189,31 @@ export default Controller.extend({
|
||||||
return !!filterTags;
|
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
|
@action
|
||||||
setSelectedTopic(topicId) {
|
setSelectedTopic(topicId) {
|
||||||
this.set("selectedTopic", topicId);
|
this.set("selectedTopic", topicId);
|
||||||
|
|
|
@ -26,25 +26,59 @@
|
||||||
|
|
||||||
{{#if categories}}
|
{{#if categories}}
|
||||||
<div class="docs-items docs-categories">
|
<div class="docs-items docs-categories">
|
||||||
<h3>{{i18n "docs.categories"}}</h3>
|
<section class="item-controls">
|
||||||
{{#each categories as |category|}}
|
<h3>{{i18n "docs.categories"}}</h3>
|
||||||
{{docs-category
|
<div class="item-controls-buttons">
|
||||||
category=category
|
{{d-button class=(if (eq categorySort.type "alpha") "categories-alphabet active" "categories-alphabet") icon=categorySortAlphaIcon action=toggleCategorySort actionParam="alpha"}}
|
||||||
selectCategory=(action "updateSelectedCategories" category)
|
{{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>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if tags}}
|
{{#if (and tags shouldShowTags)}}
|
||||||
<div class="docs-items docs-tags">
|
<div class="docs-items docs-tags">
|
||||||
<h3>{{i18n "docs.tags"}}</h3>
|
<section class="item-controls">
|
||||||
{{#each tags as |tag|}}
|
<h3>{{i18n "docs.tags"}}</h3>
|
||||||
{{docs-tag
|
<div class="item-controls-buttons">
|
||||||
tag=tag
|
{{d-button class=(if (eq tagSort.type "alpha") "tags-alphabet active" "tags-alphabet") icon=tagSortAlphaIcon action=toggleTagSort actionParam="alpha"}}
|
||||||
selectTag=(action "updateSelectedTags" tag)
|
{{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>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -75,7 +75,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
padding-left: 0.35em;
|
|
||||||
font-size: $font-up-1;
|
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"
|
activity: "Activity"
|
||||||
no_topics: "No topics in Docs."
|
no_topics: "No topics in Docs."
|
||||||
categories: "Categories"
|
categories: "Categories"
|
||||||
|
categories_filter_placeholder: "Filter categories"
|
||||||
|
tags_filter_placeholder: "Filter tags"
|
||||||
tags: "Tags"
|
tags: "Tags"
|
||||||
search:
|
search:
|
||||||
results:
|
results:
|
||||||
|
|
|
@ -11,6 +11,11 @@ enabled_site_setting :docs_enabled
|
||||||
register_asset 'stylesheets/common/docs.scss'
|
register_asset 'stylesheets/common/docs.scss'
|
||||||
register_asset 'stylesheets/mobile/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/engine.rb', __dir__)
|
||||||
load File.expand_path('lib/docs/query.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) {
|
test("index page", async function (assert) {
|
||||||
|
this.siteSettings.tagging_enabled = true;
|
||||||
|
|
||||||
await visit("/");
|
await visit("/");
|
||||||
await click("#toggle-hamburger-menu");
|
await click("#toggle-hamburger-menu");
|
||||||
await click(".docs-link");
|
await click(".docs-link");
|
||||||
|
|
Loading…
Reference in New Issue