Ya veo. Creo que tiene sentido si muestras información como una etiqueta, pero aquí, es un botón para mostrar más etiquetas; el contexto es diferente para mí. Tú decides; no creo que importe mucho.
Para continuar con los comentarios:
- La lista de etiquetas se puede mostrar en otros lugares, como: la página de categorías, las actividades del usuario, etc. Probablemente eliminaría la configuración
collapse_in_topic_view y crearía una nueva con rutas específicas o simplemente la habilitaría en todas partes.
En mi código de prueba, usé algo como esto para ignorar otras rutas:
JS
function isAllowedRoute(routeName) {
const fullRoutesName = [
"index",
"userActivity.topics",
"userActivity.read",
...siteSettings.top_menu.split("|").map((item) => `discovery.${item}`),
];
const partialRoutesName = ["topic."];
if (
fullRoutesName.includes(routeName) ||
partialRoutesName.some((partial) => routeName.startsWith(partial))
) {
return true;
}
return false;
}
- La inyección de CSS se puede reemplazar usando la API para agregar una clase a
topic-list-item y a una etiqueta, luego mueves el CSS a common.css.
Por ejemplo:
JS
```js
import { defaultRenderTag } from "discourse/lib/render-tag";
api.registerValueTransformer(
"topic-list-item-class",
({ value, context }) => {
if (highlightedTagsSet.size === 0) {
return value;
}
if (context.topic?.tags?.some((tag) => highlightedTagsSet.has(tag))) {
return [...value, `highlighted-tag__${settings.highlighted_style}`];
}
return value;
}
);
api.replaceTagRenderer((tag, params) => {
if (highlightedTagsSet.has(tag)) {
params.extraClass = params.extraClass || "";
params.extraClass += "highlighted";
}
return defaultRenderTag(tag, params);
});
```
CSS
/* Oculta el último separador antes del botón de revelar */
.discourse-tags__tag-separator:has(+ .reveal-tag-action) {
visibility: hidden;
}
.reveal-tag-action {
color: var(--primary-500);
&.-box {
background-color: var(--primary-50);
outline: 1px solid var(--primary-200);
padding-inline: 8px;
}
}
.latest-topic-list-item,
.topic-list-item {
.discourse-tag.highlighted {
color: var(--tertiary);
border-color: var(--tertiary);
background: color-mix(in srgb, var(--tertiary) 12%, transparent);
font-weight: 600;
}
&.highlighted-tag {
&__left-border {
border-left: 3px solid var(--tertiary);
background: color-mix(in srgb, var(--tertiary) 6%, transparent);
transition: box-shadow 160ms ease;
&:hover {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
}
&__outline {
outline: 1px solid var(--tertiary);
outline-offset: -2px;
border-radius: 7px;
background: color-mix(in srgb, var(--tertiary) 5%, transparent);
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
transition: background-color 160ms ease;
}
&__card {
border-left: 3px solid var(--tertiary);
background: var(--tertiary-very-low);
border-radius: var(--border-radius);
padding-block: var(--space-2);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
transition: box-shadow 160ms ease;
&:hover {
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1);
}
}
}
}
```
- No necesitas establecer la ruta actual desde
onPageChange, puedes acceder a ella desde el router.
- Ten cuidado con las mayúsculas y minúsculas de las etiquetas. Tienes configuraciones del sitio que no fuerzan las minúsculas, así que creo que es mejor no modificar la etiqueta.
- Sobre restablecer el estado, probablemente puedas usar
onPageChange.
JS
```js
api.onPageChange((url) => {
const route = api.container.lookup("service:router").recognize(url);
if (!isAllowedRoute(route?.name)) {
return;
}
for (const [id, model] of topicModels) {
if (model && model.revealTags) {
model.revealTags = false;
model.notifyPropertyChange("tags");
}
}
});
```
- Si puedes, sería genial añadir pruebas.
Aquí está el código de prueba completo (hice otros cambios menores)
JS
import { computed } from "@ember/object";
import { apiInitializer } from "discourse/lib/api";
import { i18n } from "discourse-i18n";
import { defaultRenderTag } from "discourse/lib/render-tag";
import { service } from "@ember/service";
export default apiInitializer((api) => {
const siteSettings = api.container.lookup("service:site-settings");
const router = api.container.lookup("service:router");
const maxVisibleTags = Math.min(
settings.max_tags_visible,
siteSettings.max_tags_per_topic
);
const highlightedTagsSet = new Set(settings.highlighted_tags.split("|"));
const topicModels = new Map();
function isAllowedRoute(routeName) {
const fullRoutesName = [
"index",
"userActivity.topics",
"userActivity.read",
"tag.show",
...siteSettings.top_menu.split("|").map((item) => `discovery.${item}`),
];
const partialRoutesName = ["topic."];
if (
fullRoutesName.includes(routeName) ||
partialRoutesName.some((partial) => routeName.startsWith(partial))
) {
return true;
}
return false;
}
api.modifyClass(
"model:topic",
(Superclass) =>
class extends Superclass {
@service router;
revealTags = false;
init() {
super.init(...arguments);
topicModels.set(String(this.id), this);
}
willDestroy() {
super.willDestroy(...arguments);
topicModels.delete(String(this.id));
}
@computed("tags")
get visibleListTags() {
const baseTags = super.visibleListTags || [];
if (!isAllowedRoute(this.router.currentRouteName)) {
return baseTags;
}
const highlightedList = [];
const regularList = [];
baseTags.forEach((tag) => {
if (highlightedTagsSet.has(tag)) {
highlightedList.push(tag);
} else {
regularList.push(tag);
}
});
if (this.revealTags) {
return [...highlightedList, ...regularList];
}
return [...highlightedList, ...regularList.slice(0, maxVisibleTags)];
}
}
);
api.addTagsHtmlCallback(
(topic) => {
if (!isAllowedRoute(topic.router.currentRouteName)) {
return "";
}
const allTags = topic.tags || [];
if (allTags.length === 0) {
return "";
}
const highlightedCount = allTags.filter((tag) =>
highlightedTagsSet.has(tag)
).length;
const regularCount = allTags.length - highlightedCount;
const effectiveLimit =
highlightedCount + Math.min(regularCount, maxVisibleTags);
// Solo muestra el alternador si hay etiquetas ocultas
if (allTags.length <= effectiveLimit) {
return "";
}
const isExpanded = topic.revealTags;
const hiddenCount = allTags.length - effectiveLimit;
const label = isExpanded
? i18n(themePrefix("js.tag_reveal.hide"))
: i18n(themePrefix("js.tag_reveal.more_tags"), {
count: hiddenCount,
});
const classList = ["discourse-tag", "reveal-tag-action"];
if (settings.toggle_tag_style === "box") {
classList.push("-box");
}
return `<a class="${classList.join(" ")}" role="button" aria-expanded="${isExpanded}">${label}</a>`;
},
{
priority: siteSettings.max_tags_per_topic + 1,
}
);
api.registerValueTransformer(
"topic-list-item-class",
({ value, context }) => {
if (highlightedTagsSet.size === 0) {
return value;
}
if (context.topic?.tags?.some((tag) => highlightedTagsSet.has(tag))) {
return [...value, `highlighted-tag__${settings.highlighted_style}`];
}
return value;
}
);
api.replaceTagRenderer((tag, params) => {
let newParams = params;
if (highlightedTagsSet.has(tag)) {
newParams = {
...params,
extraClass: [params.extraClass, "highlighted"]
.filter(Boolean)
.join(" "),
};
}
return defaultRenderTag(tag, newParams);
});
document.addEventListener(
"click",
(event) => {
const target = event.target;
if (!target?.matches(".reveal-tag-action")) {
return;
}
event.preventDefault();
event.stopPropagation();
const element =
target.closest("[data-topic-id]") ||
document.querySelector("h1[data-topic-id]");
const topicId = element?.dataset.topicId;
if (!topicId) {
return;
}
const topicModel = topicModels.get(topicId);
if (!topicModel) {
return;
}
topicModel.revealTags = !topicModel.revealTags;
topicModel.notifyPropertyChange("tags");
},
true
);
api.onPageChange((url) => {
const route = api.container.lookup("service:router").recognize(url);
if (!isAllowedRoute(route?.name)) {
return;
}
for (const [id, model] of topicModels) {
if (model && model.revealTags) {
model.revealTags = false;
model.notifyPropertyChange("tags");
}
}
});
});