diff --git a/app/assets/javascripts/select-kit/addon/components/category-chooser.js b/app/assets/javascripts/select-kit/addon/components/category-chooser.js
index 3dcd570b568..3060665a21d 100644
--- a/app/assets/javascripts/select-kit/addon/components/category-chooser.js
+++ b/app/assets/javascripts/select-kit/addon/components/category-chooser.js
@@ -6,6 +6,7 @@ import { setting } from "discourse/lib/computed";
import Category from "discourse/models/category";
import PermissionType from "discourse/models/permission-type";
import I18n from "discourse-i18n";
+import CategoryRow from "select-kit/components/category-row";
import ComboBoxComponent from "select-kit/components/combo-box";
export default ComboBoxComponent.extend({
@@ -40,7 +41,7 @@ export default ComboBoxComponent.extend({
},
modifyComponentForRow() {
- return "category-row";
+ return CategoryRow;
},
modifyNoSelection() {
diff --git a/app/assets/javascripts/select-kit/addon/components/category-drop.js b/app/assets/javascripts/select-kit/addon/components/category-drop.js
index fa456a38b14..5afd206e174 100644
--- a/app/assets/javascripts/select-kit/addon/components/category-drop.js
+++ b/app/assets/javascripts/select-kit/addon/components/category-drop.js
@@ -8,6 +8,7 @@ import DiscourseURL, {
} from "discourse/lib/url";
import Category from "discourse/models/category";
import I18n from "discourse-i18n";
+import CategoryRow from "select-kit/components/category-row";
import ComboBoxComponent from "select-kit/components/combo-box";
export const NO_CATEGORIES_ID = "no-categories";
@@ -41,7 +42,7 @@ export default ComboBoxComponent.extend({
},
modifyComponentForRow() {
- return "category-row";
+ return CategoryRow;
},
displayCategoryDescription: computed(function () {
diff --git a/app/assets/javascripts/select-kit/addon/components/category-row.gjs b/app/assets/javascripts/select-kit/addon/components/category-row.gjs
new file mode 100644
index 00000000000..e0dea4f0485
--- /dev/null
+++ b/app/assets/javascripts/select-kit/addon/components/category-row.gjs
@@ -0,0 +1,309 @@
+import Component from "@glimmer/component";
+import { cached } from "@glimmer/tracking";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { guidFor } from "@ember/object/internals";
+import { inject as service } from "@ember/service";
+import { htmlSafe } from "@ember/template";
+import { isEmpty, isNone } from "@ember/utils";
+import { categoryBadgeHTML } from "discourse/helpers/category-link";
+import concatClass from "discourse/helpers/concat-class";
+import dirSpan from "discourse/helpers/dir-span";
+import Category from "discourse/models/category";
+
+export default class CategoryRow extends Component {
+ @service site;
+ @service siteSettings;
+
+ get isNone() {
+ return this.rowValue === this.args.selectKit?.noneItem;
+ }
+
+ get highlightedValue() {
+ return this.args.selectKit.get("highlighted.id");
+ }
+
+ get isHighlighted() {
+ return this.rowValue === this.highlightedValue;
+ }
+
+ get isSelected() {
+ return this.rowValue === this.args.value;
+ }
+
+ get hideParentCategory() {
+ return this.args.selectKit.options.hideParentCategory;
+ }
+
+ get categoryLink() {
+ return this.args.selectKit.options.categoryLink;
+ }
+
+ get countSubcategories() {
+ return this.args.selectKit.options.countSubcategories;
+ }
+
+ get allowUncategorizedTopics() {
+ return this.siteSettings.hideParentCategory;
+ }
+
+ get allowUncategorized() {
+ return this.args.selectKit.options.allowUncategorized;
+ }
+
+ get rowName() {
+ return this.args.item?.name;
+ }
+
+ get rowValue() {
+ return this.args.item?.id;
+ }
+
+ get guid() {
+ return guidFor(this.args.item);
+ }
+
+ get label() {
+ return this.args.item?.name;
+ }
+
+ get displayCategoryDescription() {
+ const option = this.args.selectKit.options.displayCategoryDescription;
+ if (isNone(option)) {
+ return true;
+ }
+
+ return option;
+ }
+
+ get title() {
+ if (this.category) {
+ return this.categoryName;
+ }
+ }
+
+ get categoryName() {
+ return this.category.name;
+ }
+
+ get categoryDescriptionText() {
+ return this.category.description_text;
+ }
+
+ @cached
+ get category() {
+ if (isEmpty(this.rowValue)) {
+ const uncategorized = Category.findUncategorized();
+ if (uncategorized && uncategorized.name === this.rowName) {
+ return uncategorized;
+ }
+ } else {
+ return Category.findById(parseInt(this.rowValue, 10));
+ }
+ }
+
+ @cached
+ get badgeForCategory() {
+ return htmlSafe(
+ categoryBadgeHTML(this.category, {
+ link: this.categoryLink,
+ allowUncategorized:
+ this.allowUncategorizedTopics || this.allowUncategorized,
+ hideParent: !!this.parentCategory,
+ topicCount: this.topicCount,
+ })
+ );
+ }
+
+ @cached
+ get badgeForParentCategory() {
+ return htmlSafe(
+ categoryBadgeHTML(this.parentCategory, {
+ link: this.categoryLink,
+ allowUncategorized:
+ this.allowUncategorizedTopics || this.allowUncategorized,
+ recursive: true,
+ })
+ );
+ }
+
+ get parentCategory() {
+ return Category.findById(this.parentCategoryId);
+ }
+
+ get hasParentCategory() {
+ return this.parentCategoryId;
+ }
+
+ get parentCategoryId() {
+ return this.category?.parent_category_id;
+ }
+
+ get categoryTotalTopicCount() {
+ return this.category?.totalTopicCount;
+ }
+
+ get categoryTopicCount() {
+ return this.category?.topic_count;
+ }
+
+ get topicCount() {
+ return this.countSubcategories
+ ? this.categoryTotalTopicCount
+ : this.categoryTopicCount;
+ }
+
+ get shouldDisplayDescription() {
+ return (
+ this.displayCategoryDescription &&
+ this.categoryDescriptionText &&
+ this.categoryDescriptionText !== "null"
+ );
+ }
+
+ @cached
+ get descriptionText() {
+ if (this.categoryDescriptionText) {
+ return this._formatDescription(this.categoryDescriptionText);
+ }
+ }
+
+ @action
+ handleMouseEnter() {
+ if (this.site.mobileView) {
+ return;
+ }
+
+ if (!this.isDestroying || !this.isDestroyed) {
+ this.args.selectKit.onHover(this.rowValue, this.args.item);
+ }
+ return false;
+ }
+
+ @action
+ handleClick(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ this.args.selectKit.select(this.rowValue, this.args.item);
+ return false;
+ }
+
+ @action
+ handleMouseDown(event) {
+ if (this.args.selectKit.options.preventHeaderFocus) {
+ event.preventDefault();
+ }
+ }
+
+ @action
+ handleFocusIn(event) {
+ event.stopImmediatePropagation();
+ }
+
+ @action
+ handleKeyDown(event) {
+ if (this.args.selectKit.isExpanded) {
+ if (event.key === "Backspace") {
+ if (this.args.selectKit.isFilterExpanded) {
+ this.args.selectKit.set(
+ "filter",
+ this.args.selectKit.filter.slice(0, -1)
+ );
+ this.args.selectKit.triggerSearch();
+ this.args.selectKit.focusFilter();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ } else if (event.key === "ArrowUp") {
+ this.args.selectKit.highlightPrevious();
+ event.preventDefault();
+ } else if (event.key === "ArrowDown") {
+ this.args.selectKit.highlightNext();
+ event.preventDefault();
+ } else if (event.key === "Enter") {
+ event.stopImmediatePropagation();
+
+ this.args.selectKit.select(
+ this.args.selectKit.highlighted.id,
+ this.args.selectKit.highlighted
+ );
+ event.preventDefault();
+ } else if (event.key === "Escape") {
+ this.args.selectKit.close(event);
+ this.args.selectKit.headerElement().focus();
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ if (this._isValidInput(event.key)) {
+ this.args.selectKit.set("filter", event.key);
+ this.args.selectKit.triggerSearch();
+ this.args.selectKit.focusFilter();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+
+ _formatDescription(description) {
+ const limit = 200;
+ return `${description.slice(0, limit)}${
+ description.length > limit ? "…" : ""
+ }`;
+ }
+ _isValidInput(eventKey) {
+ // relying on passing the event to the input is risky as it could not work
+ // dispatching the event won't work as the event won't be trusted
+ // safest solution is to filter event and prefill filter with it
+ const nonInputKeysRegex =
+ /F\d+|Arrow.+|Meta|Alt|Control|Shift|Delete|Enter|Escape|Tab|Space|Insert|Backspace/;
+ return !nonInputKeysRegex.test(eventKey);
+ }
+
+
+ {{! template-lint-disable no-pointer-down-event-binding }}
+