123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886/// FOURJS_START_COPYRIGHT(D,2015)
/// Property of Four Js*
/// (c) Copyright Four Js 2015, 2022. All Rights Reserved.
/// * Trademark of Four Js Development Tools Europe Ltd
/// in the United States and elsewhere
///
/// This file can be modified by licensees according to the
/// product manual.
/// FOURJS_END_COPYRIGHT
"use strict";
modulum('DropDownWidget', ['WidgetGroupBase', 'WidgetFactory'],
function(context, cls) {
/**
* DropDown widget.
* @class DropDownWidget
* @memberOf classes
* @extends classes.WidgetGroupBase
* @publicdoc
*/
cls.DropDownWidget = context.oo.Class(cls.WidgetGroupBase, function($super) {
return /** @lends classes.DropDownWidget.prototype */ {
__name: "DropDownWidget",
$static: {
widgetEvents: {
dropDownBeforeOpen: "dropDownBeforeOpen",
dropDownOpen: "dropDownOpen",
dropDownClose: "dropDownClose"
},
/**
* Static list of current dropdowns instances being displayed
* @type {Array}
*/
displayedDropDowns: [],
/**
* Returns true if at least one dropdown is being displayed
* @returns {boolean}
* @publicdoc
*/
hasAnyVisible: function() {
return cls.DropDownWidget.displayedDropDowns.length > 0;
},
/**
* Return current active dropdown
* @returns {classes.DropDownWidget}
* @publicdoc
*/
getActiveDropDown: function() {
return cls.DropDownWidget.displayedDropDowns[cls.DropDownWidget.displayedDropDowns.length - 1];
},
/**
* Hide and remove all active dropdowns from DOM
*/
hideAll: function() {
while (cls.DropDownWidget.displayedDropDowns.length) {
cls.DropDownWidget.displayedDropDowns.pop().remove();
}
},
/**
* Returns true if targeted element is contained in one of the current displayed dropdowns
* @param {HTMLElement} targetElement
* @returns {boolean}
* @publicdoc
*/
isChildOfDropDown: function(targetElement) {
var inDropDown = false;
for (var i = 0; i < cls.DropDownWidget.displayedDropDowns.length; i++) {
var dropdown = cls.DropDownWidget.displayedDropDowns[i];
if (dropdown.getElement().contains(targetElement) || !dropdown.shouldClose(targetElement)) {
inDropDown = true;
break;
}
}
return inDropDown;
},
/**
* Returns true if targeted element is contained in one of the current displayed dropdowns or is dropdown associated widget element
* @param {HTMLElement} targetElement
* @returns {boolean}
* @publicdoc
*/
isChildOrParentOfDropDown: function(targetElement) {
var inDropDown = false;
for (var i = 0; i < cls.DropDownWidget.displayedDropDowns.length; i++) {
var dropdown = cls.DropDownWidget.displayedDropDowns[i];
if (dropdown.getElement().contains(targetElement) || dropdown.getParentWidget().getElement().contains(
targetElement) || !dropdown.shouldClose(targetElement)) {
inDropDown = true;
break;
}
}
return inDropDown;
}
},
/**
* Flag to indicate if dropdown should size accordingly to its parent element (parent element width aligned) with a maximum allowed height (afterwards dropdown is vertically scrollable)
* By default no.
* @type {boolean}
* @publicdoc
*/
autoSize: false,
/**
* Default min width of the dropdown
* @type {number}
*/
_defaultMinWidth: 0,
/**
* Default max height of the dropdown
* @type {number}
*/
_defaultMaxHeight: 0,
/**
* Flag to position dropdown using right-to-left basis (ex: arabic).
* Take note that to have a full right-to-left mode the parent widget need to be set as reversed using setReverse method
* By default its left-to-right positioned.
* @type {boolean}
* @publicdoc
*/
reverseX: false,
/**
* Flag to position dropdown below its corresponding widget or above it.
* By default its positioned below it.
* @type {boolean}
* @publicdoc
*/
reverseY: false,
/**
* Private flag set to true if we detected a need to reverse horizontal dropdown position (because of an overflow or if public reverseX attribute is set to true)
* @type {boolean}
* @private
*/
__reverseX: false,
/**
* Private flag set to true if we detected a need to reverse vertical dropdown position (because of an overflow or if public reverseY attribute is set to true)
* @type {boolean}
* @private
*/
__reverseY: false,
/**
* Horizontal absolute position.
* It replace default widget relative positioning if not null.
* @type {?number}
* @publicdoc
*/
x: null,
/**
* Vertical absolute position of the dropdown.
* It replace default widget relative positioning if not null.
* @type {?number}
* @publicdoc
*/
y: null,
/**
* Set no height
*/
_fullHeight: false,
/**
* Minimum width of the dropdown to use if not null.
* @type {?number}
* @publicdoc
*/
minWidth: null,
/**
* Maximum width of the dropdown to use if not null.
* @type {?number}
* @publicdoc
*/
maxWidth: null,
/**
* Maximum height of the dropdown to use if not null.
* @type {?number}
* @publicdoc
*/
maxHeight: null,
/**
* Parent element to use to measure and position DropDown instead of default one (default one is parent widget element).
* @type {HTMLElement}
* @publicdoc
*/
parentElement: null,
/**
* Custom rendering function to use instead of integrated one to measure and render the dropdown.
* The function is null by default.
* @type {Function}
* @publicdoc
*/
renderFunction: null,
/**
* Dropdown open/close bound handlers
* @function
*/
_handlers: null,
/**
* Dropdown container
* @type {HTMLElement}
*/
_container: null,
/**
* Flags current class as being a dropdown. Can be useful to know if a parent widget is dropdown
* @type {boolean}
* @publicdoc
*/
isDropDown: true,
/**
* True if the dropdown can overlay the widget
* @type {boolean}
*/
_widgetOverlay: true,
_stylingContext: "widget",
/**
* @inheritDoc
*/
constructor: function(opts) {
$super.constructor.call(this, opts);
this._defaultMinWidth = parseFloat(window.gbc.ThemeService.getValue("gbc-DropDownWidget-min-width"));
this._defaultMaxHeight = parseFloat(window.gbc.ThemeService.getValue("gbc-DropDownWidget-max-height"));
},
/**
* @inheritDoc
*/
_initContainerElement: function() {
$super._initContainerElement.call(this);
this._handlers = [];
this._widgetOverlay = true;
this._container = window.document.getElementsByClassName("gbc_DropDownContainerWidget")[0];
// on window close we emit dropdown close event
this._handlers.push(this.when(context.constants.widgetEvents.close, this.closeRequest.bind(this)));
// update aria selection on dropdown open/close
this.onOpen(function() {
this.setAriaSelection();
}.bind(this));
this.onClose(function() {
this._parentWidget.setAriaSelection();
}.bind(this));
},
/**
* @inheritDoc
*/
destroy: function() {
this.hide();
if (this._handlers) {
for (var i = 0; i < this._handlers.length; i++) {
this._handlers[i]();
}
this._handlers.length = 0;
}
this.unbindListeners();
if (this._parentWidget && this._parentWidget.removeChildWidget) {
this._parentWidget.removeChildWidget(this);
}
this._parentWidget = null;
$super.destroy.call(this);
},
/**
* @inheritDoc
*/
managePriorityKeyDown: function(keyString, domKeyEvent, repeat) {
var keyProcessed = false;
if (this.isVisible()) {
// if dropdown is open, all keys are prevented (no accelerator can be executed during dropdown display)
switch (keyString) {
case "enter":
case "return":
case "esc":
this.hide();
keyProcessed = true;
break;
}
}
return keyProcessed;
},
/**
* Bind an handler executed when dropdown is displayed
* @param {Hook} hook the hook to fire
* @returns {HandleRegistration} return handler to unbind reference
*/
onOpen: function(hook) {
this._handlers.push(this.when(cls.DropDownWidget.widgetEvents.dropDownOpen, hook));
return this._handlers[this._handlers.length - 1];
},
/**
* Bind an handler executed before dropdown is displayed
* @param {Hook} hook the hook to fire
* @returns {HandleRegistration} return handler to unbind reference
*/
onBeforeOpen: function(hook) {
this._handlers.push(this.when(cls.DropDownWidget.widgetEvents.dropDownBeforeOpen, hook));
return this._handlers[this._handlers.length - 1];
},
/**
* Bind an handler executed when dropdown is closed
* @param {Hook} hook the hook to fire
* @returns {HandleRegistration} return handler to unbind reference
*/
onClose: function(hook) {
this._handlers.push(this.when(cls.DropDownWidget.widgetEvents.dropDownClose, hook));
return this._handlers[this._handlers.length - 1];
},
beforeOpenRequest: function() {
this.emit(cls.DropDownWidget.widgetEvents.dropDownBeforeOpen);
},
openRequest: function() {
this.emit(cls.DropDownWidget.widgetEvents.dropDownOpen);
},
closeRequest: function() {
this.emit(cls.DropDownWidget.widgetEvents.dropDownClose);
},
/**
* Let dropdown hide on click (if outside of dropdown and widget), scroll events or dragAndDrop events.
* Setting this method to null will cancel auto hide of the dropdown
* @publicdoc
*/
bindListeners: function() {
//this._hideHandler = this.hide.bind(this);
if (this._hideHandler) {
this._hideHandler(); // removeEventListener
this._hideHandler = null;
}
if (!window.isMobile()) {
this._hideHandler = context.HostService.onScreenResize(this.hide.bind(this));
} else {
this._hideHandler = context.HostService.onOrientationChange(this.hide.bind(this));
}
},
/**
* Unbind events and listeneners used to auto hide dropdown
* @publicdoc
*/
unbindListeners: function() {
if (this._hideHandler && !cls.DropDownWidget.hasAnyVisible()) {
this._hideHandler(); // removeEventListener
this._hideHandler = null;
}
},
/**
* Hide all displayed dropdowns
* @publicdoc
*/
hide: function() {
cls.DropDownWidget.hideAll();
},
/**
* Remove current dropdown fro DOM. Use hide method to close dropdown instead as much as possible
*/
remove: function() {
// first thing to do is update active dropdowns list which is checked and used in following methods
cls.DropDownWidget.displayedDropDowns.remove(this);
this._setVisible(false);
// unbind handlers & remove overlay when no more dropdowns are visible
if (!cls.DropDownWidget.hasAnyVisible()) {
context.OverlayService.disable("dropdown");
this.unbindListeners();
}
this.removeDropDown();
this.closeRequest();
// update current dropdown being focused
var activeDropDown = cls.DropDownWidget.getActiveDropDown();
var session = context.SessionService.getCurrent();
if (session) {
var app = session.getCurrentApplication();
if (app) {
app.focus.setFocusedDropDownWidget(activeDropDown ? activeDropDown.getParentWidget() : null);
}
}
this.getParentWidget().emit(context.constants.widgetEvents.close);
},
/**
* Show the widget dropdown
* @param {boolean} multiple - if true we do not hide previous displayed dropdowns (ex: sub menus).
* @publicdoc
*/
show: function(multiple) {
if (!this.isVisible()) {
if (!multiple) {
this.hide();
}
// set visible (remove parent class hidden) before dropdown measuring
this._setVisible(true);
// bind handlers & add overlay on first dropdown display
if (!cls.DropDownWidget.hasAnyVisible()) {
var currentWindow = context.HostService.getCurrentWindowWidget();
var windowContainer = currentWindow ? currentWindow.getWindowMiddleContainer() : null;
context.OverlayService.enable("dropdown", windowContainer);
this.bindListeners();
}
// dropdown rendering (measure & positioning method)
this.beforeOpenRequest();
this.addDropDown();
this.openRequest();
// update current focused dropdown
cls.DropDownWidget.displayedDropDowns.push(this);
var session = context.SessionService.getCurrent();
if (session) {
var app = session.getCurrentApplication();
if (app) {
app.focus.setFocusedDropDownWidget(this.getParentWidget());
}
}
}
},
/**
* Toggle dropdown display
* @param {boolean} show - force display if set to true, hide if set to false
* @returns {boolean} returns dropdown visibility
* @publicdoc
*/
toggle: function(show) {
if (this.isVisible() || show === false) {
this.hide();
} else if (!this.isVisible() || show === true) {
this.show();
}
},
/**
* Check & update main dropdown container visibility on dropdown display/hide
* @param {boolean} visible - wanted visibility
* @private
*/
_setVisible: function(visible) {
if (this._container) {
if (visible) {
if (this._container.hasClass("hidden")) {
this._container.removeClass("hidden");
}
} else {
// only hide dropdowns container is we removed last dropdown child from it
if (!cls.DropDownWidget.hasAnyVisible() && !this._container.hasClass("hidden")) {
this._container.addClass("hidden");
}
}
}
this._isVisible = visible;
// flag widget as expanded for aria attribute
this.getParentWidget().setAriaAttribute("expanded", visible.toString());
// emit a visibility change notification on the dropdown
this.emit(context.constants.widgetEvents.visibilityChange, visible);
gbc.LogService.ui.log("DropDown open", visible, this.__name, this);
},
/**
* Indicate if dropdown if currently visible or not
* @returns {boolean} true if dropdown if currently visible
* @publicdoc
*/
isVisible: function() {
return this._isVisible;
},
/**
* Explicitly focus dropdown element
* @publicdoc
*/
focus: function() {
this._element.focus();
},
/**
* Set content of the dropdown
* @param {HTMLElement} content - element to add in the dropdown
* @param {WidgetBase} parentWidget - if defined, set the widget as parent widget of the dropdown (optional)
* @publicdoc
*/
setContent: function(content, parentWidget) {
this.getElement().appendChild(content);
if (parentWidget) {
this.setParentWidget(parentWidget);
}
},
/**
* Override this method if you want a custom check during dropdown hiding.
* This method is executed when you click on corresponding dropdown widget (usually parent widget of dropdown) and determines if it should close dropdown in that case.
* By default it's set to yes.
* @param {HTMLElement} targetElement - source element which raise blur event
* @returns {boolean} if false we cancel dropdown closing
*/
shouldClose: function(targetElement) {
return true;
},
/**
* Remove the content of the dropdown
* @param {HTMLElement} content - element to remove from the dropdown
* @publicdoc
*/
removeContent: function(content) {
this.getElement().removeChild(content);
},
/**
* Enable or disable/hide the dropdown.
* @param {boolean} enabled - true if dropdown is active
* @publicdoc
*/
setEnabled: function(enabled) {
$super.setEnabled.call(this, enabled);
this.hide();
},
/**
* Force the dropdown to take full height
*/
setFullHeight: function() {
this._fullHeight = true;
},
/**
* Remove the dropdown from the DOM
* @publicdoc
*/
removeDropDown: function() {
this.getElement().remove();
this._container.removeClass("dd_" + this.getParentWidget().getName());
},
/**
* Add dropdown in DOM and set its position & size
* @publicdoc
*/
addDropDown: function() {
this._container.addClass("dd_" + this.getParentWidget().getName());
// we insert dropdown in DOM before measuring it to be able to get its generated height
this._container.appendChild(this.getElement());
// measure and render dropdown
// if custom rendering function is defined we use it else use default one
if (this.renderFunction && typeof this.renderFunction === "function") {
this.renderFunction();
} else {
this.renderDropDown();
}
},
/**
* Render the dropdown. Measure its width, height and calculate its top and left position and sets them.
* By default dropdown will :
* - take width of its corresponding widget.
* - be positioned right under the corresponding widget and left aligned with it.
* @publicdoc
*/
renderDropDown: function() {
// 0 - Initialization
// need parent widget size + sidebar size in our measure process
var parentSize = (this.parentElement ? this.parentElement : this.getParentWidget().getElement()).getBoundingClientRect();
var sidebarWidth = context.HostLeftSidebarService.getSidebarWidth();
var ddSize = {};
// Flags used to reverse dropdown position if case of overflow measurements.
// These flags can be updated in next methods.
this.__reverseX = false;
this.__reverseY = false;
// 1 - Min & max width calculation
var ddWidth = this._widthMeasure(parentSize, sidebarWidth);
// typeof is string if value is "unset"
ddSize["min-width"] = ddSize["max-width"] = this._getSizeUnit(ddWidth);
// 2 - Horizontal positioning
var x = this._horizontalMeasure(ddWidth, parentSize, sidebarWidth);
ddSize[this.__reverseX ? "right" : "left"] = this._getSizeUnit(x);
ddSize[this.__reverseX ? "left" : "right"] = "unset"; // reset other flag which could have been used before
// 3 - Vertical positioning
var y = this._verticalMeasure(parentSize);
ddSize[this.__reverseY ? "bottom" : "top"] = this._getSizeUnit(y);
ddSize[this.__reverseY ? "top" : "bottom"] = this._fullHeight ? 0 :
"unset"; // reset other flag which could have been used before
// 4 - Max height calculation
var ddHeight = this._maxHeightMeasure(y);
ddSize["max-height"] = this._fullHeight ? "inherit" : this._getSizeUnit(ddHeight);
this.setStyle(ddSize);
this.__reverseX = false;
this.__reverseY = false;
},
/**
* Return value with its corresponding unit
* @param value
* @returns {string} value
* @private
*/
_getSizeUnit: function(value) {
return typeof value === "string" ? value : (value + "px");
},
/**
* We calculate horizontal position (by default using left attribute) of the dropdown depending of its width and client width
* @param {number} dropDownWidth - dropDown calculated width
* @param {ClientRect} parentSize - corresponding widget size
* @param {number} sidebarWidth - sidebar width
* @returns {number} left (or right if reversed) position in pixels
* @private
*/
_horizontalMeasure: function(dropDownWidth, parentSize, sidebarWidth) {
var x = 0;
if (this.getParentWidget().isReversed()) {
this.addClass("reverse");
}
this.__reverseX = this.reverseX || this.getParentWidget().isReversed();
// if dropDownWidth is "unset" we take scrollWidth
var ddWidth = dropDownWidth === "unset" ? this._element.scrollWidth : dropDownWidth;
// 1. Get X positioning depending of provided attributes
if (this.x) { // widget knows left location to use
// CENTER means middle of the current window
x = (this.x === "CENTER" ? (window.innerWidth - ddWidth + sidebarWidth) / 2 : this.x);
} else if (!this.__reverseX) {
x = Math.max(0, (parentSize.left + document.body.scrollLeft), sidebarWidth);
}
// 2. Adjust X positioning if horizontally overflowed
// 2.1 dropdown is going to be overflowed by screen size limit, we flag it as reverse to inverse positioning
if (!this.__reverseX && (x + ddWidth > document.body.clientWidth)) {
this.__reverseX = true;
}
// 2.2 substract provided X or parentElement right position to clientWidth
if (this.__reverseX) {
if (this.x) {
// if dropdown width can be fully visible on screen we position dropdown to the left of the filled x coordinate.
// Otherwise if dropdown width is higher and is going to be overflowed, we position it at the maximum right position of the screen.
//x = x > ddWidth ? Math.max(0, document.body.clientWidth - x) : 0;
x = x > ddWidth ? Math.max(0, document.body.clientWidth - x + parentSize.width) : 0;
// screen too small for a reversed display as well: dropdown aligned with screen left limit
if ((x + ddWidth) > document.body.clientWidth) {
this.__reverseX = false;
x = 0;
}
} else {
x = Math.max(0, (document.body.clientWidth - document.body.scrollLeft - parentSize.right));
}
}
return x;
},
/**
* Calculate the top position of the dropdown depending of the display mode
* @param {ClientRect} parentSize - corresponding widget size
* @returns {number} top (or bottom if reversed) position in pixels
* @private
*/
_verticalMeasure: function(parentSize) {
if (this._widgetOverlay) {
return this._verticalMeasureCombo(parentSize);
}
return this._verticalMeasureCompleter(parentSize);
},
/**
* Completer : the widget must stay visible so the dropdown must below or above the input widget
* @param {ClientRect} parentSize - corresponding widget size
* @returns {number} top (or bottom if reversed) position in pixels
* @private
*/
_verticalMeasureCompleter: function(parentSize) {
var bottomAbsPos = Math.max(0, parentSize.bottom + window.scrollY);
var formHeight = Math.min(screen.availHeight, document.getElementsByClassName("mt-centralcontainer")[0].getBoundingClientRect()
.height);
//Below space > Above space
if (formHeight - bottomAbsPos > parentSize.top) {
//Put the dropdown below the input widget
return parentSize.bottom + window.scrollY;
} else {
//Put the dropdown above the input widget
//var dropDownheight = this.maxHeight ? this.maxHeight : this._defaultMaxHeight;
this.__reverseY = true;
return formHeight - parentSize.top - window.scrollY + 1;
}
},
/**
* We calculate vertical position (by default using top attribute) of the dropdown depending of its height and client height
* @param {ClientRect} parentSize - corresponding widget size
* @returns {number} top (or bottom if reversed) position in pixels
* @private
*/
_verticalMeasureCombo: function(parentSize) {
var y = 0;
this.__reverseY = this.reverseY;
// 1. Get Y positioning depending of provided attributes
if (this.y) { // widget knowns top location to use
// CENTER means middle of the current window
y = (this.y === "CENTER" ? (window.innerHeight - this._element.scrollHeight) / 2 : this.y);
} else {
// Try to position dropdown right under widget if enough space available otherwise position dropdown below it
y = Math.max(0, (parentSize.bottom + document.body.scrollTop));
}
// 2. Adjust dropdown Y positioning by comparing dropdown height with remaining viewport height
var height = this.maxHeight ? this.maxHeight : Math.min(this._element.scrollHeight, this._defaultMaxHeight);
// screen size limit : we need to adjust vertical position
if (y + height > document.body.clientHeight) { // unsufficiant size below widget, check to position dropdown above it
// try to position dropdown using above widget
// 2px for box-shadow
y = Math.max(0, document.body.clientHeight - (this.y ? this.y : parentSize.top) - document.body.scrollTop + 2);
// if no space available either to position dropdown above widget, we place it at the top of the screen
if (y + height > document.body.clientHeight) { // place dropdown at top of the screen
y = 2;
} else { // place above widget (usage of bottom attribute)
this.__reverseY = true;
}
}
return y;
},
/**
* We calculate width of the dropdown. We take in consideration sidebar and menu panel which can overflow widget content (scrollbars may then appear)
* @param {ClientRect} parentSize - corresponding widget size
* @param {number} sidebarWidth - sidebar width
* @returns {string|number} width to use as a number or "unset" string if no width is required
* @private
*/
_widthMeasure: function(parentSize, sidebarWidth) {
var w = 0;
if (this.minWidth) {
w = this.minWidth;
} else if (this.autoSize) {
// Take larger width between parent widget, dropdown content and default min width.
// Check if visible area is smaller that this width (dropdown or parent widget overflowed by horizontal scrollbars) and take visible area as width if it's the case
w = Math.min(Math.max(parentSize.width, (this.width ? this.width : 0), this._defaultMinWidth), this
._visibleAreaWidthMeasure(
parentSize, sidebarWidth));
} else if (this.maxWidth) {
w = this.maxWidth;
} else {
// if element is larger than available screen width we limit dropdown width to screen width
if (this._element.scrollWidth > document.body.clientWidth - 2) {
w = document.body.clientWidth - 2;
} else {
w = "unset";
}
}
return w;
},
/**
* Calculate width of the middle visible area (we subtract menu panel and sidebar from clientWidth)
* @param {ClientRect} parentSize - corresponding widget size
* @param {number} sidebarWidth - sidebar width
* @returns {number} returns width of the middle visible area (we subtract menu panel and sidebar from clientWidth)
* @private
*/
_visibleAreaWidthMeasure: function(parentSize, sidebarWidth) {
var w = document.body.clientWidth;
if (this.autoSize) {
// horizontal scrollbars & overflow issue with right action panel in case of too large dropdown widget
// we need to subtract the overflowed width of the dropdown
// possible overflow are sidebar & right menu panel
var currentWindow = this.getWindowWidget();
if (currentWindow) {
var menuRightWidth = currentWindow.getWindowMenuContainerRight().getBoundingClientRect();
if ((menuRightWidth.width > 0 || sidebarWidth > 0) && parentSize.width > 0) {
w = menuRightWidth.left - Math.max(0, parentSize.left, sidebarWidth);
}
}
}
return w;
},
/**
* The dropdown can overlay the widget
* @param {boolean} canOverlay true : the dropdown can overlay the widget
*/
setCanOverlay: function(canOverlay) {
this._widgetOverlay = canOverlay;
},
/**
* Calculate the max height depending of the display mode
* @param {number} ddVerticalPosition - vertical (y) position of the dropdown. Basically top position.
* @returns {number} max height to use in pixels
* @private
*/
_maxHeightMeasure: function(ddVerticalPosition) {
if (this._widgetOverlay) {
return this._maxHeightMeasureCombo(ddVerticalPosition);
}
return this._maxHeightMeasureCompleter(ddVerticalPosition);
},
/**
* Completer : The dropdown must not overlay the input widget
* @param {number} ddVerticalPosition - vertical (y) position of the dropdown. Basically top position.
* @returns {number} max height to use in pixels
* @private
*/
_maxHeightMeasureCompleter: function(ddVerticalPosition) {
var parentElement = this.parentElement ? this.parentElement : this.getParentWidget().getElement();
var parentSize = parentElement.getBoundingClientRect();
var belowWidget = Math.max(0, (parentSize.bottom + window.scrollY));
var formHeight = document.getElementsByClassName("mt-centralcontainer")[0].getBoundingClientRect().height;
var h = 0;
if (this.maxHeight) {
h = this.maxHeight;
} else if (this.autoSize) {
h = this._defaultMaxHeight;
} else { // by default fit content height
h = this._element.scrollHeight + 3; // 3 for border
}
//Below space > Above space
if (formHeight - belowWidget > belowWidget - parentSize.height) {
return Math.min(document.body.clientHeight - ddVerticalPosition, h) - 3; // 3 To not touch the browser border
} else {
return Math.min(parentSize.top - 3, h);
}
},
/**
* We calculate max height of the dropdown. Then vertical scrollbars will be added in dropdown.
* @param {number} ddVerticalPosition - vertical (y) position of the dropdown. Basically top position.
* @returns {number} max height to use in pixels
* @private
*/
_maxHeightMeasureCombo: function(ddVerticalPosition) {
var h = 0;
if (this.maxHeight) {
h = this.maxHeight;
} else if (this.autoSize) {
h = this._defaultMaxHeight;
} else { // by default fit content height
h = this._element.scrollHeight + 3; // 3 for border
}
// case when screen height is smaller that dropdown height
if (Math.min(h, this._defaultMaxHeight) > document.body.clientHeight) { // dropdown take whole screen height
h = document.body.clientHeight - 5;
} else if ((h + ddVerticalPosition) > document.body.clientHeight) {
// case when dropdown has big height and is being overflowed by screen
h = document.body.clientHeight - ddVerticalPosition - 5;
}
return h;
}
};
});
cls.WidgetFactory.registerBuilder('DropDown', cls.DropDownWidget);
});