mirror of
https://github.com/discourse/discourse.git
synced 2025-02-21 19:14:11 +08:00
DEV: Plugin API to add card listener elements (#13887)
This commit is contained in:
parent
a049b8f596
commit
91456ad2cb
@ -11,7 +11,7 @@ const maxMembersToDisplay = 10;
|
|||||||
|
|
||||||
export default Component.extend(CardContentsBase, CleansUp, {
|
export default Component.extend(CardContentsBase, CleansUp, {
|
||||||
elementId: "group-card",
|
elementId: "group-card",
|
||||||
triggeringLinkClass: "mention-group",
|
mentionSelector: "a.mention-group",
|
||||||
classNames: ["no-bg", "group-card"],
|
classNames: ["no-bg", "group-card"],
|
||||||
classNameBindings: [
|
classNameBindings: [
|
||||||
"visible:show",
|
"visible:show",
|
||||||
|
@ -16,7 +16,9 @@ import { prioritizeNameInUx } from "discourse/lib/settings";
|
|||||||
export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
||||||
elementId: "user-card",
|
elementId: "user-card",
|
||||||
classNames: "user-card",
|
classNames: "user-card",
|
||||||
triggeringLinkClass: "mention",
|
avatarSelector: "[data-user-card]",
|
||||||
|
avatarDataAttrKey: "userCard",
|
||||||
|
mentionSelector: "a.mention",
|
||||||
classNameBindings: [
|
classNameBindings: [
|
||||||
"visible:show",
|
"visible:show",
|
||||||
"showBadges",
|
"showBadges",
|
||||||
|
@ -2,7 +2,8 @@ import DiscourseURL from "discourse/lib/url";
|
|||||||
|
|
||||||
export function wantsNewWindow(e) {
|
export function wantsNewWindow(e) {
|
||||||
return (
|
return (
|
||||||
e.isDefaultPrevented() ||
|
e.defaultPrevented ||
|
||||||
|
(e.isDefaultPrevernted && e.isDefaultPrevented()) ||
|
||||||
e.shiftKey ||
|
e.shiftKey ||
|
||||||
e.metaKey ||
|
e.metaKey ||
|
||||||
e.ctrlKey ||
|
e.ctrlKey ||
|
||||||
|
@ -37,6 +37,7 @@ import DiscourseBanner from "discourse/components/discourse-banner";
|
|||||||
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
|
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
|
||||||
import Sharing from "discourse/lib/sharing";
|
import Sharing from "discourse/lib/sharing";
|
||||||
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
|
||||||
|
import { addCardClickListenerSelector } from "discourse/mixins/card-contents-base";
|
||||||
import { addCategorySortCriteria } from "discourse/components/edit-category-settings";
|
import { addCategorySortCriteria } from "discourse/components/edit-category-settings";
|
||||||
import { addDecorator } from "discourse/widgets/post-cooked";
|
import { addDecorator } from "discourse/widgets/post-cooked";
|
||||||
import { addDiscoveryQueryParam } from "discourse/controllers/discovery-sortable";
|
import { addDiscoveryQueryParam } from "discourse/controllers/discovery-sortable";
|
||||||
@ -1084,6 +1085,15 @@ class PluginApi {
|
|||||||
addCategorySortCriteria(criteria);
|
addCategorySortCriteria(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Card contents mixin will add a listener to elements matching this selector
|
||||||
|
* that will open card contents when a mention of div with the correct data attribute
|
||||||
|
* is clicked
|
||||||
|
*/
|
||||||
|
addCardClickListenerSelector(selector) {
|
||||||
|
addCardClickListenerSelector(selector);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a renderer that overrides the display of category links.
|
* Registers a renderer that overrides the display of category links.
|
||||||
*
|
*
|
||||||
|
@ -8,6 +8,11 @@ import headerOutletHeights from "discourse/lib/header-outlet-height";
|
|||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||||
|
|
||||||
|
let _cardClickListenerSelectors = ["#main-outlet"];
|
||||||
|
export function addCardClickListenerSelector(selector) {
|
||||||
|
_cardClickListenerSelectors.push(selector);
|
||||||
|
}
|
||||||
|
|
||||||
export default Mixin.create({
|
export default Mixin.create({
|
||||||
router: service(),
|
router: service(),
|
||||||
|
|
||||||
@ -26,7 +31,7 @@ export default Mixin.create({
|
|||||||
isFixed: false,
|
isFixed: false,
|
||||||
isDocked: false,
|
isDocked: false,
|
||||||
|
|
||||||
_show(username, $target) {
|
_show(username, target) {
|
||||||
// No user card for anon
|
// No user card for anon
|
||||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||||
return false;
|
return false;
|
||||||
@ -35,9 +40,9 @@ export default Mixin.create({
|
|||||||
username = escapeExpression(username.toString());
|
username = escapeExpression(username.toString());
|
||||||
|
|
||||||
// Don't show if nested
|
// Don't show if nested
|
||||||
if ($target.parents(".card-content").length) {
|
if (target.closest(".card-content")) {
|
||||||
this._close();
|
this._close();
|
||||||
DiscourseURL.routeTo($target.attr("href"));
|
DiscourseURL.routeTo(target.href);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,10 +51,10 @@ export default Mixin.create({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const postId = $target.parents("article").data("post-id");
|
const closestArticle = target.closest("article");
|
||||||
|
const postId = closestArticle ? closestArticle.dataset["post-id"] : null;
|
||||||
const wasVisible = this.visible;
|
const wasVisible = this.visible;
|
||||||
const previousTarget = this.cardTarget;
|
const previousTarget = this.cardTarget;
|
||||||
const target = $target[0];
|
|
||||||
|
|
||||||
if (wasVisible) {
|
if (wasVisible) {
|
||||||
this._close();
|
this._close();
|
||||||
@ -69,7 +74,7 @@ export default Mixin.create({
|
|||||||
post,
|
post,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._showCallback(username, $target);
|
this._showCallback(username, $(target));
|
||||||
|
|
||||||
// We bind scrolling on mobile after cards are shown to hide them if user scrolls
|
// We bind scrolling on mobile after cards are shown to hide them if user scrolls
|
||||||
if (this.site.mobileView) {
|
if (this.site.mobileView) {
|
||||||
@ -85,15 +90,12 @@ export default Mixin.create({
|
|||||||
const id = this.elementId;
|
const id = this.elementId;
|
||||||
const triggeringLinkClass = this.triggeringLinkClass;
|
const triggeringLinkClass = this.triggeringLinkClass;
|
||||||
const clickOutsideEventName = `mousedown.outside-${id}`;
|
const clickOutsideEventName = `mousedown.outside-${id}`;
|
||||||
const clickDataExpand = `click.discourse-${id}`;
|
|
||||||
const clickMention = `click.discourse-${id}-${triggeringLinkClass}`;
|
|
||||||
const previewClickEvent = `click.discourse-preview-${id}-${triggeringLinkClass}`;
|
const previewClickEvent = `click.discourse-preview-${id}-${triggeringLinkClass}`;
|
||||||
const mobileScrollEvent = "scroll.mobile-card-cloak";
|
const mobileScrollEvent = "scroll.mobile-card-cloak";
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
boundCardClickHandler: this._cardClickHandler.bind(this),
|
||||||
clickOutsideEventName,
|
clickOutsideEventName,
|
||||||
clickDataExpand,
|
|
||||||
clickMention,
|
|
||||||
previewClickEvent,
|
previewClickEvent,
|
||||||
mobileScrollEvent,
|
mobileScrollEvent,
|
||||||
});
|
});
|
||||||
@ -117,20 +119,10 @@ export default Mixin.create({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#main-outlet").on(clickDataExpand, `[data-${id}]`, (e) => {
|
_cardClickListenerSelectors.forEach((selector) => {
|
||||||
if (wantsNewWindow(e)) {
|
document
|
||||||
return;
|
.querySelector(selector)
|
||||||
}
|
.addEventListener("click", this.boundCardClickHandler);
|
||||||
const $target = $(e.currentTarget);
|
|
||||||
return this._show($target.data(id), $target);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#main-outlet").on(clickMention, `a.${triggeringLinkClass}`, (e) => {
|
|
||||||
if (wantsNewWindow(e)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const $target = $(e.currentTarget);
|
|
||||||
return this._show($target.text().replace(/^@/, ""), $target);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.appEvents.on(previewClickEvent, this, "_previewClick");
|
this.appEvents.on(previewClickEvent, this, "_previewClick");
|
||||||
@ -142,6 +134,41 @@ export default Mixin.create({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_cardClickHandler(event) {
|
||||||
|
if (this.avatarSelector) {
|
||||||
|
let matched = this._showCardOnClick(
|
||||||
|
event,
|
||||||
|
this.avatarSelector,
|
||||||
|
(el) => el.dataset[this.avatarDataAttrKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
return; // Don't need to check for mention click; it's an avatar click
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mention click
|
||||||
|
this._showCardOnClick(event, this.mentionSelector, (el) =>
|
||||||
|
el.innerText.replace(/^@/, "")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_showCardOnClick(event, selector, transformText) {
|
||||||
|
let matchingEl = event.target.closest(selector);
|
||||||
|
if (matchingEl) {
|
||||||
|
if (wantsNewWindow(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return this._show(transformText(matchingEl), matchingEl);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_topicHeaderTrigger(username, $target) {
|
_topicHeaderTrigger(username, $target) {
|
||||||
this.setProperties({ isFixed: true, isDocked: true });
|
this.setProperties({ isFixed: true, isDocked: true });
|
||||||
return this._show(username, $target);
|
return this._show(username, $target);
|
||||||
@ -302,12 +329,14 @@ export default Mixin.create({
|
|||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
const clickOutsideEventName = this.clickOutsideEventName;
|
const clickOutsideEventName = this.clickOutsideEventName;
|
||||||
const clickDataExpand = this.clickDataExpand;
|
|
||||||
const clickMention = this.clickMention;
|
|
||||||
const previewClickEvent = this.previewClickEvent;
|
const previewClickEvent = this.previewClickEvent;
|
||||||
|
|
||||||
$("html").off(clickOutsideEventName);
|
$("html").off(clickOutsideEventName);
|
||||||
$("#main").off(clickDataExpand).off(clickMention);
|
_cardClickListenerSelectors.forEach((selector) => {
|
||||||
|
document
|
||||||
|
.querySelector(selector)
|
||||||
|
.removeEventListener("click", this.boundCardClickHandler);
|
||||||
|
});
|
||||||
|
|
||||||
this.appEvents.off(previewClickEvent, this, "_previewClick");
|
this.appEvents.off(previewClickEvent, this, "_previewClick");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user