import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, State, Watch, h, readTask } from '@stencil/core'; import { getIonMode } from '../../global/ionic-global'; import { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface'; import { debounceEvent, findItemLabel } from '../../utils/helpers'; import { createColorClasses } from '../../utils/theme'; /** * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use. */ @Component({ tag: 'ion-textarea', styleUrls: { ios: 'textarea.ios.scss', md: 'textarea.md.scss' }, scoped: true }) export class Textarea implements ComponentInterface { private nativeInput?: HTMLTextAreaElement; private inputId = `ion-input-${textareaIds++}`; private didBlurAfterEdit = false; private textareaWrapper?: HTMLElement; @Element() el!: HTMLElement; @State() hasFocus = false; /** * The color to use from your application's color palette. * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. * For more information on colors, see [theming](/docs/theming/basics). */ @Prop() color?: Color; /** * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. */ @Prop() autocapitalize = 'none'; /** * This Boolean attribute lets you specify that a form control should have input focus when the page loads. */ @Prop() autofocus = false; /** * If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. */ @Prop({ mutable: true }) clearOnEdit = false; /** * Set the amount of time, in milliseconds, to wait to trigger the `ionChange` event after each keystroke. */ @Prop() debounce = 0; @Watch('debounce') protected debounceChanged() { this.ionChange = debounceEvent(this.ionChange, this.debounce); } /** * If `true`, the user cannot interact with the textarea. */ @Prop() disabled = false; @Watch('disabled') protected disabledChanged() { this.emitStyle(); } /** * If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter. */ @Prop() maxlength?: number; /** * If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter. */ @Prop() minlength?: number; /** * The name of the control, which is submitted with the form data. */ @Prop() name: string = this.inputId; /** * Instructional text that shows before the input has a value. */ @Prop() placeholder?: string | null; /** * If `true`, the user cannot modify the value. */ @Prop() readonly = false; /** * If `true`, the user must fill in a value before submitting a form. */ @Prop() required = false; /** * If `true`, the element will have its spelling and grammar checked. */ @Prop() spellcheck = false; /** * The visible width of the text control, in average character widths. If it is specified, it must be a positive integer. */ @Prop() cols?: number; /** * The number of visible text lines for the control. */ @Prop() rows?: number; /** * Indicates how the control wraps text. */ @Prop() wrap?: 'hard' | 'soft' | 'off'; /** * If `true`, the element height will increase based on the value. */ @Prop() autoGrow = false; /** * The value of the textarea. */ @Prop({ mutable: true }) value?: string | null = ''; /** * Update the native input element when the value changes */ @Watch('value') protected valueChanged() { const nativeInput = this.nativeInput; const value = this.getValue(); if (nativeInput && nativeInput.value !== value) { nativeInput.value = value; } this.runAutoGrow(); this.emitStyle(); this.ionChange.emit({ value }); } /** * Emitted when the input value has changed. */ @Event() ionChange!: EventEmitter; /** * Emitted when a keyboard input occurred. */ @Event() ionInput!: EventEmitter; /** * Emitted when the styles change. * @internal */ @Event() ionStyle!: EventEmitter; /** * Emitted when the input loses focus. */ @Event() ionBlur!: EventEmitter; /** * Emitted when the input has focus. */ @Event() ionFocus!: EventEmitter; connectedCallback() { this.emitStyle(); this.debounceChanged(); if (Build.isBrowser) { document.dispatchEvent(new CustomEvent('ionInputDidLoad', { detail: this.el })); } } disconnectedCallback() { if (Build.isBrowser) { document.dispatchEvent(new CustomEvent('ionInputDidUnload', { detail: this.el })); } } componentDidLoad() { this.runAutoGrow(); } private runAutoGrow() { const nativeInput = this.nativeInput; if (nativeInput && this.autoGrow) { readTask(() => { nativeInput.style.height = 'auto'; nativeInput.style.height = nativeInput.scrollHeight + 'px'; if (this.textareaWrapper) { this.textareaWrapper.style.height = nativeInput.scrollHeight + 'px'; } }); } } /** * Sets focus on the specified `ion-textarea`. Use this method instead of the global * `input.focus()`. */ @Method() async setFocus() { if (this.nativeInput) { this.nativeInput.focus(); } } /** * Returns the native ` ); } } let textareaIds = 0;