FEATURE: unified popover implementation (#7244)
This commit is contained in:
parent
4a1096f14a
commit
8fb63b2706
|
@ -4,10 +4,6 @@ import { exportEntity } from "discourse/lib/export-csv";
|
||||||
import { outputExportResult } from "discourse/lib/export-result";
|
import { outputExportResult } from "discourse/lib/export-result";
|
||||||
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
import {
|
|
||||||
registerHoverTooltip,
|
|
||||||
unregisterHoverTooltip
|
|
||||||
} from "discourse/lib/tooltip";
|
|
||||||
|
|
||||||
const TABLE_OPTIONS = {
|
const TABLE_OPTIONS = {
|
||||||
perPage: 8,
|
perPage: 8,
|
||||||
|
@ -102,18 +98,6 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
didRender() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
registerHoverTooltip($(".info[data-tooltip]"));
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
unregisterHoverTooltip($(".info[data-tooltip]"));
|
|
||||||
},
|
|
||||||
|
|
||||||
showError: Ember.computed.or(
|
showError: Ember.computed.or(
|
||||||
"showTimeoutError",
|
"showTimeoutError",
|
||||||
"showExceptionError",
|
"showExceptionError",
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { showPopover, hidePopover } from "discourse/lib/d-popover";
|
||||||
|
|
||||||
|
const SELECTORS =
|
||||||
|
"[data-html-popover],[data-tooltip],[data-popover],[data-html-tooltip]";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "d-popover",
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
$("#main").on("click.d-popover mouseenter.d-popover", SELECTORS, event =>
|
||||||
|
showPopover(event)
|
||||||
|
);
|
||||||
|
|
||||||
|
$("#main").on("mouseleave.d-popover", SELECTORS, event =>
|
||||||
|
hidePopover(event)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,173 @@
|
||||||
|
import { siteDir } from "discourse/lib/text-direction";
|
||||||
|
|
||||||
|
const D_POPOVER_ID = "d-popover";
|
||||||
|
|
||||||
|
const D_POPOVER_TEMPLATE = `
|
||||||
|
<div id="${D_POPOVER_ID}" class="is-under">
|
||||||
|
<div class="d-popover-arrow d-popover-top-arrow"></div>
|
||||||
|
<div class="d-popover-content">
|
||||||
|
<div class="spinner small"></div>
|
||||||
|
</div>
|
||||||
|
<div class="d-popover-arrow d-popover-bottom-arrow"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const D_ARROW_HEIGHT = 10;
|
||||||
|
|
||||||
|
const D_HORIZONTAL_MARGIN = 5;
|
||||||
|
|
||||||
|
export function hidePopover() {
|
||||||
|
getPopover()
|
||||||
|
.fadeOut()
|
||||||
|
.remove();
|
||||||
|
|
||||||
|
return getPopover();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPopover(event, options = {}) {
|
||||||
|
const $enteredElement = $(event.currentTarget);
|
||||||
|
|
||||||
|
if (isRetina()) {
|
||||||
|
getPopover().addClass("retina");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getPopover().length) {
|
||||||
|
$("body").append($(D_POPOVER_TEMPLATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
setPopoverHtmlContent($enteredElement, options.htmlContent);
|
||||||
|
setPopoverTextContent($enteredElement, options.textContent);
|
||||||
|
|
||||||
|
getPopover().fadeIn();
|
||||||
|
|
||||||
|
positionPopover($enteredElement);
|
||||||
|
|
||||||
|
return {
|
||||||
|
html: content => replaceHtmlContent($enteredElement, content),
|
||||||
|
text: content => replaceTextContent($enteredElement, content),
|
||||||
|
hide: hidePopover
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopoverHtmlContent($enteredElement, content) {
|
||||||
|
content =
|
||||||
|
content ||
|
||||||
|
$enteredElement.attr("data-html-popover") ||
|
||||||
|
$enteredElement.attr("data-html-tooltip");
|
||||||
|
|
||||||
|
replaceHtmlContent($enteredElement, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPopoverTextContent($enteredElement, content) {
|
||||||
|
content =
|
||||||
|
content ||
|
||||||
|
$enteredElement.attr("data-popover") ||
|
||||||
|
$enteredElement.attr("data-tooltip");
|
||||||
|
|
||||||
|
replaceTextContent($enteredElement, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceTextContent($enteredElement, content) {
|
||||||
|
if (content) {
|
||||||
|
getPopover()
|
||||||
|
.find(".d-popover-content")
|
||||||
|
.text(content);
|
||||||
|
window.requestAnimationFrame(() => positionPopover($enteredElement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceHtmlContent($enteredElement, content) {
|
||||||
|
if (content) {
|
||||||
|
getPopover()
|
||||||
|
.find(".d-popover-content")
|
||||||
|
.html(content);
|
||||||
|
window.requestAnimationFrame(() => positionPopover($enteredElement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function positionPopover($element) {
|
||||||
|
const $popover = getPopover();
|
||||||
|
$popover.removeClass("is-above is-under is-left-aligned is-right-aligned");
|
||||||
|
|
||||||
|
const $dHeader = $(".d-header");
|
||||||
|
const windowRect = {
|
||||||
|
left: 0,
|
||||||
|
top: $dHeader.length ? $dHeader[0].getBoundingClientRect().bottom : 0,
|
||||||
|
width: $(window).width(),
|
||||||
|
height: $(window).height()
|
||||||
|
};
|
||||||
|
|
||||||
|
const popoverRect = {
|
||||||
|
width: $popover.width(),
|
||||||
|
height: $popover.height(),
|
||||||
|
left: null,
|
||||||
|
right: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (popoverRect.width > windowRect.width - D_HORIZONTAL_MARGIN * 2) {
|
||||||
|
popoverRect.width = windowRect.width - D_HORIZONTAL_MARGIN * 2;
|
||||||
|
$popover.width(popoverRect.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetRect = $element[0].getBoundingClientRect();
|
||||||
|
const underSpace = windowRect.height - targetRect.bottom - D_ARROW_HEIGHT;
|
||||||
|
const topSpace = targetRect.top - windowRect.top - D_ARROW_HEIGHT;
|
||||||
|
|
||||||
|
if (
|
||||||
|
underSpace > popoverRect.height + D_HORIZONTAL_MARGIN ||
|
||||||
|
underSpace > topSpace
|
||||||
|
) {
|
||||||
|
$popover
|
||||||
|
.css("top", targetRect.bottom + window.pageYOffset + D_ARROW_HEIGHT)
|
||||||
|
.addClass("is-under");
|
||||||
|
} else {
|
||||||
|
$popover
|
||||||
|
.css(
|
||||||
|
"top",
|
||||||
|
targetRect.top +
|
||||||
|
window.pageYOffset -
|
||||||
|
popoverRect.height -
|
||||||
|
D_ARROW_HEIGHT
|
||||||
|
)
|
||||||
|
.addClass("is-above");
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftSpace = targetRect.left + targetRect.width / 2;
|
||||||
|
|
||||||
|
if (siteDir() === "ltr") {
|
||||||
|
if (leftSpace > popoverRect.width / 2 + D_HORIZONTAL_MARGIN) {
|
||||||
|
popoverRect.left = leftSpace - popoverRect.width / 2;
|
||||||
|
$popover.css("left", popoverRect.left);
|
||||||
|
} else {
|
||||||
|
popoverRect.left = D_HORIZONTAL_MARGIN;
|
||||||
|
$popover.css("left", popoverRect.left).addClass("is-left-aligned");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const rightSpace = windowRect.width - targetRect.right;
|
||||||
|
|
||||||
|
if (rightSpace > popoverRect.width / 2 + D_HORIZONTAL_MARGIN) {
|
||||||
|
popoverRect.left = leftSpace - popoverRect.width / 2;
|
||||||
|
$popover.css("left", popoverRect.left);
|
||||||
|
} else {
|
||||||
|
popoverRect.left =
|
||||||
|
windowRect.width - popoverRect.width - D_HORIZONTAL_MARGIN * 2;
|
||||||
|
$popover.css("left", popoverRect.left).addClass("is-right-aligned");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arrowPosition;
|
||||||
|
if (siteDir() === "ltr") {
|
||||||
|
arrowPosition = Math.abs(targetRect.left - popoverRect.left);
|
||||||
|
} else {
|
||||||
|
arrowPosition = targetRect.left - popoverRect.left + targetRect.width / 2;
|
||||||
|
}
|
||||||
|
$popover.find(".d-popover-arrow").css("left", arrowPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRetina() {
|
||||||
|
return window.devicePixelRatio && window.devicePixelRatio > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPopover() {
|
||||||
|
return $(document.getElementById(D_POPOVER_ID));
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
|
||||||
const fadeSpeed = 300;
|
const fadeSpeed = 300;
|
||||||
|
@ -77,12 +78,16 @@ export function hideTooltip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerTooltip(jqueryContext) {
|
export function registerTooltip(jqueryContext) {
|
||||||
|
deprecated("tooltip is getting deprecated. Use d-popover instead");
|
||||||
|
|
||||||
if (jqueryContext.length) {
|
if (jqueryContext.length) {
|
||||||
jqueryContext.off("click").on("click", event => showTooltip(event));
|
jqueryContext.off("click").on("click", event => showTooltip(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerHoverTooltip(jqueryContext) {
|
export function registerHoverTooltip(jqueryContext) {
|
||||||
|
deprecated("tooltip is getting deprecated. Use d-popover instead");
|
||||||
|
|
||||||
if (jqueryContext.length) {
|
if (jqueryContext.length) {
|
||||||
jqueryContext
|
jqueryContext
|
||||||
.off("mouseenter mouseleave click")
|
.off("mouseenter mouseleave click")
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
$d-popover-background: $secondary;
|
||||||
|
$d-popover-border: $primary-medium;
|
||||||
|
|
||||||
|
@-webkit-keyframes popoverFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popoverFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#d-popover {
|
||||||
|
background-color: $d-popover-background;
|
||||||
|
position: absolute;
|
||||||
|
z-index: z("tooltip");
|
||||||
|
border-color: $d-popover-border;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
max-width: 300px;
|
||||||
|
-webkit-animation: popoverFadeIn 0.5s;
|
||||||
|
animation: popoverFadeIn 0.5s;
|
||||||
|
background-clip: padding-box;
|
||||||
|
display: block;
|
||||||
|
box-shadow: shadow("dropdown");
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
&.is-under {
|
||||||
|
margin-top: 0px;
|
||||||
|
|
||||||
|
.d-popover-top-arrow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-bottom-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-above {
|
||||||
|
margin-top: 0px;
|
||||||
|
|
||||||
|
.d-popover-bottom-arrow {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-top-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.retina {
|
||||||
|
border-width: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-content {
|
||||||
|
padding: 1em;
|
||||||
|
font-size: $font-down-1;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
-webkit-animation: popoverFadeIn 0.5s;
|
||||||
|
animation: popoverFadeIn 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-arrow {
|
||||||
|
border-style: solid;
|
||||||
|
color: transparent;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
z-index: calc(z("tooltip") - 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-top-arrow {
|
||||||
|
border-color: transparent transparent $d-popover-border;
|
||||||
|
top: 8px;
|
||||||
|
transform: translate(0, -15px);
|
||||||
|
border-width: 0 8px 8px;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border-color: transparent transparent $d-popover-background;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 7px 7px;
|
||||||
|
bottom: -8px;
|
||||||
|
margin-left: -7px;
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-popover-bottom-arrow {
|
||||||
|
border-color: $d-popover-border transparent transparent;
|
||||||
|
top: calc(100% + 16px);
|
||||||
|
transform: translate(0, -16px);
|
||||||
|
border-width: 8px 8px 0;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
border-color: $d-popover-background transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 7px 7px 0;
|
||||||
|
bottom: 2px;
|
||||||
|
transform: translate(-7px, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,9 @@
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
import { registerTooltip } from "discourse/lib/tooltip";
|
|
||||||
|
|
||||||
function initializeDiscourseLocalDates(api) {
|
function initializeDiscourseLocalDates(api) {
|
||||||
api.decorateCooked($elem => {
|
api.decorateCooked($elem => {
|
||||||
$(".discourse-local-date", $elem).applyLocalDates();
|
$(".discourse-local-date", $elem).applyLocalDates();
|
||||||
registerTooltip($(".discourse-local-date", $elem));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
api.addToolbarPopupMenuOptionsCallback(() => {
|
api.addToolbarPopupMenuOptionsCallback(() => {
|
||||||
|
|
Loading…
Reference in New Issue