ion-searchbar.entry.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { r as registerInstance, c as createEvent, i as forceUpdate, h, e as Host, f as getElement } from './index-527b9e34.js';
  5. import { e as debounceEvent, h as inheritAttributes, c as componentOnReady, r as raf } from './helpers-d94bc8ad.js';
  6. import { i as isRTL } from './dir-babeabeb.js';
  7. import { c as createColorClasses } from './theme-01f3f29c.js';
  8. import { a as arrowBackSharp, b as closeCircle, d as closeSharp, s as searchOutline, e as searchSharp } from './index-e2cf2ceb.js';
  9. import { c as config } from './index-cfd9c1f2.js';
  10. import { b as getIonMode } from './ionic-global-b26f573e.js';
  11. const searchbarIosCss = ".sc-ion-searchbar-ios-h{--placeholder-color:initial;--placeholder-font-style:initial;--placeholder-font-weight:initial;--placeholder-opacity:var(--ion-placeholder-opacity, 0.6);-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;width:100%;color:var(--color);font-family:var(--ion-font-family, inherit);-webkit-box-sizing:border-box;box-sizing:border-box}.ion-color.sc-ion-searchbar-ios-h{color:var(--ion-color-contrast)}.ion-color.sc-ion-searchbar-ios-h .searchbar-input.sc-ion-searchbar-ios{background:var(--ion-color-base)}.ion-color.sc-ion-searchbar-ios-h .searchbar-clear-button.sc-ion-searchbar-ios,.ion-color.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios,.ion-color.sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios{color:inherit}.searchbar-search-icon.sc-ion-searchbar-ios{color:var(--icon-color);pointer-events:none}.searchbar-input-container.sc-ion-searchbar-ios{display:block;position:relative;-ms-flex-negative:1;flex-shrink:1;width:100%}.searchbar-input.sc-ion-searchbar-ios{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;border-radius:var(--border-radius);display:block;width:100%;min-height:inherit;border:0;outline:none;background:var(--background);font-family:inherit;-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-input.sc-ion-searchbar-ios::-webkit-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-ios::-moz-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-ios:-ms-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-ios::-ms-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-ios::placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-ios::-webkit-search-cancel-button,.searchbar-input.sc-ion-searchbar-ios::-ms-clear{display:none}.searchbar-cancel-button.sc-ion-searchbar-ios{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;display:none;height:100%;border:0;outline:none;color:var(--cancel-button-color);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-cancel-button.sc-ion-searchbar-ios>div.sc-ion-searchbar-ios{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%}.searchbar-clear-button.sc-ion-searchbar-ios{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;display:none;min-height:0;outline:none;color:var(--clear-button-color);-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-clear-button.sc-ion-searchbar-ios:focus{opacity:0.5}.searchbar-has-value.searchbar-should-show-clear.sc-ion-searchbar-ios-h .searchbar-clear-button.sc-ion-searchbar-ios{display:block}.searchbar-disabled.sc-ion-searchbar-ios-h{cursor:default;opacity:0.4;pointer-events:none}.sc-ion-searchbar-ios-h{--background:rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.07);--border-radius:10px;--box-shadow:none;--cancel-button-color:var(--ion-color-primary, #0054e9);--clear-button-color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666));--color:var(--ion-text-color, #000);--icon-color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666));-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px;padding-top:12px;padding-bottom:12px;min-height:60px;contain:content}.searchbar-input-container.sc-ion-searchbar-ios{min-height:36px}.searchbar-search-icon.sc-ion-searchbar-ios{-webkit-margin-start:calc(50% - 60px);margin-inline-start:calc(50% - 60px);top:0;position:absolute;width:1.375rem;height:100%;contain:strict}.searchbar-search-icon.sc-ion-searchbar-ios{inset-inline-start:5px}.searchbar-input.sc-ion-searchbar-ios{-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0px;padding-inline-end:0px;padding-top:6px;padding-bottom:6px;height:100%;font-size:1.0625rem;font-weight:400;contain:strict}.searchbar-has-value.searchbar-should-show-clear.sc-ion-searchbar-ios-h .searchbar-input.sc-ion-searchbar-ios{-webkit-padding-start:1.75rem;padding-inline-start:1.75rem;-webkit-padding-end:1.75rem;padding-inline-end:1.75rem}.searchbar-clear-button.sc-ion-searchbar-ios{top:0;background-position:center;position:absolute;width:1.875rem;height:100%;border:0;background-color:transparent}.searchbar-clear-button.sc-ion-searchbar-ios{inset-inline-end:0}.searchbar-clear-icon.sc-ion-searchbar-ios{width:1.125rem;height:100%}.searchbar-cancel-button.sc-ion-searchbar-ios{-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:0;padding-inline-end:0;padding-top:0;padding-bottom:0;-ms-flex-negative:0;flex-shrink:0;background-color:transparent;font-size:17px}.searchbar-left-aligned.sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios{-webkit-margin-start:0;margin-inline-start:0}.searchbar-left-aligned.sc-ion-searchbar-ios-h .searchbar-input.sc-ion-searchbar-ios{-webkit-padding-start:1.875rem;padding-inline-start:1.875rem}.searchbar-has-focus.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios,.searchbar-should-show-cancel.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios,.searchbar-animated.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios{display:block}.searchbar-animated.sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios,.searchbar-animated.sc-ion-searchbar-ios-h .searchbar-input.sc-ion-searchbar-ios{-webkit-transition:all 300ms ease;transition:all 300ms ease}.searchbar-animated.searchbar-has-focus.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios,.searchbar-animated.searchbar-should-show-cancel.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios{opacity:1;pointer-events:auto}.searchbar-animated.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios{-webkit-margin-end:-100%;margin-inline-end:-100%;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);-webkit-transition:all 300ms ease;transition:all 300ms ease;opacity:0;pointer-events:none}.searchbar-no-animate.sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios,.searchbar-no-animate.sc-ion-searchbar-ios-h .searchbar-input.sc-ion-searchbar-ios,.searchbar-no-animate.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios{-webkit-transition-duration:0ms;transition-duration:0ms}.ion-color.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios{color:var(--ion-color-base)}@media (any-hover: hover){.ion-color.sc-ion-searchbar-ios-h .searchbar-cancel-button.sc-ion-searchbar-ios:hover{color:var(--ion-color-tint)}}ion-toolbar.sc-ion-searchbar-ios-h,ion-toolbar .sc-ion-searchbar-ios-h{padding-top:1px;padding-bottom:15px;min-height:52px}ion-toolbar.ion-color.sc-ion-searchbar-ios-h:not(.ion-color),ion-toolbar.ion-color .sc-ion-searchbar-ios-h:not(.ion-color){color:inherit}ion-toolbar.ion-color.sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-cancel-button.sc-ion-searchbar-ios,ion-toolbar.ion-color .sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-cancel-button.sc-ion-searchbar-ios{color:currentColor}ion-toolbar.ion-color.sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios,ion-toolbar.ion-color .sc-ion-searchbar-ios-h .searchbar-search-icon.sc-ion-searchbar-ios{color:currentColor;opacity:0.5}ion-toolbar.ion-color.sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-input.sc-ion-searchbar-ios,ion-toolbar.ion-color .sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-input.sc-ion-searchbar-ios{background:rgba(var(--ion-color-contrast-rgb), 0.07);color:currentColor}ion-toolbar.ion-color.sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-clear-button.sc-ion-searchbar-ios,ion-toolbar.ion-color .sc-ion-searchbar-ios-h:not(.ion-color) .searchbar-clear-button.sc-ion-searchbar-ios{color:currentColor;opacity:0.5}";
  12. const IonSearchbarIosStyle0 = searchbarIosCss;
  13. const searchbarMdCss = ".sc-ion-searchbar-md-h{--placeholder-color:initial;--placeholder-font-style:initial;--placeholder-font-weight:initial;--placeholder-opacity:var(--ion-placeholder-opacity, 0.6);-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:-ms-flexbox;display:flex;position:relative;-ms-flex-align:center;align-items:center;width:100%;color:var(--color);font-family:var(--ion-font-family, inherit);-webkit-box-sizing:border-box;box-sizing:border-box}.ion-color.sc-ion-searchbar-md-h{color:var(--ion-color-contrast)}.ion-color.sc-ion-searchbar-md-h .searchbar-input.sc-ion-searchbar-md{background:var(--ion-color-base)}.ion-color.sc-ion-searchbar-md-h .searchbar-clear-button.sc-ion-searchbar-md,.ion-color.sc-ion-searchbar-md-h .searchbar-cancel-button.sc-ion-searchbar-md,.ion-color.sc-ion-searchbar-md-h .searchbar-search-icon.sc-ion-searchbar-md{color:inherit}.searchbar-search-icon.sc-ion-searchbar-md{color:var(--icon-color);pointer-events:none}.searchbar-input-container.sc-ion-searchbar-md{display:block;position:relative;-ms-flex-negative:1;flex-shrink:1;width:100%}.searchbar-input.sc-ion-searchbar-md{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;border-radius:var(--border-radius);display:block;width:100%;min-height:inherit;border:0;outline:none;background:var(--background);font-family:inherit;-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-input.sc-ion-searchbar-md::-webkit-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-md::-moz-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-md:-ms-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-md::-ms-input-placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-md::placeholder{color:var(--placeholder-color);font-family:inherit;font-style:var(--placeholder-font-style);font-weight:var(--placeholder-font-weight);opacity:var(--placeholder-opacity)}.searchbar-input.sc-ion-searchbar-md::-webkit-search-cancel-button,.searchbar-input.sc-ion-searchbar-md::-ms-clear{display:none}.searchbar-cancel-button.sc-ion-searchbar-md{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;display:none;height:100%;border:0;outline:none;color:var(--cancel-button-color);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-cancel-button.sc-ion-searchbar-md>div.sc-ion-searchbar-md{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%}.searchbar-clear-button.sc-ion-searchbar-md{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;display:none;min-height:0;outline:none;color:var(--clear-button-color);-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbar-clear-button.sc-ion-searchbar-md:focus{opacity:0.5}.searchbar-has-value.searchbar-should-show-clear.sc-ion-searchbar-md-h .searchbar-clear-button.sc-ion-searchbar-md{display:block}.searchbar-disabled.sc-ion-searchbar-md-h{cursor:default;opacity:0.4;pointer-events:none}.sc-ion-searchbar-md-h{--background:var(--ion-background-color, #fff);--border-radius:2px;--box-shadow:0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);--cancel-button-color:var(--ion-color-step-900, var(--ion-text-color-step-100, #1a1a1a));--clear-button-color:initial;--color:var(--ion-color-step-850, var(--ion-text-color-step-150, #262626));--icon-color:var(--ion-color-step-600, var(--ion-text-color-step-400, #666666));-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px;padding-top:8px;padding-bottom:8px;background:inherit}.searchbar-search-icon.sc-ion-searchbar-md{top:11px;width:1.3125rem;height:1.3125rem}.searchbar-search-icon.sc-ion-searchbar-md{inset-inline-start:16px}.searchbar-cancel-button.sc-ion-searchbar-md{top:0;background-color:transparent;font-size:1.5em}.searchbar-cancel-button.sc-ion-searchbar-md{inset-inline-start:9px}.searchbar-search-icon.sc-ion-searchbar-md,.searchbar-cancel-button.sc-ion-searchbar-md{position:absolute}.searchbar-search-icon.ion-activated.sc-ion-searchbar-md,.searchbar-cancel-button.ion-activated.sc-ion-searchbar-md{background-color:transparent}.searchbar-input.sc-ion-searchbar-md{-webkit-padding-start:3.4375rem;padding-inline-start:3.4375rem;-webkit-padding-end:3.4375rem;padding-inline-end:3.4375rem;padding-top:0.375rem;padding-bottom:0.375rem;background-position:left 8px center;height:auto;font-size:1rem;font-weight:400;line-height:30px}[dir=rtl].sc-ion-searchbar-md-h .searchbar-input.sc-ion-searchbar-md,[dir=rtl] .sc-ion-searchbar-md-h .searchbar-input.sc-ion-searchbar-md{background-position:right 8px center}[dir=rtl].sc-ion-searchbar-md .searchbar-input.sc-ion-searchbar-md{background-position:right 8px center}@supports selector(:dir(rtl)){.searchbar-input.sc-ion-searchbar-md:dir(rtl){background-position:right 8px center}}.searchbar-clear-button.sc-ion-searchbar-md{top:0;padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;position:absolute;height:100%;border:0;background-color:transparent}.searchbar-clear-button.sc-ion-searchbar-md{inset-inline-end:13px}.searchbar-clear-button.ion-activated.sc-ion-searchbar-md{background-color:transparent}.searchbar-clear-icon.sc-ion-searchbar-md{width:1.375rem;height:100%}.searchbar-has-focus.sc-ion-searchbar-md-h .searchbar-search-icon.sc-ion-searchbar-md{display:block}.searchbar-has-focus.sc-ion-searchbar-md-h .searchbar-cancel-button.sc-ion-searchbar-md,.searchbar-should-show-cancel.sc-ion-searchbar-md-h .searchbar-cancel-button.sc-ion-searchbar-md{display:block}.searchbar-has-focus.sc-ion-searchbar-md-h .searchbar-cancel-button.sc-ion-searchbar-md+.searchbar-search-icon.sc-ion-searchbar-md,.searchbar-should-show-cancel.sc-ion-searchbar-md-h .searchbar-cancel-button.sc-ion-searchbar-md+.searchbar-search-icon.sc-ion-searchbar-md{display:none}ion-toolbar.sc-ion-searchbar-md-h,ion-toolbar .sc-ion-searchbar-md-h{-webkit-padding-start:7px;padding-inline-start:7px;-webkit-padding-end:7px;padding-inline-end:7px;padding-top:3px;padding-bottom:3px}";
  14. const IonSearchbarMdStyle0 = searchbarMdCss;
  15. const Searchbar = class {
  16. constructor(hostRef) {
  17. registerInstance(this, hostRef);
  18. this.ionInput = createEvent(this, "ionInput", 7);
  19. this.ionChange = createEvent(this, "ionChange", 7);
  20. this.ionCancel = createEvent(this, "ionCancel", 7);
  21. this.ionClear = createEvent(this, "ionClear", 7);
  22. this.ionBlur = createEvent(this, "ionBlur", 7);
  23. this.ionFocus = createEvent(this, "ionFocus", 7);
  24. this.ionStyle = createEvent(this, "ionStyle", 7);
  25. this.isCancelVisible = false;
  26. this.shouldAlignLeft = true;
  27. this.inputId = `ion-searchbar-${searchbarIds++}`;
  28. this.inheritedAttributes = {};
  29. /**
  30. * Clears the input field and triggers the control change.
  31. */
  32. this.onClearInput = async (shouldFocus) => {
  33. this.ionClear.emit();
  34. return new Promise((resolve) => {
  35. // setTimeout() fixes https://github.com/ionic-team/ionic-framework/issues/7527
  36. // wait for 4 frames
  37. setTimeout(() => {
  38. const value = this.getValue();
  39. if (value !== '') {
  40. this.value = '';
  41. this.emitInputChange();
  42. /**
  43. * When tapping clear button
  44. * ensure input is focused after
  45. * clearing input so users
  46. * can quickly start typing.
  47. */
  48. if (shouldFocus && !this.focused) {
  49. this.setFocus();
  50. /**
  51. * The setFocus call above will clear focusedValue,
  52. * but ionChange will never have gotten a chance to
  53. * fire. Manually revert focusedValue so onBlur can
  54. * compare against what was in the box before the clear.
  55. */
  56. this.focusedValue = value;
  57. }
  58. }
  59. resolve();
  60. }, 16 * 4);
  61. });
  62. };
  63. /**
  64. * Clears the input field and tells the input to blur since
  65. * the clearInput function doesn't want the input to blur
  66. * then calls the custom cancel function if the user passed one in.
  67. */
  68. this.onCancelSearchbar = async (ev) => {
  69. if (ev) {
  70. ev.preventDefault();
  71. ev.stopPropagation();
  72. }
  73. this.ionCancel.emit();
  74. // get cached values before clearing the input
  75. const value = this.getValue();
  76. const focused = this.focused;
  77. await this.onClearInput();
  78. /**
  79. * If there used to be something in the box, and we weren't focused
  80. * beforehand (meaning no blur fired that would already handle this),
  81. * manually fire ionChange.
  82. */
  83. if (value && !focused) {
  84. this.emitValueChange(ev);
  85. }
  86. if (this.nativeInput) {
  87. this.nativeInput.blur();
  88. }
  89. };
  90. /**
  91. * Update the Searchbar input value when the input changes
  92. */
  93. this.onInput = (ev) => {
  94. const input = ev.target;
  95. if (input) {
  96. this.value = input.value;
  97. }
  98. this.emitInputChange(ev);
  99. };
  100. this.onChange = (ev) => {
  101. this.emitValueChange(ev);
  102. };
  103. /**
  104. * Sets the Searchbar to not focused and checks if it should align left
  105. * based on whether there is a value in the searchbar or not.
  106. */
  107. this.onBlur = (ev) => {
  108. this.focused = false;
  109. this.ionBlur.emit();
  110. this.positionElements();
  111. if (this.focusedValue !== this.value) {
  112. this.emitValueChange(ev);
  113. }
  114. this.focusedValue = undefined;
  115. };
  116. /**
  117. * Sets the Searchbar to focused and active on input focus.
  118. */
  119. this.onFocus = () => {
  120. this.focused = true;
  121. this.focusedValue = this.value;
  122. this.ionFocus.emit();
  123. this.positionElements();
  124. };
  125. this.focused = false;
  126. this.noAnimate = true;
  127. this.color = undefined;
  128. this.animated = false;
  129. this.autocapitalize = 'off';
  130. this.autocomplete = 'off';
  131. this.autocorrect = 'off';
  132. this.cancelButtonIcon = config.get('backButtonIcon', arrowBackSharp);
  133. this.cancelButtonText = 'Cancel';
  134. this.clearIcon = undefined;
  135. this.debounce = undefined;
  136. this.disabled = false;
  137. this.inputmode = undefined;
  138. this.enterkeyhint = undefined;
  139. this.maxlength = undefined;
  140. this.minlength = undefined;
  141. this.name = this.inputId;
  142. this.placeholder = 'Search';
  143. this.searchIcon = undefined;
  144. this.showCancelButton = 'never';
  145. this.showClearButton = 'always';
  146. this.spellcheck = false;
  147. this.type = 'search';
  148. this.value = '';
  149. }
  150. /**
  151. * lang and dir are globally enumerated attributes.
  152. * As a result, creating these as properties
  153. * can have unintended side effects. Instead, we
  154. * listen for attribute changes and inherit them
  155. * to the inner `<input>` element.
  156. */
  157. onLangChanged(newValue) {
  158. this.inheritedAttributes = Object.assign(Object.assign({}, this.inheritedAttributes), { lang: newValue });
  159. forceUpdate(this);
  160. }
  161. onDirChanged(newValue) {
  162. this.inheritedAttributes = Object.assign(Object.assign({}, this.inheritedAttributes), { dir: newValue });
  163. forceUpdate(this);
  164. }
  165. debounceChanged() {
  166. const { ionInput, debounce, originalIonInput } = this;
  167. /**
  168. * If debounce is undefined, we have to manually revert the ionInput emitter in case
  169. * debounce used to be set to a number. Otherwise, the event would stay debounced.
  170. */
  171. this.ionInput = debounce === undefined ? originalIonInput !== null && originalIonInput !== void 0 ? originalIonInput : ionInput : debounceEvent(ionInput, debounce);
  172. }
  173. valueChanged() {
  174. const inputEl = this.nativeInput;
  175. const value = this.getValue();
  176. if (inputEl && inputEl.value !== value) {
  177. inputEl.value = value;
  178. }
  179. }
  180. showCancelButtonChanged() {
  181. requestAnimationFrame(() => {
  182. this.positionElements();
  183. forceUpdate(this);
  184. });
  185. }
  186. connectedCallback() {
  187. this.emitStyle();
  188. }
  189. componentWillLoad() {
  190. this.inheritedAttributes = Object.assign({}, inheritAttributes(this.el, ['lang', 'dir']));
  191. }
  192. componentDidLoad() {
  193. this.originalIonInput = this.ionInput;
  194. this.positionElements();
  195. this.debounceChanged();
  196. setTimeout(() => {
  197. this.noAnimate = false;
  198. }, 300);
  199. }
  200. emitStyle() {
  201. this.ionStyle.emit({
  202. searchbar: true,
  203. });
  204. }
  205. /**
  206. * Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global
  207. * `input.focus()`.
  208. *
  209. * Developers who wish to focus an input when a page enters
  210. * should call `setFocus()` in the `ionViewDidEnter()` lifecycle method.
  211. *
  212. * Developers who wish to focus an input when an overlay is presented
  213. * should call `setFocus` after `didPresent` has resolved.
  214. *
  215. * See [managing focus](/docs/developing/managing-focus) for more information.
  216. */
  217. async setFocus() {
  218. if (this.nativeInput) {
  219. this.nativeInput.focus();
  220. }
  221. }
  222. /**
  223. * Returns the native `<input>` element used under the hood.
  224. */
  225. async getInputElement() {
  226. /**
  227. * If this gets called in certain early lifecycle hooks (ex: Vue onMounted),
  228. * nativeInput won't be defined yet with the custom elements build, so wait for it to load in.
  229. */
  230. if (!this.nativeInput) {
  231. await new Promise((resolve) => componentOnReady(this.el, resolve));
  232. }
  233. return Promise.resolve(this.nativeInput);
  234. }
  235. /**
  236. * Emits an `ionChange` event.
  237. *
  238. * This API should be called for user committed changes.
  239. * This API should not be used for external value changes.
  240. */
  241. emitValueChange(event) {
  242. const { value } = this;
  243. // Checks for both null and undefined values
  244. const newValue = value == null ? value : value.toString();
  245. // Emitting a value change should update the internal state for tracking the focused value
  246. this.focusedValue = newValue;
  247. this.ionChange.emit({ value: newValue, event });
  248. }
  249. /**
  250. * Emits an `ionInput` event.
  251. */
  252. emitInputChange(event) {
  253. const { value } = this;
  254. this.ionInput.emit({ value, event });
  255. }
  256. /**
  257. * Positions the input search icon, placeholder, and the cancel button
  258. * based on the input value and if it is focused. (ios only)
  259. */
  260. positionElements() {
  261. const value = this.getValue();
  262. const prevAlignLeft = this.shouldAlignLeft;
  263. const mode = getIonMode(this);
  264. const shouldAlignLeft = !this.animated || value.trim() !== '' || !!this.focused;
  265. this.shouldAlignLeft = shouldAlignLeft;
  266. if (mode !== 'ios') {
  267. return;
  268. }
  269. if (prevAlignLeft !== shouldAlignLeft) {
  270. this.positionPlaceholder();
  271. }
  272. if (this.animated) {
  273. this.positionCancelButton();
  274. }
  275. }
  276. /**
  277. * Positions the input placeholder
  278. */
  279. positionPlaceholder() {
  280. const inputEl = this.nativeInput;
  281. if (!inputEl) {
  282. return;
  283. }
  284. const rtl = isRTL(this.el);
  285. const iconEl = (this.el.shadowRoot || this.el).querySelector('.searchbar-search-icon');
  286. if (this.shouldAlignLeft) {
  287. inputEl.removeAttribute('style');
  288. iconEl.removeAttribute('style');
  289. }
  290. else {
  291. // Create a dummy span to get the placeholder width
  292. const doc = document;
  293. const tempSpan = doc.createElement('span');
  294. tempSpan.innerText = this.placeholder || '';
  295. doc.body.appendChild(tempSpan);
  296. // Get the width of the span then remove it
  297. raf(() => {
  298. const textWidth = tempSpan.offsetWidth;
  299. tempSpan.remove();
  300. // Calculate the input padding
  301. const inputLeft = 'calc(50% - ' + textWidth / 2 + 'px)';
  302. // Calculate the icon margin
  303. /**
  304. * We take the icon width to account
  305. * for any text scales applied to the icon
  306. * such as Dynamic Type on iOS as well as 8px
  307. * of padding.
  308. */
  309. const iconLeft = 'calc(50% - ' + (textWidth / 2 + iconEl.clientWidth + 8) + 'px)';
  310. // Set the input padding start and icon margin start
  311. if (rtl) {
  312. inputEl.style.paddingRight = inputLeft;
  313. iconEl.style.marginRight = iconLeft;
  314. }
  315. else {
  316. inputEl.style.paddingLeft = inputLeft;
  317. iconEl.style.marginLeft = iconLeft;
  318. }
  319. });
  320. }
  321. }
  322. /**
  323. * Show the iOS Cancel button on focus, hide it offscreen otherwise
  324. */
  325. positionCancelButton() {
  326. const rtl = isRTL(this.el);
  327. const cancelButton = (this.el.shadowRoot || this.el).querySelector('.searchbar-cancel-button');
  328. const shouldShowCancel = this.shouldShowCancelButton();
  329. if (cancelButton !== null && shouldShowCancel !== this.isCancelVisible) {
  330. const cancelStyle = cancelButton.style;
  331. this.isCancelVisible = shouldShowCancel;
  332. if (shouldShowCancel) {
  333. if (rtl) {
  334. cancelStyle.marginLeft = '0';
  335. }
  336. else {
  337. cancelStyle.marginRight = '0';
  338. }
  339. }
  340. else {
  341. const offset = cancelButton.offsetWidth;
  342. if (offset > 0) {
  343. if (rtl) {
  344. cancelStyle.marginLeft = -offset + 'px';
  345. }
  346. else {
  347. cancelStyle.marginRight = -offset + 'px';
  348. }
  349. }
  350. }
  351. }
  352. }
  353. getValue() {
  354. return this.value || '';
  355. }
  356. hasValue() {
  357. return this.getValue() !== '';
  358. }
  359. /**
  360. * Determines whether or not the cancel button should be visible onscreen.
  361. * Cancel button should be shown if one of two conditions applies:
  362. * 1. `showCancelButton` is set to `always`.
  363. * 2. `showCancelButton` is set to `focus`, and the searchbar has been focused.
  364. */
  365. shouldShowCancelButton() {
  366. if (this.showCancelButton === 'never' || (this.showCancelButton === 'focus' && !this.focused)) {
  367. return false;
  368. }
  369. return true;
  370. }
  371. /**
  372. * Determines whether or not the clear button should be visible onscreen.
  373. * Clear button should be shown if one of two conditions applies:
  374. * 1. `showClearButton` is set to `always`.
  375. * 2. `showClearButton` is set to `focus`, and the searchbar has been focused.
  376. */
  377. shouldShowClearButton() {
  378. if (this.showClearButton === 'never' || (this.showClearButton === 'focus' && !this.focused)) {
  379. return false;
  380. }
  381. return true;
  382. }
  383. render() {
  384. const { cancelButtonText, autocapitalize } = this;
  385. const animated = this.animated && config.getBoolean('animated', true);
  386. const mode = getIonMode(this);
  387. const clearIcon = this.clearIcon || (mode === 'ios' ? closeCircle : closeSharp);
  388. const searchIcon = this.searchIcon || (mode === 'ios' ? searchOutline : searchSharp);
  389. const shouldShowCancelButton = this.shouldShowCancelButton();
  390. const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '989f3e84c472ada6e66dd9b249d0d268bf17ce26', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: '7d335d4fde33822dc79d26b748ba2e98db7494bb', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
  391. return (h(Host, { key: 'd1a1972725e949fb102c91487aaa7b9d10c2d718', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
  392. [mode]: true,
  393. 'searchbar-animated': animated,
  394. 'searchbar-disabled': this.disabled,
  395. 'searchbar-no-animate': animated && this.noAnimate,
  396. 'searchbar-has-value': this.hasValue(),
  397. 'searchbar-left-aligned': this.shouldAlignLeft,
  398. 'searchbar-has-focus': this.focused,
  399. 'searchbar-should-show-clear': this.shouldShowClearButton(),
  400. 'searchbar-should-show-cancel': this.shouldShowCancelButton(),
  401. }) }, h("div", { key: 'add53640b2994cb6b2bf02792dafe51aba6b1684', class: "searchbar-input-container" }, h("input", Object.assign({ key: '160cc36459a4a652e7f41ccd14dcdc782278779e', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: '8825fd13af0d2dea451ccc0e00ae7b5021dc01c4', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: '8a7b56da278b9ca5c4f5a4ee9c01924fd5ae29d8', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
  402. /**
  403. * This prevents mobile browsers from
  404. * blurring the input when the clear
  405. * button is activated.
  406. */
  407. ev.preventDefault();
  408. }, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: '24c55274516ab012d8c25f03525c6cdb9409e52f', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
  409. }
  410. get el() { return getElement(this); }
  411. static get watchers() { return {
  412. "lang": ["onLangChanged"],
  413. "dir": ["onDirChanged"],
  414. "debounce": ["debounceChanged"],
  415. "value": ["valueChanged"],
  416. "showCancelButton": ["showCancelButtonChanged"]
  417. }; }
  418. };
  419. let searchbarIds = 0;
  420. Searchbar.style = {
  421. ios: IonSearchbarIosStyle0,
  422. md: IonSearchbarMdStyle0
  423. };
  424. export { Searchbar as ion_searchbar };