REFACTOR: Update from widget to a glimmer component (#28)
This commit is contained in:
parent
43c9dc5c40
commit
ca80cd248c
|
@ -1,4 +1,4 @@
|
|||
# discourse-category-banners
|
||||
# Discourse Category Banners
|
||||
|
||||
A theme component to add category banners to your Discourse community.
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
{{#if (theme-setting "show_above_site_header")}}
|
||||
{{mount-widget widget="category-header-widget"}}
|
||||
<DiscourseCategoryBanners />
|
||||
{{/if}}
|
|
@ -1,3 +1,3 @@
|
|||
{{#if (theme-setting "show_below_site_header")}}
|
||||
{{mount-widget widget="category-header-widget"}}
|
||||
<DiscourseCategoryBanners />
|
||||
{{/if}}
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
|
@ -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");
|
||||
}
|
||||
},
|
||||
});
|
14
settings.yml
14
settings.yml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue