framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
592 lines (587 loc) • 21.6 kB
JavaScript
import { getDocument } from 'ssr-window';
import $ from '../../shared/dom7.js';
import { extend, nextTick, deleteProps } from '../../shared/utils.js';
import FrameworkClass from '../../shared/class.js';
import { getDevice } from '../../shared/get-device.js';
import removeDiacritics from './remove-diacritics.js';
class Searchbar extends FrameworkClass {
constructor(app, params) {
if (params === void 0) {
params = {};
}
super(params, [app]);
const sb = this;
const defaults = {
el: undefined,
inputEl: undefined,
inputEvents: 'change input compositionend',
disableButton: true,
disableButtonEl: undefined,
backdropEl: undefined,
searchContainer: undefined,
// container to search, HTMLElement or CSS selector
searchItem: 'li',
// single item selector, CSS selector
searchIn: undefined,
// where to search in item, CSS selector
searchGroup: '.list-group',
searchGroupTitle: '.list-group-title',
ignore: '.searchbar-ignore',
foundEl: '.searchbar-found',
notFoundEl: '.searchbar-not-found',
hideOnEnableEl: '.searchbar-hide-on-enable',
hideOnSearchEl: '.searchbar-hide-on-search',
backdrop: true,
removeDiacritics: true,
customSearch: false,
hideGroupTitles: true,
hideGroups: true,
disableOnBackdropClick: true,
expandable: false,
inline: false
};
// Extend defaults with modules params
sb.useModulesParams(defaults);
sb.params = extend(defaults, params);
const $el = $(sb.params.el);
if ($el.length === 0) return sb;
if ($el[0].f7Searchbar) return $el[0].f7Searchbar;
$el[0].f7Searchbar = sb;
let $pageEl;
const $navbarEl = $el.parents('.navbar');
if ($el.parents('.page').length > 0) {
$pageEl = $el.parents('.page');
} else if ($navbarEl.length > 0) {
$pageEl = $(app.navbar.getPageByEl($navbarEl[0]));
if (!$pageEl.length) {
const $currentPageEl = $el.parents('.view').find('.page-current');
if ($currentPageEl[0] && $currentPageEl[0].f7Page && $currentPageEl[0].f7Page.navbarEl === $navbarEl[0]) {
$pageEl = $currentPageEl;
}
}
}
let $foundEl;
if (params.foundEl) {
$foundEl = $(params.foundEl);
} else if (typeof sb.params.foundEl === 'string' && $pageEl) {
$foundEl = $pageEl.find(sb.params.foundEl);
}
let $notFoundEl;
if (params.notFoundEl) {
$notFoundEl = $(params.notFoundEl);
} else if (typeof sb.params.notFoundEl === 'string' && $pageEl) {
$notFoundEl = $pageEl.find(sb.params.notFoundEl);
}
let $hideOnEnableEl;
if (params.hideOnEnableEl) {
$hideOnEnableEl = $(params.hideOnEnableEl);
} else if (typeof sb.params.hideOnEnableEl === 'string' && $pageEl) {
$hideOnEnableEl = $pageEl.find(sb.params.hideOnEnableEl);
}
let $hideOnSearchEl;
if (params.hideOnSearchEl) {
$hideOnSearchEl = $(params.hideOnSearchEl);
} else if (typeof sb.params.hideOnSearchEl === 'string' && $pageEl) {
$hideOnSearchEl = $pageEl.find(sb.params.hideOnSearchEl);
}
const expandable = sb.params.expandable || $el.hasClass('searchbar-expandable');
const inline = sb.params.inline || $el.hasClass('searchbar-inline');
if (typeof sb.params.backdrop === 'undefined') {
sb.params.backdrop = !inline;
}
let $backdropEl;
if (sb.params.backdrop) {
if (sb.params.backdropEl) {
$backdropEl = $(sb.params.backdropEl);
} else if ($pageEl && $pageEl.length > 0) {
$backdropEl = $pageEl.find('.searchbar-backdrop');
} else {
$backdropEl = $el.siblings('.searchbar-backdrop');
}
if ($backdropEl.length === 0) {
$backdropEl = $('<div class="searchbar-backdrop"></div>');
if ($pageEl && $pageEl.length) {
if ($el.parents($pageEl).length > 0 && $navbarEl && $el.parents($navbarEl).length === 0) {
$backdropEl.insertBefore($el);
} else {
$backdropEl.insertBefore($pageEl.find('.page-content').eq(0));
}
} else {
$backdropEl.insertBefore($el);
}
}
}
let $searchContainer;
if (sb.params.searchContainer) {
$searchContainer = $(sb.params.searchContainer);
}
let $inputEl;
if (sb.params.inputEl) {
$inputEl = $(sb.params.inputEl);
} else {
$inputEl = $el.find('input[type="search"]').eq(0);
}
let $disableButtonEl;
if (sb.params.disableButton) {
if (sb.params.disableButtonEl) {
$disableButtonEl = $(sb.params.disableButtonEl);
} else {
$disableButtonEl = $el.find('.searchbar-disable-button');
}
}
extend(sb, {
app,
view: app.views.get($el.parents('.view')),
$el,
el: $el[0],
$backdropEl,
backdropEl: $backdropEl && $backdropEl[0],
$searchContainer,
searchContainer: $searchContainer && $searchContainer[0],
$inputEl,
inputEl: $inputEl[0],
$disableButtonEl,
disableButtonEl: $disableButtonEl && $disableButtonEl[0],
disableButtonHasMargin: false,
$pageEl,
pageEl: $pageEl && $pageEl[0],
$navbarEl,
navbarEl: $navbarEl && $navbarEl[0],
$foundEl,
foundEl: $foundEl && $foundEl[0],
$notFoundEl,
notFoundEl: $notFoundEl && $notFoundEl[0],
$hideOnEnableEl,
hideOnEnableEl: $hideOnEnableEl && $hideOnEnableEl[0],
$hideOnSearchEl,
hideOnSearchEl: $hideOnSearchEl && $hideOnSearchEl[0],
previousQuery: '',
query: '',
isVirtualList: $searchContainer && $searchContainer.hasClass('virtual-list'),
virtualList: undefined,
enabled: false,
expandable,
inline
});
// Events
function preventSubmit(e) {
e.preventDefault();
}
function onInputFocus(e) {
sb.enable(e);
sb.$el.addClass('searchbar-focused');
}
function onInputBlur() {
sb.$el.removeClass('searchbar-focused');
}
function onInputChange() {
const value = sb.$inputEl.val().trim();
if (sb.$searchContainer && sb.$searchContainer.length > 0 && (sb.params.searchIn || sb.isVirtualList || sb.params.searchIn === sb.params.searchItem) || sb.params.customSearch) {
sb.search(value, true);
}
}
function onInputClear(e, previousValue) {
sb.$el.trigger('searchbar:clear', previousValue);
sb.emit('local::clear searchbarClear', sb, previousValue);
}
function disableOnClick(e) {
sb.disable(e);
}
function onPageBeforeOut() {
if (!sb || sb && !sb.$el) return;
if (sb.enabled) {
sb.$el.removeClass('searchbar-enabled');
if (sb.expandable) {
sb.$el.parents('.navbar').removeClass('with-searchbar-expandable-enabled with-searchbar-expandable-enabled-no-transition');
}
}
}
function onPageBeforeIn() {
if (!sb || sb && !sb.$el) return;
if (sb.enabled) {
sb.$el.addClass('searchbar-enabled');
if (sb.expandable) {
sb.$el.parents('.navbar').addClass('with-searchbar-expandable-enabled-no-transition');
}
}
}
sb.attachEvents = function attachEvents() {
$el.on('submit', preventSubmit);
if (sb.params.disableButton) {
sb.$disableButtonEl.on('click', disableOnClick);
}
if (sb.params.disableOnBackdropClick && sb.$backdropEl) {
sb.$backdropEl.on('click', disableOnClick);
}
if (sb.expandable && app.theme === 'ios' && sb.view && $navbarEl.length && sb.$pageEl) {
sb.$pageEl.on('page:beforeout', onPageBeforeOut);
sb.$pageEl.on('page:beforein', onPageBeforeIn);
}
sb.$inputEl.on('focus', onInputFocus);
sb.$inputEl.on('blur', onInputBlur);
sb.$inputEl.on(sb.params.inputEvents, onInputChange);
sb.$inputEl.on('input:clear', onInputClear);
};
sb.detachEvents = function detachEvents() {
$el.off('submit', preventSubmit);
if (sb.params.disableButton) {
sb.$disableButtonEl.off('click', disableOnClick);
}
if (sb.params.disableOnBackdropClick && sb.$backdropEl) {
sb.$backdropEl.off('click', disableOnClick);
}
if (sb.expandable && app.theme === 'ios' && sb.view && $navbarEl.length && sb.$pageEl) {
sb.$pageEl.off('page:beforeout', onPageBeforeOut);
sb.$pageEl.off('page:beforein', onPageBeforeIn);
}
sb.$inputEl.off('focus', onInputFocus);
sb.$inputEl.off('blur', onInputBlur);
sb.$inputEl.off(sb.params.inputEvents, onInputChange);
sb.$inputEl.off('input:clear', onInputClear);
};
// Install Modules
sb.useModules();
// Init
sb.init();
return sb;
}
clear(e) {
const sb = this;
if (!sb.query && e && $(e.target).hasClass('searchbar-clear')) {
sb.disable();
return sb;
}
const previousQuery = sb.value;
sb.$inputEl.val('').trigger('change').focus();
sb.$el.trigger('searchbar:clear', previousQuery);
sb.emit('local::clear searchbarClear', sb, previousQuery);
return sb;
}
setDisableButtonMargin() {
const sb = this;
if (sb.expandable) return;
const app = sb.app;
sb.$disableButtonEl.transition(0).show();
sb.$disableButtonEl.css(`margin-${app.rtl ? 'left' : 'right'}`, `${-sb.disableButtonEl.offsetWidth}px`);
/* eslint no-underscore-dangle: ["error", { "allow": ["_clientLeft"] }] */
sb._clientLeft = sb.$disableButtonEl[0].clientLeft;
sb.$disableButtonEl.transition('');
sb.disableButtonHasMargin = true;
}
enable(setFocus) {
const sb = this;
if (sb.enabled) return sb;
const app = sb.app;
const document = getDocument();
const device = getDevice();
sb.enabled = true;
function enable() {
if (sb.$backdropEl && (sb.$searchContainer && sb.$searchContainer.length || sb.params.customSearch) && !sb.$el.hasClass('searchbar-enabled') && !sb.query) {
sb.backdropShow();
}
sb.$el.addClass('searchbar-enabled');
if (!sb.$disableButtonEl || sb.$disableButtonEl && sb.$disableButtonEl.length === 0) {
sb.$el.addClass('searchbar-enabled-no-disable-button');
}
if (!sb.expandable && sb.$disableButtonEl && sb.$disableButtonEl.length > 0 && app.theme !== 'md') {
if (!sb.disableButtonHasMargin) {
sb.setDisableButtonMargin();
}
sb.$disableButtonEl.css(`margin-${app.rtl ? 'left' : 'right'}`, '0px');
}
if (sb.expandable) {
const $navbarEl = sb.$el.parents('.navbar');
if ($navbarEl.hasClass('navbar-large') && sb.$pageEl) {
const $pageContentEl = sb.$pageEl.find('.page-content');
const $titleLargeEl = $navbarEl.find('.title-large');
$pageContentEl.addClass('with-searchbar-expandable-enabled');
if ($navbarEl.hasClass('navbar-large') && $navbarEl.hasClass('navbar-large-collapsed') && $titleLargeEl.length && $pageContentEl.length) {
$pageContentEl.transition(0);
$pageContentEl[0].scrollTop -= $titleLargeEl[0].offsetHeight;
setTimeout(() => {
$pageContentEl.transition('');
}, 200);
}
}
if (app.theme === 'md' && $navbarEl.length) {
$navbarEl.addClass('with-searchbar-expandable-enabled');
} else {
$navbarEl.addClass('with-searchbar-expandable-enabled');
if ($navbarEl.hasClass('navbar-large')) {
$navbarEl.addClass('navbar-large-collapsed');
}
}
}
if (sb.$hideOnEnableEl) sb.$hideOnEnableEl.addClass('hidden-by-searchbar');
sb.$el.trigger('searchbar:enable');
sb.emit('local::enable searchbarEnable', sb);
}
let needsFocus = false;
if (setFocus === true) {
if (document.activeElement !== sb.inputEl) {
needsFocus = true;
}
}
const isIos = device.ios && app.theme === 'ios';
if (isIos) {
if (sb.expandable) {
if (needsFocus) sb.$inputEl.focus();
enable();
} else {
if (needsFocus) sb.$inputEl.focus();
if (setFocus && (setFocus.type === 'focus' || setFocus === true)) {
nextTick(() => {
enable();
}, 400);
} else {
enable();
}
}
} else {
if (needsFocus) sb.$inputEl.focus();
if (app.theme === 'md' && sb.expandable) {
sb.$el.parents('.page, .view, .navbar-inner, .navbar').scrollLeft(app.rtl ? 100 : 0);
}
enable();
}
return sb;
}
disable() {
const sb = this;
if (!sb.enabled) return sb;
const app = sb.app;
sb.$inputEl.val('').trigger('change');
sb.$el.removeClass('searchbar-enabled searchbar-focused searchbar-enabled-no-disable-button');
if (sb.expandable) {
const $navbarEl = sb.$el.parents('.navbar');
const $pageContentEl = sb.$pageEl && sb.$pageEl.find('.page-content');
if ($navbarEl.hasClass('navbar-large') && $pageContentEl.length) {
const $titleLargeEl = $navbarEl.find('.title-large');
sb.$el.transitionEnd(() => {
$pageContentEl.removeClass('with-searchbar-expandable-closing');
});
if ($navbarEl.hasClass('navbar-large') && $navbarEl.hasClass('navbar-large-collapsed') && $titleLargeEl.length) {
const scrollTop = $pageContentEl[0].scrollTop;
const titleLargeHeight = $titleLargeEl[0].offsetHeight;
if (scrollTop > titleLargeHeight) {
$pageContentEl.transition(0);
$pageContentEl[0].scrollTop = scrollTop + titleLargeHeight;
setTimeout(() => {
$pageContentEl.transition('');
}, 200);
}
}
$pageContentEl.removeClass('with-searchbar-expandable-enabled').addClass('with-searchbar-expandable-closing');
}
if (app.theme === 'md' && $navbarEl.length) {
$navbarEl.removeClass('with-searchbar-expandable-enabled with-searchbar-expandable-enabled-no-transition').addClass('with-searchbar-expandable-closing');
sb.$el.transitionEnd(() => {
$navbarEl.removeClass('with-searchbar-expandable-closing');
});
} else {
$navbarEl.removeClass('with-searchbar-expandable-enabled with-searchbar-expandable-enabled-no-transition').addClass('with-searchbar-expandable-closing');
sb.$el.transitionEnd(() => {
$navbarEl.removeClass('with-searchbar-expandable-closing');
});
if (sb.$pageEl) {
sb.$pageEl.find('.page-content').trigger('scroll');
}
}
}
if (!sb.expandable && sb.$disableButtonEl && sb.$disableButtonEl.length > 0 && app.theme !== 'md') {
sb.$disableButtonEl.css(`margin-${app.rtl ? 'left' : 'right'}`, `${-sb.disableButtonEl.offsetWidth}px`);
}
if (sb.$backdropEl && (sb.$searchContainer && sb.$searchContainer.length || sb.params.customSearch)) {
sb.backdropHide();
}
sb.enabled = false;
sb.$inputEl.blur();
if (sb.$hideOnEnableEl) sb.$hideOnEnableEl.removeClass('hidden-by-searchbar');
sb.$el.trigger('searchbar:disable');
sb.emit('local::disable searchbarDisable', sb);
return sb;
}
toggle() {
const sb = this;
if (sb.enabled) sb.disable();else sb.enable(true);
return sb;
}
backdropShow() {
const sb = this;
if (sb.$backdropEl) {
sb.$backdropEl.addClass('searchbar-backdrop-in');
}
return sb;
}
backdropHide() {
const sb = this;
if (sb.$backdropEl) {
sb.$backdropEl.removeClass('searchbar-backdrop-in');
}
return sb;
}
search(query, internal) {
const sb = this;
sb.previousQuery = sb.query || '';
if (query === sb.previousQuery) return sb;
if (!internal) {
if (!sb.enabled) {
sb.enable();
}
sb.$inputEl.val(query);
sb.$inputEl.trigger('input');
}
sb.query = query;
sb.value = query;
const {
$searchContainer,
$el,
$foundEl,
$notFoundEl,
$hideOnSearchEl,
isVirtualList
} = sb;
// Hide on search element
if (query.length > 0 && $hideOnSearchEl) {
$hideOnSearchEl.addClass('hidden-by-searchbar');
} else if ($hideOnSearchEl) {
$hideOnSearchEl.removeClass('hidden-by-searchbar');
}
// Add active/inactive classes on overlay
if ($searchContainer && $searchContainer.length && $el.hasClass('searchbar-enabled') || sb.params.customSearch && $el.hasClass('searchbar-enabled')) {
if (query.length === 0) {
sb.backdropShow();
} else {
sb.backdropHide();
}
}
if (sb.params.customSearch) {
$el.trigger('searchbar:search', {
query,
previousQuery: sb.previousQuery
});
sb.emit('local::search searchbarSearch', sb, query, sb.previousQuery);
return sb;
}
let foundItems = [];
let vlQuery;
if (isVirtualList) {
sb.virtualList = $searchContainer[0].f7VirtualList;
if (query.trim() === '') {
sb.virtualList.resetFilter();
if ($notFoundEl) $notFoundEl.hide();
if ($foundEl) $foundEl.show();
$el.trigger('searchbar:search', {
query,
previousQuery: sb.previousQuery
});
sb.emit('local::search searchbarSearch', sb, query, sb.previousQuery);
return sb;
}
vlQuery = sb.params.removeDiacritics ? removeDiacritics(query) : query;
if (sb.virtualList.params.searchAll) {
foundItems = sb.virtualList.params.searchAll(vlQuery, sb.virtualList.items) || [];
} else if (sb.virtualList.params.searchByItem) {
for (let i = 0; i < sb.virtualList.items.length; i += 1) {
if (sb.virtualList.params.searchByItem(vlQuery, sb.virtualList.items[i], i)) {
foundItems.push(i);
}
}
}
} else {
let values;
if (sb.params.removeDiacritics) values = removeDiacritics(query.trim().toLowerCase()).split(' ');else {
values = query.trim().toLowerCase().split(' ');
}
$searchContainer.find(sb.params.searchItem).removeClass('hidden-by-searchbar').each(itemEl => {
const $itemEl = $(itemEl);
let compareWithText = [];
let $searchIn = sb.params.searchIn ? $itemEl.find(sb.params.searchIn) : $itemEl;
if (sb.params.searchIn === sb.params.searchItem) {
$searchIn = $itemEl;
}
$searchIn.each(searchInEl => {
let itemText = $(searchInEl).text().trim().toLowerCase();
if (sb.params.removeDiacritics) itemText = removeDiacritics(itemText);
compareWithText.push(itemText);
});
compareWithText = compareWithText.join(' ');
let wordsMatch = 0;
for (let i = 0; i < values.length; i += 1) {
if (compareWithText.indexOf(values[i]) >= 0) wordsMatch += 1;
}
if (wordsMatch !== values.length && !(sb.params.ignore && $itemEl.is(sb.params.ignore))) {
$itemEl.addClass('hidden-by-searchbar');
} else {
foundItems.push($itemEl[0]);
}
});
if (sb.params.hideGroupTitles) {
$searchContainer.find(sb.params.searchGroupTitle).each(titleEl => {
const $titleEl = $(titleEl);
const $nextElements = $titleEl.nextAll(sb.params.searchItem);
let hide = true;
for (let i = 0; i < $nextElements.length; i += 1) {
const $nextEl = $nextElements.eq(i);
if ($nextEl.is(sb.params.searchGroupTitle)) break;
if (!$nextEl.hasClass('hidden-by-searchbar')) {
hide = false;
}
}
const ignore = sb.params.ignore && $titleEl.is(sb.params.ignore);
if (hide && !ignore) $titleEl.addClass('hidden-by-searchbar');else $titleEl.removeClass('hidden-by-searchbar');
});
}
if (sb.params.hideGroups) {
$searchContainer.find(sb.params.searchGroup).each(groupEl => {
const $groupEl = $(groupEl);
const ignore = sb.params.ignore && $groupEl.is(sb.params.ignore);
// eslint-disable-next-line
const notHidden = $groupEl.find(sb.params.searchItem).filter(el => {
return !$(el).hasClass('hidden-by-searchbar');
});
if (notHidden.length === 0 && !ignore) {
$groupEl.addClass('hidden-by-searchbar');
} else {
$groupEl.removeClass('hidden-by-searchbar');
}
});
}
}
if (foundItems.length === 0) {
if ($notFoundEl) $notFoundEl.show();
if ($foundEl) $foundEl.hide();
} else {
if ($notFoundEl) $notFoundEl.hide();
if ($foundEl) $foundEl.show();
}
if (isVirtualList && sb.virtualList) {
sb.virtualList.filterItems(foundItems);
}
$el.trigger('searchbar:search', {
query,
previousQuery: sb.previousQuery,
foundItems
});
sb.emit('local::search searchbarSearch', sb, query, sb.previousQuery, foundItems);
return sb;
}
init() {
const sb = this;
if (sb.expandable && sb.$el) sb.$el.addClass('searchbar-expandable');
if (sb.inline && sb.$el) sb.$el.addClass('searchbar-inline');
sb.attachEvents();
}
destroy() {
const sb = this;
sb.emit('local::beforeDestroy searchbarBeforeDestroy', sb);
sb.$el.trigger('searchbar:beforedestroy');
sb.detachEvents();
if (sb.$el[0]) {
sb.$el[0].f7Searchbar = null;
delete sb.$el[0].f7Searchbar;
}
deleteProps(sb);
}
}
export default Searchbar;