autocomplete-class.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. /* eslint "no-useless-escape": "off" */
  2. import $ from '../../shared/dom7.js';
  3. import { extend, id, nextTick, deleteProps, iosPreloaderContent, mdPreloaderContent, auroraPreloaderContent } from '../../shared/utils.js';
  4. import { getDevice } from '../../shared/get-device.js';
  5. import Framework7Class from '../../shared/class.js';
  6. /** @jsx $jsx */
  7. import $jsx from '../../shared/$jsx.js';
  8. class Autocomplete extends Framework7Class {
  9. constructor(app, params) {
  10. if (params === void 0) {
  11. params = {};
  12. }
  13. super(params, [app]);
  14. const ac = this;
  15. ac.app = app;
  16. const device = getDevice();
  17. const defaults = extend({
  18. on: {}
  19. }, app.params.autocomplete);
  20. if (typeof defaults.searchbarDisableButton === 'undefined') {
  21. defaults.searchbarDisableButton = app.theme !== 'aurora';
  22. } // Extend defaults with modules params
  23. ac.useModulesParams(defaults);
  24. ac.params = extend(defaults, params);
  25. let $openerEl;
  26. if (ac.params.openerEl) {
  27. $openerEl = $(ac.params.openerEl);
  28. if ($openerEl.length) $openerEl[0].f7Autocomplete = ac;
  29. }
  30. let $inputEl;
  31. if (ac.params.inputEl) {
  32. $inputEl = $(ac.params.inputEl);
  33. if ($inputEl.length) $inputEl[0].f7Autocomplete = ac;
  34. }
  35. const uniqueId = id();
  36. let url = params.url;
  37. if (!url && $openerEl && $openerEl.length) {
  38. if ($openerEl.attr('href')) url = $openerEl.attr('href');else if ($openerEl.find('a').length > 0) {
  39. url = $openerEl.find('a').attr('href');
  40. }
  41. }
  42. if (!url || url === '#' || url === '') url = ac.params.url;
  43. const inputType = ac.params.multiple ? 'checkbox' : 'radio';
  44. extend(ac, {
  45. $openerEl,
  46. openerEl: $openerEl && $openerEl[0],
  47. $inputEl,
  48. inputEl: $inputEl && $inputEl[0],
  49. id: uniqueId,
  50. url,
  51. value: ac.params.value || [],
  52. inputType,
  53. inputName: `${inputType}-${uniqueId}`,
  54. $modalEl: undefined,
  55. $dropdownEl: undefined
  56. });
  57. let previousQuery = '';
  58. function onInputChange() {
  59. let query = ac.$inputEl.val().trim();
  60. if (!ac.params.source) return;
  61. ac.params.source.call(ac, query, items => {
  62. let itemsHTML = '';
  63. const limit = ac.params.limit ? Math.min(ac.params.limit, items.length) : items.length;
  64. ac.items = items;
  65. let regExp;
  66. if (ac.params.highlightMatches) {
  67. query = query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
  68. regExp = new RegExp(`(${query})`, 'i');
  69. }
  70. let firstValue;
  71. let firstItem;
  72. for (let i = 0; i < limit; i += 1) {
  73. const itemValue = typeof items[i] === 'object' ? items[i][ac.params.valueProperty] : items[i];
  74. const itemText = typeof items[i] === 'object' ? items[i][ac.params.textProperty] : items[i];
  75. if (i === 0) {
  76. firstValue = itemValue;
  77. firstItem = ac.items[i];
  78. }
  79. itemsHTML += ac.renderItem({
  80. value: itemValue,
  81. text: ac.params.highlightMatches ? itemText.replace(regExp, '<b>$1</b>') : itemText
  82. }, i);
  83. }
  84. if (itemsHTML === '' && query === '' && ac.params.dropdownPlaceholderText) {
  85. itemsHTML += ac.renderItem({
  86. placeholder: true,
  87. text: ac.params.dropdownPlaceholderText
  88. });
  89. }
  90. ac.$dropdownEl.find('ul').html(itemsHTML);
  91. if (ac.params.typeahead) {
  92. if (!firstValue || !firstItem) {
  93. return;
  94. }
  95. if (firstValue.toLowerCase().indexOf(query.toLowerCase()) !== 0) {
  96. return;
  97. }
  98. if (previousQuery.toLowerCase() === query.toLowerCase()) {
  99. ac.value = [];
  100. return;
  101. }
  102. if (previousQuery.toLowerCase().indexOf(query.toLowerCase()) === 0) {
  103. previousQuery = query;
  104. ac.value = [];
  105. return;
  106. }
  107. $inputEl.val(firstValue);
  108. $inputEl[0].setSelectionRange(query.length, firstValue.length);
  109. const previousValue = typeof ac.value[0] === 'object' ? ac.value[0][ac.params.valueProperty] : ac.value[0];
  110. if (!previousValue || firstValue.toLowerCase() !== previousValue.toLowerCase()) {
  111. ac.value = [firstItem];
  112. ac.emit('local::change autocompleteChange', [firstItem]);
  113. }
  114. }
  115. previousQuery = query;
  116. });
  117. }
  118. function onPageInputChange() {
  119. const inputEl = this;
  120. const value = inputEl.value;
  121. const isValues = $(inputEl).parents('.autocomplete-values').length > 0;
  122. let item;
  123. let itemValue;
  124. let aValue;
  125. if (isValues) {
  126. if (ac.inputType === 'checkbox' && !inputEl.checked) {
  127. for (let i = 0; i < ac.value.length; i += 1) {
  128. aValue = typeof ac.value[i] === 'string' ? ac.value[i] : ac.value[i][ac.params.valueProperty];
  129. if (aValue === value || aValue * 1 === value * 1) {
  130. ac.value.splice(i, 1);
  131. }
  132. }
  133. ac.updateValues();
  134. ac.emit('local::change autocompleteChange', ac.value);
  135. }
  136. return;
  137. } // Find Related Item
  138. for (let i = 0; i < ac.items.length; i += 1) {
  139. itemValue = typeof ac.items[i] === 'object' ? ac.items[i][ac.params.valueProperty] : ac.items[i];
  140. if (itemValue === value || itemValue * 1 === value * 1) item = ac.items[i];
  141. }
  142. if (ac.inputType === 'radio') {
  143. ac.value = [item];
  144. } else if (inputEl.checked) {
  145. ac.value.push(item);
  146. } else {
  147. for (let i = 0; i < ac.value.length; i += 1) {
  148. aValue = typeof ac.value[i] === 'object' ? ac.value[i][ac.params.valueProperty] : ac.value[i];
  149. if (aValue === value || aValue * 1 === value * 1) {
  150. ac.value.splice(i, 1);
  151. }
  152. }
  153. } // Update Values Block
  154. ac.updateValues(); // On Select Callback
  155. if (ac.inputType === 'radio' && inputEl.checked || ac.inputType === 'checkbox') {
  156. ac.emit('local::change autocompleteChange', ac.value);
  157. }
  158. }
  159. function onHtmlClick(e) {
  160. const $targetEl = $(e.target);
  161. if ($targetEl.is(ac.$inputEl[0]) || ac.$dropdownEl && $targetEl.closest(ac.$dropdownEl[0]).length) return;
  162. ac.close();
  163. }
  164. function onOpenerClick() {
  165. ac.open();
  166. }
  167. function onInputFocus() {
  168. ac.open();
  169. }
  170. function onInputBlur() {
  171. if (ac.$dropdownEl.find('label.active-state').length > 0) return;
  172. setTimeout(() => {
  173. ac.close();
  174. }, 0);
  175. }
  176. function onResize() {
  177. ac.positionDropdown();
  178. }
  179. function onKeyDown(e) {
  180. if (!ac.opened) return;
  181. if (e.keyCode === 27) {
  182. // ESC
  183. e.preventDefault();
  184. ac.$inputEl.blur();
  185. return;
  186. }
  187. if (e.keyCode === 13) {
  188. // Enter
  189. const $selectedItemLabel = ac.$dropdownEl.find('.autocomplete-dropdown-selected label');
  190. if ($selectedItemLabel.length) {
  191. e.preventDefault();
  192. $selectedItemLabel.trigger('click');
  193. ac.$inputEl.blur();
  194. return;
  195. }
  196. if (ac.params.typeahead) {
  197. e.preventDefault();
  198. ac.$inputEl.blur();
  199. }
  200. return;
  201. }
  202. if (e.keyCode !== 40 && e.keyCode !== 38) return;
  203. e.preventDefault();
  204. const $selectedItem = ac.$dropdownEl.find('.autocomplete-dropdown-selected');
  205. let $newItem;
  206. if ($selectedItem.length) {
  207. $newItem = $selectedItem[e.keyCode === 40 ? 'next' : 'prev']('li');
  208. if (!$newItem.length) {
  209. $newItem = ac.$dropdownEl.find('li').eq(e.keyCode === 40 ? 0 : ac.$dropdownEl.find('li').length - 1);
  210. }
  211. } else {
  212. $newItem = ac.$dropdownEl.find('li').eq(e.keyCode === 40 ? 0 : ac.$dropdownEl.find('li').length - 1);
  213. }
  214. if ($newItem.hasClass('autocomplete-dropdown-placeholder')) return;
  215. $selectedItem.removeClass('autocomplete-dropdown-selected');
  216. $newItem.addClass('autocomplete-dropdown-selected');
  217. }
  218. function onDropdownClick() {
  219. const $clickedEl = $(this);
  220. let clickedItem;
  221. for (let i = 0; i < ac.items.length; i += 1) {
  222. const itemValue = typeof ac.items[i] === 'object' ? ac.items[i][ac.params.valueProperty] : ac.items[i];
  223. const value = $clickedEl.attr('data-value');
  224. if (itemValue === value || itemValue * 1 === value * 1) {
  225. clickedItem = ac.items[i];
  226. }
  227. }
  228. if (ac.params.updateInputValueOnSelect) {
  229. ac.$inputEl.val(typeof clickedItem === 'object' ? clickedItem[ac.params.valueProperty] : clickedItem);
  230. ac.$inputEl.trigger('input change');
  231. }
  232. ac.value = [clickedItem];
  233. ac.emit('local::change autocompleteChange', [clickedItem]);
  234. ac.close();
  235. }
  236. ac.attachEvents = function attachEvents() {
  237. if (ac.params.openIn !== 'dropdown' && ac.$openerEl) {
  238. ac.$openerEl.on('click', onOpenerClick);
  239. }
  240. if (ac.params.openIn === 'dropdown' && ac.$inputEl) {
  241. ac.$inputEl.on('focus', onInputFocus);
  242. ac.$inputEl.on(ac.params.inputEvents, onInputChange);
  243. if (device.android) {
  244. $('html').on('click', onHtmlClick);
  245. } else {
  246. ac.$inputEl.on('blur', onInputBlur);
  247. }
  248. ac.$inputEl.on('keydown', onKeyDown);
  249. }
  250. };
  251. ac.detachEvents = function attachEvents() {
  252. if (ac.params.openIn !== 'dropdown' && ac.$openerEl) {
  253. ac.$openerEl.off('click', onOpenerClick);
  254. }
  255. if (ac.params.openIn === 'dropdown' && ac.$inputEl) {
  256. ac.$inputEl.off('focus', onInputFocus);
  257. ac.$inputEl.off(ac.params.inputEvents, onInputChange);
  258. if (device.android) {
  259. $('html').off('click', onHtmlClick);
  260. } else {
  261. ac.$inputEl.off('blur', onInputBlur);
  262. }
  263. ac.$inputEl.off('keydown', onKeyDown);
  264. }
  265. };
  266. ac.attachDropdownEvents = function attachDropdownEvents() {
  267. ac.$dropdownEl.on('click', 'label', onDropdownClick);
  268. app.on('resize', onResize);
  269. };
  270. ac.detachDropdownEvents = function detachDropdownEvents() {
  271. ac.$dropdownEl.off('click', 'label', onDropdownClick);
  272. app.off('resize', onResize);
  273. };
  274. ac.attachPageEvents = function attachPageEvents() {
  275. ac.$el.on('change', 'input[type="radio"], input[type="checkbox"]', onPageInputChange);
  276. if (ac.params.closeOnSelect && !ac.params.multiple) {
  277. ac.$el.once('click', '.list label', () => {
  278. nextTick(() => {
  279. ac.close();
  280. });
  281. });
  282. }
  283. };
  284. ac.detachPageEvents = function detachPageEvents() {
  285. ac.$el.off('change', 'input[type="radio"], input[type="checkbox"]', onPageInputChange);
  286. }; // Install Modules
  287. ac.useModules(); // Init
  288. ac.init();
  289. return ac;
  290. }
  291. get view() {
  292. const ac = this;
  293. const {
  294. $openerEl,
  295. $inputEl,
  296. app
  297. } = ac;
  298. let view;
  299. if (ac.params.view) {
  300. view = ac.params.view;
  301. } else if ($openerEl || $inputEl) {
  302. const $el = $openerEl || $inputEl;
  303. view = $el.closest('.view').length && $el.closest('.view')[0].f7View;
  304. }
  305. if (!view) view = app.views.main;
  306. return view;
  307. }
  308. positionDropdown() {
  309. const ac = this;
  310. const {
  311. $inputEl,
  312. app,
  313. $dropdownEl
  314. } = ac;
  315. const $pageContentEl = $inputEl.parents('.page-content');
  316. if ($pageContentEl.length === 0) return;
  317. const inputOffset = $inputEl.offset();
  318. const inputOffsetWidth = $inputEl[0].offsetWidth;
  319. const inputOffsetHeight = $inputEl[0].offsetHeight;
  320. const $listEl = $inputEl.parents('.list');
  321. let $listParent;
  322. $listEl.parents().each(parentEl => {
  323. if ($listParent) return;
  324. const $parentEl = $(parentEl);
  325. if ($parentEl.parent($pageContentEl).length) $listParent = $parentEl;
  326. });
  327. const listOffset = $listEl.offset();
  328. const paddingBottom = parseInt($pageContentEl.css('padding-bottom'), 10);
  329. const listOffsetLeft = $listEl.length > 0 ? listOffset.left - $pageContentEl.offset().left : 0;
  330. const inputOffsetLeft = inputOffset.left - ($listEl.length > 0 ? listOffset.left : 0) - (app.rtl ? 0 : 0);
  331. const inputOffsetTop = inputOffset.top - ($pageContentEl.offset().top - $pageContentEl[0].scrollTop);
  332. const maxHeight = $pageContentEl[0].scrollHeight - paddingBottom - (inputOffsetTop + $pageContentEl[0].scrollTop) - $inputEl[0].offsetHeight;
  333. const paddingProp = app.rtl ? 'padding-right' : 'padding-left';
  334. let paddingValue;
  335. if ($listEl.length && !ac.params.expandInput) {
  336. paddingValue = (app.rtl ? $listEl[0].offsetWidth - inputOffsetLeft - inputOffsetWidth : inputOffsetLeft) - (app.theme === 'md' ? 16 : 15);
  337. }
  338. $dropdownEl.css({
  339. left: `${$listEl.length > 0 ? listOffsetLeft : inputOffsetLeft}px`,
  340. top: `${inputOffsetTop + $pageContentEl[0].scrollTop + inputOffsetHeight}px`,
  341. width: `${$listEl.length > 0 ? $listEl[0].offsetWidth : inputOffsetWidth}px`
  342. });
  343. $dropdownEl.children('.autocomplete-dropdown-inner').css({
  344. maxHeight: `${maxHeight}px`,
  345. [paddingProp]: $listEl.length > 0 && !ac.params.expandInput ? `${paddingValue}px` : ''
  346. });
  347. }
  348. focus() {
  349. const ac = this;
  350. ac.$el.find('input[type=search]').focus();
  351. }
  352. source(query) {
  353. const ac = this;
  354. if (!ac.params.source) return;
  355. const {
  356. $el
  357. } = ac;
  358. ac.params.source.call(ac, query, items => {
  359. let itemsHTML = '';
  360. const limit = ac.params.limit ? Math.min(ac.params.limit, items.length) : items.length;
  361. ac.items = items;
  362. for (let i = 0; i < limit; i += 1) {
  363. let selected = false;
  364. const itemValue = typeof items[i] === 'object' ? items[i][ac.params.valueProperty] : items[i];
  365. for (let j = 0; j < ac.value.length; j += 1) {
  366. const aValue = typeof ac.value[j] === 'object' ? ac.value[j][ac.params.valueProperty] : ac.value[j];
  367. if (aValue === itemValue || aValue * 1 === itemValue * 1) selected = true;
  368. }
  369. itemsHTML += ac.renderItem({
  370. value: itemValue,
  371. text: typeof items[i] === 'object' ? items[i][ac.params.textProperty] : items[i],
  372. inputType: ac.inputType,
  373. id: ac.id,
  374. inputName: ac.inputName,
  375. selected
  376. }, i);
  377. }
  378. $el.find('.autocomplete-found ul').html(itemsHTML);
  379. if (items.length === 0) {
  380. if (query.length !== 0) {
  381. $el.find('.autocomplete-not-found').show();
  382. $el.find('.autocomplete-found, .autocomplete-values').hide();
  383. } else {
  384. $el.find('.autocomplete-values').show();
  385. $el.find('.autocomplete-found, .autocomplete-not-found').hide();
  386. }
  387. } else {
  388. $el.find('.autocomplete-found').show();
  389. $el.find('.autocomplete-not-found, .autocomplete-values').hide();
  390. }
  391. });
  392. }
  393. updateValues() {
  394. const ac = this;
  395. let valuesHTML = '';
  396. for (let i = 0; i < ac.value.length; i += 1) {
  397. valuesHTML += ac.renderItem({
  398. value: typeof ac.value[i] === 'object' ? ac.value[i][ac.params.valueProperty] : ac.value[i],
  399. text: typeof ac.value[i] === 'object' ? ac.value[i][ac.params.textProperty] : ac.value[i],
  400. inputType: ac.inputType,
  401. id: ac.id,
  402. inputName: `${ac.inputName}-checked}`,
  403. selected: true
  404. }, i);
  405. }
  406. ac.$el.find('.autocomplete-values ul').html(valuesHTML);
  407. }
  408. preloaderHide() {
  409. const ac = this;
  410. if (ac.params.openIn === 'dropdown' && ac.$dropdownEl) {
  411. ac.$dropdownEl.find('.autocomplete-preloader').removeClass('autocomplete-preloader-visible');
  412. } else {
  413. $('.autocomplete-preloader').removeClass('autocomplete-preloader-visible');
  414. }
  415. }
  416. preloaderShow() {
  417. const ac = this;
  418. if (ac.params.openIn === 'dropdown' && ac.$dropdownEl) {
  419. ac.$dropdownEl.find('.autocomplete-preloader').addClass('autocomplete-preloader-visible');
  420. } else {
  421. $('.autocomplete-preloader').addClass('autocomplete-preloader-visible');
  422. }
  423. }
  424. renderPreloader() {
  425. const ac = this;
  426. const preloaders = {
  427. iosPreloaderContent,
  428. mdPreloaderContent,
  429. auroraPreloaderContent
  430. };
  431. return $jsx("div", {
  432. class: `autocomplete-preloader preloader ${ac.params.preloaderColor ? `color-${ac.params.preloaderColor}` : ''}`
  433. }, preloaders[`${ac.app.theme}PreloaderContent`] || '');
  434. }
  435. renderSearchbar() {
  436. const ac = this;
  437. if (ac.params.renderSearchbar) return ac.params.renderSearchbar.call(ac);
  438. return $jsx("form", {
  439. class: "searchbar"
  440. }, $jsx("div", {
  441. class: "searchbar-inner"
  442. }, $jsx("div", {
  443. class: "searchbar-input-wrap"
  444. }, $jsx("input", {
  445. type: "search",
  446. spellcheck: ac.params.searchbarSpellcheck || 'false',
  447. placeholder: ac.params.searchbarPlaceholder
  448. }), $jsx("i", {
  449. class: "searchbar-icon"
  450. }), $jsx("span", {
  451. class: "input-clear-button"
  452. })), ac.params.searchbarDisableButton && $jsx("span", {
  453. class: "searchbar-disable-button"
  454. }, ac.params.searchbarDisableText)));
  455. }
  456. renderItem(item, index) {
  457. const ac = this;
  458. if (ac.params.renderItem) return ac.params.renderItem.call(ac, item, index);
  459. const itemValue = item.value && typeof item.value === 'string' ? item.value.replace(/"/g, '&quot;') : item.value;
  460. if (ac.params.openIn !== 'dropdown') {
  461. return $jsx("li", null, $jsx("label", {
  462. class: `item-${item.inputType} item-content`
  463. }, $jsx("input", {
  464. type: item.inputType,
  465. name: item.inputName,
  466. value: itemValue,
  467. _checked: item.selected
  468. }), $jsx("i", {
  469. class: `icon icon-${item.inputType}`
  470. }), $jsx("div", {
  471. class: "item-inner"
  472. }, $jsx("div", {
  473. class: "item-title"
  474. }, item.text))));
  475. } // Dropdown
  476. if (!item.placeholder) {
  477. return $jsx("li", null, $jsx("label", {
  478. class: "item-radio item-content",
  479. "data-value": itemValue
  480. }, $jsx("div", {
  481. class: "item-inner"
  482. }, $jsx("div", {
  483. class: "item-title"
  484. }, item.text))));
  485. } // Dropwdown placeholder
  486. return $jsx("li", {
  487. class: "autocomplete-dropdown-placeholder"
  488. }, $jsx("label", {
  489. class: "item-content"
  490. }, $jsx("div", {
  491. class: "item-inner"
  492. }, $jsx("div", {
  493. class: "item-title"
  494. }, item.text))));
  495. }
  496. renderNavbar() {
  497. const ac = this;
  498. if (ac.params.renderNavbar) return ac.params.renderNavbar.call(ac);
  499. let pageTitle = ac.params.pageTitle;
  500. if (typeof pageTitle === 'undefined' && ac.$openerEl && ac.$openerEl.length) {
  501. pageTitle = ac.$openerEl.find('.item-title').text().trim();
  502. }
  503. const inPopup = ac.params.openIn === 'popup'; // eslint-disable-next-line
  504. const navbarLeft = inPopup ? ac.params.preloader && $jsx("div", {
  505. class: "left"
  506. }, ac.renderPreloader()) : $jsx("div", {
  507. class: "left sliding"
  508. }, $jsx("a", {
  509. class: "link back"
  510. }, $jsx("i", {
  511. class: "icon icon-back"
  512. }), $jsx("span", {
  513. class: "if-not-md"
  514. }, ac.params.pageBackLinkText)));
  515. const navbarRight = inPopup ? $jsx("div", {
  516. class: "right"
  517. }, $jsx("a", {
  518. class: "link popup-close",
  519. "data-popup": ".autocomplete-popup"
  520. }, ac.params.popupCloseLinkText)) : ac.params.preloader && $jsx("div", {
  521. class: "right"
  522. }, ac.renderPreloader());
  523. return $jsx("div", {
  524. class: `navbar ${ac.params.navbarColorTheme ? `color-${ac.params.navbarColorTheme}` : ''}`
  525. }, $jsx("div", {
  526. class: "navbar-bg"
  527. }), $jsx("div", {
  528. class: `navbar-inner ${ac.params.navbarColorTheme ? `color-${ac.params.navbarColorTheme}` : ''}`
  529. }, navbarLeft, pageTitle && $jsx("div", {
  530. class: "title sliding"
  531. }, pageTitle), navbarRight, $jsx("div", {
  532. class: "subnavbar sliding"
  533. }, ac.renderSearchbar())));
  534. }
  535. renderDropdown() {
  536. const ac = this;
  537. if (ac.params.renderDropdown) return ac.params.renderDropdown.call(ac, ac.items);
  538. return $jsx("div", {
  539. class: "autocomplete-dropdown"
  540. }, $jsx("div", {
  541. class: "autocomplete-dropdown-inner"
  542. }, $jsx("div", {
  543. class: `list ${!ac.params.expandInput ? 'no-safe-areas' : ''}`
  544. }, $jsx("ul", null))), ac.params.preloader && ac.renderPreloader());
  545. }
  546. renderPage(inPopup) {
  547. const ac = this;
  548. if (ac.params.renderPage) return ac.params.renderPage.call(ac, ac.items);
  549. return $jsx("div", {
  550. class: "page page-with-subnavbar autocomplete-page",
  551. "data-name": "autocomplete-page"
  552. }, ac.renderNavbar(inPopup), $jsx("div", {
  553. class: "searchbar-backdrop"
  554. }), $jsx("div", {
  555. class: "page-content"
  556. }, $jsx("div", {
  557. class: `list autocomplete-list autocomplete-found autocomplete-list-${ac.id} ${ac.params.formColorTheme ? `color-${ac.params.formColorTheme}` : ''}`
  558. }, $jsx("ul", null)), $jsx("div", {
  559. class: "list autocomplete-not-found"
  560. }, $jsx("ul", null, $jsx("li", {
  561. class: "item-content"
  562. }, $jsx("div", {
  563. class: "item-inner"
  564. }, $jsx("div", {
  565. class: "item-title"
  566. }, ac.params.notFoundText))))), $jsx("div", {
  567. class: "list autocomplete-values"
  568. }, $jsx("ul", null))));
  569. }
  570. renderPopup() {
  571. const ac = this;
  572. if (ac.params.renderPopup) return ac.params.renderPopup.call(ac, ac.items);
  573. return $jsx("div", {
  574. class: "popup autocomplete-popup"
  575. }, $jsx("div", {
  576. class: "view"
  577. }, ac.renderPage(true), ";"));
  578. }
  579. onOpen(type, el) {
  580. const ac = this;
  581. const app = ac.app;
  582. const $el = $(el);
  583. ac.$el = $el;
  584. ac.el = $el[0];
  585. ac.openedIn = type;
  586. ac.opened = true;
  587. if (ac.params.openIn === 'dropdown') {
  588. ac.attachDropdownEvents();
  589. ac.$dropdownEl.addClass('autocomplete-dropdown-in');
  590. ac.$inputEl.trigger('input');
  591. } else {
  592. // Init SB
  593. let $searchbarEl = $el.find('.searchbar');
  594. if (ac.params.openIn === 'page' && app.theme === 'ios' && $searchbarEl.length === 0) {
  595. $searchbarEl = $(app.navbar.getElByPage($el)).find('.searchbar');
  596. }
  597. ac.searchbar = app.searchbar.create({
  598. el: $searchbarEl,
  599. backdropEl: $el.find('.searchbar-backdrop'),
  600. customSearch: true,
  601. on: {
  602. search(sb, query) {
  603. if (query.length === 0 && ac.searchbar.enabled) {
  604. ac.searchbar.backdropShow();
  605. } else {
  606. ac.searchbar.backdropHide();
  607. }
  608. ac.source(query);
  609. }
  610. }
  611. }); // Attach page events
  612. ac.attachPageEvents(); // Update Values On Page Init
  613. ac.updateValues(); // Source on load
  614. if (ac.params.requestSourceOnOpen) ac.source('');
  615. }
  616. ac.emit('local::open autocompleteOpen', ac);
  617. }
  618. autoFocus() {
  619. const ac = this;
  620. if (ac.searchbar && ac.searchbar.$inputEl) {
  621. ac.searchbar.$inputEl.focus();
  622. }
  623. return ac;
  624. }
  625. onOpened() {
  626. const ac = this;
  627. if (ac.params.openIn !== 'dropdown' && ac.params.autoFocus) {
  628. ac.autoFocus();
  629. }
  630. ac.emit('local::opened autocompleteOpened', ac);
  631. }
  632. onClose() {
  633. const ac = this;
  634. if (ac.destroyed) return; // Destroy SB
  635. if (ac.searchbar && ac.searchbar.destroy) {
  636. ac.searchbar.destroy();
  637. ac.searchbar = null;
  638. delete ac.searchbar;
  639. }
  640. if (ac.params.openIn === 'dropdown') {
  641. ac.detachDropdownEvents();
  642. ac.$dropdownEl.removeClass('autocomplete-dropdown-in').remove();
  643. ac.$inputEl.parents('.item-content-dropdown-expanded').removeClass('item-content-dropdown-expanded');
  644. } else {
  645. ac.detachPageEvents();
  646. }
  647. ac.emit('local::close autocompleteClose', ac);
  648. }
  649. onClosed() {
  650. const ac = this;
  651. if (ac.destroyed) return;
  652. ac.opened = false;
  653. ac.$el = null;
  654. ac.el = null;
  655. delete ac.$el;
  656. delete ac.el;
  657. ac.emit('local::closed autocompleteClosed', ac);
  658. }
  659. openPage() {
  660. const ac = this;
  661. if (ac.opened) return ac;
  662. const pageHtml = ac.renderPage();
  663. ac.view.router.navigate({
  664. url: ac.url,
  665. route: {
  666. content: pageHtml,
  667. path: ac.url,
  668. on: {
  669. pageBeforeIn(e, page) {
  670. ac.onOpen('page', page.el);
  671. },
  672. pageAfterIn(e, page) {
  673. ac.onOpened('page', page.el);
  674. },
  675. pageBeforeOut(e, page) {
  676. ac.onClose('page', page.el);
  677. },
  678. pageAfterOut(e, page) {
  679. ac.onClosed('page', page.el);
  680. }
  681. },
  682. options: {
  683. animate: ac.params.animate
  684. }
  685. }
  686. });
  687. return ac;
  688. }
  689. openPopup() {
  690. const ac = this;
  691. if (ac.opened) return ac;
  692. const popupHtml = ac.renderPopup();
  693. const popupParams = {
  694. content: popupHtml,
  695. animate: ac.params.animate,
  696. push: ac.params.popupPush,
  697. swipeToClose: ac.params.popupSwipeToClose,
  698. on: {
  699. popupOpen(popup) {
  700. ac.onOpen('popup', popup.el);
  701. },
  702. popupOpened(popup) {
  703. ac.onOpened('popup', popup.el);
  704. },
  705. popupClose(popup) {
  706. ac.onClose('popup', popup.el);
  707. },
  708. popupClosed(popup) {
  709. ac.onClosed('popup', popup.el);
  710. }
  711. }
  712. };
  713. if (ac.params.routableModals && ac.view) {
  714. ac.view.router.navigate({
  715. url: ac.url,
  716. route: {
  717. path: ac.url,
  718. popup: popupParams
  719. }
  720. });
  721. } else {
  722. ac.modal = ac.app.popup.create(popupParams).open(ac.params.animate);
  723. }
  724. return ac;
  725. }
  726. openDropdown() {
  727. const ac = this;
  728. if (!ac.$dropdownEl) {
  729. ac.$dropdownEl = $(ac.renderDropdown());
  730. }
  731. const $listEl = ac.$inputEl.parents('.list');
  732. if ($listEl.length && ac.$inputEl.parents('.item-content').length > 0 && ac.params.expandInput) {
  733. ac.$inputEl.parents('.item-content').addClass('item-content-dropdown-expanded');
  734. }
  735. const $pageContentEl = ac.$inputEl.parents('.page-content');
  736. if (ac.params.dropdownContainerEl) {
  737. $(ac.params.dropdownContainerEl).append(ac.$dropdownEl);
  738. } else if ($pageContentEl.length === 0) {
  739. ac.$dropdownEl.insertAfter(ac.$inputEl);
  740. } else {
  741. ac.positionDropdown();
  742. $pageContentEl.append(ac.$dropdownEl);
  743. }
  744. ac.onOpen('dropdown', ac.$dropdownEl);
  745. ac.onOpened('dropdown', ac.$dropdownEl);
  746. }
  747. open() {
  748. const ac = this;
  749. if (ac.opened) return ac;
  750. const openIn = ac.params.openIn;
  751. ac[`open${openIn.split('').map((el, index) => {
  752. if (index === 0) return el.toUpperCase();
  753. return el;
  754. }).join('')}`]();
  755. return ac;
  756. }
  757. close() {
  758. const ac = this;
  759. if (!ac.opened) return ac;
  760. if (ac.params.openIn === 'dropdown') {
  761. ac.onClose();
  762. ac.onClosed();
  763. } else if (ac.params.routableModals && ac.view || ac.openedIn === 'page') {
  764. ac.view.router.back({
  765. animate: ac.params.animate
  766. });
  767. } else {
  768. ac.modal.once('modalClosed', () => {
  769. nextTick(() => {
  770. if (ac.destroyed) return;
  771. ac.modal.destroy();
  772. delete ac.modal;
  773. });
  774. });
  775. ac.modal.close();
  776. }
  777. return ac;
  778. }
  779. init() {
  780. const ac = this;
  781. ac.attachEvents();
  782. }
  783. destroy() {
  784. const ac = this;
  785. ac.emit('local::beforeDestroy autocompleteBeforeDestroy', ac);
  786. ac.detachEvents();
  787. if (ac.$inputEl && ac.$inputEl[0]) {
  788. delete ac.$inputEl[0].f7Autocomplete;
  789. }
  790. if (ac.$openerEl && ac.$openerEl[0]) {
  791. delete ac.$openerEl[0].f7Autocomplete;
  792. }
  793. deleteProps(ac);
  794. ac.destroyed = true;
  795. }
  796. }
  797. export default Autocomplete;