All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.primefaces.menu.menu.base.js Maven / Gradle / Ivy

There is a newer version: 14.0.0
Show newest version
/**
 * __PrimeFaces Menu Widget__
 * 
 * Base class for the different menu widgets, such as the `PlainMenu` or the `TieredMenu`.
 * 
 * @prop {PrimeFaces.UnbindCallback} [hideOverlayHandler] Unbind callback for the hide overlay handler.
 * @prop {boolean} itemMouseDown `true` if a menu item was clicked and the mouse button is still pressed.
 * @prop {JQuery} keyboardTarget The DOM element for the form element that can be targeted via arrow or tab keys. 
 * @prop {PrimeFaces.UnbindCallback} [resizeHandler] Unbind callback for the resize handler.
 * @prop {PrimeFaces.UnbindCallback} [scrollHandler] Unbind callback for the scroll handler.
 * @prop {PrimeFaces.CssTransitionHandler | null} [transition] Handler for CSS transitions used by this widget.
 * @prop {JQuery} trigger DOM element which triggers this menu.
 * 
 * @interface {PrimeFaces.widget.MenuCfg} cfg The configuration for the {@link  Menu| Menu widget}.
 * You can access this configuration via {@link PrimeFaces.widget.BaseWidget.cfg|BaseWidget.cfg}. Please note that this
 * configuration is usually meant to be read-only and should not be modified.
 * @extends {PrimeFaces.widget.BaseWidgetCfg} cfg 
 * 
 * @prop {string} cfg.appendTo Search expression for the element to which the menu overlay is appended.
 * @prop {string} cfg.at Defines which position on the target element to align the positioned element against
 * @prop {string} cfg.collision When the positioned element overflows the window in some direction, move it to an
 * alternative position.
 * @prop {string} cfg.my Defines which position on the element being positioned to align with the target element.
 * @prop {boolean} cfg.overlay `true` if this menu is displayed as an overlay, or `false` otherwise.
 * @prop {JQueryUI.JQueryPositionOptions} cfg.pos Describes how to align this menu.
 * @prop {string} cfg.trigger ID of the event which triggers this menu.
 * @prop {string} cfg.triggerEvent Event which triggers this menu.
 */
