frdh-mmenu-js/dist/addons/searchfield/mmenu.searchfield.js
2022-11-18 21:38:41 +01:00

508 lines
20 KiB
JavaScript

import Mmenu from '../../core/oncanvas/mmenu.oncanvas';
import options from './_options';
import configs from './_configs';
import translate from './translations/translate';
import { extendShorthandOptions } from './_options';
import * as DOM from '../../_modules/dom';
import * as events from '../../_modules/eventlisteners';
import { type, extend } from '../../_modules/helpers';
// Add the translations.
translate();
// Add the options and configs.
Mmenu.options.searchfield = options;
Mmenu.configs.searchfield = configs;
export default function () {
var _this = this;
var options = extendShorthandOptions(this.opts.searchfield);
this.opts.searchfield = extend(options, Mmenu.options.searchfield);
var configs = this.conf.searchfield;
if (!options.add) {
return;
}
// Blur searchfield
this.bind('close:start', function () {
DOM.find(_this.node.menu, '.mm-searchfield').forEach(function (input) {
input.blur();
});
});
this.bind('initPanel:after', function (panel) {
var searchpanel = null;
// Add the search panel
if (options.panel.add) {
searchpanel = initSearchPanel.call(_this);
}
// Add the searchfield
var addTo = null;
switch (options.addTo) {
case 'panels':
addTo = [panel];
break;
case 'panel':
addTo = [searchpanel];
break;
default:
if (typeof options.addTo == 'string') {
addTo = DOM.find(_this.node.menu, options.addTo);
}
else if (type(options.addTo) == 'array') {
addTo = options.addTo;
}
break;
}
addTo.forEach(function (form) {
form = initSearchfield.call(_this, form);
if (options.search && form) {
initSearching.call(_this, form);
}
});
// Add the no-results message
if (options.noResults) {
initNoResultsMsg.call(_this, options.panel.add ? searchpanel : panel);
}
});
// Add click behavior.
// Prevents default behavior when clicking an anchor
this.clck.push(function (anchor, args) {
if (args.inMenu) {
if (anchor.matches('.mm-searchfield__btn')) {
// Clicking the clear button
if (anchor.matches('.mm-btn_close')) {
var form = anchor.closest('.mm-searchfield'), input = DOM.find(form, 'input')[0];
input.value = '';
_this.search(input);
return true;
}
// Clicking the submit button
if (anchor.matches('.mm-btn_next')) {
var form = anchor.closest('form');
if (form) {
form.submit();
}
return true;
}
}
}
});
}
var initSearchPanel = function () {
var options = this.opts.searchfield, configs = this.conf.searchfield;
var searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0];
// Only once
if (searchpanel) {
return searchpanel;
}
searchpanel = DOM.create('div.mm-panel.mm-panel_search.mm-hidden');
if (options.panel.id) {
searchpanel.id = options.panel.id;
}
if (options.panel.title) {
searchpanel.setAttribute('data-mm-title', options.panel.title);
// searchpanel.dataset.mmTitle = options.panel.title; // IE10 has no dataset :(
}
var listview = DOM.create('ul');
searchpanel.append(listview);
this.node.pnls.append(searchpanel);
this.initListview(listview);
this._initNavbar(searchpanel);
switch (options.panel.fx) {
case false:
break;
case 'none':
searchpanel.classList.add('mm-panel_noanimation');
break;
default:
searchpanel.classList.add('mm-panel_fx-' + options.panel.fx);
break;
}
// Add splash content
if (options.panel.splash) {
var splash = DOM.create('div.mm-panel__content');
splash.innerHTML = options.panel.splash;
searchpanel.append(splash);
}
searchpanel.classList.add('mm-panel');
searchpanel.classList.add('mm-hidden');
this.node.pnls.append(searchpanel);
return searchpanel;
};
var initSearchfield = function (wrapper) {
var options = this.opts.searchfield, configs = this.conf.searchfield;
// No searchfield in vertical submenus
if (wrapper.parentElement.matches('.mm-listitem_vertical')) {
return null;
}
// Only one searchfield per panel
var form = DOM.find(wrapper, '.mm-searchfield')[0];
if (form) {
return form;
}
function addAttributes(element, attr) {
if (attr) {
for (var a in attr) {
element.setAttribute(a, attr[a]);
}
}
}
var form = DOM.create((configs.form ? 'form' : 'div') + '.mm-searchfield'), field = DOM.create('div.mm-searchfield__input'), input = DOM.create('input');
input.type = 'text';
input.autocomplete = 'off';
input.placeholder = this.i18n(options.placeholder);
field.append(input);
form.append(field);
wrapper.prepend(form);
// Add attributes to the input
addAttributes(input, configs.input);
// Add the clear button
if (configs.clear) {
var anchor = DOM.create('a.mm-btn.mm-btn_close.mm-searchfield__btn');
anchor.setAttribute('href', '#');
field.append(anchor);
}
// Add attributes and submit to the form
addAttributes(form, configs.form);
if (configs.form && configs.submit && !configs.clear) {
var anchor = DOM.create('a.mm-btn.mm-btn_next.mm-searchfield__btn');
anchor.setAttribute('href', '#');
field.append(anchor);
}
if (options.cancel) {
var anchor = DOM.create('a.mm-searchfield__cancel');
anchor.setAttribute('href', '#');
anchor.textContent = this.i18n('cancel');
form.append(anchor);
}
return form;
};
var initSearching = function (form) {
var _this = this;
var options = this.opts.searchfield, configs = this.conf.searchfield;
var data = {};
// In the searchpanel.
if (form.closest('.mm-panel_search')) {
data.panels = DOM.find(this.node.pnls, '.mm-panel');
data.noresults = [form.closest('.mm-panel')];
// In a panel
}
else if (form.closest('.mm-panel')) {
data.panels = [form.closest('.mm-panel')];
data.noresults = data.panels;
// Not in a panel, global
}
else {
data.panels = DOM.find(this.node.pnls, '.mm-panel');
data.noresults = [this.node.menu];
}
// Filter out search panel
data.panels = data.panels.filter(function (panel) { return !panel.matches('.mm-panel_search'); });
// Filter out vertical submenus
data.panels = data.panels.filter(function (panel) { return !panel.parentElement.matches('.mm-listitem_vertical'); });
// Find listitems and dividers.
data.listitems = [];
data.dividers = [];
data.panels.forEach(function (panel) {
var _a, _b;
(_a = data.listitems).push.apply(_a, DOM.find(panel, '.mm-listitem'));
(_b = data.dividers).push.apply(_b, DOM.find(panel, '.mm-divider'));
});
var searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0], input = DOM.find(form, 'input')[0], cancel = DOM.find(form, '.mm-searchfield__cancel')[0];
input['mmSearchfield'] = data;
// Open the splash panel when focussing the input.
if (options.panel.add && options.panel.splash) {
events.off(input, 'focus.splash');
events.on(input, 'focus.splash', function (evnt) {
_this.openPanel(searchpanel);
});
}
if (options.cancel) {
// Show the cancel button when focussing the input.
events.off(input, 'focus.cancel');
events.on(input, 'focus.cancel', function (evnt) {
cancel.classList.add('mm-searchfield__cancel-active');
});
// Close the splash panel when clicking the cancel button.
events.off(cancel, 'click.splash');
events.on(cancel, 'click.splash', function (evnt) {
evnt.preventDefault();
cancel.classList.remove('mm-searchfield__cancel-active');
if (searchpanel.matches('.mm-panel_opened')) {
var parents = DOM.children(_this.node.pnls, '.mm-panel_opened-parent');
if (parents.length) {
_this.openPanel(parents[parents.length - 1]);
}
}
});
}
// Focus the input in the searchpanel when opening the searchpanel.
if (options.panel.add && options.addTo == 'panel') {
this.bind('openPanel:finish', function (panel) {
if (panel === searchpanel) {
input.focus();
}
});
}
// Search while typing.
events.off(input, 'input.search');
events.on(input, 'input.search', function (evnt) {
switch (evnt.keyCode) {
case 9: // tab
case 16: // shift
case 17: // control
case 18: // alt
case 37: // left
case 38: // top
case 39: // right
case 40: // bottom
break;
default:
_this.search(input);
break;
}
});
// Search initially.
this.search(input);
};
var initNoResultsMsg = function (wrapper) {
if (!wrapper) {
return;
}
var options = this.opts.searchfield, configs = this.conf.searchfield;
// Not in a panel
if (!wrapper.closest('.mm-panel')) {
wrapper = DOM.children(this.node.pnls, '.mm-panel')[0];
}
// Only once
if (DOM.children(wrapper, '.mm-panel__noresultsmsg').length) {
return;
}
// Add no-results message
var message = DOM.create('div.mm-panel__noresultsmsg.mm-hidden');
message.innerHTML = this.i18n(options.noResults);
wrapper.append(message);
};
Mmenu.prototype.search = function (input, query) {
var _this = this;
var _a;
var options = this.opts.searchfield, configs = this.conf.searchfield;
query = query || '' + input.value;
query = query.toLowerCase().trim();
var data = input['mmSearchfield'];
var form = input.closest('.mm-searchfield'), buttons = DOM.find(form, '.mm-btn'), searchpanel = DOM.children(this.node.pnls, '.mm-panel_search')[0];
/** The panels. */
var panels = data.panels;
/** The "no results" messages in a cloned array. */
var noresults = data.noresults;
/** The listitems in a cloned array. */
var listitems = data.listitems;
/** Tje dividers in a cloned array. */
var dividers = data.dividers;
// Reset previous results
listitems.forEach(function (listitem) {
listitem.classList.remove('mm-listitem_nosubitems');
listitem.classList.remove('mm-listitem_onlysubitems');
listitem.classList.remove('mm-hidden');
});
if (searchpanel) {
DOM.children(searchpanel, '.mm-listview')[0].innerHTML = '';
}
panels.forEach(function (panel) {
panel.scrollTop = 0;
});
// Search
if (query.length) {
// Initially hide all dividers.
dividers.forEach(function (divider) {
divider.classList.add('mm-hidden');
});
// Hide listitems that do not match.
listitems.forEach(function (listitem) {
var text = DOM.children(listitem, '.mm-listitem__text')[0];
var add = false;
// The listitem should be shown if:
// 1) The text matches the query and
// 2a) The text is a open-button and
// 2b) the option showSubPanels is set to true.
// or 3a) The text is not an anchor and
// 3b) the option showTextItems is set to true.
// or 4) The text is an anchor.
// 1
if (text &&
DOM.text(text)
.toLowerCase()
.indexOf(query) > -1) {
// 2a
if (text.matches('.mm-listitem__btn')) {
// 2b
if (options.showSubPanels) {
add = true;
}
}
// 3a
else if (!text.matches('a')) {
// 3b
if (options.showTextItems) {
add = true;
}
}
// 4
else {
add = true;
}
}
if (!add) {
listitem.classList.add('mm-hidden');
}
});
/** Whether or not the query yielded results. */
var hasResults = listitems.filter(function (listitem) { return !listitem.matches('.mm-hidden'); }).length;
// Show all mached listitems in the search panel
if (options.panel.add) {
// Clone all matched listitems into the search panel
var allitems_1 = [];
panels.forEach(function (panel) {
var listitems = DOM.filterLI(DOM.find(panel, '.mm-listitem'));
listitems = listitems.filter(function (listitem) { return !listitem.matches('.mm-hidden'); });
if (listitems.length) {
// Add a divider to indicate in what panel the listitems were.
if (options.panel.dividers) {
var divider = DOM.create('li.mm-divider');
var title = DOM.find(panel, '.mm-navbar__title')[0];
if (title) {
divider.innerHTML = title.innerHTML;
allitems_1.push(divider);
}
}
listitems.forEach(function (listitem) {
allitems_1.push(listitem.cloneNode(true));
});
}
});
// Remove toggles and checks.
allitems_1.forEach(function (listitem) {
listitem
.querySelectorAll('.mm-toggle, .mm-check')
.forEach(function (element) {
element.remove();
});
});
// Add to the search panel.
(_a = DOM.children(searchpanel, '.mm-listview')[0]).append.apply(_a, allitems_1);
// Open the search panel.
this.openPanel(searchpanel);
}
else {
// Also show listitems in sub-panels for matched listitems
if (options.showSubPanels) {
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
var child = listitem['mmChild'];
if (child) {
DOM.find(child, '.mm-listitem').forEach(function (listitem) {
listitem.classList.remove('mm-hidden');
});
}
});
});
}
// Update parent for sub-panel
// .reverse() mutates the original array, therefor we "clone" it first using [...panels].
panels.slice().reverse().forEach(function (panel, p) {
var parent = panel['mmParent'];
if (parent) {
// The current panel has mached listitems
var listitems_1 = DOM.find(panel, '.mm-listitem');
if (DOM.filterLI(listitems_1).length) {
// Show parent
if (parent.matches('.mm-hidden')) {
parent.classList.remove('mm-hidden');
}
parent.classList.add('mm-listitem_onlysubitems');
}
else if (!input.closest('.mm-panel')) {
if (panel.matches('.mm-panel_opened') ||
panel.matches('.mm-panel_opened-parent')) {
// Compensate the timeout for the opening animation
setTimeout(function () {
_this.openPanel(parent.closest('.mm-panel'));
}, (p + 1) * (_this.conf.openingInterval * 1.5));
}
parent.classList.add('mm-listitem_nosubitems');
}
}
});
// Show parent panels of vertical submenus
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
DOM.parents(listitem, '.mm-listitem_vertical').forEach(function (parent) {
if (parent.matches('.mm-hidden')) {
parent.classList.remove('mm-hidden');
parent.classList.add('mm-listitem_onlysubitems');
}
});
});
});
// Show first preceeding divider of parent
panels.forEach(function (panel) {
var listitems = DOM.find(panel, '.mm-listitem');
DOM.filterLI(listitems).forEach(function (listitem) {
var divider = DOM.prevAll(listitem, '.mm-divider')[0];
if (divider) {
divider.classList.remove('mm-hidden');
}
});
});
}
// Show submit / clear button
buttons.forEach(function (button) { return button.classList.remove('mm-hidden'); });
// Show/hide no results message
noresults.forEach(function (wrapper) {
DOM.find(wrapper, '.mm-panel__noresultsmsg').forEach(function (message) {
return message.classList[hasResults ? 'add' : 'remove']('mm-hidden');
});
});
if (options.panel.add) {
// Hide splash
if (options.panel.splash) {
DOM.find(searchpanel, '.mm-panel__content').forEach(function (splash) {
return splash.classList.add('mm-hidden');
});
}
// Re-show original listitems when in search panel
listitems.forEach(function (listitem) {
return listitem.classList.remove('mm-hidden');
});
dividers.forEach(function (divider) { return divider.classList.remove('mm-hidden'); });
}
// Don't search
}
else {
// Show all items
listitems.forEach(function (listitem) { return listitem.classList.remove('mm-hidden'); });
dividers.forEach(function (divider) { return divider.classList.remove('mm-hidden'); });
// Hide submit / clear button
buttons.forEach(function (button) { return button.classList.add('mm-hidden'); });
// Hide no results message
noresults.forEach(function (wrapper) {
DOM.find(wrapper, '.mm-panel__noresultsmsg').forEach(function (message) {
return message.classList.add('mm-hidden');
});
});
if (options.panel.add) {
// Show splash
if (options.panel.splash) {
DOM.find(searchpanel, '.mm-panel__content').forEach(function (splash) {
return splash.classList.remove('mm-hidden');
});
// Close panel
}
else if (!input.closest('.mm-panel_search')) {
var opened = DOM.children(this.node.pnls, '.mm-panel_opened-parent');
this.openPanel(opened.slice(-1)[0]);
}
}
}
// Update for other addons
this.trigger('updateListview');
};