diff --git a/src/pin-input-cell/constants.ts b/src/pin-input-cell/constants.ts new file mode 100644 index 00000000..76476c91 --- /dev/null +++ b/src/pin-input-cell/constants.ts @@ -0,0 +1,5 @@ +export const PIN_INPUT_CELL_FILLED_TYPE = 'cell-filled'; +export const PIN_INPUT_CELL_CLEARED_TYPE = 'cell-cleared'; +export const PIN_INPUT_CELL_CLEARED_ALL_TYPE = 'cell-cleared-all-with-meta-key'; +export const PIN_INPUT_CELL_ARROW_KEY_PRESSED_TYPE = 'arrow-key-pressed'; +export const PIN_INPUT_CELL_OVERFLOW_VALUE_TYPE = 'overflow-value'; diff --git a/src/pin-input-cell/events.ts b/src/pin-input-cell/events.ts new file mode 100644 index 00000000..63144258 --- /dev/null +++ b/src/pin-input-cell/events.ts @@ -0,0 +1,122 @@ +import { + PIN_INPUT_CELL_ARROW_KEY_PRESSED_TYPE, + PIN_INPUT_CELL_CLEARED_ALL_TYPE, + PIN_INPUT_CELL_CLEARED_TYPE, + PIN_INPUT_CELL_FILLED_TYPE, + PIN_INPUT_CELL_OVERFLOW_VALUE_TYPE, +} from './constants'; +import { ValueChangedEventParams } from './types'; + +export class PinInputCellFilled extends Event { + public message: string; + public details: ValueChangedEventParams; + + constructor( + message: string, + details: ValueChangedEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + const type = PIN_INPUT_CELL_FILLED_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} + +export class PinInputCellCleared extends Event { + public message: string; + public details: ValueChangedEventParams; + + constructor( + message: string, + details: ValueChangedEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + + const type = PIN_INPUT_CELL_CLEARED_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} + +export class PinInputCellClearedAll extends Event { + public message: string; + public details: ValueChangedEventParams; + + constructor( + message: string, + details: ValueChangedEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + + const type = PIN_INPUT_CELL_CLEARED_ALL_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} + +export class PinInputCellArrowKeyPressed extends Event { + public message: string; + public details: ValueChangedEventParams; + + constructor( + message: string, + details: ValueChangedEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + + const type = PIN_INPUT_CELL_ARROW_KEY_PRESSED_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} + +export class PinInputCellOverflowValue extends Event { + public message: string; + public details: ValueChangedEventParams; + + constructor( + message: string, + details: ValueChangedEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + + const type = PIN_INPUT_CELL_OVERFLOW_VALUE_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} diff --git a/src/pin-input-cell/index.ts b/src/pin-input-cell/index.ts index f459296b..c7556922 100644 --- a/src/pin-input-cell/index.ts +++ b/src/pin-input-cell/index.ts @@ -1,6 +1,8 @@ import { customElement } from 'lit/decorators.js'; import { PinInputCell } from './pin-input-cell.js'; import styles from './pin-input-cell.style.js'; +export * from './events.js'; +export * from './constants.js'; @customElement('tap-pin-input-cell') export class TapPinInputCell extends PinInputCell { diff --git a/src/pin-input-cell/pin-input-cell.style.ts b/src/pin-input-cell/pin-input-cell.style.ts index 3bb00936..acce4dc1 100644 --- a/src/pin-input-cell/pin-input-cell.style.ts +++ b/src/pin-input-cell/pin-input-cell.style.ts @@ -1,4 +1,4 @@ -import {css} from 'lit'; +import { css } from 'lit'; export default css` :host { @@ -7,17 +7,44 @@ export default css` --tap-pin-input-cell-max-cell-width-ratio: 1.25; --tap-pin-input-cell-font-family: var(--tap-sys-font-family); - --tap-pin-input-cell-font-size-small: var(--tap-sys-typography-headline-xs-size, 16px); - --tap-pin-input-cell-font-size-medium: var(--tap-sys-typography-headline-sm-size, 20px); - --tap-pin-input-cell-font-size-large: var(--tap-sys-typography-headline-md-size, 24px); + --tap-pin-input-cell-font-size-small: var( + --tap-sys-typography-headline-xs-size, + 16px + ); + --tap-pin-input-cell-font-size-medium: var( + --tap-sys-typography-headline-sm-size, + 20px + ); + --tap-pin-input-cell-font-size-large: var( + --tap-sys-typography-headline-md-size, + 24px + ); - --tap-pin-input-cell-font-weight-small: var(--tap-sys-typography-headline-xs-weight, 600); - --tap-pin-input-cell-font-weight-medium: var(--tap-sys-typography-headline-sm-weight, 600); - --tap-pin-input-cell-font-weight-large: var(--tap-sys-typography-headline-md-weight, 600); + --tap-pin-input-cell-font-weight-small: var( + --tap-sys-typography-headline-xs-weight, + 600 + ); + --tap-pin-input-cell-font-weight-medium: var( + --tap-sys-typography-headline-sm-weight, + 600 + ); + --tap-pin-input-cell-font-weight-large: var( + --tap-sys-typography-headline-md-weight, + 600 + ); - --tap-pin-input-cell-line-height-small: var(--tap-sys-typography-headline-xs-height, 26px); - --tap-pin-input-cell-line-height-medium: var(--tap-sys-typography-headline-sm-height, 32px); - --tap-pin-input-cell-line-height-large: var(--tap-sys-typography-headline-md-height, 36px); + --tap-pin-input-cell-line-height-small: var( + --tap-sys-typography-headline-xs-height, + 26px + ); + --tap-pin-input-cell-line-height-medium: var( + --tap-sys-typography-headline-sm-height, + 32px + ); + --tap-pin-input-cell-line-height-large: var( + --tap-sys-typography-headline-md-height, + 36px + ); --tap-pin-input-cell-padding-small: var(--tap-sys-spacing-3, 6px); --tap-pin-input-cell-padding-medium: var(--tap-sys-spacing-4, 8px); @@ -25,12 +52,16 @@ export default css` --tap-pin-input-cell-bg-color: var(--tap-palette-gray-100); --tap-pin-input-cell-error-bg-color: var(--tap-palette-red-50); - --tap-pin-input-cell-disabled-bg-color: var(--tap-sys-color-surface-disabled); + --tap-pin-input-cell-disabled-bg-color: var( + --tap-sys-color-surface-disabled + ); --tap-pin-input-cell-color: var(--tap-palette-black); --tap-pin-input-cell-disabled-color: var(--tap-sys-color-content-disabled); --tap-pin-input-cell-border-focus-color: var(--tap-sys-color-border-focus); - --tap-pin-input-cell-border-error-color: var(--tap-sys-color-border-negative); + --tap-pin-input-cell-border-error-color: var( + --tap-sys-color-border-negative + ); --tap-pin-input-cell-border-disabled-color: transparent; --tap-pin-input-cell-border-radius: var(--tap-sys-radius-3); --tap-pin-input-cell-border-width: var(--tap-sys-stroke-4); @@ -53,7 +84,8 @@ export default css` margin: 0; background-color: var(--tap-pin-input-cell-bg-color); color: var(--tap-pin-input-cell-color); - border: var(--tap-pin-input-cell-border-width) solid var(--tap-pin-input-cell-border-default-color); + border: var(--tap-pin-input-cell-border-width) solid + var(--tap-pin-input-cell-border-default-color); border-radius: var(--tap-pin-input-cell-border-radius); outline: none; min-width: 0; @@ -62,7 +94,7 @@ export default css` min-width: var(--tap-pin-input-cell-height); max-width: calc( var(--tap-pin-input-cell-height) * - var(--tap-pin-input-cell-max-cell-width-ratio) + var(--tap-pin-input-cell-max-cell-width-ratio) ); } diff --git a/src/pin-input-cell/pin-input-cell.ts b/src/pin-input-cell/pin-input-cell.ts index 9045ab76..93307077 100644 --- a/src/pin-input-cell/pin-input-cell.ts +++ b/src/pin-input-cell/pin-input-cell.ts @@ -1,6 +1,6 @@ -import {html, LitElement, PropertyValues} from 'lit'; -import {property, query} from 'lit/decorators.js'; -import {classMap} from 'lit/directives/class-map.js'; +import { html, LitElement, PropertyValues } from 'lit'; +import { property, query } from 'lit/decorators.js'; +import { classMap } from 'lit/directives/class-map.js'; import { isArrowKeyPressed, isDeletionKeyPressed, @@ -8,26 +8,32 @@ import { isValidDigit, persianToEnglish, } from './util'; -import {ValueChangedEventParams} from './types'; +import { + PinInputCellArrowKeyPressed, + PinInputCellCleared, + PinInputCellClearedAll, + PinInputCellFilled, + PinInputCellOverflowValue, +} from './events'; export class PinInputCell extends LitElement { @query('.cell') _cell!: HTMLInputElement; - @property({type: Boolean, reflect: true}) disabled: boolean = false; + @property({ type: Boolean, reflect: true }) disabled: boolean = false; - @property({attribute: 'auto-focus', type: Boolean}) autoFocus: boolean = + @property({ attribute: 'auto-focus', type: Boolean }) autoFocus: boolean = false; - @property({reflect: true, type: Boolean, attribute: 'has-error'}) hasError = + @property({ reflect: true, type: Boolean, attribute: 'has-error' }) hasError = false; - @property({reflect: true, type: String}) value: string = ''; + @property({ reflect: true, type: String }) value: string = ''; @property() label = ''; @property() size: 'small' | 'medium' | 'large' = 'medium'; - @property({type: Number}) index: number = null!; + @property({ type: Number }) index: number = null!; protected updated(changed: PropertyValues) { if (changed.has('value') && !Number.isNaN(this.value)) { @@ -37,7 +43,7 @@ export class PinInputCell extends LitElement { private async updateInputValue(newValue: string) { this.value = persianToEnglish(newValue); - this._cell.value = (newValue); + this._cell.value = newValue; if (newValue.length) { await this.emitValueChanged(); @@ -50,71 +56,63 @@ export class PinInputCell extends LitElement { private async emitValueChanged() { await this.updateComplete; - const event = new CustomEvent('cell-filled', { - bubbles: true, - composed: false, - detail: { - cell: this, - index: this.index, - value: this.value, - } as ValueChangedEventParams, + const event = new PinInputCellFilled('PinInputCell filled', { + cell: this, + index: this.index, + value: this.value, }); + this.dispatchEvent(event); } private async emitValueCleared() { await this.updateComplete; - const event = new CustomEvent('cell-cleared', { - bubbles: true, - composed: false, - detail: { - cell: this, - index: this.index, - value: this.value, - } as ValueChangedEventParams, + const event = new PinInputCellCleared('PinInputCell cleared', { + cell: this, + index: this.index, + value: this.value, }); + this.dispatchEvent(event); } private async emitDeletionWithMetaKeys() { await this.updateComplete; - const event = new CustomEvent('clear-all', { - bubbles: true, - composed: false, - detail: { - cell: this, - index: this.index, - value: this.value, - } as ValueChangedEventParams, + const event = new PinInputCellClearedAll('PinInputCell cleared all', { + cell: this, + index: this.index, + value: this.value, }); + this.dispatchEvent(event); } private async emitArrowKeyPressed(key: 'ArrowLeft' | 'ArrowRight') { await this.updateComplete; - const event = new CustomEvent('arrow-key-pressed', { - bubbles: true, - composed: false, - detail: { + + const event = new PinInputCellArrowKeyPressed<'left' | 'right'>( + 'PinInputCell arrow key pressed', + { cell: this, index: this.index, value: key === 'ArrowLeft' ? 'left' : 'right', - } as ValueChangedEventParams<'left' | 'right'>, - }); + }, + ); + this.dispatchEvent(event); } private async emitOverflowedValue(value: string) { await this.updateComplete; - const event = new CustomEvent('overflow-value', { - bubbles: true, - composed: false, - detail: { + const event = new PinInputCellOverflowValue( + 'PinInputCell Overflowed value', + { cell: this, index: this.index, value: value, - } as ValueChangedEventParams, - }); + }, + ); + this.dispatchEvent(event); } diff --git a/src/pin-input/constants.ts b/src/pin-input/constants.ts new file mode 100644 index 00000000..6a3fd641 --- /dev/null +++ b/src/pin-input/constants.ts @@ -0,0 +1 @@ +export const PIN_INPUT_FILLED_TYPE = 'input-filled'; diff --git a/src/pin-input/events.ts b/src/pin-input/events.ts new file mode 100644 index 00000000..9c608c45 --- /dev/null +++ b/src/pin-input/events.ts @@ -0,0 +1,25 @@ +import { PIN_INPUT_FILLED_TYPE } from './constants'; +import { InputFilledEventParams } from './types'; + +export class PinInputFilled extends Event { + public message: string; + public details: InputFilledEventParams; + + constructor( + message: string, + details: InputFilledEventParams, + eventInitDict: EventInit = {}, + ) { + const _eventInitDict = { + bubbles: true, + composed: false, + ...eventInitDict, + }; + + const type = PIN_INPUT_FILLED_TYPE; + super(type, _eventInitDict); + + this.details = details; + this.message = message; + } +} diff --git a/src/pin-input/index.ts b/src/pin-input/index.ts index e6630a6a..d82b9389 100644 --- a/src/pin-input/index.ts +++ b/src/pin-input/index.ts @@ -1,6 +1,8 @@ import { customElement } from 'lit/decorators.js'; import { PinInput } from './pin-input.js'; import styles from './pin-input.style.js'; +export * from './events.js'; +export * from './constants.js'; @customElement('tap-pin-input') export class TapPinInput extends PinInput { diff --git a/src/pin-input/pin-input.stories.ts b/src/pin-input/pin-input.stories.ts index f8058e62..7cdb834c 100644 --- a/src/pin-input/pin-input.stories.ts +++ b/src/pin-input/pin-input.stories.ts @@ -20,7 +20,7 @@ const Template: Story = (/*{}: ArgTypes*/) => html` console.log(e)} + @input-filled=${(e: Error) => console.log(e)} > `; diff --git a/src/pin-input/pin-input.style.ts b/src/pin-input/pin-input.style.ts index c4d7c01e..622d7f0c 100644 --- a/src/pin-input/pin-input.style.ts +++ b/src/pin-input/pin-input.style.ts @@ -1,29 +1,57 @@ -import {css} from 'lit'; +import { css } from 'lit'; export default css` :host { --tap-pin-input-box-sizing: border-box; --tap-pin-input-direction: rtl; - --tap-pin-input-description-font-family: var(--tap-sys-typography-body-sm-font); - --tap-pin-input-description-font-size: var(--tap-sys-typography-body-sm-size); - --tap-pin-input-description-line-height: var(--tap-sys-typography-body-sm-height); - --tap-pin-input-description-font-weight: var(--tap-sys-typography-body-sm-weight); + --tap-pin-input-description-font-family: var( + --tap-sys-typography-body-sm-font + ); + --tap-pin-input-description-font-size: var( + --tap-sys-typography-body-sm-size + ); + --tap-pin-input-description-line-height: var( + --tap-sys-typography-body-sm-height + ); + --tap-pin-input-description-font-weight: var( + --tap-sys-typography-body-sm-weight + ); - --tap-pin-input-description-font-family: var(--tap-sys-typography-body-sm-font); - --tap-pin-input-description-font-size: var(--tap-sys-typography-body-sm-size); - --tap-pin-input-description-line-height: var(--tap-sys-typography-body-sm-height); - --tap-pin-input-description-font-weight: var(--tap-sys-typography-body-sm-weight); - --tap-pin-input-description-text-color: var(--tap-sys-color-content-tertiary); - --tap-pin-input-description-error-text-color: var(--tap-sys-color-content-negative); - --tap-pin-input-description-disabled-text-color: var(--tap-sys-color-content-disabled); + --tap-pin-input-description-font-family: var( + --tap-sys-typography-body-sm-font + ); + --tap-pin-input-description-font-size: var( + --tap-sys-typography-body-sm-size + ); + --tap-pin-input-description-line-height: var( + --tap-sys-typography-body-sm-height + ); + --tap-pin-input-description-font-weight: var( + --tap-sys-typography-body-sm-weight + ); + --tap-pin-input-description-text-color: var( + --tap-sys-color-content-tertiary + ); + --tap-pin-input-description-error-text-color: var( + --tap-sys-color-content-negative + ); + --tap-pin-input-description-disabled-text-color: var( + --tap-sys-color-content-disabled + ); --tap-inpu-input-title-font-family: var(--tap-sys-typography-body-md-font); --tap-inpu-input-title-font-size: var(--tap-sys-typography-body-md-size); - --tap-inpu-input-title-line-height: var(--tap-sys-typography-body-md-height); - --tap-inpu-input-title-font-weight: var(--tap-sys-typography-body-md-weight); + --tap-inpu-input-title-line-height: var( + --tap-sys-typography-body-md-height + ); + --tap-inpu-input-title-font-weight: var( + --tap-sys-typography-body-md-weight + ); --tap-inpu-input-title-text-color: var(--tap-sys-color-content-primary); - --tap-inpu-input-title-disabled-text-color: var(--tap-sys-color-content-disabled); + --tap-inpu-input-title-disabled-text-color: var( + --tap-sys-color-content-disabled + ); --tap-pin-input-justify-content: start; --tap-input-input-vertical-gap: 0.5rem; diff --git a/src/pin-input/pin-input.ts b/src/pin-input/pin-input.ts index 770245b5..2471e63a 100644 --- a/src/pin-input/pin-input.ts +++ b/src/pin-input/pin-input.ts @@ -4,9 +4,14 @@ import { range } from 'lit/directives/range.js'; import { repeat } from 'lit/directives/repeat.js'; import '../pin-input-cell'; import { PinInputCell } from '../pin-input-cell/pin-input-cell'; -import { englishToPersian } from '../pin-input-cell/util'; -import { InputFilledEventParams } from './types'; -import { ValueChangedEventParams } from '../pin-input-cell/types'; +import { + PinInputCellArrowKeyPressed, + PinInputCellCleared, + PinInputCellClearedAll, + PinInputCellFilled, + PinInputCellOverflowValue, +} from '../pin-input-cell/events'; +import { PinInputFilled } from './events'; export class PinInput extends LitElement { @queryAll('.pin-input-cell') _cells!: PinInputCell[]; @@ -39,13 +44,13 @@ export class PinInput extends LitElement { return this.autoFocusFirstCell && index === 0; } - private handleCellFilled(event: CustomEvent) { - this.focusNextElementByIndex(event.detail.index); + private handleCellFilled(event: PinInputCellFilled) { + this.focusNextElementByIndex(event.details.index); this.handleCellsFilled(); } - private handleCellCleared(event: CustomEvent) { - this.focusPrevElementByIndex(event.detail.index); + private handleCellCleared(event: PinInputCellCleared) { + this.focusPrevElementByIndex(event.details.index); } private handleCellsFilled() { @@ -54,11 +59,9 @@ export class PinInput extends LitElement { } } - private async handleOverflowedCell( - event: CustomEvent, - ) { - let overflowedText = event.detail.value; - const cellIndex = event.detail.index; + private async handleOverflowedCell(event: PinInputCellOverflowValue) { + let overflowedText = event.details.value; + const cellIndex = event.details.index; const isLongerThanRemainingCells = overflowedText.length > this.lastCellIndex - cellIndex; @@ -68,11 +71,10 @@ export class PinInput extends LitElement { await this.fillCells(overflowedText, cellIndex + 1); } - private async handleClearPrevCells( - event: CustomEvent, - ) { + + private async handleClearPrevCells(event: PinInputCellClearedAll) { await this.updateComplete; - const currentIndex = event.detail.index; + const currentIndex = event.details.index; const isNotFirstItem = currentIndex > 0 && this.checkIndexIsInRange(currentIndex); @@ -83,19 +85,17 @@ export class PinInput extends LitElement { } } - private async handleArrowKeyPressed( - event: CustomEvent>, - ) { + private async handleArrowKeyPressed(event: PinInputCellArrowKeyPressed) { await this.updateComplete; - const currentIndex = event.detail.index; + const currentIndex = event.details.index; const shouldPrevItemFocus = - event.detail.value === 'left' && + event.details.value === 'left' && this.checkIndexIsInRange(currentIndex) && !this.checkIndexIsFirst(currentIndex); const shouldNextItemFocus = - event.detail.value === 'right' && + event.details.value === 'right' && this.checkIndexIsInRange(currentIndex) && !this.checkIndexIsLast(currentIndex); @@ -118,14 +118,10 @@ export class PinInput extends LitElement { } private emitPinInputFilled() { - const event = new CustomEvent('filled', { - bubbles: true, - composed: true, - detail: { - value: this.inputValue, - cellCount: this.count, - displayValue: englishToPersian(this.inputValue!), - } as InputFilledEventParams, + const event = new PinInputFilled('PinInput Filled.', { + value: this.inputValue!, + cellCount: this.count, + displayValue: this.inputValue!, }); this.dispatchEvent(event); @@ -200,17 +196,16 @@ export class PinInput extends LitElement { index=${index} ?disabled=${this.disabled} ?has-error=${this.hasError} - @cell-filled=${(e: CustomEvent) => + @cell-filled=${(e: PinInputCellFilled) => this.handleCellFilled(e)} - @cell-cleared=${(e: CustomEvent) => + @cell-cleared=${(e: PinInputCellCleared) => this.handleCellCleared(e)} - @overflow-value=${(e: CustomEvent) => + @overflow-value=${(e: PinInputCellOverflowValue) => this.handleOverflowedCell(e)} - @clear-all=${(e: CustomEvent) => + @cell-cleared-all-with-meta-key=${(e: PinInputCellClearedAll) => this.handleClearPrevCells(e)} - @arrow-key-pressed=${( - e: CustomEvent>, - ) => this.handleArrowKeyPressed(e)} + @arrow-key-pressed=${(e: PinInputCellArrowKeyPressed) => + this.handleArrowKeyPressed(e)} ?auto-focus=${this.isFirstCellShouldAutoFocus(index)} .size=${this.size} >`; diff --git a/src/pin-input/types.ts b/src/pin-input/types.ts index ce99f268..a173751e 100644 --- a/src/pin-input/types.ts +++ b/src/pin-input/types.ts @@ -1,5 +1,5 @@ export type InputFilledEventParams = { - cellCount: number, - value: string, - displayValue: string, -} + cellCount: number; + value: string; + displayValue: string; +};