summaryrefslogtreecommitdiff
path: root/assets/js/components
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2016-12-11 15:46:54 -0500
committerFrederic Guillot <fred@kanboard.net>2016-12-11 15:46:54 -0500
commitffb392617895095b824a35150e620a68920f9260 (patch)
tree25bee1b1833e9f9bde608c38c3f46fb4e291c2b4 /assets/js/components
parenta2b44371e050aca123e0e16c3458c7af2f11b48f (diff)
Replace Chosen jQuery plugin by custom UI component
Diffstat (limited to 'assets/js/components')
-rw-r--r--assets/js/components/keyboard-shortcuts.js4
-rw-r--r--assets/js/components/select-dropdown-autocomplete.js240
2 files changed, 244 insertions, 0 deletions
diff --git a/assets/js/components/keyboard-shortcuts.js b/assets/js/components/keyboard-shortcuts.js
new file mode 100644
index 00000000..49524a76
--- /dev/null
+++ b/assets/js/components/keyboard-shortcuts.js
@@ -0,0 +1,4 @@
+// Open board selector: "b"
+KB.onKey(98, function () {
+ KB.trigger('board.selector.open');
+});
diff --git a/assets/js/components/select-dropdown-autocomplete.js b/assets/js/components/select-dropdown-autocomplete.js
new file mode 100644
index 00000000..565be21d
--- /dev/null
+++ b/assets/js/components/select-dropdown-autocomplete.js
@@ -0,0 +1,240 @@
+KB.component('select-dropdown-autocomplete', function(containerElement, options) {
+ var componentElement, inputElement, inputHiddenElement;
+
+ function onKeyDown(e) {
+ switch (e.keyCode) {
+ case 27:
+ inputElement.value = '';
+ destroyDropdownMenu();
+ break;
+ case 38:
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ moveUp();
+ break;
+ case 40:
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ moveDown();
+ break;
+ case 13:
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ insertSelectedItem();
+ break;
+ }
+ }
+
+ function onInputChanged() {
+ destroyDropdownMenu();
+ renderDropdownMenu();
+ }
+
+ function onItemMouseOver(element) {
+ if (KB.dom(element).hasClass('select-dropdown-menu-item')) {
+ KB.find('.select-dropdown-menu-item.active').removeClass('active');
+ KB.dom(element).addClass('active');
+ }
+ }
+
+ function onItemClick() {
+ insertSelectedItem();
+ }
+
+ function onDocumentClick(e) {
+ if (! containerElement.contains(e.target)) {
+ inputElement.value = '';
+ destroyDropdownMenu();
+ }
+ }
+
+ function toggleDropdownMenu() {
+ var menuElement = KB.find('#select-dropdown-menu');
+
+ if (menuElement === null) {
+ renderDropdownMenu();
+ } else {
+ destroyDropdownMenu();
+ }
+ }
+
+ function insertSelectedItem() {
+ var element = KB.find('.select-dropdown-menu-item.active');
+ var value = element.data('value');
+ inputHiddenElement.value = value;
+ inputElement.value = options.items[value];
+ destroyDropdownMenu();
+
+ if (options.redirect) {
+ var regex = new RegExp(options.redirect.regex, 'g');
+ window.location = options.redirect.url.replace(regex, value);
+ }
+ }
+
+ function resetSelection() {
+ var elements = document.querySelectorAll('.select-dropdown-menu-item');
+
+ for (var i = 0; i < elements.length; i++) {
+ if (KB.dom(elements[i]).hasClass('active')) {
+ KB.dom(elements[i]).removeClass('active');
+ break;
+ }
+ }
+
+ return {items: elements, index: i};
+ }
+
+ function moveUp() {
+ var result = resetSelection();
+
+ if (result.index > 0) {
+ result.index = result.index - 1;
+ }
+
+ KB.dom(result.items[result.index]).addClass('active');
+ }
+
+ function moveDown() {
+ var result = resetSelection();
+
+ if (result.index < result.items.length - 1) {
+ result.index++;
+ }
+
+ KB.dom(result.items[result.index]).addClass('active');
+ }
+
+ function buildItems(items) {
+ var elements = [];
+ var keys = Object.keys(items);
+
+ if (options.sortByKeys) {
+ keys.sort();
+ }
+
+ for (var i = 0; i < keys.length; i++) {
+ elements.push({
+ 'class': 'select-dropdown-menu-item',
+ 'text': items[keys[i]],
+ 'data-label': items[keys[i]],
+ 'data-value': keys[i]
+ });
+ }
+
+ return elements;
+ }
+
+ function filterItems(text, items) {
+ var filteredItems = [];
+ var hasActiveItem = false;
+
+ for (var i = 0; i < items.length; i++) {
+ if (text.length === 0 || items[i]['data-label'].toLowerCase().indexOf(text.toLowerCase()) === 0) {
+ var item = items[i];
+
+ if (typeof options.defaultValue !== 'undefined' && String(options.defaultValue) === item['data-value']) {
+ item.class += ' active';
+ hasActiveItem = true;
+ }
+
+ filteredItems.push(item);
+ }
+ }
+
+ if (! hasActiveItem && filteredItems.length > 0) {
+ filteredItems[0].class += ' active';
+ }
+
+ return filteredItems;
+ }
+
+ function buildDropdownMenu() {
+ var itemElements = filterItems(inputElement.value, buildItems(options.items));
+ var componentPosition = componentElement.getBoundingClientRect();
+
+ if (itemElements.length === 0) {
+ return null;
+ }
+
+ return KB.dom('ul')
+ .attr('id', 'select-dropdown-menu')
+ .style('top', componentPosition.bottom + 'px')
+ .style('left', componentPosition.left + 'px')
+ .style('width', componentPosition.width + 'px')
+ .mouseover(onItemMouseOver)
+ .click(onItemClick)
+ .for('li', itemElements)
+ .build();
+ }
+
+ function destroyDropdownMenu() {
+ var menuElement = KB.find('#select-dropdown-menu');
+
+ if (menuElement !== null) {
+ menuElement.remove();
+ }
+
+ document.removeEventListener('keydown', onKeyDown, false);
+ document.removeEventListener('click', onDocumentClick, false);
+ }
+
+ function renderDropdownMenu() {
+ var element = buildDropdownMenu();
+
+ if (element !== null) {
+ document.body.appendChild(element);
+ }
+
+ document.addEventListener('keydown', onKeyDown, false);
+ document.addEventListener('click', onDocumentClick, false);
+ }
+
+ function getPlaceholderValue() {
+ if (options.defaultValue && options.defaultValue in options.items) {
+ return options.items[options.defaultValue];
+ }
+
+ if (options.placeholder) {
+ return options.placeholder;
+ }
+
+ return '';
+ }
+
+ this.render = function () {
+ var dropdownIconElement = KB.dom('i')
+ .attr('class', 'fa fa-chevron-down select-dropdown-chevron')
+ .click(toggleDropdownMenu)
+ .build();
+
+ inputHiddenElement = KB.dom('input')
+ .attr('type', 'hidden')
+ .attr('name', options.name)
+ .attr('value', options.defaultValue || '')
+ .build();
+
+ inputElement = KB.dom('input')
+ .attr('type', 'text')
+ .attr('placeholder', getPlaceholderValue())
+ .addClass('select-dropdown-input')
+ .style('width', (containerElement.offsetWidth - 30) + 'px')
+ .on('focus', toggleDropdownMenu)
+ .on('input', onInputChanged, true)
+ .build();
+
+ componentElement = KB.dom('div')
+ .addClass('select-dropdown-input-container')
+ .add(inputHiddenElement)
+ .add(inputElement)
+ .add(dropdownIconElement)
+ .build();
+
+ containerElement.appendChild(componentElement);
+
+ if (options.onFocus) {
+ options.onFocus.forEach(function (eventName) {
+ KB.on(eventName, function() { inputElement.focus(); });
+ });
+ }
+ };
+});