text-field.es5.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { Platform, normalizePassiveListenerOptions, PlatformModule } from '@angular/cdk/platform';
  9. import { Directive, ElementRef, EventEmitter, Injectable, NgZone, Output, Input, NgModule, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
  10. import { coerceElement, coerceBooleanProperty } from '@angular/cdk/coercion';
  11. import { EMPTY, Subject, fromEvent } from 'rxjs';
  12. import { auditTime, takeUntil } from 'rxjs/operators';
  13. /**
  14. * @fileoverview added by tsickle
  15. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  16. */
  17. /**
  18. * Options to pass to the animationstart listener.
  19. * @type {?}
  20. */
  21. var listenerOptions = normalizePassiveListenerOptions({ passive: true });
  22. /**
  23. * An injectable service that can be used to monitor the autofill state of an input.
  24. * Based on the following blog post:
  25. * https://medium.com/\@brunn/detecting-autofilled-fields-in-javascript-aed598d25da7
  26. */
  27. var AutofillMonitor = /** @class */ (function () {
  28. function AutofillMonitor(_platform, _ngZone) {
  29. this._platform = _platform;
  30. this._ngZone = _ngZone;
  31. this._monitoredElements = new Map();
  32. }
  33. /**
  34. * @param {?} elementOrRef
  35. * @return {?}
  36. */
  37. AutofillMonitor.prototype.monitor = /**
  38. * @param {?} elementOrRef
  39. * @return {?}
  40. */
  41. function (elementOrRef) {
  42. var _this = this;
  43. if (!this._platform.isBrowser) {
  44. return EMPTY;
  45. }
  46. /** @type {?} */
  47. var element = coerceElement(elementOrRef);
  48. /** @type {?} */
  49. var info = this._monitoredElements.get(element);
  50. if (info) {
  51. return info.subject.asObservable();
  52. }
  53. /** @type {?} */
  54. var result = new Subject();
  55. /** @type {?} */
  56. var cssClass = 'cdk-text-field-autofilled';
  57. /** @type {?} */
  58. var listener = (/** @type {?} */ (((/**
  59. * @param {?} event
  60. * @return {?}
  61. */
  62. function (event) {
  63. // Animation events fire on initial element render, we check for the presence of the autofill
  64. // CSS class to make sure this is a real change in state, not just the initial render before
  65. // we fire off events.
  66. if (event.animationName === 'cdk-text-field-autofill-start' &&
  67. !element.classList.contains(cssClass)) {
  68. element.classList.add(cssClass);
  69. _this._ngZone.run((/**
  70. * @return {?}
  71. */
  72. function () { return result.next({ target: (/** @type {?} */ (event.target)), isAutofilled: true }); }));
  73. }
  74. else if (event.animationName === 'cdk-text-field-autofill-end' &&
  75. element.classList.contains(cssClass)) {
  76. element.classList.remove(cssClass);
  77. _this._ngZone.run((/**
  78. * @return {?}
  79. */
  80. function () { return result.next({ target: (/** @type {?} */ (event.target)), isAutofilled: false }); }));
  81. }
  82. }))));
  83. this._ngZone.runOutsideAngular((/**
  84. * @return {?}
  85. */
  86. function () {
  87. element.addEventListener('animationstart', listener, listenerOptions);
  88. element.classList.add('cdk-text-field-autofill-monitored');
  89. }));
  90. this._monitoredElements.set(element, {
  91. subject: result,
  92. unlisten: (/**
  93. * @return {?}
  94. */
  95. function () {
  96. element.removeEventListener('animationstart', listener, listenerOptions);
  97. })
  98. });
  99. return result.asObservable();
  100. };
  101. /**
  102. * @param {?} elementOrRef
  103. * @return {?}
  104. */
  105. AutofillMonitor.prototype.stopMonitoring = /**
  106. * @param {?} elementOrRef
  107. * @return {?}
  108. */
  109. function (elementOrRef) {
  110. /** @type {?} */
  111. var element = coerceElement(elementOrRef);
  112. /** @type {?} */
  113. var info = this._monitoredElements.get(element);
  114. if (info) {
  115. info.unlisten();
  116. info.subject.complete();
  117. element.classList.remove('cdk-text-field-autofill-monitored');
  118. element.classList.remove('cdk-text-field-autofilled');
  119. this._monitoredElements.delete(element);
  120. }
  121. };
  122. /**
  123. * @return {?}
  124. */
  125. AutofillMonitor.prototype.ngOnDestroy = /**
  126. * @return {?}
  127. */
  128. function () {
  129. var _this = this;
  130. this._monitoredElements.forEach((/**
  131. * @param {?} _info
  132. * @param {?} element
  133. * @return {?}
  134. */
  135. function (_info, element) { return _this.stopMonitoring(element); }));
  136. };
  137. AutofillMonitor.decorators = [
  138. { type: Injectable, args: [{ providedIn: 'root' },] },
  139. ];
  140. /** @nocollapse */
  141. AutofillMonitor.ctorParameters = function () { return [
  142. { type: Platform },
  143. { type: NgZone }
  144. ]; };
  145. /** @nocollapse */ AutofillMonitor.ngInjectableDef = ɵɵdefineInjectable({ factory: function AutofillMonitor_Factory() { return new AutofillMonitor(ɵɵinject(Platform), ɵɵinject(NgZone)); }, token: AutofillMonitor, providedIn: "root" });
  146. return AutofillMonitor;
  147. }());
  148. /**
  149. * A directive that can be used to monitor the autofill state of an input.
  150. */
  151. var CdkAutofill = /** @class */ (function () {
  152. function CdkAutofill(_elementRef, _autofillMonitor) {
  153. this._elementRef = _elementRef;
  154. this._autofillMonitor = _autofillMonitor;
  155. /**
  156. * Emits when the autofill state of the element changes.
  157. */
  158. this.cdkAutofill = new EventEmitter();
  159. }
  160. /**
  161. * @return {?}
  162. */
  163. CdkAutofill.prototype.ngOnInit = /**
  164. * @return {?}
  165. */
  166. function () {
  167. var _this = this;
  168. this._autofillMonitor
  169. .monitor(this._elementRef)
  170. .subscribe((/**
  171. * @param {?} event
  172. * @return {?}
  173. */
  174. function (event) { return _this.cdkAutofill.emit(event); }));
  175. };
  176. /**
  177. * @return {?}
  178. */
  179. CdkAutofill.prototype.ngOnDestroy = /**
  180. * @return {?}
  181. */
  182. function () {
  183. this._autofillMonitor.stopMonitoring(this._elementRef);
  184. };
  185. CdkAutofill.decorators = [
  186. { type: Directive, args: [{
  187. selector: '[cdkAutofill]',
  188. },] },
  189. ];
  190. /** @nocollapse */
  191. CdkAutofill.ctorParameters = function () { return [
  192. { type: ElementRef },
  193. { type: AutofillMonitor }
  194. ]; };
  195. CdkAutofill.propDecorators = {
  196. cdkAutofill: [{ type: Output }]
  197. };
  198. return CdkAutofill;
  199. }());
  200. /**
  201. * @fileoverview added by tsickle
  202. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  203. */
  204. /**
  205. * Directive to automatically resize a textarea to fit its content.
  206. */
  207. var CdkTextareaAutosize = /** @class */ (function () {
  208. function CdkTextareaAutosize(_elementRef, _platform, _ngZone) {
  209. this._elementRef = _elementRef;
  210. this._platform = _platform;
  211. this._ngZone = _ngZone;
  212. this._destroyed = new Subject();
  213. this._enabled = true;
  214. /**
  215. * Value of minRows as of last resize. If the minRows has decreased, the
  216. * height of the textarea needs to be recomputed to reflect the new minimum. The maxHeight
  217. * does not have the same problem because it does not affect the textarea's scrollHeight.
  218. */
  219. this._previousMinRows = -1;
  220. this._textareaElement = (/** @type {?} */ (this._elementRef.nativeElement));
  221. }
  222. Object.defineProperty(CdkTextareaAutosize.prototype, "minRows", {
  223. /** Minimum amount of rows in the textarea. */
  224. get: /**
  225. * Minimum amount of rows in the textarea.
  226. * @return {?}
  227. */
  228. function () { return this._minRows; },
  229. set: /**
  230. * @param {?} value
  231. * @return {?}
  232. */
  233. function (value) {
  234. this._minRows = value;
  235. this._setMinHeight();
  236. },
  237. enumerable: true,
  238. configurable: true
  239. });
  240. Object.defineProperty(CdkTextareaAutosize.prototype, "maxRows", {
  241. /** Maximum amount of rows in the textarea. */
  242. get: /**
  243. * Maximum amount of rows in the textarea.
  244. * @return {?}
  245. */
  246. function () { return this._maxRows; },
  247. set: /**
  248. * @param {?} value
  249. * @return {?}
  250. */
  251. function (value) {
  252. this._maxRows = value;
  253. this._setMaxHeight();
  254. },
  255. enumerable: true,
  256. configurable: true
  257. });
  258. Object.defineProperty(CdkTextareaAutosize.prototype, "enabled", {
  259. /** Whether autosizing is enabled or not */
  260. get: /**
  261. * Whether autosizing is enabled or not
  262. * @return {?}
  263. */
  264. function () { return this._enabled; },
  265. set: /**
  266. * @param {?} value
  267. * @return {?}
  268. */
  269. function (value) {
  270. value = coerceBooleanProperty(value);
  271. // Only act if the actual value changed. This specifically helps to not run
  272. // resizeToFitContent too early (i.e. before ngAfterViewInit)
  273. if (this._enabled !== value) {
  274. (this._enabled = value) ? this.resizeToFitContent(true) : this.reset();
  275. }
  276. },
  277. enumerable: true,
  278. configurable: true
  279. });
  280. /** Sets the minimum height of the textarea as determined by minRows. */
  281. /**
  282. * Sets the minimum height of the textarea as determined by minRows.
  283. * @return {?}
  284. */
  285. CdkTextareaAutosize.prototype._setMinHeight = /**
  286. * Sets the minimum height of the textarea as determined by minRows.
  287. * @return {?}
  288. */
  289. function () {
  290. /** @type {?} */
  291. var minHeight = this.minRows && this._cachedLineHeight ?
  292. this.minRows * this._cachedLineHeight + "px" : null;
  293. if (minHeight) {
  294. this._textareaElement.style.minHeight = minHeight;
  295. }
  296. };
  297. /** Sets the maximum height of the textarea as determined by maxRows. */
  298. /**
  299. * Sets the maximum height of the textarea as determined by maxRows.
  300. * @return {?}
  301. */
  302. CdkTextareaAutosize.prototype._setMaxHeight = /**
  303. * Sets the maximum height of the textarea as determined by maxRows.
  304. * @return {?}
  305. */
  306. function () {
  307. /** @type {?} */
  308. var maxHeight = this.maxRows && this._cachedLineHeight ?
  309. this.maxRows * this._cachedLineHeight + "px" : null;
  310. if (maxHeight) {
  311. this._textareaElement.style.maxHeight = maxHeight;
  312. }
  313. };
  314. /**
  315. * @return {?}
  316. */
  317. CdkTextareaAutosize.prototype.ngAfterViewInit = /**
  318. * @return {?}
  319. */
  320. function () {
  321. var _this = this;
  322. if (this._platform.isBrowser) {
  323. // Remember the height which we started with in case autosizing is disabled
  324. this._initialHeight = this._textareaElement.style.height;
  325. this.resizeToFitContent();
  326. this._ngZone.runOutsideAngular((/**
  327. * @return {?}
  328. */
  329. function () {
  330. fromEvent(window, 'resize')
  331. .pipe(auditTime(16), takeUntil(_this._destroyed))
  332. .subscribe((/**
  333. * @return {?}
  334. */
  335. function () { return _this.resizeToFitContent(true); }));
  336. }));
  337. }
  338. };
  339. /**
  340. * @return {?}
  341. */
  342. CdkTextareaAutosize.prototype.ngOnDestroy = /**
  343. * @return {?}
  344. */
  345. function () {
  346. this._destroyed.next();
  347. this._destroyed.complete();
  348. };
  349. /**
  350. * Cache the height of a single-row textarea if it has not already been cached.
  351. *
  352. * We need to know how large a single "row" of a textarea is in order to apply minRows and
  353. * maxRows. For the initial version, we will assume that the height of a single line in the
  354. * textarea does not ever change.
  355. */
  356. /**
  357. * Cache the height of a single-row textarea if it has not already been cached.
  358. *
  359. * We need to know how large a single "row" of a textarea is in order to apply minRows and
  360. * maxRows. For the initial version, we will assume that the height of a single line in the
  361. * textarea does not ever change.
  362. * @private
  363. * @return {?}
  364. */
  365. CdkTextareaAutosize.prototype._cacheTextareaLineHeight = /**
  366. * Cache the height of a single-row textarea if it has not already been cached.
  367. *
  368. * We need to know how large a single "row" of a textarea is in order to apply minRows and
  369. * maxRows. For the initial version, we will assume that the height of a single line in the
  370. * textarea does not ever change.
  371. * @private
  372. * @return {?}
  373. */
  374. function () {
  375. if (this._cachedLineHeight) {
  376. return;
  377. }
  378. // Use a clone element because we have to override some styles.
  379. /** @type {?} */
  380. var textareaClone = (/** @type {?} */ (this._textareaElement.cloneNode(false)));
  381. textareaClone.rows = 1;
  382. // Use `position: absolute` so that this doesn't cause a browser layout and use
  383. // `visibility: hidden` so that nothing is rendered. Clear any other styles that
  384. // would affect the height.
  385. textareaClone.style.position = 'absolute';
  386. textareaClone.style.visibility = 'hidden';
  387. textareaClone.style.border = 'none';
  388. textareaClone.style.padding = '0';
  389. textareaClone.style.height = '';
  390. textareaClone.style.minHeight = '';
  391. textareaClone.style.maxHeight = '';
  392. // In Firefox it happens that textarea elements are always bigger than the specified amount
  393. // of rows. This is because Firefox tries to add extra space for the horizontal scrollbar.
  394. // As a workaround that removes the extra space for the scrollbar, we can just set overflow
  395. // to hidden. This ensures that there is no invalid calculation of the line height.
  396. // See Firefox bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=33654
  397. textareaClone.style.overflow = 'hidden';
  398. (/** @type {?} */ (this._textareaElement.parentNode)).appendChild(textareaClone);
  399. this._cachedLineHeight = textareaClone.clientHeight;
  400. (/** @type {?} */ (this._textareaElement.parentNode)).removeChild(textareaClone);
  401. // Min and max heights have to be re-calculated if the cached line height changes
  402. this._setMinHeight();
  403. this._setMaxHeight();
  404. };
  405. /**
  406. * @return {?}
  407. */
  408. CdkTextareaAutosize.prototype.ngDoCheck = /**
  409. * @return {?}
  410. */
  411. function () {
  412. if (this._platform.isBrowser) {
  413. this.resizeToFitContent();
  414. }
  415. };
  416. /**
  417. * Resize the textarea to fit its content.
  418. * @param force Whether to force a height recalculation. By default the height will be
  419. * recalculated only if the value changed since the last call.
  420. */
  421. /**
  422. * Resize the textarea to fit its content.
  423. * @param {?=} force Whether to force a height recalculation. By default the height will be
  424. * recalculated only if the value changed since the last call.
  425. * @return {?}
  426. */
  427. CdkTextareaAutosize.prototype.resizeToFitContent = /**
  428. * Resize the textarea to fit its content.
  429. * @param {?=} force Whether to force a height recalculation. By default the height will be
  430. * recalculated only if the value changed since the last call.
  431. * @return {?}
  432. */
  433. function (force) {
  434. var _this = this;
  435. if (force === void 0) { force = false; }
  436. // If autosizing is disabled, just skip everything else
  437. if (!this._enabled) {
  438. return;
  439. }
  440. this._cacheTextareaLineHeight();
  441. // If we haven't determined the line-height yet, we know we're still hidden and there's no point
  442. // in checking the height of the textarea.
  443. if (!this._cachedLineHeight) {
  444. return;
  445. }
  446. /** @type {?} */
  447. var textarea = (/** @type {?} */ (this._elementRef.nativeElement));
  448. /** @type {?} */
  449. var value = textarea.value;
  450. // Only resize if the value or minRows have changed since these calculations can be expensive.
  451. if (!force && this._minRows === this._previousMinRows && value === this._previousValue) {
  452. return;
  453. }
  454. /** @type {?} */
  455. var placeholderText = textarea.placeholder;
  456. // Reset the textarea height to auto in order to shrink back to its default size.
  457. // Also temporarily force overflow:hidden, so scroll bars do not interfere with calculations.
  458. // Long placeholders that are wider than the textarea width may lead to a bigger scrollHeight
  459. // value. To ensure that the scrollHeight is not bigger than the content, the placeholders
  460. // need to be removed temporarily.
  461. textarea.classList.add('cdk-textarea-autosize-measuring');
  462. textarea.placeholder = '';
  463. // The cdk-textarea-autosize-measuring class includes a 2px padding to workaround an issue with
  464. // Chrome, so we account for that extra space here by subtracting 4 (2px top + 2px bottom).
  465. /** @type {?} */
  466. var height = textarea.scrollHeight - 4;
  467. // Use the scrollHeight to know how large the textarea *would* be if fit its entire value.
  468. textarea.style.height = height + "px";
  469. textarea.classList.remove('cdk-textarea-autosize-measuring');
  470. textarea.placeholder = placeholderText;
  471. this._ngZone.runOutsideAngular((/**
  472. * @return {?}
  473. */
  474. function () {
  475. if (typeof requestAnimationFrame !== 'undefined') {
  476. requestAnimationFrame((/**
  477. * @return {?}
  478. */
  479. function () { return _this._scrollToCaretPosition(textarea); }));
  480. }
  481. else {
  482. setTimeout((/**
  483. * @return {?}
  484. */
  485. function () { return _this._scrollToCaretPosition(textarea); }));
  486. }
  487. }));
  488. this._previousValue = value;
  489. this._previousMinRows = this._minRows;
  490. };
  491. /**
  492. * Resets the textarea to its original size
  493. */
  494. /**
  495. * Resets the textarea to its original size
  496. * @return {?}
  497. */
  498. CdkTextareaAutosize.prototype.reset = /**
  499. * Resets the textarea to its original size
  500. * @return {?}
  501. */
  502. function () {
  503. // Do not try to change the textarea, if the initialHeight has not been determined yet
  504. // This might potentially remove styles when reset() is called before ngAfterViewInit
  505. if (this._initialHeight === undefined) {
  506. return;
  507. }
  508. this._textareaElement.style.height = this._initialHeight;
  509. };
  510. /**
  511. * @return {?}
  512. */
  513. CdkTextareaAutosize.prototype._noopInputHandler = /**
  514. * @return {?}
  515. */
  516. function () {
  517. // no-op handler that ensures we're running change detection on input events.
  518. };
  519. /**
  520. * Scrolls a textarea to the caret position. On Firefox resizing the textarea will
  521. * prevent it from scrolling to the caret position. We need to re-set the selection
  522. * in order for it to scroll to the proper position.
  523. */
  524. /**
  525. * Scrolls a textarea to the caret position. On Firefox resizing the textarea will
  526. * prevent it from scrolling to the caret position. We need to re-set the selection
  527. * in order for it to scroll to the proper position.
  528. * @private
  529. * @param {?} textarea
  530. * @return {?}
  531. */
  532. CdkTextareaAutosize.prototype._scrollToCaretPosition = /**
  533. * Scrolls a textarea to the caret position. On Firefox resizing the textarea will
  534. * prevent it from scrolling to the caret position. We need to re-set the selection
  535. * in order for it to scroll to the proper position.
  536. * @private
  537. * @param {?} textarea
  538. * @return {?}
  539. */
  540. function (textarea) {
  541. var selectionStart = textarea.selectionStart, selectionEnd = textarea.selectionEnd;
  542. // IE will throw an "Unspecified error" if we try to set the selection range after the
  543. // element has been removed from the DOM. Assert that the directive hasn't been destroyed
  544. // between the time we requested the animation frame and when it was executed.
  545. // Also note that we have to assert that the textarea is focused before we set the
  546. // selection range. Setting the selection range on a non-focused textarea will cause
  547. // it to receive focus on IE and Edge.
  548. if (!this._destroyed.isStopped && document.activeElement === textarea) {
  549. textarea.setSelectionRange(selectionStart, selectionEnd);
  550. }
  551. };
  552. CdkTextareaAutosize.decorators = [
  553. { type: Directive, args: [{
  554. selector: 'textarea[cdkTextareaAutosize]',
  555. exportAs: 'cdkTextareaAutosize',
  556. host: {
  557. 'class': 'cdk-textarea-autosize',
  558. // Textarea elements that have the directive applied should have a single row by default.
  559. // Browsers normally show two rows by default and therefore this limits the minRows binding.
  560. 'rows': '1',
  561. '(input)': '_noopInputHandler()',
  562. },
  563. },] },
  564. ];
  565. /** @nocollapse */
  566. CdkTextareaAutosize.ctorParameters = function () { return [
  567. { type: ElementRef },
  568. { type: Platform },
  569. { type: NgZone }
  570. ]; };
  571. CdkTextareaAutosize.propDecorators = {
  572. minRows: [{ type: Input, args: ['cdkAutosizeMinRows',] }],
  573. maxRows: [{ type: Input, args: ['cdkAutosizeMaxRows',] }],
  574. enabled: [{ type: Input, args: ['cdkTextareaAutosize',] }]
  575. };
  576. return CdkTextareaAutosize;
  577. }());
  578. /**
  579. * @fileoverview added by tsickle
  580. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  581. */
  582. var TextFieldModule = /** @class */ (function () {
  583. function TextFieldModule() {
  584. }
  585. TextFieldModule.decorators = [
  586. { type: NgModule, args: [{
  587. declarations: [CdkAutofill, CdkTextareaAutosize],
  588. imports: [PlatformModule],
  589. exports: [CdkAutofill, CdkTextareaAutosize],
  590. },] },
  591. ];
  592. return TextFieldModule;
  593. }());
  594. /**
  595. * @fileoverview added by tsickle
  596. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  597. */
  598. /**
  599. * @fileoverview added by tsickle
  600. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  601. */
  602. export { AutofillMonitor, CdkAutofill, CdkTextareaAutosize, TextFieldModule };
  603. //# sourceMappingURL=text-field.es5.js.map