Да, добавление URL вызовет навигацию, так как нет логики для перехвата события.
Ссылка вызывает действие Ember. Все действия в Discourse расширяемы. Таким образом, они работают как хуки кастомизации. Так как же изменить действие? Давайте сначала посмотрим, что оно делает.
Ищите на GitHub или локально по имени действия. Действия всегда определяются в JS-файлах и упоминаются в Handlebars. Нам нужно увидеть определение, поэтому сузим поиск до JS-файлов.
Repository search results · GitHub
Вы получите четыре файла. Какой из них нужно просмотреть? Вы хотите кастомизировать шаблон
discovery/topics.hbs.
Значит, вам нужен файл
discovery/topics.js.
Помогает ли какой-то из этих фрагментов кода? Нет, но теперь мы знаем, где определено действие. Давайте его изменим.
discovery/topics.js — это класс Ember. Вы можете модифицировать классы Ember с помощью метода в plugin-api, который называется… modifyClass ![]()
Мы уже знаем класс, который хотим изменить. Это discovery/topics. Нам нужно понять, к какому типу классов он относится. Проверим структуру каталогов.
discourse/app/controllers/discovery/topics.js
Это контроллер.
Также мы знаем, что хотим изменить действие showInserted в этом классе. Начнём с этого:
api.modifyClass("controller:discovery/topics", {
pluginId: 'sticky-new-topics-banner',
actions: {
showInserted() {
// давайте сделаем какую-то работу
}
}
});
Затем вы можете добавить любой код, который прокручивает окно при клике пользователя на баннер «новые темы». Я использовал что-то вроде этого:
const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();
Подробнее о scrollIntoView() можно прочитать здесь.
Затем добавьте это в метод plugin-api следующим образом:
api.modifyClass("controller:discovery/topics", {
pluginId: 'sticky-new-topics-banner',
actions: {
showInserted() {
+ const listControls = document.querySelector(".list-controls");
+ listControls.scrollIntoView();
}
}
});
Итак, мы закончили? Нет. Это ломает Discourse, потому что вы полностью переопределяете действие. При клике на ссылку теперь будет происходить прокрутка к элементу list-controls, но новые темы не загрузятся. Почему? Потому что код из ядра больше не выполняется. Имеется в виду вот этот код:
Так как это исправить? С помощью одной простой строки:
this._super(...arguments);
Вам не нужно копировать код из ядра, если вы просто хотите добавить что-то к нему. Выполните свою задачу, затем добавьте эту строку. Всё, что она делает, — гарантирует, что код из ядра будет применён.
api.modifyClass("controller:discovery/topics", {
pluginId: 'sticky-new-topics-banner',
actions: {
showInserted() {
const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();
+
+ this._super(...arguments);
}
}
});
Если вы протестируете это, то увидите, что почти всё работает отлично, кроме одного… заголовок перекрывает list-controls. Почему? Потому что заголовок установлен как sticky.
Это можно исправить несколькими способами на JS — рассчитать высоту, получить смещение, импортировать вспомогательный метод из Discourse и т.д. Я не буду углубляться в эти варианты.
Самый простой способ — использовать CSS со свойством scroll-margin-top, о котором можно прочитать здесь.
Добавим следующее:
.list-controls {
scroll-margin-top: calc(var(--header-offset) * 2);
}
По-русски: при клике на ссылку прокрутка должна остановиться на верхней части list-controls минус удвоенная высота заголовка, чтобы не было перекрытия и оставался небольшой отступ снизу.
Итак, давайте соберём всё вместе.
Вкладка common header:
<script type="text/discourse-plugin" version="0.8">
api.modifyClass("controller:discovery/topics", {
pluginId: "sticky-new-topics-banner",
actions: {
showInserted() {
const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();
this._super(...arguments);
}
}
});
</script>
Общие стили CSS:
#list-area {
// на мобильных устройствах используется другая раскладка
.alert-info,
.show-more.has-topics {
position: sticky;
// Safari иногда капризен без префикса
position: -webkit-sticky;
top: var(--header-offset);
// баннер должен быть поверх контента
z-index: z("header");
}
}
.list-controls {
scroll-margin-top: calc(var(--header-offset) * 2);
}