mirror of
https://github.com/discourse/discourse.git
synced 2025-05-25 00:32:52 +08:00
FEATURE: Filter with CategoryDrop on category page (#26689)
Using the CategoryDrop on the categories page redirected the user to the "latest topics" page with topics only from that category. With these changes, selecting a category will take the user to a "subcategories page" where only the subcategories of the selected property will be displayed.
This commit is contained in:
@ -24,11 +24,13 @@
|
|||||||
@tagId={{this.tag.id}}
|
@tagId={{this.tag.id}}
|
||||||
@editingCategory={{this.editingCategory}}
|
@editingCategory={{this.editingCategory}}
|
||||||
@editingCategoryTab={{this.editingCategoryTab}}
|
@editingCategoryTab={{this.editingCategoryTab}}
|
||||||
|
@filterType={{this.filterType}}
|
||||||
@options={{hash
|
@options={{hash
|
||||||
parentCategory=breadcrumb.parentCategory
|
parentCategory=breadcrumb.parentCategory
|
||||||
subCategory=breadcrumb.isSubcategory
|
subCategory=breadcrumb.isSubcategory
|
||||||
noSubcategories=breadcrumb.noSubcategories
|
noSubcategories=breadcrumb.noSubcategories
|
||||||
autoFilterable=true
|
autoFilterable=true
|
||||||
|
disableIfHasNoChildren=(eq this.filterType "categories")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@noSubcategories={{this.noSubcategories}}
|
@noSubcategories={{this.noSubcategories}}
|
||||||
@tag={{this.tag}}
|
@tag={{this.tag}}
|
||||||
@additionalTags={{this.additionalTags}}
|
@additionalTags={{this.additionalTags}}
|
||||||
|
@filterType={{this.filterType}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{#unless this.additionalTags}}
|
{{#unless this.additionalTags}}
|
||||||
|
@ -4,6 +4,7 @@ import { number } from "discourse/lib/formatter";
|
|||||||
import PreloadStore from "discourse/lib/preload-store";
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
import Site from "discourse/models/site";
|
import Site from "discourse/models/site";
|
||||||
import Topic from "discourse/models/topic";
|
import Topic from "discourse/models/topic";
|
||||||
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
@ -29,8 +30,8 @@ export default class CategoryList extends ArrayProxy {
|
|||||||
result.category_list.categories.forEach((c) => {
|
result.category_list.categories.forEach((c) => {
|
||||||
c = this._buildCategoryResult(c, statPeriod);
|
c = this._buildCategoryResult(c, statPeriod);
|
||||||
if (
|
if (
|
||||||
!c.parent_category_id ||
|
(parentCategory && c.parent_category_id === parentCategory.id) ||
|
||||||
c.parent_category_id === parentCategory?.id
|
(!parentCategory && !c.parent_category_id)
|
||||||
) {
|
) {
|
||||||
categories.pushObject(c);
|
categories.pushObject(c);
|
||||||
}
|
}
|
||||||
@ -79,28 +80,30 @@ export default class CategoryList extends ArrayProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static listForParent(store, category) {
|
static listForParent(store, category) {
|
||||||
return ajax(
|
deprecated(
|
||||||
`/categories.json?parent_category_id=${category.get("id")}`
|
"The listForParent method of CategoryList is deprecated. Use list instead",
|
||||||
).then((result) =>
|
{ id: "discourse.category-list.listForParent" }
|
||||||
CategoryList.create({
|
|
||||||
store,
|
|
||||||
categories: this.categoriesFrom(store, result, category),
|
|
||||||
parentCategory: category,
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return CategoryList.list(store, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
static list(store) {
|
static list(store, parentCategory = null) {
|
||||||
return PreloadStore.getAndRemove("categories_list", () =>
|
return PreloadStore.getAndRemove("categories_list", () => {
|
||||||
ajax("/categories.json")
|
const data = {};
|
||||||
).then((result) =>
|
if (parentCategory) {
|
||||||
CategoryList.create({
|
data.parent_category_id = parentCategory?.id;
|
||||||
|
}
|
||||||
|
return ajax("/categories.json", { data });
|
||||||
|
}).then((result) => {
|
||||||
|
return CategoryList.create({
|
||||||
store,
|
store,
|
||||||
categories: this.categoriesFrom(store, result),
|
categories: this.categoriesFrom(store, result, parentCategory),
|
||||||
|
parentCategory,
|
||||||
can_create_category: result.category_list.can_create_category,
|
can_create_category: result.category_list.can_create_category,
|
||||||
can_create_topic: result.category_list.can_create_topic,
|
can_create_topic: result.category_list.can_create_topic,
|
||||||
})
|
});
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@ -119,6 +122,9 @@ export default class CategoryList extends ArrayProxy {
|
|||||||
this.set("isLoading", true);
|
this.set("isLoading", true);
|
||||||
|
|
||||||
const data = { page: this.page + 1 };
|
const data = { page: this.page + 1 };
|
||||||
|
if (this.parentCategory) {
|
||||||
|
data.parent_category_id = this.parentCategory.id;
|
||||||
|
}
|
||||||
const result = await ajax("/categories.json", { data });
|
const result = await ajax("/categories.json", { data });
|
||||||
|
|
||||||
this.set("page", data.page);
|
this.set("page", data.page);
|
||||||
@ -127,7 +133,10 @@ export default class CategoryList extends ArrayProxy {
|
|||||||
}
|
}
|
||||||
this.set("isLoading", false);
|
this.set("isLoading", false);
|
||||||
|
|
||||||
const newCategoryList = CategoryList.categoriesFrom(this.store, result);
|
CategoryList.categoriesFrom(
|
||||||
newCategoryList.forEach((c) => this.categories.pushObject(c));
|
this.store,
|
||||||
|
result,
|
||||||
|
this.parentCategory
|
||||||
|
).forEach((c) => this.categories.pushObject(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,9 @@ export default function () {
|
|||||||
// default filter for a category
|
// default filter for a category
|
||||||
this.route("categoryNone", { path: "/c/*category_slug_path_with_id/none" });
|
this.route("categoryNone", { path: "/c/*category_slug_path_with_id/none" });
|
||||||
this.route("categoryAll", { path: "/c/*category_slug_path_with_id/all" });
|
this.route("categoryAll", { path: "/c/*category_slug_path_with_id/all" });
|
||||||
|
this.route("subcategories", {
|
||||||
|
path: "/c/*category_slug_path_with_id/subcategories",
|
||||||
|
});
|
||||||
this.route("category", { path: "/c/*category_slug_path_with_id" });
|
this.route("category", { path: "/c/*category_slug_path_with_id" });
|
||||||
|
|
||||||
this.route("custom");
|
this.route("custom");
|
||||||
|
@ -84,7 +84,7 @@ class AbstractCategoryRoute extends DiscourseRoute {
|
|||||||
|
|
||||||
async _createSubcategoryList(category) {
|
async _createSubcategoryList(category) {
|
||||||
if (category.isParent && category.show_subcategory_list) {
|
if (category.isParent && category.show_subcategory_list) {
|
||||||
return CategoryList.listForParent(this.store, category);
|
return CategoryList.list(this.store, category);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { hash } from "rsvp";
|
|||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import PreloadStore from "discourse/lib/preload-store";
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
import { defaultHomepage } from "discourse/lib/utilities";
|
import { defaultHomepage } from "discourse/lib/utilities";
|
||||||
|
import Category from "discourse/models/category";
|
||||||
import CategoryList from "discourse/models/category-list";
|
import CategoryList from "discourse/models/category-list";
|
||||||
import TopicList from "discourse/models/topic-list";
|
import TopicList from "discourse/models/topic-list";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
@ -17,28 +18,40 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute {
|
|||||||
templateName = "discovery/categories";
|
templateName = "discovery/categories";
|
||||||
controllerName = "discovery/categories";
|
controllerName = "discovery/categories";
|
||||||
|
|
||||||
findCategories() {
|
async findCategories(parentCategory) {
|
||||||
let style =
|
let model;
|
||||||
|
|
||||||
|
const style =
|
||||||
this.site.desktopView && this.siteSettings.desktop_category_page_style;
|
this.site.desktopView && this.siteSettings.desktop_category_page_style;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
style === "categories_and_latest_topics" ||
|
style === "categories_and_latest_topics" ||
|
||||||
style === "categories_and_latest_topics_created_date"
|
style === "categories_and_latest_topics_created_date"
|
||||||
) {
|
) {
|
||||||
return this._findCategoriesAndTopics("latest");
|
model = await this._findCategoriesAndTopics("latest", parentCategory);
|
||||||
} else if (style === "categories_and_top_topics") {
|
} else if (style === "categories_and_top_topics") {
|
||||||
return this._findCategoriesAndTopics("top");
|
model = await this._findCategoriesAndTopics("top", parentCategory);
|
||||||
} else {
|
} else {
|
||||||
// The server may have serialized this. Based on the logic above, we don't need it
|
// The server may have serialized this. Based on the logic above, we don't need it
|
||||||
// so remove it to avoid it being used later by another TopicList route.
|
// so remove it to avoid it being used later by another TopicList route.
|
||||||
PreloadStore.remove("topic_list");
|
PreloadStore.remove("topic_list");
|
||||||
|
model = await CategoryList.list(this.store, parentCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CategoryList.list(this.store);
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
model() {
|
async model(params) {
|
||||||
return this.findCategories().then((model) => {
|
let parentCategory;
|
||||||
|
if (params.category_slug_path_with_id) {
|
||||||
|
parentCategory = this.site.lazy_load_categories
|
||||||
|
? await Category.asyncFindBySlugPathWithID(
|
||||||
|
params.category_slug_path_with_id
|
||||||
|
)
|
||||||
|
: Category.findBySlugPathWithID(params.category_slug_path_with_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.findCategories(parentCategory).then((model) => {
|
||||||
const tracking = this.topicTrackingState;
|
const tracking = this.topicTrackingState;
|
||||||
if (tracking) {
|
if (tracking) {
|
||||||
tracking.sync(model, "categories");
|
tracking.sync(model, "categories");
|
||||||
@ -79,7 +92,7 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _findCategoriesAndTopics(filter) {
|
async _findCategoriesAndTopics(filter, parentCategory = null) {
|
||||||
return hash({
|
return hash({
|
||||||
categoriesList: PreloadStore.getAndRemove("categories_list"),
|
categoriesList: PreloadStore.getAndRemove("categories_list"),
|
||||||
topicsList: PreloadStore.getAndRemove("topic_list"),
|
topicsList: PreloadStore.getAndRemove("topic_list"),
|
||||||
@ -92,7 +105,11 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute {
|
|||||||
return { ...result.categoriesList, ...result.topicsList };
|
return { ...result.categoriesList, ...result.topicsList };
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, return the ajax result
|
// Otherwise, return the ajax result
|
||||||
return ajax(`/categories_and_${filter}`);
|
const data = {};
|
||||||
|
if (parentCategory) {
|
||||||
|
data.parent_category_id = parentCategory.id;
|
||||||
|
}
|
||||||
|
return ajax(`/categories_and_${filter}`, { data });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -102,7 +119,12 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute {
|
|||||||
|
|
||||||
return CategoryList.create({
|
return CategoryList.create({
|
||||||
store: this.store,
|
store: this.store,
|
||||||
categories: CategoryList.categoriesFrom(this.store, result),
|
categories: CategoryList.categoriesFrom(
|
||||||
|
this.store,
|
||||||
|
result,
|
||||||
|
parentCategory
|
||||||
|
),
|
||||||
|
parentCategory,
|
||||||
topics: TopicList.topicsFrom(this.store, result),
|
topics: TopicList.topicsFrom(this.store, result),
|
||||||
can_create_category: result.category_list.can_create_category,
|
can_create_category: result.category_list.can_create_category,
|
||||||
can_create_topic: result.category_list.can_create_topic,
|
can_create_topic: result.category_list.can_create_topic,
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
import DiscoveryCategoriesRoute from "discourse/routes/discovery-categories";
|
||||||
|
|
||||||
|
export default class DiscoverySubcategoriesRoute extends DiscoveryCategoriesRoute {}
|
@ -1,6 +1,7 @@
|
|||||||
<Discovery::Layout @model={{this.model}}>
|
<Discovery::Layout @model={{this.model}}>
|
||||||
<:navigation>
|
<:navigation>
|
||||||
<Discovery::Navigation
|
<Discovery::Navigation
|
||||||
|
@category={{this.model.parentCategory}}
|
||||||
@showCategoryAdmin={{this.model.can_create_category}}
|
@showCategoryAdmin={{this.model.can_create_category}}
|
||||||
@canCreateTopic={{this.model.can_create_topic}}
|
@canCreateTopic={{this.model.can_create_topic}}
|
||||||
@createTopic={{this.createTopic}}
|
@createTopic={{this.createTopic}}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import { currentRouteName, currentURL, visit } from "@ember/test-helpers";
|
||||||
|
import { test } from "qunit";
|
||||||
|
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||||
|
|
||||||
|
acceptance("Subcategories", function (needs) {
|
||||||
|
needs.site({
|
||||||
|
lazy_load_categories: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
test("navigation can be used to navigate subcategories pages", async function (assert) {
|
||||||
|
await visit("/categories");
|
||||||
|
|
||||||
|
let categoryDrop = selectKit(
|
||||||
|
".category-breadcrumb li:nth-of-type(1) .category-drop"
|
||||||
|
);
|
||||||
|
await categoryDrop.expand();
|
||||||
|
await categoryDrop.selectRowByValue("2"); // "feature" category
|
||||||
|
|
||||||
|
assert.strictEqual(currentRouteName(), "discovery.subcategories");
|
||||||
|
assert.strictEqual(currentURL(), "/c/feature/2/subcategories");
|
||||||
|
|
||||||
|
categoryDrop = selectKit(
|
||||||
|
".category-breadcrumb li:nth-of-type(2) .category-drop"
|
||||||
|
);
|
||||||
|
await categoryDrop.expand();
|
||||||
|
await categoryDrop.selectRowByValue("26"); // "spec" category
|
||||||
|
|
||||||
|
assert.strictEqual(currentRouteName(), "discovery.subcategories");
|
||||||
|
assert.strictEqual(currentURL(), "/c/feature/spec/26/subcategories");
|
||||||
|
});
|
||||||
|
});
|
@ -492,9 +492,43 @@ export function applyDefaultHandlers(pretender) {
|
|||||||
return response([{ id: 1234, cooked: "wat" }]);
|
return response([{ id: 1234, cooked: "wat" }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
pretender.get("/categories_and_latest", () =>
|
pretender.get("/categories.json", (request) => {
|
||||||
response(fixturesByUrl["/categories_and_latest.json"])
|
const data = cloneJSON(fixturesByUrl["/categories.json"]);
|
||||||
);
|
|
||||||
|
// replace categories list if parent_category_id filter is present
|
||||||
|
if (request.queryParams.parent_category_id) {
|
||||||
|
const parentCategoryId = parseInt(
|
||||||
|
request.queryParams.parent_category_id,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
data.category_list.categories = fixturesByUrl[
|
||||||
|
"site.json"
|
||||||
|
].site.categories.filter(
|
||||||
|
(c) => c.parent_category_id === parentCategoryId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
pretender.get("/categories_and_latest", (request) => {
|
||||||
|
const data = cloneJSON(fixturesByUrl["/categories_and_latest.json"]);
|
||||||
|
|
||||||
|
// replace categories list if parent_category_id filter is present
|
||||||
|
if (request.queryParams.parent_category_id) {
|
||||||
|
const parentCategoryId = parseInt(
|
||||||
|
request.queryParams.parent_category_id,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
data.category_list.categories = fixturesByUrl[
|
||||||
|
"site.json"
|
||||||
|
].site.categories.filter(
|
||||||
|
(c) => c.parent_category_id === parentCategoryId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response(data);
|
||||||
|
});
|
||||||
|
|
||||||
pretender.get("/c/bug/find_by_slug.json", () =>
|
pretender.get("/c/bug/find_by_slug.json", () =>
|
||||||
response(fixturesByUrl["/c/1/show.json"])
|
response(fixturesByUrl["/c/1/show.json"])
|
||||||
@ -529,6 +563,26 @@ export function applyDefaultHandlers(pretender) {
|
|||||||
response(fixturesByUrl["/c/11/show.json"])
|
response(fixturesByUrl["/c/11/show.json"])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pretender.get("/categories/find", () => {
|
||||||
|
return response({
|
||||||
|
categories: fixturesByUrl["site.json"].site.categories,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pretender.post("/categories/search", (request) => {
|
||||||
|
const data = parsePostData(request.requestBody);
|
||||||
|
if (data.include_ancestors) {
|
||||||
|
return response({
|
||||||
|
categories: fixturesByUrl["site.json"].site.categories,
|
||||||
|
ancestors: fixturesByUrl["site.json"].site.categories,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return response({
|
||||||
|
categories: fixturesByUrl["site.json"].site.categories,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pretender.get("/c/testing/find_by_slug.json", () =>
|
pretender.get("/c/testing/find_by_slug.json", () =>
|
||||||
response(fixturesByUrl["/c/11/show.json"])
|
response(fixturesByUrl["/c/11/show.json"])
|
||||||
);
|
);
|
||||||
|
@ -30,14 +30,24 @@ export default class CategoryDropMoreCollection extends Component {
|
|||||||
<template>
|
<template>
|
||||||
{{#if this.moreCount}}
|
{{#if this.moreCount}}
|
||||||
<div class="category-drop-footer">
|
<div class="category-drop-footer">
|
||||||
<span>{{i18n
|
<span>
|
||||||
"categories.plus_more_count"
|
{{i18n "categories.plus_more_count" (hash count=this.moreCount)}}
|
||||||
(hash count=this.moreCount)
|
</span>
|
||||||
}}</span>
|
|
||||||
<LinkTo @route="discovery.categories">
|
{{#if @selectKit.options.parentCategory}}
|
||||||
{{i18n "categories.view_all"}}
|
<LinkTo
|
||||||
{{icon "external-link-alt"}}
|
@route="discovery.subcategories"
|
||||||
</LinkTo>
|
@model={{@selectKit.options.parentCategory.id}}
|
||||||
|
>
|
||||||
|
{{i18n "categories.view_all"}}
|
||||||
|
{{icon "external-link-alt"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo @route="discovery.categories">
|
||||||
|
{{i18n "categories.view_all"}}
|
||||||
|
{{icon "external-link-alt"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
|
@ -46,6 +46,7 @@ export default ComboBoxComponent.extend({
|
|||||||
headerComponent: "category-drop/category-drop-header",
|
headerComponent: "category-drop/category-drop-header",
|
||||||
parentCategory: false,
|
parentCategory: false,
|
||||||
allowUncategorized: "allowUncategorized",
|
allowUncategorized: "allowUncategorized",
|
||||||
|
disableIfHasNoChildren: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@ -93,6 +94,7 @@ export default ComboBoxComponent.extend({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
this.selectKit.options.subCategory &&
|
this.selectKit.options.subCategory &&
|
||||||
|
this.filterType !== "categories" &&
|
||||||
(this.value || !this.selectKit.options.noSubcategories)
|
(this.value || !this.selectKit.options.noSubcategories)
|
||||||
) {
|
) {
|
||||||
shortcuts.push({
|
shortcuts.push({
|
||||||
@ -157,6 +159,7 @@ export default ComboBoxComponent.extend({
|
|||||||
if (this.editingCategory) {
|
if (this.editingCategory) {
|
||||||
return this.noCategoriesLabel;
|
return this.noCategoriesLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectKit.options.subCategory) {
|
if (this.selectKit.options.subCategory) {
|
||||||
return I18n.t("categories.all_subcategories", {
|
return I18n.t("categories.all_subcategories", {
|
||||||
categoryName: this.parentCategoryName,
|
categoryName: this.parentCategoryName,
|
||||||
@ -225,17 +228,35 @@ export default ComboBoxComponent.extend({
|
|||||||
? this.selectKit.options.parentCategory
|
? this.selectKit.options.parentCategory
|
||||||
: Category.findById(parseInt(categoryId, 10));
|
: Category.findById(parseInt(categoryId, 10));
|
||||||
|
|
||||||
const route = this.editingCategory
|
let route;
|
||||||
? getEditCategoryUrl(
|
if (this.editingCategoryTab) {
|
||||||
category,
|
// rendered on category page
|
||||||
categoryId !== NO_CATEGORIES_ID,
|
route = getEditCategoryUrl(
|
||||||
this.editingCategoryTab
|
category,
|
||||||
)
|
categoryId !== NO_CATEGORIES_ID,
|
||||||
: getCategoryAndTagUrl(
|
this.editingCategoryTab
|
||||||
category,
|
);
|
||||||
categoryId !== NO_CATEGORIES_ID,
|
} else if (
|
||||||
this.tagId
|
this.site.lazy_load_categories &&
|
||||||
);
|
this.filterType === "categories"
|
||||||
|
) {
|
||||||
|
// rendered on categories page
|
||||||
|
if (categoryId === "all-categories" || categoryId === "no-categories") {
|
||||||
|
route = this.selectKit.options.parentCategory
|
||||||
|
? `${this.selectKit.options.parentCategory.url}/subcategories`
|
||||||
|
: "/categories";
|
||||||
|
} else if (categoryId) {
|
||||||
|
route = `${Category.findById(categoryId).url}/subcategories`;
|
||||||
|
} else {
|
||||||
|
route = "/categories";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
route = getCategoryAndTagUrl(
|
||||||
|
category,
|
||||||
|
categoryId !== NO_CATEGORIES_ID,
|
||||||
|
this.tagId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
DiscourseURL.routeToUrl(route);
|
DiscourseURL.routeToUrl(route);
|
||||||
},
|
},
|
||||||
|
@ -90,6 +90,13 @@ export default class CategoryRow extends Component {
|
|||||||
return this.category.description_text;
|
return this.category.description_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isDisabled() {
|
||||||
|
return (
|
||||||
|
this.args.selectKit.options.disableIfHasNoChildren &&
|
||||||
|
this.args.item.has_children === false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
get category() {
|
get category() {
|
||||||
if (isEmpty(this.rowValue)) {
|
if (isEmpty(this.rowValue)) {
|
||||||
@ -187,7 +194,9 @@ export default class CategoryRow extends Component {
|
|||||||
handleClick(event) {
|
handleClick(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.args.selectKit.select(this.rowValue, this.args.item);
|
if (!this.isDisabled) {
|
||||||
|
this.args.selectKit.select(this.rowValue, this.args.item);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,10 +235,12 @@ export default class CategoryRow extends Component {
|
|||||||
} else if (event.key === "Enter") {
|
} else if (event.key === "Enter") {
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
this.args.selectKit.select(
|
if (!this.isDisabled) {
|
||||||
this.args.selectKit.highlighted.id,
|
this.args.selectKit.select(
|
||||||
this.args.selectKit.highlighted
|
this.args.selectKit.highlighted.id,
|
||||||
);
|
this.args.selectKit.highlighted
|
||||||
|
);
|
||||||
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (event.key === "Escape") {
|
} else if (event.key === "Escape") {
|
||||||
this.args.selectKit.close(event);
|
this.args.selectKit.close(event);
|
||||||
@ -272,6 +283,7 @@ export default class CategoryRow extends Component {
|
|||||||
(if this.isSelected "is-selected")
|
(if this.isSelected "is-selected")
|
||||||
(if this.isHighlighted "is-highlighted")
|
(if this.isHighlighted "is-highlighted")
|
||||||
(if this.isNone "is-none")
|
(if this.isNone "is-none")
|
||||||
|
(if this.isDisabled "is-disabled")
|
||||||
}}
|
}}
|
||||||
role="menuitemradio"
|
role="menuitemradio"
|
||||||
data-index={{@index}}
|
data-index={{@index}}
|
||||||
|
@ -29,6 +29,14 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
.badge-category__name {
|
||||||
|
color: var(--primary-low-mid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.category-desc {
|
.category-desc {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
@ -60,6 +68,10 @@
|
|||||||
span {
|
span {
|
||||||
color: var(--primary-high);
|
color: var(--primary-high);
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,12 @@ class CategoriesController < ApplicationController
|
|||||||
@description = SiteSetting.site_description
|
@description = SiteSetting.site_description
|
||||||
|
|
||||||
parent_category =
|
parent_category =
|
||||||
Category.find_by_slug(params[:parent_category_id]) ||
|
if params[:parent_category_id].present?
|
||||||
Category.find_by(id: params[:parent_category_id].to_i)
|
Category.find_by_slug(params[:parent_category_id]) ||
|
||||||
|
Category.find_by(id: params[:parent_category_id].to_i)
|
||||||
|
elsif params[:category_slug_path_with_id].present?
|
||||||
|
Category.find_by_slug_path_with_id(params[:category_slug_path_with_id])
|
||||||
|
end
|
||||||
|
|
||||||
include_subcategories =
|
include_subcategories =
|
||||||
SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
SiteSetting.desktop_category_page_style == "subcategories_with_featured_topics" ||
|
||||||
@ -43,7 +47,7 @@ class CategoriesController < ApplicationController
|
|||||||
|
|
||||||
category_options = {
|
category_options = {
|
||||||
is_homepage: current_homepage == "categories",
|
is_homepage: current_homepage == "categories",
|
||||||
parent_category_id: params[:parent_category_id],
|
parent_category_id: parent_category&.id,
|
||||||
include_topics: include_topics(parent_category),
|
include_topics: include_topics(parent_category),
|
||||||
include_subcategories: include_subcategories,
|
include_subcategories: include_subcategories,
|
||||||
tag: params[:tag],
|
tag: params[:tag],
|
||||||
|
@ -180,9 +180,6 @@ class CategoryList
|
|||||||
|
|
||||||
include_subcategories = @options[:include_subcategories] == true
|
include_subcategories = @options[:include_subcategories] == true
|
||||||
|
|
||||||
notification_levels = CategoryUser.notification_levels_for(@guardian.user)
|
|
||||||
default_notification_level = CategoryUser.default_notification_level
|
|
||||||
|
|
||||||
if @guardian.can_lazy_load_categories?
|
if @guardian.can_lazy_load_categories?
|
||||||
subcategory_ids = {}
|
subcategory_ids = {}
|
||||||
Category
|
Category
|
||||||
|
@ -1166,10 +1166,11 @@ Discourse::Application.routes.draw do
|
|||||||
|
|
||||||
get "/c", to: redirect(relative_url_root + "categories")
|
get "/c", to: redirect(relative_url_root + "categories")
|
||||||
|
|
||||||
resources :categories, except: %i[show new edit]
|
resources :categories, only: %i[index create update destroy]
|
||||||
post "categories/reorder" => "categories#reorder"
|
post "categories/reorder" => "categories#reorder"
|
||||||
get "categories/find" => "categories#find"
|
get "categories/find" => "categories#find"
|
||||||
post "categories/search" => "categories#search"
|
post "categories/search" => "categories#search"
|
||||||
|
get "categories/:parent_category_id" => "categories#index"
|
||||||
|
|
||||||
scope path: "category/:category_id" do
|
scope path: "category/:category_id" do
|
||||||
post "/move" => "categories#move"
|
post "/move" => "categories#move"
|
||||||
@ -1211,6 +1212,9 @@ Discourse::Application.routes.draw do
|
|||||||
:constraints => {
|
:constraints => {
|
||||||
format: "html",
|
format: "html",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get "/subcategories" => "categories#index"
|
||||||
|
|
||||||
get "/" => "list#category_default", :as => "category_default"
|
get "/" => "list#category_default", :as => "category_default"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user