META-INF.resources.primefaces.forms.forms.cascadeselect.js Maven / Gradle / Ivy
/**
* __PrimeFaces CascadeSelect Widget__
*
* CascadeSelect CascadeSelect displays a nested structure of options.
*
* @prop {JQuery} contents The DOM element for the content in the available selectable options.
* @prop {PrimeFaces.UnbindCallback} [hideOverlayHandler] Unbind callback for the hide overlay handler.
* @prop {JQuery} input The DOM element for the hidden input with the current value.
* @prop {JQuery} items The DOM elements for the the available selectable options.
* @prop {JQuery} itemsWrapper The DOM element for the wrapper with the container with the available selectable
* options.
* @prop {JQuery} label The DOM element for the label indicating the currently selected option.
* @prop {JQuery} panel The DOM element for the overlay panel with the available selectable options.
* @prop {PrimeFaces.CssTransitionHandler | null} [transition] Handler for CSS transitions used by this widget.
* @prop {PrimeFaces.UnbindCallback} [resizeHandler] Unbind callback for the resize handler.
* @prop {PrimeFaces.UnbindCallback} [scrollHandler] Unbind callback for the scroll handler.
* @prop {JQuery} triggers The DOM elements for the buttons that can trigger (hide or show) the overlay panel with the
* available selectable options.
*
* @interface {PrimeFaces.widget.CascadeSelectCfg} cfg The configuration for the {@link CascadeSelect| CascadeSelect 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.DynamicOverlayWidgetCfg} cfg
*
* @prop {string} cfg.appendTo Appends the overlay to the element defined by search expression. Defaults to the document
* body.
* @prop {boolean} cfg.disabled If true, disables the component.
*/
PrimeFaces.widget.CascadeSelect = PrimeFaces.widget.DynamicOverlayWidget.extend({
/**
* @override
* @inheritdoc
* @param {PrimeFaces.PartialWidgetCfg} cfg
*/
init: function(cfg) {
this.panel = $(PrimeFaces.escapeClientId(cfg.id) + '_panel');
this._super(cfg, this.panel, cfg.id + '_panel');
this.input = $(this.jqId + '_input');
this.label = this.jq.children('.ui-cascadeselect-label');
this.triggers = this.jq.children('.ui-cascadeselect-trigger').add(this.label);
this.itemsWrapper = this.panel.children('.ui-cascadeselect-items-wrapper');
this.items = this.itemsWrapper.find('li.ui-cascadeselect-item');
this.contents = this.items.children('.ui-cascadeselect-item-content');
this.cfg.disabled = this.jq.hasClass('ui-state-disabled');
if (!this.cfg.disabled) {
this.bindEvents();
this.transition = PrimeFaces.utils.registerCSSTransition(this.panel, 'ui-connected-overlay');
}
},
/**
* Sets up all event listeners that are required by this widget.
* @private
*/
bindEvents: function() {
var $this = this;
this.triggers.off('click.cascadeselect').on('click.cascadeselect', function(e) {
if ($this.panel.is(':hidden')) {
$this.show();
}
else {
$this.hide();
}
$this.input.trigger('focus.cascadeselect');
e.preventDefault();
});
this.input.off('focus.cascadeselect blur.cascadeselect keydown.cascadeselect')
.on('focus.cascadeselect', function() {
$this.jq.addClass('ui-state-focus');
})
.on('blur.cascadeselect', function(){
$this.jq.removeClass('ui-state-focus');
})
.on('keydown.cascadeselect', function(e) {
switch(e.key) {
case 'ArrowDown':
if ($this.panel.is(':visible')) {
$this.panel.find('.ui-cascadeselect-item:first > .ui-cascadeselect-item-content').focus();
}
else if (e.altKey) {
$this.show();
}
e.preventDefault();
break;
case 'Escape':
if ($this.panel.is(':visible')) {
$this.hide();
e.preventDefault();
}
break;
case 'Tab':
$this.hide();
break;
default:
break;
}
});
this.contents.off('click.cascadeselect keydown.cascadeselect')
.on('click.cascadeselect', function(e) {
var item = $(this).parent();
var subpanel = item.children('.ui-cascadeselect-panel');
$this.deactivateItems(item);
item.addClass('ui-cascadeselect-item-active ui-state-highlight');
if (subpanel.length > 0) {
var parentPanel = item.closest('.ui-cascadeselect-panel');
$this.alignSubPanel(subpanel, parentPanel);
subpanel.show();
}
else {
$this.input.val(item.attr('data-value'));
$this.label.text(item.attr('data-label'));
$this.callBehavior('itemSelect');
$this.hide();
e.stopPropagation();
}
})
.on('keydown.cascadeselect', function(e) {
var item = $(this).parent();
switch(e.key) {
case 'ArrowDown':
var nextItem = item.next();
if (nextItem) {
nextItem.children('.ui-cascadeselect-item-content').focus();
}
break;
case 'ArrowUp':
var prevItem = item.prev();
if (prevItem) {
prevItem.children('.ui-cascadeselect-item-content').focus();
}
break;
case 'ArrowRight':
if (item.hasClass('ui-cascadeselect-item-group')) {
if (item.hasClass('ui-cascadeselect-item-active')) {
item.find('> .ui-cascadeselect-panel > .ui-cascadeselect-item:first > .ui-cascadeselect-item-content').focus();
}
else {
item.children('.ui-cascadeselect-item-content').trigger('click.cascadeselect');
}
}
break;
case 'ArrowLeft':
$this.hideGroup(item);
$this.hideGroup(item.siblings('.ui-cascadeselect-item-active'));
var parentItem = item.parent().closest('.ui-cascadeselect-item');
if (parentItem) {
parentItem.children('.ui-cascadeselect-item-content').focus();
}
break;
case 'Enter':
case 'NumpadEnter':
item.children('.ui-cascadeselect-item-content').trigger('click.cascadeselect');
if (!item.hasClass('ui-cascadeselect-item-group')) {
$this.input.trigger('focus.cascadeselect');
}
break;
default:
break;
}
e.preventDefault();
});
},
/**
* Removes some event listeners when this widget was disabled.
* @private
*/
unbindEvents: function() {
this.contents.off();
this.triggers.off();
this.input.off();
},
/**
* Disables this widget so that the user cannot select any option.
*/
disable: function() {
if (!this.cfg.disabled) {
this.cfg.disabled = true;
this.jq.addClass('ui-state-disabled');
this.input.attr('disabled', 'disabled');
this.unbindEvents();
}
},
/**
* Enables this widget so that the user can select an option.
*/
enable: function() {
if (this.cfg.disabled) {
this.cfg.disabled = false;
this.jq.removeClass('ui-state-disabled');
this.input.removeAttr('disabled');
this.bindEvents();
}
},
/**
* Deactivate siblings and active children of an item.
* @private
* @param {JQuery} item Cascade select panel element.
*/
deactivateItems: function(item) {
var parentItem = item.parent().parent();
var siblings = item.siblings('.ui-cascadeselect-item-active');
this.hideGroup(siblings);
this.hideGroup(siblings.find('.ui-cascadeselect-item-active'));
if (!parentItem.is(this.itemsWrapper)) {
this.deactivateItems(parentItem);
}
},
/**
* 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();
});
// #12008 - Only register scroll handler if there is a small number of items
if (this.items.length < 10) {
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();
}
},
/**
* Brings up the overlay panel with the available options.
*/
show: function() {
var $this = this;
if (this.transition) {
this.transition.show({
onEnter: function() {
$this.panel.css('z-index', PrimeFaces.nextZindex());
$this.alignPanel();
},
onEntered: function() {
$this.input.attr('aria-expanded', true);
$this.bindPanelEvents();
}
});
}
},
/**
* Hides the panel of a group item.
* @param {JQuery} item Dom element of the cascadeselect.
*/
hideGroup: function(item) {
item.removeClass('ui-cascadeselect-item-active ui-state-highlight').children('.ui-cascadeselect-panel').hide();
},
/**
* Hides the overlay panel with the available options.
*/
hide: function() {
if (this.panel.is(':visible') && this.transition) {
var $this = this;
this.transition.hide({
onExit: function() {
$this.unbindPanelEvents();
},
onExited: function() {
$this.panel.css('z-index', '');
$this.input.attr('aria-expanded', false);
}
});
}
},
/**
* Adjust the width of the overlay panel.
* @private
*/
alignPanelWidth: function() {
//align panel and container
if (this.cfg.appendTo) {
this.panel.css('min-width', this.jq.outerWidth());
}
},
/**
* Align the overlay panel with the available options.
* @private
*/
alignPanel: function() {
this.alignPanelWidth();
if (this.panel.parent().is(this.jq)) {
this.panel.css({
left: '0px',
top: this.jq.innerHeight() + 'px',
'transform-origin': 'center top'
});
}
else {
this.panel.css({left:'0px', top:'0px', 'transform-origin': 'center top'}).position({
my: 'left top'
,at: 'left bottom'
,of: this.jq
,collision: 'flipfit'
,using: function(pos, directions) {
$(this).css('transform-origin', 'center ' + directions.vertical).css(pos);
}
});
}
},
/**
* Align the sub overlay panel with the available options.
* @private
* @param {JQuery} subpanel Sub panel element of the cascade select panel.
* @param {JQuery} parentPanel Parent panel element of the sub panel element.
*/
alignSubPanel: function(subpanel, parentPanel) {
var subitemWrapper = subpanel.children('.ui-cascadeselect-items-wrapper');
subpanel.css({'display':'block', 'opacity':'0', 'pointer-events': 'none'});
subitemWrapper.css({'overflow': 'scroll'});
subpanel.css({left:'0px', top:'0px'}).position({
my: 'left top'
,at: 'right top'
,of: parentPanel.children('.ui-cascadeselect-item-active:first')
,collision: 'flipfit'
});
subpanel.css({'display':'none', 'opacity':'', 'pointer-events': '', 'z-index': PrimeFaces.nextZindex()});
subitemWrapper.css({'overflow': ''});
}
});
© 2015 - 2024 Weber Informatics LLC | Privacy Policy