UNPKG

@kelvininc/ui-components

Version:
1,117 lines (1,116 loc) 43.3 kB
import { h, Host } from "@stencil/core"; import { isEmpty, isNil, merge } from "lodash-es"; import { EComponentSize } from "../../utils/types"; import { EInputFieldType, EValidationState } from "./text-field.types"; import { DEFAULT_TEXT_TOOLTIP_CONFIG } from "./text-field.config"; import { buildInputMask, getValueAsString, isInputMaskCompatibleType } from "./text-field.utils"; import Inputmask from "inputmask"; import { getUTF8StringLength } from "../../utils/string.helper"; /** * @part input-container - container that includes the input, right and left slot * @part badge - badge rendered at the right of the text field */ export class KvTextField { constructor() { /** @inheritdoc */ this.type = EInputFieldType.Text; /** @inheritdoc */ this.placeholder = ''; /** @inheritdoc */ this.size = EComponentSize.Large; /** @inheritdoc */ this.inputDisabled = false; /** @inheritdoc */ this.inputRequired = false; /** @inheritdoc */ this.loading = false; /** @inheritdoc */ this.state = EValidationState.None; /** @inheritdoc */ this.inputReadonly = false; /** @inheritdoc */ this.helpText = []; /** @inheritdoc */ this.forcedFocus = false; /** @inheritdoc */ this.value = ''; /** @inheritdoc */ this.isDirty = false; /** @inheritdoc */ this.inputMaskRegex = ''; /** @inheritdoc */ this.fitContent = true; /** @inheritdoc */ this.hideBadge = false; /** Text field focus state */ this.focused = false; this.onHostClick = (event) => { if (this.inputDisabled) return; event.preventDefault(); this.fieldClick.emit(event); }; this.onRightActionClick = (event) => { event.preventDefault(); this.rightActionClick.emit(event); }; this.onInputHandler = ({ target }) => { const input = target; if (this.maxLength && getUTF8StringLength(input.value) > this.maxLength) { const caretPositionIdx = input.selectionStart - 1; input.value = this.getValue().substring(0, this.maxLength); input.setSelectionRange(caretPositionIdx, caretPositionIdx); return; } if (!isNil(input)) { this.value = input.value || ''; } this.textChange.emit(this.getValue()); }; this.onPasteHandler = (event) => { const textLength = getUTF8StringLength(this.nativeInput.value); const pasteData = event.clipboardData.getData('text/plain'); const shouldDisablePaste = this.maxLength && textLength + getUTF8StringLength(pasteData) > this.maxLength; if (shouldDisablePaste) { event.preventDefault(); } }; this.onBlurHandler = ({ target }) => { this.textFieldBlur.emit(target.value); if (this.forcedFocus) { return; } this.focused = false; }; this.onFocusHandler = () => { this.textFieldFocus.emit(this.nativeInput.value); this.focused = true; }; this.getTooltipConfig = () => { var _a; return merge({}, DEFAULT_TEXT_TOOLTIP_CONFIG, (_a = this.tooltipConfig) !== null && _a !== void 0 ? _a : {}); }; this.getMinValue = () => { if (this.min !== undefined) { return this.min; } if (this.type === EInputFieldType.Number) { return Number.MIN_SAFE_INTEGER; } }; this.getMaxValue = () => { if (this.max !== undefined) { return this.max; } if (this.type === EInputFieldType.Number) { return Number.MAX_SAFE_INTEGER; } }; this.getUseInputMask = () => { var _a; return (_a = this.useInputMask) !== null && _a !== void 0 ? _a : this.type === EInputFieldType.Number; }; } /** Focuses the input */ async focusInput() { this.nativeInput.focus(); } /** Watch `value` property for changes and update native input element accordingly */ valueChangeHandler(newValue) { const newStringValue = getValueAsString(newValue); if (this.nativeInput && this.nativeInput.value !== newStringValue) { /** * Assigning the native input's value on attribute * value change, allows `textChange` implementations * to override the control's value. * * Used for patterns such as input trimming (removing whitespace), * or input masking. */ this.updateAndEmitValue(newStringValue); } } /** Watch the `helpText` property and update internal state accordingly */ helpTextChangeHandler(newValue) { this._helpTexts = this.buildHelpTextMessages(newValue); } /** Watch the `forcedFocus` property and update internal state accordingly */ forcedFocusChangeHandler(newValue) { this.focused = newValue; if (!this.focused) { this.el.blur(); this.nativeInput.blur(); } else { this.el.focus(); this.nativeInput.focus(); } } handleUseInputMask(newValue = this.type === EInputFieldType.Number) { if (newValue && isInputMaskCompatibleType(this.type) && !this.maskInstance) { this.maskInstance = buildInputMask(this.nativeInput, this.type, { min: this.getMinValue(), max: this.getMaxValue(), regex: this.inputMaskRegex }, this.maxLength); this.maskInstance.shadowRoot = this.el.shadowRoot; } else { if (this.nativeInput) { Inputmask.remove(this.nativeInput); this.valueChangeHandler(this.value); } } } componentWillLoad() { // Init the states because Watches run only on component updates this._helpTexts = this.buildHelpTextMessages(this.helpText); this.focused = this.forcedFocus; } componentDidLoad() { var _a, _b; this.handleUseInputMask(this.getUseInputMask()); this.updateAndEmitValue(this.getValue()); if (!this.focused) { this.el.blur(); (_a = this.nativeInput) === null || _a === void 0 ? void 0 : _a.blur(); } else { this.el.focus(); (_b = this.nativeInput) === null || _b === void 0 ? void 0 : _b.focus(); } } updateAndEmitValue(newString) { this.value = this.maxLength ? newString.substring(0, this.maxLength) : newString; if (this.nativeInput && this.nativeInput.value) { this.nativeInput.value = this.value; } if (this.value !== newString) { this.textChange.emit(this.value); } } buildHelpTextMessages(value) { value = value || []; return Array.isArray(value) ? value : [value]; } getValue() { return getValueAsString(this.value); } getType() { return this.getUseInputMask() ? EInputFieldType.Text : this.type; } render() { var _a; const id = this.el.getAttribute('id'); const value = this.getValue(); const type = this.getType(); return (h(Host, { key: '44ee367f8924242448bd82fcfab54cfd3476febc', onClick: this.onHostClick, style: Object.assign({}, this.customStyle) }, h("kv-tooltip", Object.assign({ key: '4adcf9b956b9a23e3e997cf1a97614aa61bceebf' }, this.getTooltipConfig()), h("div", { key: '2dd4367e4420fb48361ab90b72d2c8bef98dbffe', class: "text-field-container" }, this.label && h("kv-form-label", { key: '2837e4ff1c3e996cc040b4f86b1ed147eca654b3', label: this.label, required: this.inputRequired }), !this.loading ? (h("div", { part: "input-container", class: { 'input-container': true, [`input-container--size-${this.size}`]: true, 'invalid': this.state === EValidationState.Invalid, 'focused': this.focused, 'disabled': this.inputDisabled, 'filled': !isEmpty(value) } }, h("div", { class: { 'left-slot-container': true, 'focus': this.focused, 'invalid': this.state === EValidationState.Invalid, 'disabled': this.inputDisabled, 'filled': !isEmpty(value), 'has-value-prefix': this.valuePrefix && !isEmpty(value) } }, h("slot", { name: "left-slot" }), this.icon && (h("kv-icon", { name: this.icon, exportparts: "icon", class: { disabled: this.inputDisabled, focus: this.focused, filled: !isEmpty(value) } })), value && this.valuePrefix && h("div", { class: "value-prefix" }, this.valuePrefix)), h("div", { class: "resize-container" }, this.fitContent && (h("span", { class: "resize-text", part: "input-text" }, value || this.placeholder)), h("slot", { name: "input" }, h("input", { ref: input => (this.nativeInput = input), type: type, list: !isNil(this.examples) ? `examples_${id}` : undefined, name: this.inputName, placeholder: this.placeholder, disabled: this.inputDisabled, max: this.getMaxValue(), min: this.getMinValue(), minLength: this.minLength, step: this.step, value: value, onInput: this.onInputHandler, onBlur: this.onBlurHandler, onFocus: this.onFocusHandler, onPaste: this.onPasteHandler, class: { 'resize-input': true, 'forced-focus': this.focused }, readonly: this.inputReadonly, part: "input-text" }))), h("div", { class: { 'right-slot-container': true, 'focus': this.focused, 'disabled': this.inputDisabled, 'filled': !isEmpty(value) } }, h("slot", { name: "right-slot" }), this.isDirty && h("kv-dirty-dot", null), !this.hideBadge && ((_a = this.badge) === null || _a === void 0 ? void 0 : _a.trim()) && h("kv-badge", { exportparts: "badge" }, this.badge), this.actionIcon && (h("kv-icon", { name: this.actionIcon, onClick: this.onRightActionClick, class: { disabled: this.inputDisabled, focus: this.focused, filled: !isEmpty(value) } }))))) : (h("div", { class: { 'input-container-loading': true, [`input-container-loading--size-${this.size}`]: true } })), !isEmpty(this._helpTexts) && h("kv-form-help-text", { key: '1e1e36bed67c4ec2fb531ac190c07a7a380623ee', helpText: this._helpTexts, state: this.state })), !isEmpty(this.examples) ? (h("datalist", { id: `examples_${id}` }, this.examples.map(example => (h("option", { key: example, value: example }))))) : null))); } static get is() { return "kv-text-field"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["text-field.scss"] }; } static get styleUrls() { return { "$": ["text-field.css"] }; } static get properties() { return { "type": { "type": "string", "attribute": "type", "mutable": false, "complexType": { "original": "EInputFieldType", "resolved": "EInputFieldType.Date | EInputFieldType.DateTime | EInputFieldType.Email | EInputFieldType.Number | EInputFieldType.Password | EInputFieldType.Radio | EInputFieldType.Text", "references": { "EInputFieldType": { "location": "import", "path": "./text-field.types", "id": "src/components/text-field/text-field.types.ts::EInputFieldType" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field type" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "EInputFieldType.Text" }, "label": { "type": "string", "attribute": "label", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field label" }, "getter": false, "setter": false, "reflect": true }, "examples": { "type": "unknown", "attribute": "examples", "mutable": false, "complexType": { "original": "string[]", "resolved": "string[]", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field example values" }, "getter": false, "setter": false }, "icon": { "type": "string", "attribute": "icon", "mutable": false, "complexType": { "original": "EIconName", "resolved": "EIconName", "references": { "EIconName": { "location": "import", "path": "../icon/icon.types", "id": "src/components/icon/icon.types.ts::EIconName" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field's icon symbol name" }, "getter": false, "setter": false, "reflect": true }, "actionIcon": { "type": "string", "attribute": "action-icon", "mutable": false, "complexType": { "original": "EIconName", "resolved": "EIconName", "references": { "EIconName": { "location": "import", "path": "../icon/icon.types", "id": "src/components/icon/icon.types.ts::EIconName" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Icon that is added on the right of the input. Its clickable." }, "getter": false, "setter": false, "reflect": true }, "inputName": { "type": "string", "attribute": "input-name", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field input name" }, "getter": false, "setter": false, "reflect": true }, "placeholder": { "type": "string", "attribute": "placeholder", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field place holder" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "''" }, "maxLength": { "type": "number", "attribute": "max-length", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field maximum number of characters required" }, "getter": false, "setter": false, "reflect": true }, "minLength": { "type": "number", "attribute": "min-length", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field minimum number of characters required" }, "getter": false, "setter": false, "reflect": true }, "max": { "type": "any", "attribute": "max", "mutable": false, "complexType": { "original": "string | number", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field maximum value" }, "getter": false, "setter": false, "reflect": true }, "min": { "type": "any", "attribute": "min", "mutable": false, "complexType": { "original": "string | number", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field minimum value" }, "getter": false, "setter": false, "reflect": true }, "step": { "type": "any", "attribute": "step", "mutable": false, "complexType": { "original": "string | number", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field interval between legal numbers" }, "getter": false, "setter": false, "reflect": true }, "size": { "type": "string", "attribute": "size", "mutable": false, "complexType": { "original": "EComponentSize", "resolved": "EComponentSize.Large | EComponentSize.Small", "references": { "EComponentSize": { "location": "import", "path": "../../utils/types", "id": "src/utils/types/index.ts::EComponentSize" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Sets this tab item to a different styling configuration" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "EComponentSize.Large" }, "inputDisabled": { "type": "boolean", "attribute": "input-disabled", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field disabled" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "false" }, "inputRequired": { "type": "boolean", "attribute": "input-required", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field required" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "loading": { "type": "boolean", "attribute": "loading", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field loading state" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "state": { "type": "string", "attribute": "state", "mutable": false, "complexType": { "original": "EValidationState", "resolved": "EValidationState.Invalid | EValidationState.None | EValidationState.Valid", "references": { "EValidationState": { "location": "import", "path": "./text-field.types", "id": "src/components/text-field/text-field.types.ts::EValidationState" } } }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field state" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "EValidationState.None" }, "inputReadonly": { "type": "boolean", "attribute": "input-readonly", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field is readonly" }, "getter": false, "setter": false, "reflect": false, "defaultValue": "false" }, "helpText": { "type": "string", "attribute": "help-text", "mutable": false, "complexType": { "original": "string | string[]", "resolved": "string | string[]", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field help text" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "[]" }, "forcedFocus": { "type": "boolean", "attribute": "forced-focus", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field focus state" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "tooltipConfig": { "type": "unknown", "attribute": "tooltip-config", "mutable": false, "complexType": { "original": "Partial<ITooltip>", "resolved": "{ text?: string; position?: ETooltipPosition; allowedPositions?: ETooltipPosition[]; options?: Partial<{ strategy?: Strategy; placement?: Placement; middleware?: (false | { name: string; options?: any; fn: (state: { x: number; y: number; initialPlacement: Placement; strategy: Strategy; platform: Platform; placement: Placement; middlewareData: MiddlewareData; rects: ElementRects; elements: Elements; }) => Promisable<MiddlewareReturn>; })[]; platform?: Platform; }>; disabled?: boolean; contentElement?: HTMLElement; truncate?: boolean; delay?: number; hideDelay?: number; withArrow?: boolean; customStyle?: { [key: string]: string; }; customClass?: CustomCssClass; }", "references": { "Partial": { "location": "global", "id": "global::Partial" }, "ITooltip": { "location": "import", "path": "../tooltip/tooltip.types", "id": "src/components/tooltip/tooltip.types.ts::ITooltip" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field tooltip configuration" }, "getter": false, "setter": false }, "value": { "type": "any", "attribute": "value", "mutable": true, "complexType": { "original": "string | number | null", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text field value" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "''" }, "valuePrefix": { "type": "string", "attribute": "value-prefix", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Defines the prefix that adds context to displayed values" }, "getter": false, "setter": false, "reflect": true }, "badge": { "type": "string", "attribute": "badge", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Text to display inside a badge on the right side of the displayed value" }, "getter": false, "setter": false, "reflect": true }, "isDirty": { "type": "boolean", "attribute": "is-dirty", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) If true, a dirty dot indicator will be added to right side of the displayed value." }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" }, "useInputMask": { "type": "boolean", "attribute": "use-input-mask", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Use a input mask when the text field type is number (default true)" }, "getter": false, "setter": false, "reflect": true }, "inputMaskRegex": { "type": "string", "attribute": "input-mask-regex", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Input mask regex" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "''" }, "fitContent": { "type": "boolean", "attribute": "fit-content", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Enable/disable the resize of input (default: true)" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "true" }, "customStyle": { "type": "unknown", "attribute": "custom-style", "mutable": false, "complexType": { "original": "HostAttributes['style']", "resolved": "{ [key: string]: string; }", "references": { "HostAttributes": { "location": "import", "path": "@stencil/core/internal", "id": "../../node_modules/.pnpm/@stencil+core@4.36.3/node_modules/@stencil/core/internal/index.d.ts::HostAttributes" } } }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) Additional style to apply for custom CSS." }, "getter": false, "setter": false }, "hideBadge": { "type": "boolean", "attribute": "hide-badge", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "(optional) If true, the badge is not displayed" }, "getter": false, "setter": false, "reflect": true, "defaultValue": "false" } }; } static get states() { return { "_helpTexts": {}, "focused": {} }; } static get events() { return [{ "method": "textChange", "name": "textChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when a keyboard input occurred" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }, { "method": "textFieldBlur", "name": "textFieldBlur", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when text field lost focus" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }, { "method": "textFieldFocus", "name": "textFieldFocus", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when text field gained focus" }, "complexType": { "original": "string", "resolved": "string", "references": {} } }, { "method": "rightActionClick", "name": "rightActionClick", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emitted when the right icon is clicked" }, "complexType": { "original": "MouseEvent", "resolved": "MouseEvent", "references": { "MouseEvent": { "location": "global", "id": "global::MouseEvent" } } } }, { "method": "fieldClick", "name": "fieldClick", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [{ "name": "inheritdoc", "text": undefined }], "text": "Emmited when there's a click on this element" }, "complexType": { "original": "MouseEvent", "resolved": "MouseEvent", "references": { "MouseEvent": { "location": "global", "id": "global::MouseEvent" } } } }]; } static get methods() { return { "focusInput": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Focuses the input", "tags": [] } } }; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "value", "methodName": "valueChangeHandler" }, { "propName": "helpText", "methodName": "helpTextChangeHandler" }, { "propName": "forcedFocus", "methodName": "forcedFocusChangeHandler" }, { "propName": "useInputMask", "methodName": "handleUseInputMask" }]; } }