|
@@ -206,207 +206,3 @@ escapeActions.forEach(actionName => {
|
|
},
|
|
},
|
|
);
|
|
);
|
|
});
|
|
});
|
|
-
|
|
|
|
-// Prevent @member mentions on Add Comment input field
|
|
|
|
-// from closing card, part 5.
|
|
|
|
-// This duplicate below of above popup function is needed, because at
|
|
|
|
-// wekan/components/main/editor.js at bottom is popping up visible
|
|
|
|
-// @member mention, and it seems to trigger closing also card popup,
|
|
|
|
-// so in below closing popup is disabled.
|
|
|
|
-window.PopupNoClose = new (class {
|
|
|
|
- constructor() {
|
|
|
|
- // The template we use to render popups
|
|
|
|
- this.template = Template.popup;
|
|
|
|
-
|
|
|
|
- // We only want to display one popup at a time and we keep the view object
|
|
|
|
- // in this `Popup.current` variable. If there is no popup currently opened
|
|
|
|
- // the value is `null`.
|
|
|
|
- this.current = null;
|
|
|
|
-
|
|
|
|
- // It's possible to open a sub-popup B from a popup A. In that case we keep
|
|
|
|
- // the data of popup A so we can return back to it. Every time we open a new
|
|
|
|
- // popup the stack grows, every time we go back the stack decrease, and if
|
|
|
|
- // we close the popup the stack is reseted to the empty stack [].
|
|
|
|
- this._stack = [];
|
|
|
|
-
|
|
|
|
- // We invalidate this internal dependency every time the top of the stack
|
|
|
|
- // has changed and we want to re-render a popup with the new top-stack data.
|
|
|
|
- this._dep = new Tracker.Dependency();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// 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) {
|
|
|
|
- const self = this;
|
|
|
|
- const popupName = `${name}Popup`;
|
|
|
|
- function clickFromPopup(evt) {
|
|
|
|
- return $(evt.target).closest('.js-pop-over').length !== 0;
|
|
|
|
- }
|
|
|
|
- return function(evt) {
|
|
|
|
- // If a popup is already opened, clicking again on the opener element
|
|
|
|
- // should close it -- and interrupt the current `open` function.
|
|
|
|
- /*
|
|
|
|
- if (self.isOpen()) {
|
|
|
|
- const previousOpenerElement = self._getTopStack().openerElement;
|
|
|
|
- if (previousOpenerElement === evt.currentTarget) {
|
|
|
|
- self.close();
|
|
|
|
- return;
|
|
|
|
- } else {
|
|
|
|
- $(previousOpenerElement).removeClass('is-active');
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- */
|
|
|
|
- // We determine the `openerElement` (the DOM element that is being clicked
|
|
|
|
- // and the one we take in reference to position the popup) from the event
|
|
|
|
- // if the popup has no parent, or from the parent `openerElement` if it
|
|
|
|
- // has one. This allows us to position a sub-popup exactly at the same
|
|
|
|
- // position than its parent.
|
|
|
|
- let openerElement;
|
|
|
|
- if (clickFromPopup(evt)) {
|
|
|
|
- openerElement = self._getTopStack().openerElement;
|
|
|
|
- } else {
|
|
|
|
- self._stack = [];
|
|
|
|
- openerElement = evt.currentTarget;
|
|
|
|
- }
|
|
|
|
- $(openerElement).addClass('is-active');
|
|
|
|
- evt.preventDefault();
|
|
|
|
-
|
|
|
|
- // We push our popup data to the stack. The top of the stack is always
|
|
|
|
- // used as the data source for our current popup.
|
|
|
|
- self._stack.push({
|
|
|
|
- popupName,
|
|
|
|
- openerElement,
|
|
|
|
- hasPopupParent: clickFromPopup(evt),
|
|
|
|
- title: self._getTitle(popupName),
|
|
|
|
- depth: self._stack.length,
|
|
|
|
- offset: self._getOffset(openerElement),
|
|
|
|
- dataContext: (this && this.currentData && this.currentData()) || this,
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // If there are no popup currently opened we use the Blaze API to render
|
|
|
|
- // one into the DOM. We use a reactive function as the data parameter that
|
|
|
|
- // return the complete along with its top element and depends on our
|
|
|
|
- // internal dependency that is being invalidated every time the top
|
|
|
|
- // element of the stack has changed and we want to update the popup.
|
|
|
|
- //
|
|
|
|
- // Otherwise if there is already a popup open we just need to invalidate
|
|
|
|
- // our internal dependency, and since we just changed the top element of
|
|
|
|
- // our internal stack, the popup will be updated with the new data.
|
|
|
|
- if (!self.isOpen()) {
|
|
|
|
- self.current = Blaze.renderWithData(
|
|
|
|
- self.template,
|
|
|
|
- () => {
|
|
|
|
- self._dep.depend();
|
|
|
|
- return { ...self._getTopStack(), stack: self._stack };
|
|
|
|
- },
|
|
|
|
- document.body,
|
|
|
|
- );
|
|
|
|
- } else {
|
|
|
|
- self._dep.changed();
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// 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) {
|
|
|
|
- const context = (this.currentData && this.currentData()) || this;
|
|
|
|
- context.__afterConfirmAction = action;
|
|
|
|
- self.open(name).call(context, evt, tpl);
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// The public reactive state of the popup.
|
|
|
|
- isOpen() {
|
|
|
|
- this._dep.changed();
|
|
|
|
- return Boolean(this.current);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// 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) {
|
|
|
|
- if (this._stack.length > n) {
|
|
|
|
- _.times(n, () => this._stack.pop());
|
|
|
|
- this._dep.changed();
|
|
|
|
- }
|
|
|
|
- // else {
|
|
|
|
- // this.close();
|
|
|
|
- //}
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /// Close the current opened popup.
|
|
|
|
- /*
|
|
|
|
- close() {
|
|
|
|
- if (this.isOpen()) {
|
|
|
|
- Blaze.remove(this.current);
|
|
|
|
- this.current = null;
|
|
|
|
-
|
|
|
|
- const openerElement = this._getTopStack().openerElement;
|
|
|
|
- $(openerElement).removeClass('is-active');
|
|
|
|
-
|
|
|
|
- this._stack = [];
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- getOpenerComponent() {
|
|
|
|
- const { openerElement } = Template.parentData(4);
|
|
|
|
- return BlazeComponent.getComponentForElement(openerElement);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // An utility fonction that returns the top element of the internal stack
|
|
|
|
- _getTopStack() {
|
|
|
|
- return this._stack[this._stack.length - 1];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // We automatically calculate the popup offset from the reference element
|
|
|
|
- // position and dimensions. We also reactively use the window dimensions to
|
|
|
|
- // ensure that the popup is always visible on the screen.
|
|
|
|
- _getOffset(element) {
|
|
|
|
- const $element = $(element);
|
|
|
|
- return () => {
|
|
|
|
- Utils.windowResizeDep.depend();
|
|
|
|
-
|
|
|
|
- if (Utils.isMiniScreen()) return { left: 0, top: 0 };
|
|
|
|
-
|
|
|
|
- const offset = $element.offset();
|
|
|
|
- const popupWidth = 300 + 15;
|
|
|
|
- return {
|
|
|
|
- left: Math.min(offset.left, $(window).width() - popupWidth),
|
|
|
|
- top: offset.top + $element.outerHeight(),
|
|
|
|
- };
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 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;
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
-})();
|
|
|