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.
|
A theme component to add category banners to your Discourse community.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "discourse-category-banners",
|
"name": "Category Banners",
|
||||||
"component": true,
|
"component": true,
|
||||||
"about_url": "https://meta.discourse.org/t/discourse-category-banners/86241",
|
"about_url": "https://meta.discourse.org/t/discourse-category-banners/86241",
|
||||||
"license_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"] {
|
div[class^="category-title-header"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -21,13 +28,13 @@ div[class^="category-title-header"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-icon-widget {
|
.category-icon-widget {
|
||||||
display: inline;
|
display: flex;
|
||||||
|
|
||||||
.category-icon {
|
.category-icon {
|
||||||
|
display: flex;
|
||||||
.d-icon {
|
.d-icon {
|
||||||
height: 1.5em;
|
height: 0.75em;
|
||||||
width: 1.5em;
|
width: 0.75em;
|
||||||
margin-right: 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")}}
|
{{#if (theme-setting "show_above_site_header")}}
|
||||||
{{mount-widget widget="category-header-widget"}}
|
<DiscourseCategoryBanners />
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -1,3 +1,3 @@
|
||||||
{{#if (theme-setting "show_below_site_header")}}
|
{{#if (theme-setting "show_below_site_header")}}
|
||||||
{{mount-widget widget="category-header-widget"}}
|
<DiscourseCategoryBanners />
|
||||||
{{/if}}
|
{{/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: ""
|
default: ""
|
||||||
type: list
|
type: list
|
||||||
list_type: simple
|
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:
|
categories:
|
||||||
default: ""
|
default: ""
|
||||||
type: list
|
type: list
|
||||||
description: |
|
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:
|
Accepted targets:
|
||||||
all - named category and subcategories (default);
|
<ul>
|
||||||
no_sub - only the named category;
|
<li> all - named category and subcategories (default);
|
||||||
only_sub - only subcategories of the named category.
|
<li> no_sub - only the named category;
|
||||||
|
<li> only_sub - only subcategories of the named category.
|
||||||
|
</ul>
|
||||||
|
|
||||||
show_above_site_header:
|
show_above_site_header:
|
||||||
default: false
|
default: false
|
||||||
|
@ -42,4 +44,4 @@ show_below_site_header:
|
||||||
|
|
||||||
show_category_icon:
|
show_category_icon:
|
||||||
default: false
|
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