REFACTOR: Update from widget to a glimmer component (#28)

This commit is contained in:
Kris 2023-03-28 16:08:09 -04:00 committed by GitHub
parent 43c9dc5c40
commit ca80cd248c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 167 additions and 137 deletions

View File

@ -1,4 +1,4 @@
# discourse-category-banners
# Discourse Category Banners
A theme component to add category banners to your Discourse community.

View File

@ -1,5 +1,5 @@
{
"name": "discourse-category-banners",
"name": "Category Banners",
"component": true,
"about_url": "https://meta.discourse.org/t/discourse-category-banners/86241",
"license_url": "https://meta.discourse.org/t/discourse-category-banners/86241"

View File

@ -1,3 +1,10 @@
body:not(.category-header) {
// hides banners based on outcome of shouldShow
.category-header-banner {
display: none;
}
}
div[class^="category-title-header"] {
display: flex;
text-align: center;
@ -21,13 +28,13 @@ div[class^="category-title-header"] {
}
.category-icon-widget {
display: inline;
display: flex;
.category-icon {
display: flex;
.d-icon {
height: 1.5em;
width: 1.5em;
margin-right: 0.75em;
height: 0.75em;
width: 0.75em;
margin-right: 0.25em;
}
}
}
@ -41,9 +48,3 @@ div[class^="category-title-header"] {
}
}
}
.category-header {
#main-outlet {
padding-top: 20px;
}
}

View File

@ -0,0 +1,29 @@
{{#if this.shouldRender}}
<div
{{did-insert this.getCategory}}
{{did-update this.getCategory this.isVisible}}
class="category-title-header
{{if this.category (concat 'category-banner-' this.category.slug)}}"
style={{if this.category this.safeStyle}}
>
<div class="category-title-contents">
<h1 class="category-title">
{{#if (and (theme-setting "show_category_icon") this.category)}}
{{! For compatibility with https://meta.discourse.org/t/category-icons/104683}}
<CategoryIcon @category={{this.category}} />
{{/if}}
{{#if this.category.read_restricted}}
{{d-icon "lock"}}
{{/if}}
{{this.category.name}}
</h1>
{{#if (theme-setting "show_description")}}
<div class="category-title-description">
<div class="cooked" innerHTML={{this.category.description}}></div>
</div>
{{/if}}
</div>
</div>
{{/if}}

View File

@ -0,0 +1,114 @@
import Category from "discourse/models/category";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { htmlSafe } from "@ember/template";
export default class DiscourseCategoryBanners extends Component {
@service router;
@service site;
@tracked category = null;
@tracked keepDuringLoadingRoute = false;
get isVisible() {
if (this.categorySlugPathWithID) {
this.keepDuringLoadingRoute = true;
return true;
} else {
if (this.router.currentRoute.name.includes("loading")) {
return this.keepDuringLoadingRoute;
} else {
this.keepDuringLoadingRoute = false;
return false;
}
}
}
get categorySlugPathWithID() {
return this.router?.currentRoute?.params?.category_slug_path_with_id;
}
get shouldRender() {
return this.isVisible && this.keepDuringLoadingRoute;
}
get safeStyle() {
return htmlSafe(
`background-color: #${this.category.color}; color: #${this.category.text_color};`
);
}
#parseCategories(categoriesStr) {
const categories = {};
categoriesStr.split("|").forEach((item) => {
item = item.split(":");
if (item[0]) {
categories[item[0].toLowerCase()] = item[1]
? item[1].toLowerCase()
: "all";
}
});
return categories;
}
#parseExceptions(exceptionsStr) {
return exceptionsStr
.split("|")
.filter(Boolean)
.map((value) => value.toLowerCase());
}
#checkTargetCategory(categories) {
const currentCategoryName = this.category.name.toLowerCase();
const parentCategoryName = this.category.parentCategory
? this.category.parentCategory.name.toLowerCase()
: null;
return (
Object.keys(categories).length === 0 ||
categories[currentCategoryName] === "all" ||
categories[currentCategoryName] === "no_sub" ||
(this.category.parentCategory &&
(categories[parentCategoryName] === "all" ||
categories[parentCategoryName] === "only_sub"))
);
}
@action
getCategory() {
if (!this.isVisible) {
return;
}
if (this.categorySlugPathWithID) {
this.category = Category.findBySlugPathWithID(
this.categorySlugPathWithID
);
}
const categories = this.#parseCategories(settings.categories);
const exceptions = this.#parseExceptions(settings.exceptions);
const isException = exceptions.includes(this.category.name.toLowerCase());
const isTarget = this.#checkTargetCategory(categories);
const hideMobile = !settings.show_mobile && this.site.mobileView;
const isSubCategory =
!settings.show_subcategory && this.category.parentCategory;
const hasNoCategoryDescription =
settings.hide_if_no_description && !this.category.description_text;
if (
isTarget &&
!isException &&
!hasNoCategoryDescription &&
!isSubCategory &&
!hideMobile
) {
document.body.classList.add("category-header");
} else {
document.body.classList.remove("category-header");
}
}
}

View File

@ -1,3 +1,3 @@
{{#if (theme-setting "show_above_site_header")}}
{{mount-widget widget="category-header-widget"}}
<DiscourseCategoryBanners />
{{/if}}

View File

@ -1,3 +1,3 @@
{{#if (theme-setting "show_below_site_header")}}
{{mount-widget widget="category-header-widget"}}
<DiscourseCategoryBanners />
{{/if}}

View File

@ -1,15 +0,0 @@
import { withPluginApi } from "discourse/lib/plugin-api";
export default {
name: "discourse-category-banners",
initialize() {
withPluginApi("0.8", (api) => {
api.decorateWidget("category-header-widget:after", (helper) => {
helper.widget.appEvents.on("page:changed", () => {
helper.widget.scheduleRerender();
});
});
});
},
};

View File

@ -1,101 +0,0 @@
import { getOwner } from "discourse-common/lib/get-owner";
import { h } from "virtual-dom";
import { iconNode } from "discourse-common/lib/icon-library";
import { createWidget } from "discourse/widgets/widget";
import Category from "discourse/models/category";
function buildCategory(category, widget) {
const content = [];
if (settings.show_category_icon) {
try {
content.push(widget.attach("category-icon", { category }));
} catch {
// if widget attaching fails, ignore it as it's probably the missing component
}
}
let categoryTitle = category.read_restricted
? [iconNode("lock"), category.name]
: category.name;
content.push(h("h1.category-title", categoryTitle));
if (settings.show_description) {
content.push(
h(
"div.category-title-description",
h("div.cooked", { innerHTML: category.description })
)
);
}
return content;
}
export default createWidget("category-header-widget", {
tagName: "span.discourse-category-banners",
html() {
const router = getOwner(this).lookup("router:main");
const route = router.currentRoute;
if (
route &&
route.params &&
route.params.hasOwnProperty("category_slug_path_with_id")
) {
const categories = {};
settings.categories.split("|").forEach((item) => {
item = item.split(":");
if (item[0]) {
categories[item[0]] = item[1] || "all";
}
});
const category = Category.findBySlugPathWithID(
route.params.category_slug_path_with_id
);
const isException = settings.exceptions
.split("|")
.filter(Boolean)
.includes(category.name);
const isTarget =
Object.keys(categories).length === 0 ||
categories[category.name] === "all" ||
categories[category.name] === "no_sub" ||
(category.parentCategory &&
(categories[category.parentCategory.name] === "all" ||
categories[category.parentCategory.name] === "only_sub"));
const hideMobile = !settings.show_mobile && this.site.mobileView;
const isSubCategory =
!settings.show_subcategory && category.parentCategory;
const hasNoCategoryDescription =
settings.hide_if_no_description && !category.description_text;
if (
isTarget &&
!isException &&
!hasNoCategoryDescription &&
!isSubCategory &&
!hideMobile
) {
document.body.classList.add("category-header");
return h(
`div.category-title-header.category-banner-${category.slug}`,
{
attributes: {
style: `background-color: #${category.color}; color: #${category.text_color};`,
},
},
h("div.category-title-contents", buildCategory(category, this))
);
}
} else {
document.body.classList.remove("category-header");
}
},
});

View File

@ -18,17 +18,19 @@ exceptions:
default: ""
type: list
list_type: simple
description: "Banner will not show for these categories NAMES. This is case sensitive."
description: "Banner will not show for these category NAMES."
categories:
default: ""
type: list
description: |
Banner will only show for these categories. Format: name:target.
Banner will only show for these categories. Format as <code>name:target</code> (e.g., <code>staff:all</code>).
Accepted targets:
all - named category and subcategories (default);
no_sub - only the named category;
only_sub - only subcategories of the named category.
<ul>
<li> all - named category and subcategories (default);
<li> no_sub - only the named category;
<li> only_sub - only subcategories of the named category.
</ul>
show_above_site_header:
default: false
@ -42,4 +44,4 @@ show_below_site_header:
show_category_icon:
default: false
description: "Show category icon from Discourse Category Icons component"
description: Show category icon from the <a href="https://meta.discourse.org/t/category-icons/104683" target="_blank">Discourse Category Icons component</a>