wekan/client/lib/popup.js
copilot-swe-agent[bot] 97dd5d2064 Resolve merge conflicts by accepting PR #6131 changes
Co-authored-by: xet7 <15545+xet7@users.noreply.github.com>
2026-02-07 16:30:08 +00:00

102 lines
No EOL
3.9 KiB
JavaScript

import PopupComponent from '/client/components/main/popup';
import { TAPi18n } from '/imports/i18n';
window.Popup = new (class {
/// This function returns a callback that can be used in an event map:
/// Template.tplName.events({
/// 'click .elementClass': Popup.open("popupName"),
/// });
/// The popup inherit the data context of its parent.
open(name, args) {
const self = this;
return function(evt, options) {
const popupName = `${name}Popup`;
const openerElement = evt.target;
let classicArgs = { openerElement: openerElement, name: popupName, title: self._getTitle(popupName), miscOptions: options };
if (typeof(args) === "object") {
classicArgs = Object.assign(classicArgs, args);
}
PopupComponent.open(classicArgs);
evt.preventDefault();
// important so that one click does not opens multiple, stacked popups
evt.stopPropagation();
};
}
/// This function returns a callback that can be used in an event map:
/// Template.tplName.events({
/// 'click .elementClass': Popup.afterConfirm("popupName", function() {
/// // What to do after the user has confirmed the action
/// }),
/// });
afterConfirm(name, action) {
const self = this;
return function(evt, tpl) {
tpl ??= {};
tpl.afterConfirm = action;
// Just a wrapper of open which will call `action` on some events
// see PopupDetachedComponent; for now this is hardcoded
self.open(name)(evt, tpl);
evt.preventDefault();
};
}
/// In case the popup was opened from a parent popup we can get back to it
/// with this `Popup.back()` function. You can go back several steps at once
/// by providing a number to this function, e.g. `Popup.back(2)`. In this case
/// intermediate popup won't even be rendered on the DOM. If the number of
/// steps back is greater than the popup stack size, the popup will be closed.
back(n = 1) {
_.times(n, () => PopupComponent.destroy());
}
/// Close the current opened popup.
close() {
this.back();
}
closeAll() {
this.back(PopupComponent.stack.length)
}
getOpenerComponent(n=4) {
const { openerElement } = Template.parentData(n);
return BlazeComponent.getComponentForElement(openerElement);
}
// We get the title from the translation files. Instead of returning the
// result, we return a function that compute the result and since `TAPi18n.__`
// is a reactive data source, the title will be changed reactively.
_getTitle(popupName) {
return () => {
const translationKey = `${popupName}-title`;
// XXX There is no public API to check if there is an available
// translation for a given key. So we try to translate the key and if the
// translation output equals the key input we deduce that no translation
// was available and returns `false`. There is a (small) risk a false
// positives.
const title = TAPi18n.__(translationKey);
// when popup showed as full of small screen, we need a default header to clearly see [X] button
const defaultTitle = Utils.isMiniScreen() ? '' : false;
return title !== translationKey ? title : defaultTitle;
};
}
})();
// We close a potential opened popup on any left click on the document, or go
// one step back by pressing escape.
const escapeActions = ['back', 'close'];
escapeActions.forEach(actionName => {
EscapeActions.register(
`popup-${actionName}`,
() => Popup[actionName](),
() => PopupComponent.stack.length > 0,
{
// will maybe need something more robust, but for now it enables multiple cards opened without closing each other when clicking on common UI elements
noClickEscapeOn: '.js-pop-over,.js-open-card-title-popup,.js-open-inlined-form,.textcomplete-dropdown,.js-card-details,.board-sidebar,#header,.add-comment-reaction',
enabledOnClick: actionName === 'close',
},
);
});