FIX: uses popper for cards
This commit is contained in:
parent
fda834d01c
commit
73488f2f33
|
@ -2,12 +2,13 @@ import { alias, match } from "@ember/object/computed";
|
|||
import { schedule, throttle } from "@ember/runloop";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import afterTransition from "discourse/lib/after-transition";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { createPopper } from "@popperjs/core";
|
||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||
|
||||
const DEFAULT_SELECTOR = "#main-outlet";
|
||||
|
||||
|
@ -36,9 +37,10 @@ export default Mixin.create({
|
|||
loading: null,
|
||||
cardTarget: null,
|
||||
post: null,
|
||||
isFixed: false,
|
||||
isDocked: false,
|
||||
|
||||
_popperReference: null,
|
||||
|
||||
_show(username, target, event) {
|
||||
// No user card for anon
|
||||
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||
|
@ -56,12 +58,6 @@ export default Mixin.create({
|
|||
|
||||
this.appEvents.trigger("card:show", username, target, event);
|
||||
|
||||
const currentUsername = this.username;
|
||||
if (username === currentUsername || this.loading === username) {
|
||||
this._positionCard($(target));
|
||||
return;
|
||||
}
|
||||
|
||||
const closestArticle = target.closest("article");
|
||||
const postId = closestArticle?.dataset?.postId || null;
|
||||
const wasVisible = this.visible;
|
||||
|
@ -88,6 +84,7 @@ export default Mixin.create({
|
|||
this.appEvents.trigger("user-card:show", { username });
|
||||
this._showCallback(username, $(target)).then((user) => {
|
||||
this.appEvents.trigger("user-card:after-show", { user });
|
||||
this._positionCard($(target), event);
|
||||
});
|
||||
|
||||
// We bind scrolling on mobile after cards are shown to hide them if user scrolls
|
||||
|
@ -100,7 +97,7 @@ export default Mixin.create({
|
|||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
afterTransition($(this.element), this._hide);
|
||||
|
||||
const id = this.elementId;
|
||||
const triggeringLinkClass = this.triggeringLinkClass;
|
||||
const previewClickEvent = `click.discourse-preview-${id}-${triggeringLinkClass}`;
|
||||
|
@ -168,7 +165,7 @@ export default Mixin.create({
|
|||
},
|
||||
|
||||
_topicHeaderTrigger(username, target) {
|
||||
this.setProperties({ isFixed: true, isDocked: true });
|
||||
this.setProperties({ isDocked: true });
|
||||
return this._show(username, target);
|
||||
},
|
||||
|
||||
|
@ -188,104 +185,75 @@ export default Mixin.create({
|
|||
},
|
||||
|
||||
_previewClick($target) {
|
||||
this.set("isFixed", true);
|
||||
return this._show($target.text().replace(/^@/, ""), $target);
|
||||
},
|
||||
|
||||
_positionCard(target) {
|
||||
const rtl = $("html").css("direction") === "rtl";
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const width = $(this.element).width();
|
||||
const height = 175;
|
||||
const isFixed = this.isFixed;
|
||||
const isDocked = this.isDocked;
|
||||
|
||||
let verticalAdjustments = 0;
|
||||
_positionCard(target, event) {
|
||||
this._popperReference?.destroy();
|
||||
|
||||
schedule("afterRender", () => {
|
||||
if (target) {
|
||||
if (!this.site.mobileView) {
|
||||
let position = target.offset();
|
||||
if (target.parents(".d-header").length > 0) {
|
||||
position.top = target.position().top;
|
||||
}
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position) {
|
||||
position.bottom = "unset";
|
||||
if (this.site.desktopView) {
|
||||
const avatarOverflowSize = 44;
|
||||
this._popperReference = createPopper(target[0], this.element, {
|
||||
placement: "right",
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
options: {
|
||||
padding: {
|
||||
top: headerOffset() + avatarOverflowSize,
|
||||
right: 10,
|
||||
bottom: 10,
|
||||
left: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "eventListeners", enabled: false },
|
||||
{ name: "offset", options: { offset: [10, 10] } },
|
||||
],
|
||||
});
|
||||
} else {
|
||||
document.querySelector(".card-cloak")?.classList.remove("hidden");
|
||||
this._popperReference = createPopper(target[0], this.element, {
|
||||
modifiers: [
|
||||
{ name: "eventListeners", enabled: false },
|
||||
{
|
||||
name: "computeStyles",
|
||||
enabled: true,
|
||||
fn({ state }) {
|
||||
// mimics our modal top of the screen positioning
|
||||
state.styles.popper = {
|
||||
...state.styles.popper,
|
||||
position: "fixed",
|
||||
left: `${
|
||||
(window.innerWidth - state.rects.popper.width) / 2
|
||||
}px`,
|
||||
top: "10%",
|
||||
transform: "translateY(-10%)",
|
||||
};
|
||||
|
||||
if (rtl) {
|
||||
// The site direction is rtl
|
||||
position.right = $(window).width() - position.left + 10;
|
||||
position.left = "auto";
|
||||
let overage = $(window).width() - 50 - (position.right + width);
|
||||
if (overage < 0) {
|
||||
position.right += overage;
|
||||
position.top += target.height() + 48;
|
||||
verticalAdjustments += target.height() + 48;
|
||||
}
|
||||
} else {
|
||||
// The site direction is ltr
|
||||
position.left += target.width() + 10;
|
||||
return state;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
let overage = $(window).width() - 50 - (position.left + width);
|
||||
if (overage < 0) {
|
||||
position.left += overage;
|
||||
position.top += target.height() + 48;
|
||||
verticalAdjustments += target.height() + 48;
|
||||
}
|
||||
}
|
||||
this.element.classList.toggle("docked-card", this.isDocked);
|
||||
|
||||
// It looks better to have the card aligned slightly higher
|
||||
position.top -= 24;
|
||||
|
||||
if (isFixed) {
|
||||
position.top -= $("html").scrollTop();
|
||||
//if content is fixed and will be cut off on the bottom, display it above...
|
||||
if (
|
||||
position.top + height + verticalAdjustments >
|
||||
$(window).height() - 50
|
||||
) {
|
||||
position.bottom =
|
||||
$(window).height() -
|
||||
(target.offset().top - $("html").scrollTop());
|
||||
if (verticalAdjustments > 0) {
|
||||
position.bottom += 48;
|
||||
}
|
||||
position.top = "unset";
|
||||
}
|
||||
}
|
||||
|
||||
const avatarOverflowSize = 44;
|
||||
if (isDocked && position.top < avatarOverflowSize) {
|
||||
position.top = avatarOverflowSize;
|
||||
}
|
||||
|
||||
$(this.element).css(position);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.site.mobileView) {
|
||||
$(".card-cloak").removeClass("hidden");
|
||||
let position = target.offset();
|
||||
position.top = "10%"; // match modal behaviour
|
||||
position.left = 0;
|
||||
$(this.element).css(position);
|
||||
}
|
||||
$(this.element).toggleClass("docked-card", isDocked);
|
||||
|
||||
// After the card is shown, focus on the first link
|
||||
//
|
||||
// note: we DO NOT use afterRender here cause _positionCard may
|
||||
// run afterwards, if we allowed this to happen the usercard
|
||||
// may be offscreen and we may scroll all the way to it on focus
|
||||
if (event.pointerId === -1) {
|
||||
discourseLater(() => {
|
||||
const firstLink = this.element.querySelector("a");
|
||||
firstLink && firstLink.focus();
|
||||
}, 350);
|
||||
}
|
||||
// After the card is shown, focus on the first link
|
||||
//
|
||||
// note: we DO NOT use afterRender here cause _positionCard may
|
||||
// run afterwards, if we allowed this to happen the usercard
|
||||
// may be offscreen and we may scroll all the way to it on focus
|
||||
if (event?.pointerId === -1) {
|
||||
discourseLater(() => {
|
||||
this.element.querySelector("a")?.focus();
|
||||
}, 350);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -307,7 +275,6 @@ export default Mixin.create({
|
|||
loading: null,
|
||||
cardTarget: null,
|
||||
post: null,
|
||||
isFixed: false,
|
||||
isDocked: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -37,14 +37,8 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
|
|||
color: var(--primary);
|
||||
background: var(--secondary) center center;
|
||||
background-size: cover;
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
opacity: 0;
|
||||
outline: 2px solid transparent;
|
||||
@include transform(scale(0.9));
|
||||
&.show {
|
||||
opacity: 1;
|
||||
@include transform(scale(1));
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 10px;
|
||||
background: rgba(var(--secondary-rgb), 0.85);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// shared styles for user and group cards
|
||||
.user-card,
|
||||
.group-card {
|
||||
position: absolute;
|
||||
z-index: z("usercard");
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
|
|
|
@ -3,7 +3,6 @@ $avatar_width: 120px;
|
|||
// shared styles for user and group cards
|
||||
.user-card,
|
||||
.group-card {
|
||||
position: fixed;
|
||||
// mobile cards should always be on top of everything - 1102
|
||||
z-index: z("mobile-composer") + 2;
|
||||
max-width: 95vw;
|
||||
|
|
Loading…
Reference in New Issue