UNPKG

@scania/tegel

Version:
738 lines (737 loc) 29.2 kB
import { h, } from "@stencil/core"; import generateUniqueId from "../../utils/generateUniqueId"; export class TdsDatetime { constructor() { /** Boolean indicator to trigger input validation */ this.shouldValidate = false; /** Sets an input type */ this.type = 'datetime-local'; /** Value of the input text */ this.value = ''; /** Default value of the component.<br/>Format for date-time: yyyy-MM-ddTHH:mm.<br/>Format for date: yyyy-MM-dd.<br/>Format for month: yyyy-MM.<br/>Format for week: yyyy-Www.<br/>Format for time: HH:mm. */ this.defaultValue = 'none'; /** Set input in disabled state */ this.disabled = false; /** Size of the input */ this.size = 'lg'; /** Resets min width rule */ this.noMinWidth = false; /** Set the variant of the Datetime component. */ this.modeVariant = null; /** Name property. Uses a unique ID as fallback if not specified. */ this.name = `datetime-${generateUniqueId()}`; /** Autofocus for input */ this.autofocus = false; /** Label text for the component */ this.label = ''; /** Position of the label */ this.labelPosition = 'no-label'; /** Default contextual helper text for the component for states = success or none */ this.helper = ''; /** Contextual helper text for the component when input is invalid */ this.helperErrorInvalid = 'Invalid input'; /** Listen to the focus state of the input */ this.focusInput = false; this.nativeValidation = () => { return !((this.min && this.textInput.validity.rangeUnderflow) || (this.max && this.textInput.validity.rangeOverflow) || this.textInput.validity.badInput); }; this.getDefaultValue = () => { const dateTimeObj = { year: this.defaultValue.slice(0, 4), month: this.defaultValue.slice(5, 7), week: this.defaultValue.slice(6, 8), day: this.defaultValue.slice(8, 10), hours: this.defaultValue.slice(11, 13), minutes: this.defaultValue.slice(14, 16), }; switch (this.type) { case 'datetime-local': return `${dateTimeObj.year}-${dateTimeObj.month}-${dateTimeObj.day}T${dateTimeObj.hours}:${dateTimeObj.minutes}`; case 'date': return `${dateTimeObj.year}-${dateTimeObj.month}-${dateTimeObj.day}`; case 'month': return `${dateTimeObj.year}-${dateTimeObj.month}`; case 'week': return `${dateTimeObj.year}-W${dateTimeObj.week}`; case 'time': return `${this.defaultValue.slice(0, 2)}:${this.defaultValue.slice(3, 5)}`; default: throw new Error('Invalid type.'); } }; } /** Method that resets the value of the Datetime, using defaultValue if is not 'none' */ async reset() { this.internalReset(); this.tdsChange.emit({ name: this.name, value: this.value, }); } /** Method that sets the value of the datetime element */ async setValue(newValue) { this.value = newValue; } /** Method to programmatically focus the datetime element */ async focusElement() { if (this.textInput) { this.textInput.focus(); this.focusInput = true; } } componentWillLoad() { if (this.defaultValue !== 'none') { this.value = this.getDefaultValue(); } } componentDidRender() { if (!this.shouldValidate) return; this.shouldValidate = false; this.validateDate(); } // Listener if input enters focus state handleFocusIn() { this.focusInput = true; } // Listener if input leaves focus state handleFocusOut() { this.validateDate(); this.focusInput = false; } // Decorator to activate validation when input value changes onValueChanged() { this.shouldValidate = true; } validateDate() { this.state = 'none'; if (!this.nativeValidation() || (this.customValidator && !this.customValidator(this.textInput.value))) { this.state = 'error'; } else if (this.textInput.value) { this.state = 'success'; } } // Data input event in value prop handleInput(e) { this.value = e.target.value; this.tdsInput.emit(e); } // Change event isn't a composed:true by default in for input handleChange(e) { this.tdsChange.emit(e); } /** Set the input as focus when clicking the whole Datetime with suffix/prefix */ handleFocusClick(e) { this.textInput.focus(); this.tdsFocus.emit(e); } /** Set the input as focus when clicking the whole Datetime with suffix/prefix */ handleBlur(e) { this.textInput.blur(); this.tdsBlur.emit(e); } /** Method that resets the dateteime without emitting an event. */ internalReset() { const value = ''; if (this.defaultValue !== 'none') { this.value = this.getDefaultValue(); } this.value = value; } // iOS native date picker doesn't support min/max in the UI; we rely on validation + error message. // The .iphone class enables iOS-specific styling for error/success states. render() { var _a, _b; const iphone = navigator.userAgent.toLowerCase().includes('iphone'); let className = ' tds-datetime-input'; if (this.size === 'md') { className += `${className}-md`; } if (this.size === 'sm') { className += `${className}-sm`; } if (iphone) { className += ' iphone'; } const classNames = { 'tds-form-datetime-nomin': this.noMinWidth, 'tds-form-datetime': true, 'tds-datetime-focus': this.focusInput, 'tds-datetime-data': this.value.length > 0, 'tds-form-datetime-disabled': this.disabled, [`tds-form-datetime-${this.size}`]: ['md', 'sm'].includes(this.size), [`tds-form-datetime-${this.state}`]: ['error', 'success'].includes(`${this.state}`), [`tds-mode-variant-${this.modeVariant}`]: this.modeVariant !== null, 'tds-datetime-container-label-inside': !!(this.label && this.labelPosition === 'inside' && this.size !== 'sm'), }; return (h("div", { key: 'a55d784701c35fda184c9e17fbe8466854f9140d', class: classNames, onKeyDown: (e) => { if (e.key === 'Enter') { const browserIsChrome = navigator.userAgent.toLowerCase().includes('chrome'); if (browserIsChrome) { // showPicker currently only works reliably for date inputs in Chrome and Chromium-based browsers: this.textInput.showPicker(); } } } }, this.labelPosition === 'outside' && this.label && (h("label", { key: 'ff47d44b79345c816cf9b22bd913b6a595299dce', htmlFor: this.name, class: "tds-datetime-label" }, this.label)), h("div", { key: '013669e9b4f3421e96b9239a187515967baf7619', onClick: (e) => this.handleFocusClick(e), class: "tds-datetime-container" }, h("div", { key: 'f06de994b6b1c8fe1db77f914be22797db14018d', class: `tds-datetime-input-container type-${this.type}` }, h("input", { key: 'c003adf22a66a2a04f13abd636c924bd1ad1c6c9', ref: (inputEl) => { if (inputEl) this.textInput = inputEl; }, class: className, type: this.type, disabled: this.disabled, value: this.value, min: this.min, max: this.max, autofocus: this.autofocus, name: this.name, id: this.name, onInput: (e) => this.handleInput(e), onBlur: (e) => this.handleBlur(e), onChange: (e) => this.handleChange(e), "aria-label": this.tdsAriaLabel ? this.tdsAriaLabel : this.label }), this.labelPosition === 'inside' && this.size !== 'sm' && this.label && (h("label", { key: '128a8b90a28b6c1c9c2eacc292603b3d1f8e5f10', class: "tds-datetime-label-inside", htmlFor: this.name }, this.label)), h("div", { key: '21331931956e4abf045a01a4af7f5fcf602990bd', class: "datetime-icon icon-datetime-local" }, h("tds-icon", { key: '77c1f611eae6c6ff3d102681b28ce42e0709ff84', size: "20px", name: "calendar", svgTitle: "Calendar" })), h("div", { key: '36f01c633df5181ad6107aa158de499fafa2569f', class: "datetime-icon icon-time" }, h("tds-icon", { key: '03bb2f56e70a68c8d882bdfdce747a15d1b4cfdc', size: "20px", name: "clock", svgTitle: "Clock" })))), this.state === 'error' && (h("div", { key: '40fbf2006d5709b84468e0e05b5b51804c141923', class: "tds-datetime-helper" }, !((_b = (_a = this.textInput) === null || _a === void 0 ? void 0 : _a.validity) === null || _b === void 0 ? void 0 : _b.badInput) && (h("div", { key: '7f2990d54c62fbef3743662b944a09aab28a8a56', class: "tds-helper" }, h("tds-icon", { key: 'cdcb11a48a1d1fc3298711ed9f3b96b6a7ecef0d', name: "error", size: "16px", svgTitle: "error" }), " ", this.helperError)), this.textInput && this.textInput.validity.badInput && (h("div", { key: '51489ac6f3dbcb31f8ff89c1544a6a17c19ab239', class: "tds-helper" }, h("tds-icon", { key: '6fb19117b7f191564ed2b1c4071500a16ea40219', name: "error", size: "16px", svgTitle: "error" }), " ", this.helperErrorInvalid)))), this.helper && this.state !== 'error' && (h("div", { key: 'a6766afd774a6219e783ec1b5e79c250b94bace2', class: "tds-datetime-helper" }, h("div", { key: 'bba762fea80aca3e14da8ac9f9f210234edd9c70', class: "tds-helper" }, this.helper))))); } static get is() { return "tds-datetime"; } static get encapsulation() { return "scoped"; } static get originalStyleUrls() { return { "$": ["datetime.scss"] }; } static get styleUrls() { return { "$": ["datetime.css"] }; } static get properties() { return { "type": { "type": "string", "mutable": false, "complexType": { "original": "'datetime-local' | 'date' | 'month' | 'week' | 'time'", "resolved": "\"date\" | \"datetime-local\" | \"month\" | \"time\" | \"week\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Sets an input type" }, "getter": false, "setter": false, "reflect": true, "attribute": "type", "defaultValue": "'datetime-local'" }, "value": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Value of the input text" }, "getter": false, "setter": false, "reflect": true, "attribute": "value", "defaultValue": "''" }, "min": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Sets min value.<br/>Example for different types:<br/>datetime=\"2023-01-31T00:00\"<br/>date=\"2023-01-01\"<br/>month=\"2023-01\"<br/>week=\"2023-W02\"<br/>time=\"15:00\"" }, "getter": false, "setter": false, "reflect": false, "attribute": "min" }, "max": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Sets max value.<br/>Example for different types:<br/>datetime=\"2023-01-31T00:00\"<br/>date=\"2023-01-01\"<br/>month=\"2023-01\"<br/>week=\"2023-W02\"<br/>time=\"15:00\"" }, "getter": false, "setter": false, "reflect": false, "attribute": "max" }, "defaultValue": { "type": "string", "mutable": false, "complexType": { "original": "string | 'none'", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Default value of the component.<br/>Format for date-time: yyyy-MM-ddTHH:mm.<br/>Format for date: yyyy-MM-dd.<br/>Format for month: yyyy-MM.<br/>Format for week: yyyy-Www.<br/>Format for time: HH:mm." }, "getter": false, "setter": false, "reflect": false, "attribute": "default-value", "defaultValue": "'none'" }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set input in disabled state" }, "getter": false, "setter": false, "reflect": false, "attribute": "disabled", "defaultValue": "false" }, "size": { "type": "string", "mutable": false, "complexType": { "original": "'sm' | 'md' | 'lg'", "resolved": "\"lg\" | \"md\" | \"sm\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Size of the input" }, "getter": false, "setter": false, "reflect": false, "attribute": "size", "defaultValue": "'lg'" }, "noMinWidth": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Resets min width rule" }, "getter": false, "setter": false, "reflect": false, "attribute": "no-min-width", "defaultValue": "false" }, "modeVariant": { "type": "string", "mutable": false, "complexType": { "original": "'primary' | 'secondary' | null", "resolved": "\"primary\" | \"secondary\" | null", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set the variant of the Datetime component." }, "getter": false, "setter": false, "reflect": false, "attribute": "mode-variant", "defaultValue": "null" }, "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Name property. Uses a unique ID as fallback if not specified." }, "getter": false, "setter": false, "reflect": false, "attribute": "name", "defaultValue": "`datetime-${generateUniqueId()}`" }, "state": { "type": "string", "mutable": true, "complexType": { "original": "'none' | 'success' | 'error'", "resolved": "\"error\" | \"none\" | \"success\" | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Switches between success and error state." }, "getter": false, "setter": false, "reflect": false, "attribute": "state" }, "autofocus": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Autofocus for input" }, "getter": false, "setter": false, "reflect": false, "attribute": "autofocus", "defaultValue": "false" }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Label text for the component" }, "getter": false, "setter": false, "reflect": false, "attribute": "label", "defaultValue": "''" }, "labelPosition": { "type": "string", "mutable": false, "complexType": { "original": "'inside' | 'outside' | 'no-label'", "resolved": "\"inside\" | \"no-label\" | \"outside\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Position of the label" }, "getter": false, "setter": false, "reflect": false, "attribute": "label-position", "defaultValue": "'no-label'" }, "helper": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Default contextual helper text for the component for states = success or none" }, "getter": false, "setter": false, "reflect": false, "attribute": "helper", "defaultValue": "''" }, "helperError": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Contextual helper text for the component for error state" }, "getter": false, "setter": false, "reflect": false, "attribute": "helper-error" }, "helperErrorInvalid": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Contextual helper text for the component when input is invalid" }, "getter": false, "setter": false, "reflect": false, "attribute": "helper-error-invalid", "defaultValue": "'Invalid input'" }, "tdsAriaLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Value for the aria-label attribute" }, "getter": false, "setter": false, "reflect": false, "attribute": "tds-aria-label" }, "customValidator": { "type": "unknown", "mutable": false, "complexType": { "original": "(value: string) => boolean", "resolved": "((value: string) => boolean) | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Function for additional validation based on business rules" }, "getter": false, "setter": false } }; } static get states() { return { "focusInput": {} }; } static get events() { return [{ "method": "tdsChange", "name": "tdsChange", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Change event for the Datetime" }, "complexType": { "original": "any", "resolved": "any", "references": {} } }, { "method": "tdsBlur", "name": "tdsBlur", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Blur event for the Datetime" }, "complexType": { "original": "FocusEvent", "resolved": "FocusEvent", "references": { "FocusEvent": { "location": "global", "id": "global::FocusEvent" } } } }, { "method": "tdsFocus", "name": "tdsFocus", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Focus event for the Datetime" }, "complexType": { "original": "FocusEvent", "resolved": "FocusEvent", "references": { "FocusEvent": { "location": "global", "id": "global::FocusEvent" } } } }, { "method": "tdsInput", "name": "tdsInput", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Input event for the Datetime" }, "complexType": { "original": "InputEvent", "resolved": "InputEvent", "references": { "InputEvent": { "location": "global", "id": "global::InputEvent" } } } }]; } static get methods() { return { "reset": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Method that resets the value of the Datetime, using defaultValue if is not 'none'", "tags": [] } }, "setValue": { "complexType": { "signature": "(newValue: string) => Promise<void>", "parameters": [{ "name": "newValue", "type": "string", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Method that sets the value of the datetime element", "tags": [] } }, "focusElement": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Method to programmatically focus the datetime element", "tags": [] } } }; } static get watchers() { return [{ "propName": "value", "methodName": "onValueChanged" }]; } static get listeners() { return [{ "name": "focusin", "method": "handleFocusIn", "target": undefined, "capture": false, "passive": false }, { "name": "focusout", "method": "handleFocusOut", "target": undefined, "capture": false, "passive": false }]; } }