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

META-INF.resources.primefaces.forms.forms.selectcheckboxmenu.js Maven / Gradle / Ivy

/**
 * __PrimeFaces SelectCheckboxMenu Widget__
 * 
 * SelectCheckboxMenu is a multi select component that displays options in an overlay.
 * 
 * @typedef {"startsWith" |  "contains" |  "endsWith" | "custom"} PrimeFaces.widget.SelectCheckboxMenu.FilterMatchMode
 * Available modes for filtering the options of a select list box. When `custom` is set, a `filterFunction` must be
 * specified.
 * 
 * @typedef PrimeFaces.widget.SelectCheckboxMenu.FilterFunction A function for filtering the options of a select list
 * box.
 * @param {string} PrimeFaces.widget.SelectCheckboxMenu.FilterFunction.itemLabel The label of the currently selected
 * text.
 * @param {string} PrimeFaces.widget.SelectCheckboxMenu.FilterFunction.filterValue The value to search for.
 * @return {boolean} PrimeFaces.widget.SelectCheckboxMenu.FilterFunction `true` if the item label matches the filter
 * value, or `false` otherwise.
 * 
 * @typedef PrimeFaces.widget.SelectCheckboxMenu.OnChangeCallback Callback that is invoked when a checkbox option was
 * checked or unchecked. See also {@link SelectCheckboxMenuCfg.onChange}.
 * @this {PrimeFaces.widget.SelectCheckboxMenu} PrimeFaces.widget.SelectCheckboxMenu.OnChangeCallback 
 * 
 * @typedef PrimeFaces.widget.SelectCheckboxMenu.OnHideCallback Callback that is invoked when the overlay panel is
 * brought up. See also {@link SelectCheckboxMenuCfg.onHide}.
 * @this {PrimeFaces.widget.SelectCheckboxMenu} PrimeFaces.widget.SelectCheckboxMenu.OnHideCallback 
 * 
 * @typedef PrimeFaces.widget.SelectCheckboxMenu.OnShowCallback Callback that is invoked when the overlay panel is
 * hidden. See also {@link SelectCheckboxMenuCfg.onShow}.
 * @this {PrimeFaces.widget.SelectCheckboxMenu} PrimeFaces.widget.SelectCheckboxMenu.OnShowCallback 
 * 
 * @prop {JQuery} checkboxes The DOM element for the checkboxes that can be selected.
 * @prop {JQuery} [closer] The DOM element for the button that closes the overlay panel with the select
 * options (when the panel is shown).
 * @prop {JQuery} defaultLabel The DOM element for the default label.
 * @prop {boolean} disabled Whether this widget is currently disabled.
 * @prop {JQuery} [filterInput] DOM element of the input element used for entering a filter text, if filtering is
 * enabled.
 * @prop {JQuery} [filterInputWrapper] DOM element of the wrapper that holds the input element used for entering a
 * filter text, if filtering is enabled.
 * @prop {JQuery} groupHeaders The DOM elements for the headers of each option group.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.FilterFunction} filterMatcher The filter that was selected and is
 * currently used.
 * @prop {Record} filterMatchers
 * Map between the available filter types and the filter implementation.
 * @prop {JQuery} [header] DOM element of the header of the overlay panel with the available select items,
 * when the overlay panel is shown.
 * @prop {PrimeFaces.UnbindCallback} [hideOverlayHandler] Unbind callback for the hide overlay handler.
 * @prop {JQuery} inputs The DOM elements for the hidden inputs for each checkbox option.
 * @prop {boolean} isDynamicLoaded When loading the panel with the available options lazily: if they have been loaded
 * already.
 * @prop {JQuery} items The DOM elements for the the available checkbox options.
 * @prop {JQuery} itemContainer The DOM element for the container with the available checkbox options.
 * @prop {JQuery} itemContainerWrapper The DOM element for the wrapper with the container with the available checkbox
 * options.
 * @prop {JQuery} keyboardTarget The DOM element for the hidden input element that that can be selected via pressing
 * tab. 
 * @prop {JQuery} label The DOM element for the label indicating the currently selected option.
 * @prop {JQuery} labelContainer The DOM element for the container with the label indicating the currently selected
 * option.
 * @prop {string} labelId ID of the label element that indicates the currently selected option.
 * @prop {JQuery} menuIcon The DOM element for the icon for bringing up the overlay panel.
 * @prop {JQuery} multiItemContainer The DOM element for the container with the tags representing the selected options.
 * @prop {JQuery} panel The DOM element for the overlay panel with the available checkbox options.
 * @prop {JQuery} panelId ID of the DOM element for the overlay panel with the available checkbox options.
 * @prop {PrimeFaces.UnbindCallback} [resizeHandler] Unbind callback for the resize handler.
 * @prop {PrimeFaces.UnbindCallback} [scrollHandler] Unbind callback for the scroll handler.
 * @prop {string} tabindex Tab index of this widget.
 * @prop {JQuery} [toggler] The wrapping DOM element of the toggler for selecting or unselecting all options
 * in the overlay panel with all selected items (when the overlay panel is shown).
 * @prop {JQuery} [togglerBox] The DOM element with the toggler checkbox for selecting or unselecting all
 * options in the overlay panel with all selected items (when the overlay panel is shown).
 * @prop {PrimeFaces.CssTransitionHandler | null} [transition] Handler for CSS transitions used by this widget.
 * @prop {JQuery} triggers The DOM elements for the buttons that can trigger (hide or show) the overlay panel with the
 * available checkbox options.
 * @prop {boolean} widthAligned Whether the width of the overlay panel was aligned already.
 * 
 * @interface {PrimeFaces.widget.SelectCheckboxMenuCfg} cfg The configuration for the {@link  SelectCheckboxMenu| SelectCheckboxMenu 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 The search expression for the element to which the overlay panel should be appended.
 * @prop {boolean} cfg.caseSensitive Defines if filtering would be case sensitive.
 * @prop {boolean} cfg.filterNormalize Defines if filtering would be done using normalized values.
 * @prop {boolean} cfg.dynamic Defines if dynamic loading is enabled for the element's panel. If the value is `true`,
 * the overlay is not rendered on page load to improve performance.
 * @prop {string} cfg.emptyLabel Label to be shown in updateLabel mode when no item is selected. If not set the label is
 * shown.
 * @prop {boolean} cfg.filter `true` if the options can be filtered, or `false` otherwise.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.FilterFunction} cfg.filterFunction A custom filter function that is used
 * when `filterMatchMode` is set to `custom`.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.FilterMatchMode} cfg.filterMatchMode Mode of the filter. When set to
 * `custom`, a `filterFunction` must be specified.
 * @prop {string} cfg.filterPlaceholder Placeholder text to show when filter input is empty.
 * @prop {number} cfg.initialHeight Initial height of the item container.
 * @prop {string} cfg.labelSeparator Separator for joining item labels if updateLabel is set to true. Default is `,`.
 * @prop {boolean} cfg.multiple Whether to show selected items as multiple labels.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.OnChangeCallback} cfg.onChange Callback that is invoked when a checkbox
 * option was checked or unchecked.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.OnHideCallback} cfg.onHide Callback that is invoked when the overlay
 * panel is brought up.
 * @prop {PrimeFaces.widget.SelectCheckboxMenu.OnShowCallback} cfg.onShow Callback that is invoked when the overlay
 * panel is hidden.
 * @prop {string} cfg.panelStyle Inline style of the overlay panel.
 * @prop {string} cfg.panelStyleClass Style class of the overlay panel
 * @prop {number} cfg.scrollHeight Maximum height of the overlay panel.
 * @prop {number} cfg.initialHeight Initial height of the overlay panel in pixels.
 * @prop {boolean} cfg.showHeader When enabled, the header of overlay panel is displayed.
 * @prop {boolean} cfg.updateLabel When enabled, the selected items are displayed on the label.
 * @prop {boolean} cfg.renderPanelContentOnClient Renders panel content on client.
 */
