Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions javascripts/discourse/components/custom-header-icon.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import { eq } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import dasherize from "discourse/helpers/dasherize";
import { escapeExpression } from "discourse/lib/utilities";
import isValidUrl from "../lib/isValidUrl";

const MOBILE_VIEWS = new Set(["vmo", "vdm"]);
const DESKTOP_VIEWS = new Set(["vdo", "vdm"]);

function shouldRenderViewMode(view, isMobile) {
return isMobile ? MOBILE_VIEWS.has(view) : DESKTOP_VIEWS.has(view);
}

export default class CustomHeaderIcon extends Component {
static shouldRender(args, context) {
return shouldRenderViewMode(args.link.view, context.site.mobileView);
}

@service site;

get iconClassName() {
return `header-icon-${dasherize(this.args.link.title)}`;
}

get isLastLink() {
return this.args.link === this.visibleLinks.at(-1);
}

get style() {
const numericWidth = Number(this.args.link.width);
return Number.isFinite(numericWidth)
? htmlSafe(`width: ${escapeExpression(numericWidth)}px`)
: undefined;
}

get visibleLinks() {
return this.args.links.filter((link) =>
shouldRenderViewMode(link.view, this.site.mobileView)
);
}

<template>
<li
class={{concatClass
"custom-header-icon-link"
this.iconClassName
@link.view
(if this.isLastLink "last-custom-icon")
}}
>
<a
class="btn no-text icon btn-flat"
href={{@link.url}}
title={{@link.title}}
target={{if (eq @link.target "blank") "_blank"}}
rel={{if @link.target "noopener"}}
style={{this.style}}
>
{{#if (isValidUrl @link.icon)}}
<img src={{@link.icon}} aria-hidden="true" />
<span class="sr-only">{{@link.title}}</span>
{{else}}
{{icon @link.icon [email protected]}}
{{/if}}
</a>
</li>
</template>
}
Original file line number Diff line number Diff line change
@@ -1,78 +1,29 @@
import { dasherize } from "@ember/string";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import curryComponent from "ember-curry-component";
import { getOwnerWithFallback } from "discourse/lib/get-owner";
import { withPluginApi } from "discourse/lib/plugin-api";
import { escapeExpression } from "discourse/lib/utilities";
import isValidUrl from "../lib/isValidUrl";
import CustomHeaderIcon from "../components/custom-header-icon";

function buildIcon(iconNameOrImageUrl, title) {
if (isValidUrl(iconNameOrImageUrl)) {
return <template>
<img src={{iconNameOrImageUrl}} aria-hidden="true" />
<span class="sr-only">{{title}}</span>
</template>;
} else {
return <template>{{icon iconNameOrImageUrl label=title}}</template>;
}
}
const BEFORE_ICONS = ["chat", "search", "hamburger", "user-menu"];

export default {
name: "header-icon-links",
initialize() {
withPluginApi("0.8.41", (api) => {
withPluginApi((api) => {
try {
const site = api.container.lookup("service:site");
let links = settings.header_links;
if (site.mobileView) {
links = links.filter(
(link) => link.view === "vmo" || link.view === "vdm"
);
} else {
links = links.filter(
(link) => link.view === "vdo" || link.view === "vdm"
);
}

links.forEach((link, index) => {
const iconTemplate = buildIcon(link.icon, link.title);
const className = `header-icon-${dasherize(link.title)}`;
const target = link.target === "blank" ? "_blank" : "";
const rel = link.target ? "noopener" : "";
const isLastLink =
index === links.length - 1 ? "last-custom-icon" : "";

let style = "";
if (link.width) {
style = `width: ${escapeExpression(link.width)}px`;
}
const links = settings.header_links || [];

const iconComponent = <template>
<li
class={{concatClass
"custom-header-icon-link"
className
link.view
isLastLink
}}
>
<a
class="btn no-text icon btn-flat"
href={{link.url}}
title={{link.title}}
target={{target}}
rel={{rel}}
style={{style}}
>
{{iconTemplate}}
</a>
</li>
</template>;

const beforeIcon = ["chat", "search", "hamburger", "user-menu"];

api.headerIcons.add(link.title, iconComponent, {
before: beforeIcon,
});
links.forEach((link) => {
api.headerIcons.add(
link.title,
curryComponent(
CustomHeaderIcon,
{ link, links },
getOwnerWithFallback()
),
{
before: BEFORE_ICONS,
}
);
});
} catch (error) {
// eslint-disable-next-line no-console
Expand Down