dynamic select field

This commit is contained in:
Roland Gruber 2019-07-24 21:29:26 +02:00
parent f8cfcf9f34
commit 29a7b0c3e0
2 changed files with 158 additions and 0 deletions

View File

@ -1219,6 +1219,8 @@ class htmlSelect extends htmlElement {
protected $tableRowsToHide = array(); protected $tableRowsToHide = array();
/** list of enclosing table rows to show when checked */ /** list of enclosing table rows to show when checked */
protected $tableRowsToShow = array(); protected $tableRowsToShow = array();
/** dynamic scrolling */
private $dynamicScrolling = false;
/** /**
* Constructor. * Constructor.
@ -1349,6 +1351,10 @@ class htmlSelect extends htmlElement {
* @param array $elements list of options * @param array $elements list of options
*/ */
private function printOptionsHTML($elements) { private function printOptionsHTML($elements) {
if ($this->dynamicScrolling) {
echo "<option value=\"#\">#</option>\n";
return;
}
// sorting // sorting
if ($this->sortElements) { if ($this->sortElements) {
if ($this->hasDescriptiveElements) { if ($this->hasDescriptiveElements) {
@ -1568,6 +1574,24 @@ class htmlSelect extends htmlElement {
return 'tr'; return 'tr';
} }
/**
* Enable dynamic scrolling. This limits the number of select options to 10000 by dynamically adding/removing options.
* This will not be enabled when optgroups are used or the option size is less than 10000.
*/
public function enableDynamicScrolling() {
// not possible for optgroups and smaller option lists
if ((sizeof($this->elements) < 10000) || $this->containsOptgroups) {
return;
}
$this->dynamicScrolling = true;
$elementData = array();
foreach ($this->elements as $key => $value) {
$elementData[] = array('label' => $key, 'value' => $value);
}
$this->addDataAttribute('dynamic-options', json_encode($elementData));
$this->cssClasses[] = 'lam-dynamicOptions';
}
} }
/** /**

View File

@ -1111,6 +1111,139 @@ window.lam.html.preventEnter = function() {
}); });
} }
window.lam.dynamicSelect = window.lam.dynamicSelect || {};
/**
* Activates dynamic selection for all marked select fields.
*/
window.lam.dynamicSelect.activate = function() {
var dynamicSelects = jQuery('.lam-dynamicOptions');
dynamicSelects.each(function() {
var selectField = jQuery(this);
selectField.data('option-height', selectField.find("option").height());
selectField.data('select-height', selectField.height());
selectField.data('select-last-scroll-top', 0);
selectField.data('select-current-scroll', 0);
selectField.html('');
var options = selectField.data('dynamic-options');
var maxOptions = 3000;
var numOfOptionBeforeToLoadNextSet = 10;
var numberOfOptionsToLoad = 200;
for (var i = 0; i < maxOptions; i++) {
selectField.append(window.lam.dynamicSelect.createOption(options[i], i));
}
if (options.length > maxOptions) {
// activate scrolling logic only if enough options are set
selectField.scroll(function(event) {
window.lam.dynamicSelect.onScroll(selectField, event, maxOptions, numOfOptionBeforeToLoadNextSet, numberOfOptionsToLoad);
});
}
});
}
/**
* Creates an option field inside the select.
*
* @param data option data
* @param index index in list of all options
* @returns option
*/
window.lam.dynamicSelect.createOption = function(data, index) {
var newOption = jQuery(document.createElement("option"));
newOption.attr('value', data.value);
newOption.data('index', index);
newOption.text(data.label);
return newOption;
}
/**
* Onscroll event.
*
* @param selectField select field
* @param event event
* @param maxOptions maximum options to show
* @param numOfOptionBeforeToLoadNextSet number of options to reach before end of list
* @param numberOfOptionsToLoad number of options to add
*/
window.lam.dynamicSelect.onScroll = function(selectField, event, maxOptions, numOfOptionBeforeToLoadNextSet, numberOfOptionsToLoad) {
var scrollTop = selectField.scrollTop();
var totalHeight = selectField.find("option").length * selectField.data('option-height');
var lastScrollTop = selectField.data('select-last-scroll-top');
var selectBoxHeight = selectField.data('select-height');
var singleOptionHeight = selectField.data('option-height');
var currentScroll = scrollTop + selectBoxHeight;
selectField.data('select-current-scroll-top', scrollTop);
if ((scrollTop >= lastScrollTop)
&& ((currentScroll + (numOfOptionBeforeToLoadNextSet * singleOptionHeight)) >= totalHeight)) {
window.lam.dynamicSelect.loadNextOptions(selectField, maxOptions, numberOfOptionsToLoad);
}
else if ((scrollTop <= lastScrollTop)
&& ((scrollTop - (numOfOptionBeforeToLoadNextSet * singleOptionHeight)) <= 0)) {
window.lam.dynamicSelect.loadPreviousOptions(selectField, maxOptions, numberOfOptionsToLoad);
}
selectField.data('select-last-scroll-top', scrollTop);
}
/**
* Loads the next bunch of options at the end.
*
* @param selectField selct field
* @param maxOptions maximum options to show
* @param numberOfOptionsToLoad number of options to add
*/
window.lam.dynamicSelect.loadNextOptions = function(selectField, maxOptions, numberOfOptionsToLoad) {
var selectBoxHeight = selectField.data('select-height');
var singleOptionHeight = selectField.data('option-height');
var currentScrollPosition = selectField.data('select-current-scroll-top') + selectBoxHeight;
var options = selectField.data('dynamic-options');
var lastIndex = selectField.children().last().data('index');
for (var toAdd = 0; toAdd < numberOfOptionsToLoad; toAdd++) {
var addPos = lastIndex + 1 + toAdd;
if (options[addPos] === undefined) {
break;
}
selectField.append(window.lam.dynamicSelect.createOption(options[addPos], addPos));
}
var numberOfOptions = selectField.children().length;
var toRemove = numberOfOptions - maxOptions;
if (toRemove > 0) {
selectField.children().slice(0, toRemove).remove();
}
else {
toRemove = 0;
}
selectField.scrollTop(currentScrollPosition - selectBoxHeight - (toRemove * singleOptionHeight));
}
/**
* Loads the next bunch of options at the beginning.
*
* @param selectField selct field
* @param maxOptions maximum options to show
* @param numberOfOptionsToLoad number of options to add
*/
window.lam.dynamicSelect.loadPreviousOptions = function(selectField, maxOptions, numberOfOptionsToLoad) {
var singleOptionHeight = selectField.data('option-height');
var currentScrollPosition = selectField.data('select-current-scroll-top');
var options = selectField.data('dynamic-options');
var lastIndex = selectField.children().first().data('index');
var added = 0;
for (var toAdd = 0; toAdd < numberOfOptionsToLoad; toAdd++) {
var addPos = lastIndex - 1 - toAdd;
if (options[addPos] === undefined) {
break;
}
added++;
selectField.prepend(window.lam.dynamicSelect.createOption(options[addPos], addPos));
}
var numberOfOptions = selectField.children().length;
var toRemove = numberOfOptions - maxOptions;
if (toRemove > 0) {
selectField.children().slice(maxOptions).remove();
}
selectField.scrollTop(currentScrollPosition + (added * singleOptionHeight));
}
window.lam.selfservice = window.lam.selfservice || {}; window.lam.selfservice = window.lam.selfservice || {};
/** /**
@ -1173,6 +1306,7 @@ jQuery(document).ready(function() {
window.lam.tools.schema.select(); window.lam.tools.schema.select();
window.lam.html.activateLightboxes(); window.lam.html.activateLightboxes();
window.lam.html.preventEnter(); window.lam.html.preventEnter();
window.lam.dynamicSelect.activate();
}); });
/** /**