PrimeFaces.widget.SelectCheckboxMenu = PrimeFaces.widget.BaseWidget.extend({

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

        this.labelContainer = this.jq.find('.ui-selectcheckboxmenu-label-container');
        this.label = this.jq.find('.ui-selectcheckboxmenu-label');
        this.menuIcon = this.jq.children('.ui-selectcheckboxmenu-trigger');
        this.triggers = this.jq;
        this.disabled = this.jq.hasClass('ui-state-disabled');
        //get the hidden checkboxes except that ones in overlay panel
        this.inputs = this.jq.children('div.ui-helper-hidden:not(.ui-input-overlay)').children(':checkbox');
        this.panelId = this.jqId + '_panel';
        this.labelId = this.id + '_label';
        this.panel = $(this.panelId);
        this.widthAligned = false;
        this.itemContainerWrapper = this.panel.children('.ui-selectcheckboxmenu-items-wrapper');
        this.keyboardTarget = $(this.jqId + '_focus');
        this.tabindex = this.keyboardTarget.attr('tabindex');
        this.cfg.showHeader = (this.cfg.showHeader === undefined) ? true : this.cfg.showHeader;
        this.cfg.dynamic = this.cfg.dynamic === true ? true : false;
        this.isDynamicLoaded = false;
        this.cfg.labelSeparator = (this.cfg.labelSeparator === undefined) ? ', ' : this.cfg.labelSeparator;

        if (!this.disabled) {
            if (!this.cfg.dynamic) {
                this._renderPanel();
            }

            this.bindEvents();
            this.bindKeyEvents();

            if (!this.cfg.multiple) {
                this.label.attr('id', this.labelId);
                this.keyboardTarget.attr('aria-labelledby', this.labelId);
            }
            this.keyboardTarget.attr('aria-expanded', false);
            this.jq.attr('aria-expanded', false);
        }

        this.renderLabel();

        //pfs metadata
        this.inputs.data(PrimeFaces.CLIENT_ID_DATA, this.id);
    },

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

    /**
     * Creates the overlay panel with the checkboxes for the selectable option, and also sets up the event listeners
     * for the panel.
     * @private
     */
    _renderPanel: function() {
        this.renderPanel();

        if (this.tabindex) {
            this.panel.find('a, input').attr('tabindex', this.tabindex);
        }

        this.checkboxes = this.itemContainer.find('.ui-chkbox-box:not(.ui-state-disabled)');

        this.bindPanelContentEvents();
        this.bindPanelKeyEvents();

        this.isDynamicLoaded = true;
    },

    /**
     * Creates the overlay panel with the checkboxes for the selectable option.
     * @private
     */
    renderPanel: function() {
        var rawPanelId = this.id + '_panel';

        //init panel content rendered by SelectCheckboxMenuRenderer
        this.header = this.panel.children('.ui-selectcheckboxmenu-header');
        this.toggler = this.header.children('.ui-chkbox');
        this.togglerBox = this.toggler.children('.ui-chkbox-box');
        this.filterInputWrapper = this.header.children('.ui-selectcheckboxmenu-filter-container');
        this.filterInput = this.filterInputWrapper.children('.ui-inputtext');
        this.closer = this.header.children('.ui-selectcheckboxmenu-close');

        if (this.cfg.renderPanelContentOnClient && this.itemContainerWrapper.children().length === 0) {
            this.renderItems();
        }
        else {
            //init items rendered by SelectCheckboxMenuRenderer
            this.itemContainer = this.itemContainerWrapper.children('.ui-selectcheckboxmenu-items');
            this.items = this.itemContainer.find('.ui-selectcheckboxmenu-item');
            this.groupHeaders = this.itemContainer.find('.ui-selectcheckboxmenu-item-group');
        }

        if (this.cfg.scrollHeight) {
            this.itemContainerWrapper.css('max-height', this.cfg.scrollHeight);
        }
        else if (this.inputs.length > 10) {
            this.itemContainerWrapper.css('max-height', '200px');
        }

        this.cfg.appendTo = PrimeFaces.utils.resolveAppendTo(this, this.jq, this.panel);

        PrimeFaces.utils.registerDynamicOverlay(this, this.panel, rawPanelId);
        this.transition = PrimeFaces.utils.registerCSSTransition(this.panel, 'ui-connected-overlay');

        this.keyboardTarget.attr('aria-controls', rawPanelId);
    },

    /**
     * Create the label to display values
     * @private
     */
    renderLabel: function() {
        if (!this.cfg.updateLabel) {
            this.registerTrigger();
            return;
        }
        if (this.cfg.multiple) {
            this.multiItemContainer = this.multiItemContainer || this.jq.children('.ui-selectcheckboxmenu-multiple-container');
            var items = this.multiItemContainer.children();
            if (!items.length) {
                this.multiItemContainer.empty().append('
  • ' + (this.multiItemContainer.data('label') || ' ') + '
  • '); } } else { // single select this.defaultLabel = this.label.text(); this.label.css({ 'text-overflow': 'ellipsis', overflow: 'hidden' }); this.updateLabel(); } this.registerTrigger(); }, /** * Mark trigger and descandants of trigger as a trigger for a primefaces overlay. * @private */ registerTrigger: function() { if (!this.disabled) { this.triggers.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true); } }, /** * Creates the individual checkboxes for each selectable option in the overlay panel. * @private */ renderItems: function() { var $this = this; this.itemContainer = $('
      ') .appendTo(this.itemContainerWrapper); //check if inputs must be grouped var grouped = this.inputs.filter('[data-group-label]'); var currentGroupName = null; for (var i = 0; i < this.inputs.length; i++) { var input = this.inputs.eq(i), label = input.next(), disabled = input.is(':disabled'), checked = input.is(':checked'), title = input.attr('title'), boxClass = 'ui-chkbox-box ui-widget ui-corner-all ui-state-default', itemClass = 'ui-selectcheckboxmenu-item ui-selectcheckboxmenu-list-item ui-corner-all', escaped = input.data('escaped'); if (grouped.length && currentGroupName !== input.attr('data-group-label')) { currentGroupName = input.attr('data-group-label'); var itemGroup = $('
    • '); itemGroup.text(currentGroupName); $this.itemContainer.append(itemGroup); } if (disabled) { boxClass += " ui-state-disabled"; } if (checked) { boxClass += " ui-state-active"; } var iconClass = checked ? 'ui-chkbox-icon ui-icon ui-icon-check' : 'ui-chkbox-icon ui-icon ui-icon-blank', itemClass = checked ? itemClass + ' ui-selectcheckboxmenu-checked' : itemClass + ' ui-selectcheckboxmenu-unchecked'; var item = $('
    • '); item.append('
      ' + '
      '); var uuid = PrimeFaces.uuid(); var itemLabel = $(''), labelHtml = label.html().trim(), labelLength = labelHtml.length; if (labelLength > 0 && labelHtml !== ' ') if (escaped) itemLabel.text(label.text()); else itemLabel.html(label.html()); else itemLabel.text(input.val()); itemLabel.appendTo(item); if (title) { item.attr('title', title); } if ($this.cfg.multiple) { item.attr('data-item-value', input.val()); } item.find('> .ui-chkbox > .ui-helper-hidden-accessible > input').prop('checked', checked).attr('id', uuid); $this.itemContainer.attr('role', 'group'); $this.itemContainer.append(item); } this.items = this.itemContainer.children('li.ui-selectcheckboxmenu-item'); this.groupHeaders = this.itemContainer.children('li.ui-selectcheckboxmenu-item-group'); }, /** * Sets up all event listeners required by this widget. * @private */ bindEvents: function() { var $this = this; //Events to show/hide the panel this.triggers.on('mouseenter.selectCheckboxMenu', function() { if (!$this.disabled) { $this.jq.addClass('ui-state-hover'); $this.triggers.addClass('ui-state-hover'); } }).on('mouseleave.selectCheckboxMenu', function() { if (!$this.disabled) { $this.jq.removeClass('ui-state-hover'); $this.triggers.removeClass('ui-state-hover'); } }).on('mousedown.selectCheckboxMenu', function(e) { if (!$this.disabled) { if ($this.cfg.multiple && $(e.target).is('.ui-selectcheckboxmenu-token-icon')) { return; } if (!$this.isLoaded()) { $this._renderPanel(); } $this.togglePanel(); } }).on('click.selectCheckboxMenu', function(e) { $this.keyboardTarget.trigger('focus'); e.preventDefault(); }); if (this.cfg.multiple) { this.bindMultipleModeEvents(); } //Client Behaviors if (this.cfg.behaviors) { PrimeFaces.attachBehaviors(this.inputs, this.cfg.behaviors); } }, /** * Sets up the event listeners for the overlay panel with the selectable checkbox options. * @private */ bindPanelContentEvents: function() { var $this = this; //Events for checkboxes this.bindCheckboxHover(this.checkboxes); //Toggler if (this.cfg.showHeader) { this.bindCheckboxHover(this.togglerBox); this.togglerBox.on('click.selectCheckboxMenu', function() { var el = $(this); $this.toggleSelection(!el.hasClass('ui-state-active')); }); //filter if (this.cfg.filter) { this.setupFilterMatcher(); PrimeFaces.skinInput(this.filterInput); this.filterInput.on('keyup.selectCheckboxMenu', function() { $this.filter($(this).val()); }).on('keydown.selectCheckboxMenu', function(e) { if (e.key === 'Escape') { $this.hide(); } }); } //Closer this.closer.on('mouseenter.selectCheckboxMenu', function() { $(this).addClass('ui-state-hover'); }).on('mouseleave.selectCheckboxMenu', function() { $(this).removeClass('ui-state-hover'); }).on('click.selectCheckboxMenu', function(e) { $this.hide(); e.preventDefault(); }); } //Checkboxes and labels this.items.on('click.selectCheckboxMenu', function(e) { var checkbox = $(this).find('.ui-chkbox').children('.ui-chkbox-box'); $this.toggleItem(checkbox); checkbox.removeClass('ui-state-hover'); PrimeFaces.clearSelection(); e.preventDefault(); }); }, /** * Sets up all panel event listeners * @private */ bindPanelEvents: function() { var $this = this; this.hideOverlayHandler = PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', this.panel, function() { return $this.triggers; }, function(e, eventTarget) { if (!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) { $this.hide(); } }); this.resizeHandler = PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_hide', this.panel, function() { $this.handleViewportChange(); }); this.scrollHandler = PrimeFaces.utils.registerConnectedOverlayScrollHandler(this, 'scroll.' + this.id + '_hide', this.jq, function() { $this.handleViewportChange(); }); }, /** * 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.alignPanel(); } else { this.hide(); } }, /** * Unbind all panel event listeners * @private */ unbindPanelEvents: function() { if (this.hideOverlayHandler) { this.hideOverlayHandler.unbind(); } if (this.resizeHandler) { this.resizeHandler.unbind(); } if (this.scrollHandler) { this.scrollHandler.unbind(); } }, /** * Sets up the event listeners for all keyboard related events other than the overlay panel, such as pressing space * to bring up the overlay panel. * @private */ bindKeyEvents: function() { var $this = this; this.keyboardTarget.on('focus.selectCheckboxMenu', function() { $this.jq.addClass('ui-state-focus'); $this.menuIcon.addClass('ui-state-focus'); }).on('blur.selectCheckboxMenu', function() { $this.jq.removeClass('ui-state-focus'); $this.menuIcon.removeClass('ui-state-focus'); }).on('keydown.selectCheckboxMenu', function(e) { var key = e.key; if (!$this.isLoaded()) { $this._renderPanel(); } switch (key) { case 'Enter': case ' ': $this.togglePanel(); if ($this.panel.is(":hidden")) { e.stopPropagation(); // GitHub #8340 } e.preventDefault(); break; case 'ArrowDown': if (e.altKey) { $this.togglePanel(); } e.preventDefault(); break; case 'Tab': if ($this.panel.is(':visible')) { if (!$this.cfg.showHeader) { //".ui-chkbox" is a grandchild when columns are used! $this.items.filter(':not(.ui-state-disabled):first').find('div.ui-chkbox > div.ui-helper-hidden-accessible > input').trigger('focus'); } else { $this.toggler.find('> div.ui-helper-hidden-accessible > input').trigger('focus'); } e.preventDefault(); } break; case 'Escape': $this.hide(); break; }; }); }, /** * Sets up the event listeners for all keyboard related events of the overlay panel, such as pressing space to * toggle a checkbox. * @private */ bindPanelKeyEvents: function() { var $this = this; if (this.cfg.showHeader) { this.closer.on('focus.selectCheckboxMenu', function(e) { $this.closer.addClass('ui-state-focus'); }) .on('blur.selectCheckboxMenu', function(e) { $this.closer.removeClass('ui-state-focus'); }) .on('keydown.selectCheckboxMenu', function(e) { switch (e.key) { case 'Enter': case ' ': $this.hide(); e.preventDefault(); break; case 'Escape': $this.hide(); break; }; }); var togglerCheckboxInput = this.toggler.find('> div.ui-helper-hidden-accessible > input'); this.bindCheckboxKeyEvents(togglerCheckboxInput); togglerCheckboxInput.on('keydown.selectCheckboxMenu', function(e) { if (e.key === 'Tab' && e.shiftKey) { e.preventDefault(); } }).on('keyup.selectCheckboxMenu', function(e) { if (e.key === ' ') { var input = $(this); $this.toggleSelection(!input.prop('checked')); e.preventDefault(); } }).on('change.selectCheckboxMenu', function(e) { var input = $(this); $this.toggleSelection(!input.prop('checked')); }); } //".ui-chkbox" is a grandchild when columns are used! var itemKeyInputs = this.items.find('div.ui-chkbox > div.ui-helper-hidden-accessible > input'); this.bindCheckboxKeyEvents(itemKeyInputs); itemKeyInputs.on('keydown.selectCheckboxMenu', function(e) { var index = $this.items.index($(this).closest("li")); if (e.key === 'Tab') { if (!e.shiftKey && index === $this.items.length - 1) { e.preventDefault(); } else if (e.shiftKey && !$this.cfg.showHeader && index === 0) { e.preventDefault(); } } }).on('keyup.selectCheckboxMenu', function(e) { if (e.key === ' ') { var input = $(this), box = input.parent().next(); $this.toggleItem(box, input); e.preventDefault(); } }).on('change.selectCheckboxMenu', function(e) { var input = $(this), box = input.parent().next(); $this.toggleItem(box, input); }); }, /** * When multi select mode is enabled: Sets up the event listeners for the overlay panel. * @private */ bindMultipleModeEvents: function() { var $this = this; this.multiItemContainer = this.jq.children('.ui-selectcheckboxmenu-multiple-container'); var closeSelector = '> li.ui-selectcheckboxmenu-token > .ui-selectcheckboxmenu-token-icon'; this.multiItemContainer.off('click', closeSelector).on('click', closeSelector, null, function(e) { var itemValue = $(this).parent().data("item-value"); var item = $this.items.filter('[data-item-value="' + $.escapeSelector(itemValue) + '"]'); if (item && item.length) { if (!$this.isLoaded()) { $this._renderPanel(); } //".ui-chkbox" is a grandchild when columns are used! $this.uncheck(item.find('.ui-chkbox').children('.ui-chkbox-box'), true); if ($this.hasBehavior('itemUnselect')) { var ext = { params: [ { name: $this.id + '_itemUnselect', value: itemValue } ] }; $this.callBehavior('itemUnselect', ext); } } e.stopPropagation(); }); }, /** * Sets up the event listeners for hovering over the checkboxes. Adds the appropriate hover style classes. * @private * @param {JQuery} item A checkbox for which to add the event listeners. */ bindCheckboxHover: function(item) { item.on('mouseenter.selectCheckboxMenu', function() { var item = $(this); if (!item.hasClass('ui-state-disabled')) { item.addClass('ui-state-hover'); } }).on('mouseleave.selectCheckboxMenu', function() { $(this).removeClass('ui-state-hover'); }); }, /** * Filters the available options in the overlay panel by the given search value. Note that this does not bring up * the overlay panel, use `show` for that. * @param {string} value A value against which the available options are matched. */ filter: function(value) { this.cfg.initialHeight = this.cfg.initialHeight||this.itemContainerWrapper.height(); var lowercase = !this.cfg.caseSensitive, normalize = this.cfg.filterNormalize, filterValue = PrimeFaces.toSearchable(PrimeFaces.trim(value), lowercase, normalize); if (filterValue === '') { this.items.filter(':hidden').show(); this.groupHeaders.show(); } else { for (var i = 0; i < this.inputs.length; i++) { var input = this.inputs.eq(i), label = input.next(), itemLabel = PrimeFaces.toSearchable(label.text(), lowercase, normalize), item = this.items.eq(i); if(item.hasClass('ui-noselection-option')) { item.hide(); } else { if(this.filterMatcher(itemLabel, filterValue)) { item.show(); } else { item.hide(); } } } var groupHeaderLength = this.groupHeaders.length; for (var i = 0; i < groupHeaderLength; i++) { var header = $(this.groupHeaders[i]), groupedItems = header.nextUntil('.ui-selectcheckboxmenu-item-group'); if (groupedItems.length === groupedItems.filter(':hidden').length) { header.hide(); } else { header.show(); } } } var firstVisibleItem = this.items.filter(':visible:not(.ui-state-disabled):first'); if (firstVisibleItem.length) { PrimeFaces.scrollInView(this.itemContainerWrapper, firstVisibleItem); } if(this.itemContainer.height() < this.cfg.initialHeight) { //shrink to fit filtered content size this.itemContainerWrapper.css('height', 'auto'); } else { //resize to initial (max.) height this.itemContainerWrapper.height(this.cfg.initialHeight); } this.updateToggler(); this.alignPanel(); }, /** * Finds and stores the filter function which is to be used for filtering the options of this select checkbox menu. * @private */ setupFilterMatcher: function() { this.cfg.filterMatchMode = this.cfg.filterMatchMode || 'startsWith'; this.filterMatchers = { 'startsWith': this.startsWithFilter , 'contains': this.containsFilter , 'endsWith': this.endsWithFilter , 'custom': this.cfg.filterFunction }; this.filterMatcher = this.filterMatchers[this.cfg.filterMatchMode]; }, /** * Implementation of a `PrimeFaces.widget.SelectCheckboxMenu.FilterFunction` that matches the given option when it * starts with the given search text. * @param {string} value Text of an option. * @param {string} filter Value of the filter. * @return {boolean} `true` when the text of the options starts with the filter value, or `false` otherwise. */ startsWithFilter: function(value, filter) { return value.indexOf(filter) === 0; }, /** * Implementation of a `PrimeFaces.widget.SelectCheckboxMenu.FilterFunction` that matches the given option when it * contains the given search text. * @param {string} value Text of an option. * @param {string} filter Value of the filter. * @return {boolean} `true` when the text of the contains the filter value, or `false` otherwise. */ containsFilter: function(value, filter) { return value.indexOf(filter) !== -1; }, /** * Implementation of a `PrimeFaces.widget.SelectCheckboxMenu.FilterFunction` that matches the given option when it * ends with the given search text. * @param {string} value Text of an option. * @param {string} filter Value of the filter. * @return {boolean} `true` when the text of the options ends with the filter value, or `false` otherwise. */ endsWithFilter: function(value, filter) { return value.indexOf(filter, value.length - filter.length) !== -1; }, /** * Toggles either selecting all items or unselecting all items. * @param {boolean} selectAll true to select all items and false to uncheck all items */ toggleSelection: function(selectAll) { if (selectAll) { this.checkAll(); } else { this.uncheckAll(); } }, /** * Selects all available options. * @param {boolean} [silent] `true` to suppress triggering event listeners, or `false` otherwise. */ checkAll: function(silent) { if (!this.isLoaded()) { this._renderPanel(); } var panelVisible = this.panel.is(':visible'); for (var i = 0; i < this.items.length; i++) { var el = this.items.eq(i); if (!panelVisible || (panelVisible && el.is(':visible'))) { var input = this.inputs.eq(i); var inputNative = input[0]; if (!inputNative.disabled) { input.prop('checked', true); //".ui-chkbox" is a grandchild when columns are used! this.check(el.find('.ui-chkbox').children('.ui-chkbox-box')); if (this.cfg.multiple) { this.createMultipleItem(el); } } } } this.check(this.togglerBox); if (!silent) { var togglerInput = this.togglerBox.prev().children('input'); if (this.cfg.onChange) { this.cfg.onChange.call(this); } if (!this.togglerBox.hasClass('ui-state-disabled')) { togglerInput.trigger('focus.selectCheckboxMenu'); this.togglerBox.addClass('ui-state-active'); } if (this.cfg.multiple) { this.alignPanel(); } this.fireToggleSelectEvent(true); } }, /** * Unselects all available options. * @param {boolean} [silent] `true` to suppress triggering event listeners, or `false` otherwise. */ uncheckAll: function(silent) { if (!this.isLoaded()) { this._renderPanel(); } var panelVisible = this.panel.is(':visible'); for (var i = 0; i < this.items.length; i++) { var el = this.items.eq(i); if (!panelVisible || (panelVisible && el.is(':visible'))) { var input = this.inputs.eq(i); var inputNative = input[0]; if (!inputNative.disabled) { this.inputs.eq(i).prop('checked', false); //".ui-chkbox" is a grandchild when columns are used! this.uncheck(el.find('.ui-chkbox').children('.ui-chkbox-box')); if (this.cfg.multiple) { this.removeMultipleItem(el); } } } } this.uncheck(this.togglerBox); if (!silent) { var togglerInput = this.togglerBox.prev().children('input'); if (this.cfg.onChange) { this.cfg.onChange.call(this); } if (!this.togglerBox.hasClass('ui-state-disabled')) { togglerInput.trigger('focus.selectCheckboxMenu'); } if (this.cfg.multiple) { this.alignPanel(); } this.fireToggleSelectEvent(false); } this.renderLabel(); }, /** * Triggers the select behavior, if any, when a checkbox option was selected or unselected. * @private * @param {boolean} checked Whether the checkbox option is now checked. */ fireToggleSelectEvent: function(checked) { if (this.hasBehavior('toggleSelect')) { var ext = { params: [{ name: this.id + '_checked', value: checked }] }; this.callBehavior('toggleSelect', ext); } }, /** * Selects the given checkbox option. * @private * @param {JQuery} checkbox Checkbox option to select. * @param {boolean} updateInput If `true`, update the hidden input field with the current value of this widget. */ check: function(checkbox, updateInput) { if (!checkbox.hasClass('ui-state-disabled')) { var checkedInput = checkbox.prev().children('input'), item = checkbox.closest('.ui-selectcheckboxmenu-item'); checkedInput.prop('checked', true); if (updateInput) { checkedInput.trigger('focus.selectCheckboxMenu'); } checkbox.addClass('ui-state-active').children('.ui-chkbox-icon').removeClass('ui-icon-blank').addClass('ui-icon-check'); item.removeClass('ui-selectcheckboxmenu-unchecked').addClass('ui-selectcheckboxmenu-checked'); if (updateInput) { var itemGroups = item.prevAll('.ui-selectcheckboxmenu-item-group'), input = this.inputs.eq(item.index() - itemGroups.length); input.prop('checked', true).trigger('change'); this.updateToggler(); if (this.cfg.multiple) { this.createMultipleItem(item); this.alignPanel(); } } this.updateLabel(); } }, /** * Unselects the given checkbox option. * @private * @param {JQuery} checkbox Checkbox option to unselect. * @param {boolean} updateInput If `true`, update the hidden input field with the current value of this widget. */ uncheck: function(checkbox, updateInput) { if (!checkbox.hasClass('ui-state-disabled')) { var uncheckedInput = checkbox.prev().children('input'), item = checkbox.closest('.ui-selectcheckboxmenu-item'); checkbox.removeClass('ui-state-active').children('.ui-chkbox-icon').addClass('ui-icon-blank').removeClass('ui-icon-check'); checkbox.closest('.ui-selectcheckboxmenu-item').addClass('ui-selectcheckboxmenu-unchecked').removeClass('ui-selectcheckboxmenu-checked'); uncheckedInput.prop('checked', false); if (updateInput) { var itemGroups = item.prevAll('.ui-selectcheckboxmenu-item-group'), input = this.inputs.eq(item.index() - itemGroups.length); input.prop('checked', false).trigger('change'); uncheckedInput.trigger('focus.selectCheckboxMenu'); this.updateToggler(); if (this.cfg.multiple) { checkbox.removeClass('ui-state-focus'); this.removeMultipleItem(item); this.alignPanel(); } } this.updateLabel(); } }, /** * Bring up the overlay panel if its not showing or hide it if it is showing. */ togglePanel: function() { if (this.panel.is(":hidden")) { this.show(); } else { this.hide(); } }, /** * Brings up the overlay panel with the available checkbox options. */ show: function() { var $this = this; if (this.panel.is(":hidden") && this.transition) { this.transition.show({ onEnter: function() { $this.panel.css('z-index', PrimeFaces.nextZindex()); $this.alignPanel(); }, onEntered: function() { $this.keyboardTarget.attr('aria-expanded', true); $this.jq.attr('aria-expanded', true); $this.postShow(); $this.bindPanelEvents(); } }); } }, /** * Hides the overlay panel with the available checkbox options. */ hide: function() { if (this.panel.is(':visible') && this.transition) { var $this = this; this.transition.hide({ onExit: function() { $this.unbindPanelEvents(); }, onExited: function() { $this.keyboardTarget.attr('aria-expanded', false); $this.jq.attr('aria-expanded', false); $this.postHide(); } }); this.keyboardTarget.trigger('focus'); } }, /** * Callback that is invoked after the overlay panel with the checkbox options was made visible. * @private */ postShow: function() { if (this.cfg.onShow) { this.cfg.onShow.call(this); } }, /** * Callback that is invoked after the overlay panel with the checkbox options was hidden. * @private */ postHide: function() { if (this.cfg.onHide) { this.cfg.onHide.call(this); } }, /** * Align the overlay panel with the available checkbox options so that is is positioned next to the the button. */ alignPanel: function() { var fixedPosition = this.panel.css('position') == 'fixed', win = $(window), positionOffset = fixedPosition ? '-' + win.scrollLeft() + ' -' + win.scrollTop() : null, panelStyle = this.panel.attr('style'); this.panel.css({ 'left': '', 'top': '', 'z-index': PrimeFaces.nextZindex(), 'transform-origin': 'center top' }); if (this.panel.parent().attr('id') === this.id) { this.panel.css({ left: '0px', top: this.jq.innerHeight() + 'px' }); } else { this.panel.position({ my: 'left top' , at: 'left bottom' , of: this.jq , offset: positionOffset , collision: 'flipfit' , using: function(pos, directions) { $(this).css('transform-origin', 'center ' + directions.vertical).css(pos); } }); } if (!this.widthAligned && (this.panel.width() < this.jq.width()) && (!panelStyle || panelStyle.toLowerCase().indexOf('width') === -1)) { this.panel.width(this.jq.width()); this.widthAligned = true; } }, /** * Select or unselect the given checkbox option. * @param {JQuery} checkbox One of the checkbox options of this widget to toggle. * @param {JQuery} input (optional) input element representing the value of the checkbox */ toggleItem: function(checkbox, input) { if (!checkbox.hasClass('ui-state-disabled')) { var isChecked = !input || input.prop('checked'); if (checkbox.hasClass('ui-state-active') && isChecked) { this.uncheck(checkbox, true); } else { this.check(checkbox, true); } } }, /** * Updates the `select all` / `unselect all` toggler so that it reflects the currently selected options. * @private */ updateToggler: function() { if (this.cfg.showHeader) { var visibleItems = this.items.filter(':visible'); if (visibleItems.length && visibleItems.filter('.ui-selectcheckboxmenu-unchecked').length === 0) { this.check(this.togglerBox); } else { this.uncheck(this.togglerBox); } } }, /** * Sets up the keyboard event listeners for the given checkbox options. * @private * @param {JQuery} items Checkbo options for which to add the event listeners. */ bindCheckboxKeyEvents: function(items) { var $this = this; items.on('focus.selectCheckboxMenu', function(e) { var input = $(this), box = input.parent().next(); box.addClass('ui-state-focus'); PrimeFaces.scrollInView($this.itemContainerWrapper, box); }).on('blur.selectCheckboxMenu', function(e) { var input = $(this), box = input.parent().next(); box.removeClass('ui-state-focus'); }) .on('keydown.selectCheckboxMenu', function(e) { if (e.key === ' ') { e.preventDefault(); } else if (e.key === 'Escape') { $this.hide(); } }); }, /** * When multi mode is disabled: Upates the label that indicates the currently selected item. * @private */ updateLabel: function() { var checkedItems = this.jq.find(':checked'), labelText = ''; if (checkedItems && checkedItems.length) { for (var i = 0; i < checkedItems.length; i++) { if (i > 0) { labelText = labelText + this.cfg.labelSeparator; } labelText = labelText + ($(checkedItems[i]).next().text() || ''); } this.labelContainer.addClass('ui-state-active'); } else { labelText = this.cfg.emptyLabel || this.defaultLabel || ''; this.labelContainer.removeClass('ui-state-active'); } if (this.cfg.updateLabel && !(this.cfg.multiple && labelText.length > 0)) { this.label.text(labelText); this.labelContainer.attr('title', labelText); } this.registerTrigger(); }, /** * When multi mode is enabled: Creates a tag for the given item that was checked. * @private * @param {JQuery} item The checkbox item that was checked. */ createMultipleItem: function(item) { var items = this.multiItemContainer.children(); var itemCount = !items.length ? 0 : items.filter('[data-item-value="' + $.escapeSelector(item.data('item-value')) + '"]').length; if (itemCount > 0) { return; } var itemGroups = item.prevAll('.ui-selectcheckboxmenu-item-group'), input = this.inputs.eq(item.index() - itemGroups.length), escaped = input.data('escaped'), labelHtml = input.next().html().trim(), labelLength = labelHtml.length, label = labelLength > 0 && labelHtml !== ' ' ? (escaped ? PrimeFaces.escapeHTML(input.next().text()) : input.next().html()) : PrimeFaces.escapeHTML(input.val()), itemDisplayMarkup = '
    • '; itemDisplayMarkup += ''; itemDisplayMarkup += '' + label + '
    • '; if (items.filter('[class="ui-selectcheckboxmenu-emptylabel"]').length) { this.multiItemContainer.empty(); } this.multiItemContainer.append(itemDisplayMarkup); }, /** * When multi mode is enabled: Removes all visible tags with the same value as the given checkbox item. * @private * @param {JQuery} item Checkbox item that was unchecked. */ removeMultipleItem: function(item) { var items = this.multiItemContainer.children(); if (items.length) { items.filter('[data-item-value="' + $.escapeSelector(item.data('item-value')) + '"]').remove(); } // update the label if there are no more items to display empty this.renderLabel(); }, /** * Checks the checkbox option with the given value. * @param {string} value Value of the option to check. */ selectValue: function(value) { var idx = -1; // find input-index for (var i = 0; i < this.inputs.length; i++) { if (this.inputs.eq(i).val() === value) { idx = i; break; } } if (idx === -1) { return; } var input = this.inputs.eq(idx); // the hidden input var item = this.items.eq(idx); // the Overlay-Panel-Item (li) // check (see this.checkAll()) input.prop('checked', true); //".ui-chkbox" is a grandchild when columns are used! this.check(item.find('.ui-chkbox').children('.ui-chkbox-box')); if (this.cfg.multiple) { this.createMultipleItem(item); } }, /** * Enables this input so that the user can enter a value. */ enable: function() { PrimeFaces.utils.enableInputWidget(this.jq, this.inputs); this.disabled = false; }, /** * Disables this input so that the user cannot enter a value anymore. */ disable: function() { PrimeFaces.utils.disableInputWidget(this.jq, this.inputs); this.disabled = true; }, /** * Has the panel been loaded with checkbox data yet? * @return {boolean} `true` when the panel has been loaded with checkbox items */ isLoaded: function() { return this.cfg.dynamic === false || this.isDynamicLoaded; }, /** * Resets the input. * @param {boolean} [silent] `true` to suppress triggering event listeners, or `false` otherwise. */ resetValue: function(silent) { if (this.isLoaded()) { this.uncheckAll(silent); } } });




      © 2015 - 2024 Weber Informatics LLC | Privacy Policy