PrimeFaces.widget.Menu = PrimeFaces.widget.BaseWidget.extend({

    /**
     * @override
     * @inheritdoc
     * @param {PrimeFaces.PartialWidgetCfg} cfg
     */
    init: function(cfg) {
        this._super(cfg);

        if(this.cfg.overlay) {
            this.initOverlay();
        }

        this.keyboardTarget = this.jq.children('.ui-helper-hidden-accessible');
    },

    /**
     * Initializes the overlay. Finds the element to which to append this menu and appends it to that element.
     * @protected
     */
    initOverlay: function() {
        var $this = this;

        this.jq.addClass('ui-menu-overlay');

        this.cfg.trigger = this.cfg.trigger.replace(/\\\\:/g,"\\:");

        this.trigger = PrimeFaces.expressions.SearchExpressionFacade.resolveComponentsAsSelector(this.cfg.trigger);

        //mark trigger and descandants of trigger as a trigger for a primefaces overlay
        this.trigger.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);

        if (!this.cfg.appendTo) {
            this.cfg.appendTo = '@(body)';
        }
        PrimeFaces.utils.registerDynamicOverlay(this, this.jq, this.id);
        this.transition = PrimeFaces.utils.registerCSSTransition(this.jq, 'ui-connected-overlay');

        this.cfg.pos = {
            my: this.cfg.my,
            at: this.cfg.at,
            of: this.trigger,
            collision: this.cfg.collision || "flip",
            using: function(pos, directions) {
                $(this).css('transform-origin', 'center ' + directions.vertical).css(pos);
            }
        };

        this.trigger.off(this.cfg.triggerEvent + '.ui-menu').on(this.cfg.triggerEvent + '.ui-menu', function(e) {
            var trigger = $(this);

            if($this.jq.is(':visible')) {
                $this.hide();
            }
            else {
                $this.show();

                if(trigger.is(':button')) {
                    trigger.addClass('ui-state-focus');
                }

                e.preventDefault();
            }
        });

        //dialog support
        this.setupDialogSupport();
    },

    /**
     * Sets up all panel event listeners
     * @protected
     */
    bindPanelEvents: function() {
        var $this = this;

        //hide overlay on document click
        this.itemMouseDown = false;

        this.hideOverlayHandler = PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', this.jq,
            function() { return $this.trigger; },
            function(e, eventTarget) {
                var menuItemLink = '.ui-menuitem-link:not(.ui-submenu-link, .ui-state-disabled)';

                if (eventTarget.is(menuItemLink) || eventTarget.closest(menuItemLink).length) {
                    $this.itemMouseDown = true;
                }
                else if(!($this.jq.is(eventTarget) || $this.jq.has(eventTarget).length > 0)) {
                    $this.hide(e);
                }
            });

        $(document.body).on('mouseup.' + this.id, function (e) {
            if ($this.itemMouseDown) {
                $this.hide(e);
                $this.itemMouseDown = false;
            }
        });

        //Hide overlay on resize
        this.resizeHandler = PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_hide', this.jq, function() {
            $this.handleViewportChange();
        });

        this.scrollHandler = PrimeFaces.utils.registerConnectedOverlayScrollHandler(this, 'scroll.' + this.id + '_hide', this.trigger, function() {
            $this.handleViewportChange();
        });
    },

    /**
     * Unbind all panel event listeners
     * @protected
     */
    unbindPanelEvents: function() {
        if (this.hideOverlayHandler) {
            this.hideOverlayHandler.unbind();
        }

        if (this.resizeHandler) {
            this.resizeHandler.unbind();
        }
    
        if (this.scrollHandler) {
            this.scrollHandler.unbind();
        }

        $(document.body).off('mouseup.' + this.id);
    },

    /**
     * Fired when the browser viewport is resized or scrolled.  In Mobile environment we don't want to hider the overlay
     * we want to re-align it.  This is because on some mobile browser the popup may force the browser to trigger a 
     * resize immediately and close the overlay. See GitHub #7075.
     * @private
     */
    handleViewportChange: function() {
        if (PrimeFaces.env.mobile || PrimeFaces.hideOverlaysOnViewportChange === false) {
            this.align();
        } else {
            this.hide();
        }
    },

    /**
     * Performs some setup required to make this overlay menu work with dialogs.
     * @protected
     */
    setupDialogSupport: function() {
        var dialog = this.trigger.parents('.ui-dialog:first');

        if(dialog.length == 1 && dialog.css('position') === 'fixed') {
            this.jq.css('position', 'fixed');
        }
    },

    /**
     * Shows (displays) this menu so that it becomes visible and can be interacted with.
     */
    show: function() {
        var $this = this;

        if (this.transition) {
            this.transition.show({
                onEnter: function() {
                    $this.jq.css('z-index', PrimeFaces.nextZindex());
                    $this.align();
                },
                onEntered: function() {
                    $this.bindPanelEvents();
                }
            });
        }
    },

    /**
     * Hides this menu so that it becomes invisible and cannot be interacted with any longer.
     */
    hide: function() {
        if (this.transition) {
            var $this = this;

            this.transition.hide({
                onExit: function() {
                    $this.unbindPanelEvents();
                },
                onExited: function() {
                    if ($this.trigger && $this.trigger.is(':button')) {
                        $this.trigger.removeClass('ui-state-focus');
                    }
                }
            });
        }
    },

    /**
     * Aligns this menu as specified in its widget configuration (property `pos`).
     */
    align: function() {
        this.jq.css({left:'0', top:'0', 'transform-origin': 'center top'}).position(this.cfg.pos);
    }
});









© 2015 - 2024 Weber Informatics LLC | Privacy Policy