From 6c3cd423956fdcfa7ca90fdbd67bc7062e0d6b9c Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Thu, 19 Oct 2023 00:39:40 +0530 Subject: [PATCH 01/28] input element should able to create object for valid config --- tests/components/inputs.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/components/inputs.ts diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts new file mode 100644 index 0000000..8f68a92 --- /dev/null +++ b/tests/components/inputs.ts @@ -0,0 +1,26 @@ +/* eslint-disable no-console */ +import { expect } from 'chai'; +import { beforeEach, describe, it } from 'mocha'; +import InputElement from '../../src/ts/components/forms/inputs'; +import { InputElementConfig } from '../../src/ts/components/forms/types'; + +describe('Test Input Element', () => { + let config: InputElementConfig; + + beforeEach(() => { + config = { + namespace: 'test-namespace', + onValueEmpty: () => { + console.log('value empty'); + }, + onValueNonEmpty: () => { + console.log('value non empty'); + }, + }; + }); + + it('should able to create object for valid config', () => { + const inputElement = new InputElement(config); + expect(inputElement).to.be.instanceOf(InputElement); + }); +}); From 357f53cdf77f8ff7c900817834be14e7b7c61426 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Thu, 19 Oct 2023 00:41:34 +0530 Subject: [PATCH 02/28] input element should able to create object for valid config --- tests/components/inputs.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/components/inputs.ts diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts new file mode 100644 index 0000000..8f68a92 --- /dev/null +++ b/tests/components/inputs.ts @@ -0,0 +1,26 @@ +/* eslint-disable no-console */ +import { expect } from 'chai'; +import { beforeEach, describe, it } from 'mocha'; +import InputElement from '../../src/ts/components/forms/inputs'; +import { InputElementConfig } from '../../src/ts/components/forms/types'; + +describe('Test Input Element', () => { + let config: InputElementConfig; + + beforeEach(() => { + config = { + namespace: 'test-namespace', + onValueEmpty: () => { + console.log('value empty'); + }, + onValueNonEmpty: () => { + console.log('value non empty'); + }, + }; + }); + + it('should able to create object for valid config', () => { + const inputElement = new InputElement(config); + expect(inputElement).to.be.instanceOf(InputElement); + }); +}); From b79bdc5768f36034c6b137982f78706fac924960 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Thu, 19 Oct 2023 00:42:56 +0530 Subject: [PATCH 03/28] input element should has correct id --- tests/components/inputs.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 8f68a92..6cc23ce 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -20,7 +20,18 @@ describe('Test Input Element', () => { }); it('should able to create object for valid config', () => { - const inputElement = new InputElement(config); - expect(inputElement).to.be.instanceOf(InputElement); + // Create InputElement instance + const input = new InputElement(config); + + // Assert that input is instance of InputElement + expect(input).to.be.instanceOf(InputElement); + }); + + it('should has correct id', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Assert that id is correct + expect(input.id).to.equal(`input-test-namespace`); }); }); From f44ca38fb085c66e1c37530b3edffa73e852eb4f Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Fri, 20 Oct 2023 01:59:28 +0530 Subject: [PATCH 04/28] input element querySelector should retrieve button HTMLElement --- tests/components/inputs.ts | 43 +++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 6cc23ce..d6bd7da 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -1,13 +1,20 @@ /* eslint-disable no-console */ +/* eslint-disable no-unused-expressions */ import { expect } from 'chai'; -import { beforeEach, describe, it } from 'mocha'; +import { afterEach, beforeEach, describe, it } from 'mocha'; +import sinon from 'sinon'; import InputElement from '../../src/ts/components/forms/inputs'; import { InputElementConfig } from '../../src/ts/components/forms/types'; +const jsdom = require('jsdom-global'); + describe('Test Input Element', () => { let config: InputElementConfig; + let cleanup: any; beforeEach(() => { + cleanup = jsdom(); + config = { namespace: 'test-namespace', onValueEmpty: () => { @@ -19,6 +26,14 @@ describe('Test Input Element', () => { }; }); + afterEach(() => { + // cleanup jsdom + cleanup(); + + // Restore the spies + sinon.restore(); + }); + it('should able to create object for valid config', () => { // Create InputElement instance const input = new InputElement(config); @@ -34,4 +49,30 @@ describe('Test Input Element', () => { // Assert that id is correct expect(input.id).to.equal(`input-test-namespace`); }); + + it('querySelector should retrieve button HTMLElement', () => { + // Create InputElement + const input = new InputElement(config); + + // Expected input id + const inputId = `input-${config.namespace}`; + + // Create spy for document getElementById + const getElementByIdSpy = sinon.spy(document, 'getElementById'); + + // Call the build method + input.build(document.body); + + // Call querySelector + const result = input.querySelector; + + // Assert that getElementById call with expected id + expect(getElementByIdSpy.calledOnceWith(inputId)).to.be.true; + + // Assert that the result is an HTMLInputElement + expect(result).to.be.an.instanceOf(HTMLInputElement); + + // Assert that result HTMLElement has expected id + expect(result.id).to.equal(inputId); + }); }); From 65903407c43f1f336d1509a93b69605406129956 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Fri, 20 Oct 2023 02:10:00 +0530 Subject: [PATCH 05/28] make input component skeleton public for testing --- dist/js/maps/bundle.js.map | 2 +- src/ts/components/forms/inputs.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/js/maps/bundle.js.map b/dist/js/maps/bundle.js.map index 1d1d08d..043c8db 100644 --- a/dist/js/maps/bundle.js.map +++ b/dist/js/maps/bundle.js.map @@ -1 +1 @@ -{"version":3,"sources":["node_modules/browser-pack/_prelude.js","bundle.js","src/ts/components/buttons/buttons.ts","src/ts/components/forms/inputs.ts","src/ts/layout/data.ts","src/ts/layout/keypad.ts","src/ts/main.ts","src/ts/pages/dialpad.ts","src/ts/utilities/longPress.ts"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","module","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","Constructor","protoProps","staticProps","prototype","__importDefault","mod","__esModule","default","value","longPress_1","DialpadButton","config","instance","TypeError","_classCallCheck","this","undefined","subtitle","onLongPress","button","_this","apply","onLongPressCallback","onPressStart","onClick","title","onLongPressCancel","_this2","addEventListener","applyLongPressEvent","addClickEventListener","parentElement","appendChild","skeleton","get","document","createElement","id","classList","add","setAttribute","ariaLabel","titleElement","subtitleElement","configureButtonEvents","innerText","_a","namespace","getElementById","InputElement","focused","focus","querySelector","currentValue","selectionStartPosition","selectionEndPosition","updatedValue","slice","updatedCaretPosition","selectionPosition","count","arguments","currentCaretPosition","beforeCaretValue","afterCaretValue","endIndex","inputEventHandler","removeValue","insertValue","caretPositionBeforeRemoveUnwantedChars","updatedValueAfterRemoveUnwantedChars","replace","validation","console","log","onValueEmpty","onValueNonEmpty","input","className","name","type","autofocus","inputMode","autocomplete","bind","makeSureFocused","selectionStart","selectionEnd","set","position","activeElement","buttons_1","data_1","Keypad","iconName","iconElement","backspaceButtonElement","disabled","keypad","forEach","onKeypadBtnClick","onZeroBtnLongPress","onZeroBtnLongPressCancel","build","callButton","backspaceButton","callBtn","getMaterialIcon","onCallBtnClick","backspaceBtn","onBackspaceBtnClick","onBackspaceBtnLongPress","dialpad_1","window","onload","body","inputs_1","keypad_1","Dialpad","dialpad","inputFieldLayout","keypadLayout","copyrightText","element","remove","inputField","disableBackspaceButton","enableBackspaceButton","replaceValue","recentCallOnNumber","copyrightTextElement","Date","getFullYear","LongPressEvent","isHeld","activeHoldTimeoutId","longPressTimeout","onHoldStart","onHoldEnd","setTimeout","clearTimeout"],"mappings":"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","file":"../bundle.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i {\n this.config.onLongPress!(this.config.subtitle!);\n },\n onPressStart: () => {\n this.config.onClick(this.config.title);\n },\n onLongPressCancel: this.config.onLongPressCancel,\n });\n }\n\n /**\n *\n * Apply click event listener on button element\n *\n * @param button\n *\n */\n addClickEventListener(button: HTMLButtonElement) {\n button.addEventListener('click', () => {\n this.config.onClick(this.config.title);\n });\n }\n\n /**\n *\n * If onLongPress callback is exist in config apply long press event\n * Else apply click event listener on button\n *\n * @param button\n *\n */\n configureButtonEvents(button: HTMLButtonElement) {\n if (this.config.onLongPress !== undefined) {\n // apply long press event\n this.applyLongPressEvent(button);\n } else {\n // add click event listener\n this.addClickEventListener(button);\n }\n }\n\n /**\n *\n * Dialpad button tile element\n *\n */\n get titleElement(): HTMLElement {\n const title = document.createElement('h1');\n title.classList.add('dialpad-btn__title');\n title.innerText = this.config.title;\n return title;\n }\n\n /**\n *\n * Dialpad button subtile\n *\n */\n get subtitleElement(): HTMLElement {\n const subtitle = document.createElement('p');\n subtitle.classList.add('dialpad-btn__subtitle');\n subtitle.innerText = this.config.subtitle ?? '';\n return subtitle;\n }\n\n /**\n *\n * Unique id for dialpad button\n *\n */\n get id() {\n return `dialpad-btn-${this.config.namespace}`;\n }\n\n /**\n *\n * Dialpad button query selector for dom manipulation\n *\n */\n get querySelector(): HTMLButtonElement {\n return document.getElementById(this.id)! as HTMLButtonElement;\n }\n\n /**\n *\n * Appends dialpad button to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n}\n","import { InputElementConfig } from './types';\r\n\r\n/**\r\n *\r\n * Input Element\r\n *\r\n */\r\nexport default class InputElement {\r\n // input element config\r\n private config: InputElementConfig;\r\n\r\n /**\r\n *\r\n * construct InputElement instance\r\n *\r\n */\r\n constructor(config: InputElementConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * input element skeleton\r\n *\r\n */\r\n private get skeleton(): HTMLInputElement {\r\n const input = document.createElement('input');\r\n input.id = this.id;\r\n input.className = 'input-element';\r\n input.name = 'number';\r\n input.type = 'text';\r\n input.autofocus = true;\r\n input.inputMode = 'none';\r\n input.autocomplete = 'off';\r\n\r\n // add input event listener\r\n input.addEventListener('input', this.inputEventHandler.bind(this));\r\n\r\n return input;\r\n }\r\n\r\n /**\r\n *\r\n * Input element query selector for dom manipulation\r\n *\r\n */\r\n get querySelector(): HTMLInputElement {\r\n return document.getElementById(this.id) as HTMLInputElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for input element\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `input-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * Appends input element to a specified parent element.\r\n *\r\n * @param parentElement\r\n *\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton);\r\n }\r\n\r\n /**\r\n *\r\n * Make sure input element is focused\r\n *\r\n */\r\n makeSureFocused() {\r\n if (!this.focused) {\r\n this.focus();\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * Get input element selection start position\r\n *\r\n */\r\n get selectionStartPosition(): number {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n return this.querySelector.selectionStart!;\r\n }\r\n\r\n /**\r\n *\r\n * Get input element caret end position\r\n *\r\n */\r\n get selectionEndPosition(): number {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n return this.querySelector.selectionEnd!;\r\n }\r\n\r\n /**\r\n *\r\n * Set input element selection start and end positions\r\n *\r\n * @param position - desired caret position index value\r\n *\r\n */\r\n set selectionPosition(position: number) {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n\r\n // set selection\r\n this.querySelector.selectionStart = position;\r\n this.querySelector.selectionEnd = position;\r\n }\r\n\r\n /**\r\n *\r\n * To check whether the input element is focused or not\r\n *\r\n */\r\n get focused(): boolean {\r\n return document.activeElement === this.querySelector;\r\n }\r\n\r\n /**\r\n *\r\n * To get input element value\r\n *\r\n */\r\n get value(): string {\r\n return this.querySelector.value;\r\n }\r\n\r\n /**\r\n *\r\n * To set input element value\r\n *\r\n * @param value - The value to set\r\n *\r\n */\r\n set value(value: string) {\r\n this.querySelector.value = value;\r\n }\r\n\r\n /**\r\n *\r\n * Focus input element\r\n *\r\n */\r\n focus() {\r\n this.querySelector.focus();\r\n }\r\n\r\n /**\r\n *\r\n * Insert a value at the caret position\r\n *\r\n * @param value - The value to be inserted\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n insertValue(value: string) {\r\n // capture current state\r\n const currentValue = this.value;\r\n const { selectionStartPosition, selectionEndPosition } = this;\r\n\r\n // prepare updated state\r\n let updatedValue;\r\n\r\n if (selectionStartPosition !== selectionEndPosition) {\r\n // if input selection start value is not equal to selection end position\r\n // it means input value is selected, in this case remove selected value\r\n // and insert new value at the selection start (caret) position\r\n updatedValue =\r\n currentValue.slice(0, selectionStartPosition) +\r\n value +\r\n currentValue.slice(selectionEndPosition);\r\n } else {\r\n // else input selection start value is same as selection end value\r\n // insert new value at selection start (caret) position\r\n updatedValue =\r\n currentValue.slice(0, selectionStartPosition) +\r\n value +\r\n currentValue.slice(selectionStartPosition);\r\n }\r\n const updatedCaretPosition = selectionStartPosition + value.length;\r\n\r\n // update state\r\n this.value = updatedValue;\r\n this.selectionPosition = updatedCaretPosition;\r\n }\r\n\r\n /**\r\n *\r\n * Remove characters from the value starting at the caret position\r\n *\r\n * @param count - The number of characters, default value is 1\r\n *\r\n */\r\n removeValue(count: number = 1) {\r\n // capture current state\r\n const currentValue = this.value;\r\n const currentCaretPosition = this.selectionStartPosition;\r\n\r\n // prepare updated state\r\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\r\n\r\n // if before caret value is empty do nothing\r\n if (beforeCaretValue !== '') {\r\n const afterCaretValue = currentValue.slice(currentCaretPosition);\r\n const endIndex = beforeCaretValue.length - count;\r\n const updatedValue =\r\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\r\n const updatedCaretPosition = currentCaretPosition - count;\r\n\r\n // update state\r\n this.value = updatedValue;\r\n this.selectionPosition = updatedCaretPosition;\r\n\r\n // check empty or not\r\n this.inputEventHandler();\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * @param value - The value to be replaced current last value\r\n *\r\n */\r\n replaceValue(value: string) {\r\n this.removeValue();\r\n this.insertValue(value);\r\n this.inputEventHandler();\r\n }\r\n\r\n /**\r\n *\r\n * Clear the value of input element\r\n *\r\n */\r\n clear() {\r\n this.value = '';\r\n this.focus();\r\n }\r\n\r\n /**\r\n *\r\n * Remove unwanted symbols and allow only digits, +, *, and #\r\n *\r\n */\r\n validation() {\r\n // prepare updated state\r\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\r\n const updatedValueAfterRemoveUnwantedChars = this.value.replace(\r\n /[^0-9+*#]/g,\r\n ''\r\n );\r\n const updatedCaretPosition =\r\n caretPositionBeforeRemoveUnwantedChars +\r\n updatedValueAfterRemoveUnwantedChars.length -\r\n this.value.length;\r\n\r\n // update state\r\n this.value = updatedValueAfterRemoveUnwantedChars;\r\n this.selectionPosition = updatedCaretPosition;\r\n }\r\n\r\n /**\r\n *\r\n * `input` event handler\r\n *\r\n */\r\n private inputEventHandler() {\r\n // Remove unwanted symbols and allow only digits, +, *, and #\r\n this.validation();\r\n\r\n // check whether the value is empty or not, and act accordingly.\r\n if (this.value === '') {\r\n // eslint-disable-next-line no-console\r\n console.log('value empty');\r\n this.config.onValueEmpty();\r\n } else {\r\n // eslint-disable-next-line no-console\r\n console.log('value non empty');\r\n this.config.onValueNonEmpty();\r\n }\r\n }\r\n}\r\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\nimport Keypad from '../layout/keypad';\nimport DialpadConfig from './types';\n\n/**\n *\n * Dialpad Page\n *\n */\nexport default class Dialpad {\n // dialpad configuration\n private config: DialpadConfig;\n\n // input field instance\n private inputField!: InputElement;\n\n // keypad instance\n private keypad!: Keypad;\n\n // recent call number\n private recentCallOnNumber?: string;\n\n /**\n *\n * construct Dialpad instance\n *\n */\n constructor(config: DialpadConfig) {\n this.config = config;\n }\n\n /**\n *\n * page skeleton\n *\n * @returns {HTMLDivElement}\n *\n */\n private skeleton(): HTMLElement {\n const dialpad = document.createElement('section');\n dialpad.id = this.id;\n dialpad.className = 'dialpad';\n\n // build input field\n this.inputFieldLayout.build(dialpad);\n\n // build keypad layout\n this.keypadLayout.build(dialpad);\n\n // append copyright text\n dialpad.appendChild(this.copyrightText);\n\n return dialpad;\n }\n\n /**\n *\n * To get Input Element\n *\n */\n private get inputFieldLayout(): InputElement {\n this.inputField = new InputElement({\n namespace: this.config.namespace,\n onValueEmpty: () => {\n // eslint-disable-next-line no-console\n console.log('disable backspace button');\n this.keypad.disableBackspaceButton();\n },\n onValueNonEmpty: () => {\n // eslint-disable-next-line no-console\n console.log('enable backspace button');\n this.keypad.enableBackspaceButton();\n },\n });\n return this.inputField;\n }\n\n /**\n *\n * To get Keypad Layout\n *\n */\n private get keypadLayout() {\n this.keypad = new Keypad({\n namespace: this.config.namespace,\n onKeypadBtnClick: (value: string) => {\n // eslint-disable-next-line no-console\n console.log('clicked on button', value);\n\n // insert value\n this.inputField.insertValue(value);\n this.keypad.enableBackspaceButton();\n },\n onZeroBtnLongPress: (value: string) => {\n // eslint-disable-next-line no-console\n console.log('long pressed on zero button');\n\n // insert zero button subtitle value `+`\n this.inputField.replaceValue(value);\n },\n onZeroBtnLongPressCancel: () => {\n // focus input field\n this.inputField.focus();\n },\n onCallBtnClick: () => {\n // eslint-disable-next-line no-console\n console.log('clicked on call button');\n\n // check whether try to call on recent number\n if (\n this.inputField.value === '' &&\n this.recentCallOnNumber !== undefined\n ) {\n // eslint-disable-next-line no-console\n console.log('fill last call on number in input field value');\n this.inputField.value = this.recentCallOnNumber;\n this.inputField.focus();\n this.keypad.enableBackspaceButton();\n } else {\n // update last call on number\n this.recentCallOnNumber = this.inputField.value;\n\n // eslint-disable-next-line no-console\n console.log('placing call on ', this.inputField.value);\n }\n },\n onBackspaceBtnClick: () => {\n // eslint-disable-next-line no-console\n console.log('clicked on clear button');\n this.inputField.removeValue();\n },\n onBackspaceBtnLongPress: () => {\n // eslint-disable-next-line no-console\n console.log('long press on backspace button');\n\n // clear input value and hide backspace button\n this.inputField.value = '';\n this.inputField.focus();\n this.keypad.disableBackspaceButton();\n },\n });\n\n return this.keypad;\n }\n\n /**\n *\n * append copyright text element\n *\n */\n // eslint-disable-next-line class-methods-use-this\n get copyrightText(): HTMLParagraphElement {\n // create copyright text\n const copyrightTextElement = document.createElement('p');\n copyrightTextElement.className = 'copyright-text';\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\n\n return copyrightTextElement;\n }\n\n /**\n *\n * Unique id for page\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `dialpad-${this.config.namespace}`;\n }\n\n /**\n *\n * querySelect for app\n *\n * @returns {HTMLElement}\n *\n */\n get element(): HTMLElement {\n return document.getElementById(this.id)!;\n }\n\n /**\n *\n * Remove dialpad skeleton from dom\n *\n */\n destroy(): void {\n this.element.remove();\n }\n\n /**\n *\n * Append dialpad skeleton into parent element\n *\n * @param parentElement {HTMLElement}\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton());\n }\n}\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","bundle.js","src/ts/components/buttons/buttons.ts","src/ts/components/forms/inputs.ts","src/ts/layout/data.ts","src/ts/layout/keypad.ts","src/ts/main.ts","src/ts/pages/dialpad.ts","src/ts/utilities/longPress.ts"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","module","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","Constructor","protoProps","staticProps","prototype","__importDefault","mod","__esModule","default","value","longPress_1","DialpadButton","config","instance","TypeError","_classCallCheck","this","undefined","subtitle","onLongPress","button","_this","apply","onLongPressCallback","onPressStart","onClick","title","onLongPressCancel","_this2","addEventListener","applyLongPressEvent","addClickEventListener","parentElement","appendChild","skeleton","get","document","createElement","id","classList","add","setAttribute","ariaLabel","titleElement","subtitleElement","configureButtonEvents","innerText","_a","namespace","getElementById","InputElement","focused","focus","querySelector","currentValue","selectionStartPosition","selectionEndPosition","updatedValue","slice","updatedCaretPosition","selectionPosition","count","arguments","currentCaretPosition","beforeCaretValue","afterCaretValue","endIndex","inputEventHandler","removeValue","insertValue","caretPositionBeforeRemoveUnwantedChars","updatedValueAfterRemoveUnwantedChars","replace","validation","console","log","onValueEmpty","onValueNonEmpty","input","className","name","type","autofocus","inputMode","autocomplete","bind","makeSureFocused","selectionStart","selectionEnd","set","position","activeElement","buttons_1","data_1","Keypad","iconName","iconElement","backspaceButtonElement","disabled","keypad","forEach","onKeypadBtnClick","onZeroBtnLongPress","onZeroBtnLongPressCancel","build","callButton","backspaceButton","callBtn","getMaterialIcon","onCallBtnClick","backspaceBtn","onBackspaceBtnClick","onBackspaceBtnLongPress","dialpad_1","window","onload","body","inputs_1","keypad_1","Dialpad","dialpad","inputFieldLayout","keypadLayout","copyrightText","element","remove","inputField","disableBackspaceButton","enableBackspaceButton","replaceValue","recentCallOnNumber","copyrightTextElement","Date","getFullYear","LongPressEvent","isHeld","activeHoldTimeoutId","longPressTimeout","onHoldStart","onHoldEnd","setTimeout","clearTimeout"],"mappings":"CAAA,SAAAA,EAAAC,EAAAC,EAAAC,GAAA,SAAAC,EAAAC,EAAAC,GAAA,IAAAJ,EAAAG,GAAA,CAAA,IAAAJ,EAAAI,GAAA,CAAA,IAAAE,EAAA,mBAAAC,SAAAA,QAAA,IAAAF,GAAAC,EAAA,OAAAA,EAAAF,GAAA,GAAA,GAAAI,EAAA,OAAAA,EAAAJ,GAAA,GAAA,IAAAK,EAAA,IAAAC,MAAA,uBAAAN,EAAA,KAAA,MAAAK,EAAAE,KAAA,mBAAAF,CAAA,CAAA,IAAAG,EAAAX,EAAAG,GAAA,CAAAS,QAAA,CAAA,GAAAb,EAAAI,GAAA,GAAAU,KAAAF,EAAAC,SAAA,SAAAd,GAAA,OAAAI,EAAAH,EAAAI,GAAA,GAAAL,IAAAA,EAAA,GAAAa,EAAAA,EAAAC,QAAAd,EAAAC,EAAAC,EAAAC,EAAA,CAAA,OAAAD,EAAAG,GAAAS,OAAA,CAAA,IAAA,IAAAL,EAAA,mBAAAD,SAAAA,QAAAH,EAAA,EAAAA,EAAAF,EAAAa,OAAAX,IAAAD,EAAAD,EAAAE,IAAA,OAAAD,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,SAAAI,EAAAS,EAAAH,GCCA,aAEA,IAAII,EAAe,WAAc,SAASC,EAAiBC,EAAQC,GAAS,IAAK,IAAIhB,EAAI,EAAGA,EAAIgB,EAAML,OAAQX,IAAK,CAAE,IAAIiB,EAAaD,EAAMhB,GAAIiB,EAAWC,WAAaD,EAAWC,aAAc,EAAOD,EAAWE,cAAe,EAAU,UAAWF,IAAYA,EAAWG,UAAW,GAAMC,OAAOC,eAAeP,EAAQE,EAAWM,IAAKN,EAAa,CAAE,CAAE,OAAO,SAAUO,EAAaC,EAAYC,GAAiJ,OAA9HD,GAAYX,EAAiBU,EAAYG,UAAWF,GAAiBC,GAAaZ,EAAiBU,EAAaE,GAAqBF,CAAa,CAAG,CAA7hB,GAInB,IAAII,EAA4D,SAAUC,GACtE,OAAOA,GAAOA,EAAIC,WAAaD,EAAM,CAAEE,QAAWF,EACtD,EACAR,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,ICVtD,IAAAC,EAAAL,EAAAzB,EAAA,8BAQqB+B,EDKD,WCIlB,SAAAA,EAAYC,GAEV,GDdJ,SAAyBC,EAAUZ,GAAe,KAAMY,aAAoBZ,GAAgB,MAAM,IAAIa,UAAU,oCAAwC,CCY/GC,CAAAC,KAAAL,QAEbM,IAApBL,EAAOM,eAAiDD,IAAvBL,EAAOO,YAC1C,MAAM,IAAIpC,MAAM,qCAGlBiC,KAAKJ,OAASA,CACf,CD+EC,OAhFAtB,EAAaqB,EAAe,CAAC,CACzBX,IAAK,sBACLS,MAAO,SC+BOW,GAAyB,IAAAC,EAAAL,KAC3CN,EAAAF,QAAec,MAAM,CACnB9B,OAAQ4B,EACRG,oBAAqB,WACnBF,EAAKT,OAAOO,YAAaE,EAAKT,OAAOM,SACtC,EACDM,aAAc,WACZH,EAAKT,OAAOa,QAAQJ,EAAKT,OAAOc,MACjC,EACDC,kBAAmBX,KAAKJ,OAAOe,mBAElC,GD5BI,CACC3B,IAAK,wBACLS,MAAO,SCmCSW,GAAyB,IAAAQ,EAAAZ,KAC7CI,EAAOS,iBAAiB,SAAS,WAC/BD,EAAKhB,OAAOa,QAAQG,EAAKhB,OAAOc,MACjC,GACF,GDhCI,CACC1B,IAAK,wBACLS,MAAO,SCwCSW,QACYH,IAA5BD,KAAKJ,OAAOO,YAEdH,KAAKc,oBAAoBV,GAGzBJ,KAAKe,sBAAsBX,EAE9B,GDzCI,CACCpB,IAAK,QACLS,MAAO,SC0FPuB,GACJA,EAAcC,YAAYjB,KAAKkB,SAChC,GDzFI,CACClC,IAAK,WACLmC,IAAK,WChCT,IAAMf,EAASgB,SAASC,cAAc,UActC,OAbAjB,EAAOkB,GAAKtB,KAAKsB,GACjBlB,EAAOmB,UAAUC,IAAI,eACrBpB,EAAOqB,aAAa,aAAczB,KAAKJ,OAAO8B,WAG9CtB,EAAOa,YAAYjB,KAAK2B,cAGxBvB,EAAOa,YAAYjB,KAAK4B,iBAGxB5B,KAAK6B,sBAAsBzB,GAEpBA,CACR,GD2BI,CACCpB,IAAK,eACLmC,IAAK,WC8BT,IAAMT,EAAQU,SAASC,cAAc,MAGrC,OAFAX,EAAMa,UAAUC,IAAI,sBACpBd,EAAMoB,UAAY9B,KAAKJ,OAAOc,MACvBA,CACR,GD5BI,CACC1B,IAAK,kBACLmC,IAAK,WACD,IAAIY,ECiCN7B,EAAWkB,SAASC,cAAc,KAGxC,OAFAnB,EAASqB,UAAUC,IAAI,yBACvBtB,EAAS4B,UAAgC,QAApBC,EAAA/B,KAAKJ,OAAOM,gBAAQ,IAAA6B,EAAAA,EAAI,GACtC7B,CACR,GD/BI,CACClB,IAAK,KACLmC,IAAK,WCqCT,MAAA,eAAsBnB,KAAKJ,OAAOoC,SACnC,GDnCI,CACChD,IAAK,gBACLmC,IAAK,WCyCT,OAAOC,SAASa,eAAejC,KAAKsB,GACrC,KDrCQ3B,CACX,CA3FoB,GCLpBzB,EAAAsB,QAAAG,CDoGA,EAAE,CAAC,4BAA4B,IAAI,EAAE,CAAC,SAAS/B,EAAQS,EAAOH,GAC9D,aAEA,IAAII,EAAe,WAAc,SAASC,EAAiBC,EAAQC,GAAS,IAAK,IAAIhB,EAAI,EAAGA,EAAIgB,EAAML,OAAQX,IAAK,CAAE,IAAIiB,EAAaD,EAAMhB,GAAIiB,EAAWC,WAAaD,EAAWC,aAAc,EAAOD,EAAWE,cAAe,EAAU,UAAWF,IAAYA,EAAWG,UAAW,GAAMC,OAAOC,eAAeP,EAAQE,EAAWM,IAAKN,EAAa,CAAE,CAAE,OAAO,SAAUO,EAAaC,EAAYC,GAAiJ,OAA9HD,GAAYX,EAAiBU,EAAYG,UAAWF,GAAiBC,GAAaZ,EAAiBU,EAAaE,GAAqBF,CAAa,CAAG,CAA7hB,GAInBH,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,IAEtD,IE9GqByC,EF8GF,WErGjB,SAAAA,EAAYtC,IFiGd,SAAyBC,EAAUZ,GAAe,KAAMY,aAAoBZ,GAAgB,MAAM,IAAIa,UAAU,oCAAwC,CEjGhHC,CAAAC,KAAAkC,GACpClC,KAAKJ,OAASA,CACf,CF0PC,OAhJAtB,EAAa4D,EAAc,CAAC,CACxBlD,IAAK,QACLS,MAAO,SE1DPuB,GACJA,EAAcC,YAAYjB,KAAKkB,SAChC,GF2DI,CACClC,IAAK,kBACLS,MAAO,WErDNO,KAAKmC,SACRnC,KAAKoC,OAER,GFuDI,CACCpD,IAAK,QACLS,MAAO,WEkBXO,KAAKqC,cAAcD,OACpB,GFhBI,CACCpD,IAAK,cACLS,MAAO,SEwBDA,GAEV,IAAM6C,EAAetC,KAAKP,MAClB8C,EAAiDvC,KAAjDuC,uBAAwBC,EAAyBxC,KAAzBwC,qBAG5BC,OAAA,EAMFA,EAJEF,IAA2BC,EAK3BF,EAAaI,MAAM,EAAGH,GACtB9C,EACA6C,EAAaI,MAAMF,GAKnBF,EAAaI,MAAM,EAAGH,GACtB9C,EACA6C,EAAaI,MAAMH,GAEvB,IAAMI,EAAuBJ,EAAyB9C,EAAMrB,OAG5D4B,KAAKP,MAAQgD,EACbzC,KAAK4C,kBAAoBD,CAC1B,GFtCI,CACC3D,IAAK,cACLS,MAAO,WE6CgB,IAAjBoD,EAAiBC,UAAA1E,OAAA,QAAA6B,IAAA6C,UAAA,GAAAA,UAAA,GAAD,EAEpBR,EAAetC,KAAKP,MACpBsD,EAAuB/C,KAAKuC,uBAG5BS,EAAmBV,EAAaI,MAAM,EAAGK,GAG/C,GAAyB,KAArBC,EAAyB,CAC3B,IAAMC,EAAkBX,EAAaI,MAAMK,GACrCG,EAAWF,EAAiB5E,OAASyE,EACrCJ,EACJO,EAAiBN,MAAM,EAAGQ,GAAYD,EAClCN,EAAuBI,EAAuBF,EAGpD7C,KAAKP,MAAQgD,EACbzC,KAAK4C,kBAAoBD,EAGzB3C,KAAKmD,mBACN,CACF,GFpDI,CACCnE,IAAK,eACLS,MAAO,SEyDAA,GACXO,KAAKoD,cACLpD,KAAKqD,YAAY5D,GACjBO,KAAKmD,mBACN,GFxDI,CACCnE,IAAK,QACLS,MAAO,WE8DXO,KAAKP,MAAQ,GACbO,KAAKoC,OACN,GF5DI,CACCpD,IAAK,aACLS,MAAO,WEmEX,IAAM6D,EAAyCtD,KAAKuC,uBAC9CgB,EAAuCvD,KAAKP,MAAM+D,QACtD,aACA,IAEIb,EACJW,EACAC,EAAqCnF,OACrC4B,KAAKP,MAAMrB,OAGb4B,KAAKP,MAAQ8D,EACbvD,KAAK4C,kBAAoBD,CAC1B,GFzEI,CACC3D,IAAK,oBACLS,MAAO,WEgFXO,KAAKyD,aAGc,KAAfzD,KAAKP,OAEPiE,QAAQC,IAAI,eACZ3D,KAAKJ,OAAOgE,iBAGZF,QAAQC,IAAI,mBACZ3D,KAAKJ,OAAOiE,kBAEf,GFlFI,CACC7E,IAAK,WACLmC,IAAK,WE1LT,IAAM2C,EAAQ1C,SAASC,cAAc,SAYrC,OAXAyC,EAAMxC,GAAKtB,KAAKsB,GAChBwC,EAAMC,UAAY,gBAClBD,EAAME,KAAO,SACbF,EAAMG,KAAO,OACbH,EAAMI,WAAY,EAClBJ,EAAMK,UAAY,OAClBL,EAAMM,aAAe,MAGrBN,EAAMjD,iBAAiB,QAASb,KAAKmD,kBAAkBkB,KAAKrE,OAErD8D,CACR,GFyLI,CACC9E,IAAK,gBACLmC,IAAK,WEnLT,OAAOC,SAASa,eAAejC,KAAKsB,GACrC,GFqLI,CACCtC,IAAK,KACLmC,IAAK,WE7KT,MAAA,SAAgBnB,KAAKJ,OAAOoC,SAC7B,GF+KI,CACChD,IAAK,yBACLmC,IAAK,WEjJT,OADAnB,KAAKsE,kBACEtE,KAAKqC,cAAckC,cAC3B,GFoJI,CACCvF,IAAK,uBACLmC,IAAK,WE5IT,OADAnB,KAAKsE,kBACEtE,KAAKqC,cAAcmC,YAC3B,GF+II,CACCxF,IAAK,oBACLyF,IAAK,SExIWC,GAEpB1E,KAAKsE,kBAGLtE,KAAKqC,cAAckC,eAAiBG,EACpC1E,KAAKqC,cAAcmC,aAAeE,CACnC,GFsII,CACC1F,IAAK,UACLmC,IAAK,WEhIT,OAAOC,SAASuD,gBAAkB3E,KAAKqC,aACxC,GFkII,CACCrD,IAAK,QACLmC,IAAK,WE5HT,OAAOnB,KAAKqC,cAAc5C,KAC3B,EF8HKgF,IAAK,SErHDhF,GACRO,KAAKqC,cAAc5C,MAAQA,CAC5B,KFwHQyC,CACX,CAxJmB,GE9GnBhE,EAAAsB,QAAA0C,CF0QA,EAAE,CAAC,GAAG,EAAE,CAAC,SAAStE,EAAQS,EAAOH,GACjC,aAEAY,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,IGrMtDvB,EAAAsB,QAxEqD,CACnD,CACEwC,UAAW,MACXN,UAAW,MACXhB,MAAO,KAET,CACEsB,UAAW,MACXN,UAAW,MACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,QACXN,UAAW,QACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,OACXN,UAAW,OACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,OACXN,UAAW,OACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,MACXN,UAAW,MACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,QACXN,UAAW,QACXhB,MAAO,IACPR,SAAU,QAEZ,CACE8B,UAAW,QACXN,UAAW,QACXhB,MAAO,IACPR,SAAU,OAEZ,CACE8B,UAAW,OACXN,UAAW,OACXhB,MAAO,IACPR,SAAU,QAEZ,CACE8B,UAAW,OACXN,UAAW,OACXhB,MAAO,KAET,CACEsB,UAAW,OACXN,UAAW,OACXhB,MAAO,IACPR,SAAU,KAEZ,CACE8B,UAAW,OACXN,UAAW,OACXhB,MAAO,KHsQX,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS9C,EAAQS,EAAOH,GACjC,aAEA,IAAII,EAAe,WAAc,SAASC,EAAiBC,EAAQC,GAAS,IAAK,IAAIhB,EAAI,EAAGA,EAAIgB,EAAML,OAAQX,IAAK,CAAE,IAAIiB,EAAaD,EAAMhB,GAAIiB,EAAWC,WAAaD,EAAWC,aAAc,EAAOD,EAAWE,cAAe,EAAU,UAAWF,IAAYA,EAAWG,UAAW,GAAMC,OAAOC,eAAeP,EAAQE,EAAWM,IAAKN,EAAa,CAAE,CAAE,OAAO,SAAUO,EAAaC,EAAYC,GAAiJ,OAA9HD,GAAYX,EAAiBU,EAAYG,UAAWF,GAAiBC,GAAaZ,EAAiBU,EAAaE,GAAqBF,CAAa,CAAG,CAA7hB,GAInB,IAAII,EAA4D,SAAUC,GACtE,OAAOA,GAAOA,EAAIC,WAAaD,EAAM,CAAEE,QAAWF,EACtD,EACAR,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,II3VtD,IAAAmF,EAAAvF,EAAAzB,EAAA,kCACA8B,EAAAL,EAAAzB,EAAA,2BACAiH,EAAAxF,EAAAzB,EAAA,WAQqBkH,EJsVR,WI7UX,SAAAA,EAAYlF,IJmUd,SAAyBC,EAAUZ,GAAe,KAAMY,aAAoBZ,GAAgB,MAAM,IAAIa,UAAU,oCAAwC,CInUtHC,CAAAC,KAAA8E,GAC9B9E,KAAKJ,OAASA,CACf,CJ4aC,OA1FAtB,EAAawG,EAAQ,CAAC,CAClB9F,IAAK,kBACLS,MAAO,SItSGsF,GACd,IAAMC,EAAc5D,SAASC,cAAc,QAG3C,OAFA2D,EAAYjB,UAAY,4BACxBiB,EAAYlD,UAAYiD,EACjBC,CACR,GJuSI,CACChG,IAAK,QACLS,MAAO,SItNPuB,GACJA,EAAcC,YAAYjB,KAAKkB,SAChC,GJuNI,CACClC,IAAK,wBACLS,MAAO,WIjNXO,KAAKiF,uBAAuBC,UAAW,CACxC,GJmNI,CACClG,IAAK,yBACLS,MAAO,WI7MXO,KAAKiF,uBAAuBC,UAAW,CACxC,GJ+MI,CACClG,IAAK,WACLmC,IAAK,WIpWS,IAAAd,EAAAL,KACZmF,EAAS/D,SAASC,cAAc,OAkCtC,OAjCA8D,EAAO7D,GAAKtB,KAAKsB,GACjB6D,EAAO5D,UAAUC,IAAI,UAGrBqD,EAAArF,QAAoB4F,SAAQ,SAACxF,GAEf,IAAIgF,EAAApF,QAAc,CAC5BwC,UAAWpC,EAAOoC,UAClBN,UAAW9B,EAAO8B,UAClBhB,MAAOd,EAAOc,MACdR,SAAUN,EAAOM,SACjBO,QAASJ,EAAKT,OAAOyF,iBACrBlF,YACmB,MAAjBP,EAAOc,MAAgBL,EAAKT,OAAO0F,wBAAqBrF,EAC1DU,kBACmB,MAAjBf,EAAOc,MACHL,EAAKT,OAAO2F,8BACZtF,IAIJuF,MAAML,EACX,IAGDA,EAAOlE,YAAYG,SAASC,cAAc,SAG1C8D,EAAOlE,YAAYjB,KAAKyF,YAGxBN,EAAOlE,YAAYjB,KAAK0F,iBAEjBP,CACR,GJuVI,CACCnG,IAAK,aACLmC,IAAK,WIzUT,IAAMwE,EAAUvE,SAASC,cAAc,UAUvC,OATAsE,EAAQpE,UAAUC,IAAI,oBACtBmE,EAAQlE,aAAa,aAAc,eAGnCkE,EAAQ1E,YAAYjB,KAAK4F,gBAAgB,SAGzCD,EAAQ9E,iBAAiB,QAASb,KAAKJ,OAAOiG,gBAEvCF,CACR,GJsUI,CACC3G,IAAK,kBACLmC,IAAK,WIhUT,IAAM2E,EAAe1E,SAASC,cAAc,UAiB5C,OAhBAyE,EAAavE,UAAUC,IAAI,yBAC3BsE,EAAarE,aAAa,aAAc,oBACxCqE,EAAaZ,UAAW,EAGxBY,EAAa7E,YAAYjB,KAAK4F,gBAAgB,cAG9CE,EAAajF,iBAAiB,QAASb,KAAKJ,OAAOmG,qBAGnDrG,EAAAF,QAAec,MAAM,CACnB9B,OAAQsH,EACRvF,oBAAqBP,KAAKJ,OAAOoG,0BAG5BF,CACR,GJ2TI,CACC9G,IAAK,yBACLmC,IAAK,WIrTT,OAAOnB,KAAKqC,cAAcA,cACxB,yBAEH,GJqTI,CACCrD,IAAK,KACLmC,IAAK,WI/ST,MAAA,UAAiBnB,KAAKJ,OAAOoC,SAC9B,GJiTI,CACChD,IAAK,gBACLmC,IAAK,WI3ST,OAAOC,SAASa,eAAejC,KAAKsB,GACrC,KJ+SQwD,CACX,CAlGa,GItVb5G,EAAAsB,QAAAsF,CJ4bA,EAAE,CAAC,gCAAgC,EAAE,yBAAyB,EAAE,SAAS,IAAI,EAAE,CAAC,SAASlH,EAAQS,EAAOH,GACxG,aAEA,IAAImB,EAA4D,SAAUC,GACtE,OAAOA,GAAOA,EAAIC,WAAaD,EAAM,CAAEE,QAAWF,EACtD,EACAR,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,IK5ctD,IAAAwG,EAAA5G,EAAAzB,EAAA,oBAEAsI,OAAOC,OAAS,WAEE,IAAIF,EAAAzG,QAAQ,CAAEwC,UAAW,SAGjCwD,MAAMpE,SAASgF,KACxB,CL2cD,EAAE,CAAC,kBAAkB,IAAI,EAAE,CAAC,SAASxI,EAAQS,EAAOH,GACpD,aAEA,IAAII,EAAe,WAAc,SAASC,EAAiBC,EAAQC,GAAS,IAAK,IAAIhB,EAAI,EAAGA,EAAIgB,EAAML,OAAQX,IAAK,CAAE,IAAIiB,EAAaD,EAAMhB,GAAIiB,EAAWC,WAAaD,EAAWC,aAAc,EAAOD,EAAWE,cAAe,EAAU,UAAWF,IAAYA,EAAWG,UAAW,GAAMC,OAAOC,eAAeP,EAAQE,EAAWM,IAAKN,EAAa,CAAE,CAAE,OAAO,SAAUO,EAAaC,EAAYC,GAAiJ,OAA9HD,GAAYX,EAAiBU,EAAYG,UAAWF,GAAiBC,GAAaZ,EAAiBU,EAAaE,GAAqBF,CAAa,CAAG,CAA7hB,GAInB,IAAII,EAA4D,SAAUC,GACtE,OAAOA,GAAOA,EAAIC,WAAaD,EAAM,CAAEE,QAAWF,EACtD,EACAR,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,IM7dtD,IAAA4G,EAAAhH,EAAAzB,EAAA,+BACA0I,EAAAjH,EAAAzB,EAAA,qBAQqB2I,ENwdP,WMtcZ,SAAAA,EAAY3G,IN6bd,SAAyBC,EAAUZ,GAAe,KAAMY,aAAoBZ,GAAgB,MAAM,IAAIa,UAAU,oCAAwC,CM7brHC,CAAAC,KAAAuG,GAC/BvG,KAAKJ,OAASA,CACf,CNkjBC,OAvGAtB,EAAaiI,EAAS,CAAC,CACnBvH,IAAK,WACLS,MAAO,WMncX,IAAM+G,EAAUpF,SAASC,cAAc,WAavC,OAZAmF,EAAQlF,GAAKtB,KAAKsB,GAClBkF,EAAQzC,UAAY,UAGpB/D,KAAKyG,iBAAiBjB,MAAMgB,GAG5BxG,KAAK0G,aAAalB,MAAMgB,GAGxBA,EAAQvF,YAAYjB,KAAK2G,eAElBH,CACR,GN8bI,CACCxH,IAAK,UACLS,MAAO,WMzTXO,KAAK4G,QAAQC,QACd,GN2TI,CACC7H,IAAK,QACLS,MAAO,SMrTPuB,GACJA,EAAcC,YAAYjB,KAAKkB,WAChC,GNsTI,CACClC,IAAK,mBACLmC,IAAK,WMnciB,IAAAd,EAAAL,KAc1B,OAbAA,KAAK8G,WAAa,IAAIT,EAAA7G,QAAa,CACjCwC,UAAWhC,KAAKJ,OAAOoC,UACvB4B,aAAc,WAEZF,QAAQC,IAAI,4BACZtD,EAAK8E,OAAO4B,wBACb,EACDlD,gBAAiB,WAEfH,QAAQC,IAAI,2BACZtD,EAAK8E,OAAO6B,uBACb,IAEIhH,KAAK8G,UACb,GNocI,CACC9H,IAAK,eACLmC,IAAK,WM/ba,IAAAP,EAAAZ,KA4DtB,OA3DAA,KAAKmF,OAAS,IAAImB,EAAA9G,QAAO,CACvBwC,UAAWhC,KAAKJ,OAAOoC,UACvBqD,iBAAkB,SAAC5F,GAEjBiE,QAAQC,IAAI,oBAAqBlE,GAGjCmB,EAAKkG,WAAWzD,YAAY5D,GAC5BmB,EAAKuE,OAAO6B,uBACb,EACD1B,mBAAoB,SAAC7F,GAEnBiE,QAAQC,IAAI,+BAGZ/C,EAAKkG,WAAWG,aAAaxH,EAC9B,EACD8F,yBAA0B,WAExB3E,EAAKkG,WAAW1E,OACjB,EACDyD,eAAgB,WAEdnC,QAAQC,IAAI,0BAIgB,KAA1B/C,EAAKkG,WAAWrH,YACYQ,IAA5BW,EAAKsG,oBAGLxD,QAAQC,IAAI,iDACZ/C,EAAKkG,WAAWrH,MAAQmB,EAAKsG,mBAC7BtG,EAAKkG,WAAW1E,QAChBxB,EAAKuE,OAAO6B,0BAGZpG,EAAKsG,mBAAqBtG,EAAKkG,WAAWrH,MAG1CiE,QAAQC,IAAI,mBAAoB/C,EAAKkG,WAAWrH,OAEnD,EACDsG,oBAAqB,WAEnBrC,QAAQC,IAAI,2BACZ/C,EAAKkG,WAAW1D,aACjB,EACD4C,wBAAyB,WAEvBtC,QAAQC,IAAI,kCAGZ/C,EAAKkG,WAAWrH,MAAQ,GACxBmB,EAAKkG,WAAW1E,QAChBxB,EAAKuE,OAAO4B,wBACb,IAGI/G,KAAKmF,MACb,GN4aI,CACCnG,IAAK,gBACLmC,IAAK,WMpaT,IAAMgG,EAAuB/F,SAASC,cAAc,KAIpD,OAHA8F,EAAqBpD,UAAY,iBACjCoD,EAAqBrF,UAArB,4BAA4D,IAAIsF,MAAOC,cAEhEF,CACR,GNqaI,CACCnI,IAAK,KACLmC,IAAK,WM7ZT,MAAA,WAAkBnB,KAAKJ,OAAOoC,SAC/B,GN+ZI,CACChD,IAAK,UACLmC,IAAK,WMvZT,OAAOC,SAASa,eAAejC,KAAKsB,GACrC,KN2ZQiF,CACX,CA/Gc,GMxddrI,EAAAsB,QAAA+G,CN2kBA,EAAE,CAAC,6BAA6B,EAAE,mBAAmB,IAAI,EAAE,CAAC,SAAS3I,EAAQS,EAAOH,GACpF,aAEA,IAAII,EAAe,WAAc,SAASC,EAAiBC,EAAQC,GAAS,IAAK,IAAIhB,EAAI,EAAGA,EAAIgB,EAAML,OAAQX,IAAK,CAAE,IAAIiB,EAAaD,EAAMhB,GAAIiB,EAAWC,WAAaD,EAAWC,aAAc,EAAOD,EAAWE,cAAe,EAAU,UAAWF,IAAYA,EAAWG,UAAW,GAAMC,OAAOC,eAAeP,EAAQE,EAAWM,IAAKN,EAAa,CAAE,CAAE,OAAO,SAAUO,EAAaC,EAAYC,GAAiJ,OAA9HD,GAAYX,EAAiBU,EAAYG,UAAWF,GAAiBC,GAAaZ,EAAiBU,EAAaE,GAAqBF,CAAa,CAAG,CAA7hB,GAInBH,OAAOC,eAAeb,EAAS,aAAc,CAAEuB,OAAO,IAEtD,IOtlBqB6H,EPslBA,WOpkBnB,SAAAA,EAAY1H,IPgkBd,SAAyBC,EAAUZ,GAAe,KAAMY,aAAoBZ,GAAgB,MAAM,IAAIa,UAAU,oCAAwC,COhkB9GC,CAAAC,KAAAsH,GAbhCtH,KAAAuH,QAAkB,EAGlBvH,KAAAwH,oBAAqC,KAGrCxH,KAAAyH,iBAAmB,IAQzBzH,KAAKJ,OAASA,EAGdI,KAAKJ,OAAOpB,OAAOqC,iBACjB,YACAb,KAAK0H,YAAYrD,KAAKrE,OAExBA,KAAKJ,OAAOpB,OAAOqC,iBACjB,aACAb,KAAK0H,YAAYrD,KAAKrE,OAIxBA,KAAKJ,OAAOpB,OAAOqC,iBAAiB,UAAWb,KAAK2H,UAAUtD,KAAKrE,OACnEA,KAAKJ,OAAOpB,OAAOqC,iBAAiB,WAAYb,KAAK2H,UAAUtD,KAAKrE,OACpEA,KAAKJ,OAAOpB,OAAOqC,iBACjB,aACAb,KAAK2H,UAAUtD,KAAKrE,OAEtBA,KAAKJ,OAAOpB,OAAOqC,iBAAiB,WAAYb,KAAK2H,UAAUtD,KAAKrE,OACpEA,KAAKJ,OAAOpB,OAAOqC,iBACjB,cACAb,KAAK2H,UAAUtD,KAAKrE,MAEvB,CPimBC,OArCA1B,EAAagJ,EAAgB,CAAC,CAC1BtI,IAAK,cACLS,MAAO,WOvjBM,IAAAY,EAAAL,KAEjB0D,QAAQC,IAAI,kBACZ3D,KAAKuH,QAAS,EAGVvH,KAAKJ,OAAOY,cACdR,KAAKJ,OAAOY,eAGdR,KAAKwH,oBAAsBtB,OAAO0B,YAAW,YACvB,IAAhBvH,EAAKkH,SAEP7D,QAAQC,IAAI,uBACZtD,EAAKT,OAAOW,sBAEf,GAAEP,KAAKyH,iBACT,GPqjBI,CACCzI,IAAK,YACLS,MAAO,WO9iBXiE,QAAQC,IAAI,iBACZ3D,KAAKuH,QAAS,EAGVvH,KAAKJ,OAAOe,mBACdX,KAAKJ,OAAOe,oBAGmB,OAA7BX,KAAKwH,sBACPtB,OAAO2B,aAAa7H,KAAKwH,qBACzBxH,KAAKwH,oBAAsB,KAE9B,IP6iBK,CAAC,CACDxI,IAAK,QACLS,MAAO,SOxiBAG,GAEX,IAAI0H,EAAe1H,EACpB,KP0iBQ0H,CACX,CAvDqB,GOtlBrBpJ,EAAAsB,QAAA8H,CPipBA,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC","file":"../bundle.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i {\r\n this.config.onLongPress!(this.config.subtitle!);\r\n },\r\n onPressStart: () => {\r\n this.config.onClick(this.config.title);\r\n },\r\n onLongPressCancel: this.config.onLongPressCancel,\r\n });\r\n }\r\n\r\n /**\r\n *\r\n * Apply click event listener on button element\r\n *\r\n * @param button\r\n *\r\n */\r\n addClickEventListener(button: HTMLButtonElement) {\r\n button.addEventListener('click', () => {\r\n this.config.onClick(this.config.title);\r\n });\r\n }\r\n\r\n /**\r\n *\r\n * If onLongPress callback is exist in config apply long press event\r\n * Else apply click event listener on button\r\n *\r\n * @param button\r\n *\r\n */\r\n configureButtonEvents(button: HTMLButtonElement) {\r\n if (this.config.onLongPress !== undefined) {\r\n // apply long press event\r\n this.applyLongPressEvent(button);\r\n } else {\r\n // add click event listener\r\n this.addClickEventListener(button);\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * Dialpad button tile element\r\n *\r\n */\r\n get titleElement(): HTMLHeadingElement {\r\n const title = document.createElement('h1');\r\n title.classList.add('dialpad-btn__title');\r\n title.innerText = this.config.title;\r\n return title;\r\n }\r\n\r\n /**\r\n *\r\n * Dialpad button subtile\r\n *\r\n */\r\n get subtitleElement(): HTMLParagraphElement {\r\n const subtitle = document.createElement('p');\r\n subtitle.classList.add('dialpad-btn__subtitle');\r\n subtitle.innerText = this.config.subtitle ?? '';\r\n return subtitle;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for dialpad button\r\n *\r\n */\r\n get id() {\r\n return `dialpad-btn-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * Dialpad button query selector for dom manipulation\r\n *\r\n */\r\n get querySelector(): HTMLButtonElement {\r\n return document.getElementById(this.id)! as HTMLButtonElement;\r\n }\r\n\r\n /**\r\n *\r\n * Appends dialpad button to a specified parent element.\r\n *\r\n * @param parentElement\r\n *\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton);\r\n }\r\n}\r\n","import { InputElementConfig } from './types';\n\n/**\n *\n * Input Element\n *\n */\nexport default class InputElement {\n // input element config\n private config: InputElementConfig;\n\n /**\n *\n * construct InputElement instance\n *\n */\n constructor(config: InputElementConfig) {\n this.config = config;\n }\n\n /**\n *\n * input element skeleton\n *\n */\n get skeleton(): HTMLInputElement {\n const input = document.createElement('input');\n input.id = this.id;\n input.className = 'input-element';\n input.name = 'number';\n input.type = 'text';\n input.autofocus = true;\n input.inputMode = 'none';\n input.autocomplete = 'off';\n\n // add input event listener\n input.addEventListener('input', this.inputEventHandler.bind(this));\n\n return input;\n }\n\n /**\n *\n * Input element query selector for dom manipulation\n *\n */\n get querySelector(): HTMLInputElement {\n return document.getElementById(this.id) as HTMLInputElement;\n }\n\n /**\n *\n * Unique id for input element\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `input-${this.config.namespace}`;\n }\n\n /**\n *\n * Appends input element to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Make sure input element is focused\n *\n */\n makeSureFocused() {\n if (!this.focused) {\n this.focus();\n }\n }\n\n /**\n *\n * Get input element selection start position\n *\n */\n get selectionStartPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionStart!;\n }\n\n /**\n *\n * Get input element caret end position\n *\n */\n get selectionEndPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionEnd!;\n }\n\n /**\n *\n * Set input element selection start and end positions\n *\n * @param position - desired caret position index value\n *\n */\n set selectionPosition(position: number) {\n // make sure the input is focused\n this.makeSureFocused();\n\n // set selection\n this.querySelector.selectionStart = position;\n this.querySelector.selectionEnd = position;\n }\n\n /**\n *\n * To check whether the input element is focused or not\n *\n */\n get focused(): boolean {\n return document.activeElement === this.querySelector;\n }\n\n /**\n *\n * To get input element value\n *\n */\n get value(): string {\n return this.querySelector.value;\n }\n\n /**\n *\n * To set input element value\n *\n * @param value - The value to set\n *\n */\n set value(value: string) {\n this.querySelector.value = value;\n }\n\n /**\n *\n * Focus input element\n *\n */\n focus() {\n this.querySelector.focus();\n }\n\n /**\n *\n * Insert a value at the caret position\n *\n * @param value - The value to be inserted\n *\n */\n // eslint-disable-next-line class-methods-use-this\n insertValue(value: string) {\n // capture current state\n const currentValue = this.value;\n const { selectionStartPosition, selectionEndPosition } = this;\n\n // prepare updated state\n let updatedValue;\n\n if (selectionStartPosition !== selectionEndPosition) {\n // if input selection start value is not equal to selection end position\n // it means input value is selected, in this case remove selected value\n // and insert new value at the selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionEndPosition);\n } else {\n // else input selection start value is same as selection end value\n // insert new value at selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionStartPosition);\n }\n const updatedCaretPosition = selectionStartPosition + value.length;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * Remove characters from the value starting at the caret position\n *\n * @param count - The number of characters, default value is 1\n *\n */\n removeValue(count: number = 1) {\n // capture current state\n const currentValue = this.value;\n const currentCaretPosition = this.selectionStartPosition;\n\n // prepare updated state\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\n\n // if before caret value is empty do nothing\n if (beforeCaretValue !== '') {\n const afterCaretValue = currentValue.slice(currentCaretPosition);\n const endIndex = beforeCaretValue.length - count;\n const updatedValue =\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\n const updatedCaretPosition = currentCaretPosition - count;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n\n // check empty or not\n this.inputEventHandler();\n }\n }\n\n /**\n *\n * @param value - The value to be replaced current last value\n *\n */\n replaceValue(value: string) {\n this.removeValue();\n this.insertValue(value);\n this.inputEventHandler();\n }\n\n /**\n *\n * Clear the value of input element\n *\n */\n clear() {\n this.value = '';\n this.focus();\n }\n\n /**\n *\n * Remove unwanted symbols and allow only digits, +, *, and #\n *\n */\n validation() {\n // prepare updated state\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\n const updatedValueAfterRemoveUnwantedChars = this.value.replace(\n /[^0-9+*#]/g,\n ''\n );\n const updatedCaretPosition =\n caretPositionBeforeRemoveUnwantedChars +\n updatedValueAfterRemoveUnwantedChars.length -\n this.value.length;\n\n // update state\n this.value = updatedValueAfterRemoveUnwantedChars;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * `input` event handler\n *\n */\n private inputEventHandler() {\n // Remove unwanted symbols and allow only digits, +, *, and #\n this.validation();\n\n // check whether the value is empty or not, and act accordingly.\n if (this.value === '') {\n // eslint-disable-next-line no-console\n console.log('value empty');\n this.config.onValueEmpty();\n } else {\n // eslint-disable-next-line no-console\n console.log('value non empty');\n this.config.onValueNonEmpty();\n }\n }\n}\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\nimport Keypad from '../layout/keypad';\nimport DialpadConfig from './types';\n\n/**\n *\n * Dialpad Page\n *\n */\nexport default class Dialpad {\n // dialpad configuration\n private config: DialpadConfig;\n\n // input field instance\n private inputField!: InputElement;\n\n // keypad instance\n private keypad!: Keypad;\n\n // recent call number\n private recentCallOnNumber?: string;\n\n /**\n *\n * construct Dialpad instance\n *\n */\n constructor(config: DialpadConfig) {\n this.config = config;\n }\n\n /**\n *\n * page skeleton\n *\n * @returns {HTMLDivElement}\n *\n */\n private skeleton(): HTMLElement {\n const dialpad = document.createElement('section');\n dialpad.id = this.id;\n dialpad.className = 'dialpad';\n\n // build input field\n this.inputFieldLayout.build(dialpad);\n\n // build keypad layout\n this.keypadLayout.build(dialpad);\n\n // append copyright text\n dialpad.appendChild(this.copyrightText);\n\n return dialpad;\n }\n\n /**\n *\n * To get Input Element\n *\n */\n private get inputFieldLayout(): InputElement {\n this.inputField = new InputElement({\n namespace: this.config.namespace,\n onValueEmpty: () => {\n // eslint-disable-next-line no-console\n console.log('disable backspace button');\n this.keypad.disableBackspaceButton();\n },\n onValueNonEmpty: () => {\n // eslint-disable-next-line no-console\n console.log('enable backspace button');\n this.keypad.enableBackspaceButton();\n },\n });\n return this.inputField;\n }\n\n /**\n *\n * To get Keypad Layout\n *\n */\n private get keypadLayout() {\n this.keypad = new Keypad({\n namespace: this.config.namespace,\n onKeypadBtnClick: (value: string) => {\n // eslint-disable-next-line no-console\n console.log('clicked on button', value);\n\n // insert value\n this.inputField.insertValue(value);\n this.keypad.enableBackspaceButton();\n },\n onZeroBtnLongPress: (value: string) => {\n // eslint-disable-next-line no-console\n console.log('long pressed on zero button');\n\n // insert zero button subtitle value `+`\n this.inputField.replaceValue(value);\n },\n onZeroBtnLongPressCancel: () => {\n // focus input field\n this.inputField.focus();\n },\n onCallBtnClick: () => {\n // eslint-disable-next-line no-console\n console.log('clicked on call button');\n\n // check whether try to call on recent number\n if (\n this.inputField.value === '' &&\n this.recentCallOnNumber !== undefined\n ) {\n // eslint-disable-next-line no-console\n console.log('fill last call on number in input field value');\n this.inputField.value = this.recentCallOnNumber;\n this.inputField.focus();\n this.keypad.enableBackspaceButton();\n } else {\n // update last call on number\n this.recentCallOnNumber = this.inputField.value;\n\n // eslint-disable-next-line no-console\n console.log('placing call on ', this.inputField.value);\n }\n },\n onBackspaceBtnClick: () => {\n // eslint-disable-next-line no-console\n console.log('clicked on clear button');\n this.inputField.removeValue();\n },\n onBackspaceBtnLongPress: () => {\n // eslint-disable-next-line no-console\n console.log('long press on backspace button');\n\n // clear input value and hide backspace button\n this.inputField.value = '';\n this.inputField.focus();\n this.keypad.disableBackspaceButton();\n },\n });\n\n return this.keypad;\n }\n\n /**\n *\n * append copyright text element\n *\n */\n // eslint-disable-next-line class-methods-use-this\n get copyrightText(): HTMLParagraphElement {\n // create copyright text\n const copyrightTextElement = document.createElement('p');\n copyrightTextElement.className = 'copyright-text';\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\n\n return copyrightTextElement;\n }\n\n /**\n *\n * Unique id for page\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `dialpad-${this.config.namespace}`;\n }\n\n /**\n *\n * querySelect for app\n *\n * @returns {HTMLElement}\n *\n */\n get element(): HTMLElement {\n return document.getElementById(this.id)!;\n }\n\n /**\n *\n * Remove dialpad skeleton from dom\n *\n */\n destroy(): void {\n this.element.remove();\n }\n\n /**\n *\n * Append dialpad skeleton into parent element\n *\n * @param parentElement {HTMLElement}\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton());\n }\n}\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file diff --git a/src/ts/components/forms/inputs.ts b/src/ts/components/forms/inputs.ts index 9a4241d..9f0c3b5 100644 --- a/src/ts/components/forms/inputs.ts +++ b/src/ts/components/forms/inputs.ts @@ -23,7 +23,7 @@ export default class InputElement { * input element skeleton * */ - private get skeleton(): HTMLInputElement { + get skeleton(): HTMLInputElement { const input = document.createElement('input'); input.id = this.id; input.className = 'input-element'; From c9ba871b3b03bcc1a5ea87449d32ddd6d494da1f Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Fri, 20 Oct 2023 02:10:30 +0530 Subject: [PATCH 06/28] input element build should append skeleton to parentElement --- tests/components/inputs.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index d6bd7da..4e5a31d 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -75,4 +75,28 @@ describe('Test Input Element', () => { // Assert that result HTMLElement has expected id expect(result.id).to.equal(inputId); }); + + it('build should append skeleton to parentElement', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for skeleton getter + const skeletonGetterSpy = sinon.spy(input, 'skeleton', ['get']); + + // Create spy for appendChild method + const appendChildSpy = sinon.spy(document.body, 'appendChild'); + + // Call the build method + input.build(document.body); + + // Assert that the skeleton getter was accessed + expect(skeletonGetterSpy.get.calledOnce).to.be.true; + + // Assert that the parentElement appendChild was called once with the correct argument + expect(appendChildSpy.calledOnceWith(sinon.match.instanceOf(HTMLElement))) + .to.be.true; + + // Assert that the parentElement now contains the button skeleton + expect(input.querySelector).to.exist; + }); }); From b58d6383fbf888e5f38b12d922f1784a2a48b787 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 20:14:14 +0530 Subject: [PATCH 07/28] make InputElement inputEventHandler public for unit test --- src/ts/components/forms/inputs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ts/components/forms/inputs.ts b/src/ts/components/forms/inputs.ts index 9f0c3b5..41dd01e 100644 --- a/src/ts/components/forms/inputs.ts +++ b/src/ts/components/forms/inputs.ts @@ -276,7 +276,7 @@ export default class InputElement { * `input` event handler * */ - private inputEventHandler() { + inputEventHandler() { // Remove unwanted symbols and allow only digits, +, *, and # this.validation(); From 04890b9ad147d1dc2a48081726f785b9e1092d54 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 20:14:54 +0530 Subject: [PATCH 08/28] input element skeleton should return correct html element --- tests/components/inputs.ts | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 4e5a31d..857441a 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -99,4 +99,46 @@ describe('Test Input Element', () => { // Assert that the parentElement now contains the button skeleton expect(input.querySelector).to.exist; }); + + it('skeleton should return correct html element', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create stub for inputEventHandler + const inputEventHandlerStub = sinon.stub(input, 'inputEventHandler'); + + // Access skeleton + const { skeleton } = input; + + // Assert that the skeleton is an HTMLElement + expect(skeleton).to.be.instanceOf(HTMLInputElement); + + // Assert that the skeleton has the correct tag name + expect(skeleton.tagName).to.be.equal('INPUT'); + + // Assert that the skeleton has the correct class name + expect(skeleton.className).to.be.equal('input-element'); + + // Assert that the skeleton has the correct name attribute + expect(skeleton.getAttribute('name')).to.be.equal('number'); + + // Assert that the skeleton has the correct type attribute + expect(skeleton.getAttribute('type')).to.be.equal('text'); + + // Assert that the skeleton has the correct autofocus attribute + expect(skeleton.autofocus).to.be.true; + + // Assert that the skeleton has the correct inputMode attribute + expect(skeleton.getAttribute('inputMode')).to.be.equal('none'); + + // Assert that the skeleton has the correct autocomplete attribute + expect(skeleton.getAttribute('autocomplete')).to.be.equal('off'); + + // Simulate a click event on the skeleton + const inputEvent = new Event('input'); + skeleton.dispatchEvent(inputEvent); + + // Assert that the inputEventHandlerStub called once + expect(inputEventHandlerStub.calledOnce).to.be.true; + }); }); From 120bb7b6aa966f2a25fc9cb081989bba22f49eb5 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 21:25:16 +0530 Subject: [PATCH 09/28] input element makeSureFocused should focus the element when not already focused --- tests/components/inputs.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 857441a..e4dba6f 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -141,4 +141,28 @@ describe('Test Input Element', () => { // Assert that the inputEventHandlerStub called once expect(inputEventHandlerStub.calledOnce).to.be.true; }); + + it('makeSureFocused should focus the element when not already focused', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for focus + const focusSpy = sinon.spy(input, 'focus'); + + // build InputElement + input.build(document.body); + + // Call makeSureFocused to focus the element + input.makeSureFocused(); + + // Assert that focusSpy is called once + expect(focusSpy.calledOnce).to.be.true; + + // Call makeSureFocused + input.makeSureFocused(); + + // Assert that focusSpy is not called twice + // but called once because the input element is already focused. + expect(focusSpy.calledOnce).to.be.true; + }); }); From 758f7469e2154695501fc8cc43b24fd8e4cddb6a Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 22:28:05 +0530 Subject: [PATCH 10/28] input element selectionStartPosition should return the correct selection start position --- tests/components/inputs.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index e4dba6f..4c04baf 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -165,4 +165,21 @@ describe('Test Input Element', () => { // but called once because the input element is already focused. expect(focusSpy.calledOnce).to.be.true; }); + + it('selectionStartPosition should return the correct selection start position', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for makeSureFocused + const makeSureFocusedSpy = sinon.spy(input, 'makeSureFocused'); + + // build InputElement + input.build(document.body); + + // Call selectionStartPosition and assert the value + expect(input.selectionStartPosition).to.equal(0); + + // Assert that makeSureFocusedSpy call once + expect(makeSureFocusedSpy.calledOnce).to.be.true; + }); }); From 9bb6333436987150ea4248f709635d6206de557f Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 22:29:53 +0530 Subject: [PATCH 11/28] input element selectionEndPosition should return the correct selection end position --- tests/components/inputs.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 4c04baf..4729043 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -182,4 +182,21 @@ describe('Test Input Element', () => { // Assert that makeSureFocusedSpy call once expect(makeSureFocusedSpy.calledOnce).to.be.true; }); + + it('selectionEndPosition should return the correct selection end position', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for makeSureFocused + const makeSureFocusedSpy = sinon.spy(input, 'makeSureFocused'); + + // build InputElement + input.build(document.body); + + // Call selectionEndPosition and assert the value + expect(input.selectionEndPosition).to.equal(0); + + // Assert that makeSureFocusedSpy call once + expect(makeSureFocusedSpy.calledOnce).to.be.true; + }); }); From c29eea5684dc5ab321d96104863c795c503b95b4 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 23:18:53 +0530 Subject: [PATCH 12/28] input element selectionPosition should set the correct selection position --- tests/components/inputs.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 4729043..1316610 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -199,4 +199,30 @@ describe('Test Input Element', () => { // Assert that makeSureFocusedSpy call once expect(makeSureFocusedSpy.calledOnce).to.be.true; }); + + it('selectionPosition should set the correct selection position', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for makeSureFocused + const makeSureFocusedSpy = sinon.spy(input, 'makeSureFocused'); + + // build InputElement + input.build(document.body); + + // Set input field value + input.querySelector.value = 'hello'; + + // Call selectionPosition to set the selection position + input.selectionPosition = 2; + + // Assert that makeSureFocusedSpy call once + expect(makeSureFocusedSpy.calledOnce).to.be.true; + + // Assert the selection start position is correctly set + expect(input.selectionStartPosition).to.equal(2); + + // Assert the selection end position is correctly set + expect(input.selectionEndPosition).to.equal(2); + }); }); From fb792b1ff956fd64130112c514644f8d19513004 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 23:46:28 +0530 Subject: [PATCH 13/28] inpit element focused should return correct focus status --- tests/components/inputs.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 1316610..bcf6cc0 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -225,4 +225,24 @@ describe('Test Input Element', () => { // Assert the selection end position is correctly set expect(input.selectionEndPosition).to.equal(2); }); + + it('focused should return correct focus status', () => { + // Create InputElement instance + const input = new InputElement(config); + + // build InputElement + input.build(document.body); + + // Focus input element + input.querySelector.focus(); + + // Assert that the focused returns true + expect(input.focused).to.be.true; + + // Blur input element + input.querySelector.blur(); + + // Assert that the focused returns false + expect(input.focused).to.be.false; + }); }); From c3e3c9cfff88919ad8f7063b81b145924298e409 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 22 Oct 2023 23:59:54 +0530 Subject: [PATCH 14/28] input element value should return correct value --- tests/components/inputs.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index bcf6cc0..836817e 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -245,4 +245,18 @@ describe('Test Input Element', () => { // Assert that the focused returns false expect(input.focused).to.be.false; }); + + it('value should return correct value', () => { + // Create InputElement instance + const input = new InputElement(config); + + // build InputElement + input.build(document.body); + + // Set input element value + input.querySelector.value = 'hello'; + + // Assert that the focused returns false + expect(input.value).to.be.equal('hello'); + }); }); From 4b2c4a815b32483e970fdead6243ca3c4a30fea9 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Mon, 23 Oct 2023 00:01:57 +0530 Subject: [PATCH 15/28] input element value should set correct value --- tests/components/inputs.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 836817e..0d8eb56 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -259,4 +259,18 @@ describe('Test Input Element', () => { // Assert that the focused returns false expect(input.value).to.be.equal('hello'); }); + + it('value should set correct value', () => { + // Create InputElement instance + const input = new InputElement(config); + + // build InputElement + input.build(document.body); + + // Set input element value + input.value = 'hello'; + + // Assert that the focused returns false + expect(input.querySelector.value).to.be.equal('hello'); + }); }); From af0887ca7ae10c12cac6376fe91eb7b30540b4d0 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Mon, 23 Oct 2023 00:10:01 +0530 Subject: [PATCH 16/28] input element focus should focus input element --- tests/components/inputs.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 0d8eb56..65e5418 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -246,6 +246,23 @@ describe('Test Input Element', () => { expect(input.focused).to.be.false; }); + it('focus should focus input element', () => { + // Create InputElement instance + const input = new InputElement(config); + + // build InputElement + input.build(document.body); + + // Assert InputElement is not focused before call focus + expect(input.focused).to.be.false; + + // Call focus + input.focus(); + + // Assert InputElement is focused after call focus + expect(input.focused).to.be.true; + }); + it('value should return correct value', () => { // Create InputElement instance const input = new InputElement(config); From 7e58d60fff3a8250fa0819fc8f59bb5458457212 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Mon, 23 Oct 2023 00:23:18 +0530 Subject: [PATCH 17/28] input element replaceValue should working correctly --- tests/components/inputs.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 65e5418..895e781 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -290,4 +290,37 @@ describe('Test Input Element', () => { // Assert that the focused returns false expect(input.querySelector.value).to.be.equal('hello'); }); + + it('replaceValue should working correctly', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for removeValue + const removeValueSpy = sinon.spy(input, 'removeValue'); + + // Create spy for insertValue + const insertValueSpy = sinon.spy(input, 'insertValue'); + + // Create spy for inputEventHandler + const inputEventHandlerSpy = sinon.spy(input, 'inputEventHandler'); + + // build InputElement + input.build(document.body); + + // Set input element value + input.querySelector.value = 'hello'; + + // Call replaceValue + input.replaceValue('bye'); + + // Assert that the removeValueSpy called once + expect(removeValueSpy.calledOnce).to.be.true; + + // Assert that the insertValueSpy called once expected string value + expect(insertValueSpy.calledOnceWithExactly('bye')).to.be.true; + + // Assert that the inputEventHandlerSpy called twice + // (first time for remove value and second time for replaceValue) + expect(inputEventHandlerSpy.calledTwice).to.be.true; + }); }); From 0645acf080c6f689f66cb0797cec2e43487ad671 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Mon, 23 Oct 2023 00:28:19 +0530 Subject: [PATCH 18/28] input element clear should set the value to an empty string --- tests/components/inputs.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 895e781..99bfff7 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -323,4 +323,27 @@ describe('Test Input Element', () => { // (first time for remove value and second time for replaceValue) expect(inputEventHandlerSpy.calledTwice).to.be.true; }); + + it('clear should set the value to an empty string', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for focus + const focusSpy = sinon.spy(input, 'focus'); + + // build InputElement + input.build(document.body); + + // Set input element value + input.querySelector.value = 'hello'; + + // Call clear method + input.clear(); + + // Assert input value is empty + expect(input.querySelector.value).to.equal(''); + + // Assert that the focusSpy called once + expect(focusSpy.calledOnce).to.be.true; + }); }); From fe08132979013be460dae147dcc10b774902a3ad Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Tue, 14 Nov 2023 01:31:46 +0530 Subject: [PATCH 19/28] access once value getter for input validation --- src/ts/components/forms/inputs.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ts/components/forms/inputs.ts b/src/ts/components/forms/inputs.ts index 41dd01e..87bcd31 100644 --- a/src/ts/components/forms/inputs.ts +++ b/src/ts/components/forms/inputs.ts @@ -256,15 +256,16 @@ export default class InputElement { */ validation() { // prepare updated state + const { value } = this; const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition; - const updatedValueAfterRemoveUnwantedChars = this.value.replace( + const updatedValueAfterRemoveUnwantedChars = value.replace( /[^0-9+*#]/g, '' ); const updatedCaretPosition = caretPositionBeforeRemoveUnwantedChars + updatedValueAfterRemoveUnwantedChars.length - - this.value.length; + value.length; // update state this.value = updatedValueAfterRemoveUnwantedChars; From 3d117b881987d2afb5e7271a336783c288808086 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Tue, 14 Nov 2023 01:32:05 +0530 Subject: [PATCH 20/28] update dist files --- dist/js/bundle.js | 2 +- dist/js/maps/bundle.js.map | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/js/bundle.js b/dist/js/bundle.js index 348f7b0..fea99b2 100644 --- a/dist/js/bundle.js +++ b/dist/js/bundle.js @@ -1,2 +1,2 @@ -!function e(t,n,i){function o(l,s){if(!n[l]){if(!t[l]){var u="function"==typeof require&&require;if(!s&&u)return u(l,!0);if(a)return a(l,!0);var c=new Error("Cannot find module '"+l+"'");throw c.code="MODULE_NOT_FOUND",c}var r=n[l]={exports:{}};t[l][0].call(r.exports,(function(e){return o(t[l][1][e]||e)}),r,r.exports,e,t,n,i)}return n[l].exports}for(var a="function"==typeof require&&require,l=0;l0&&void 0!==arguments[0]?arguments[0]:1,t=this.value,n=this.selectionStartPosition,i=t.slice(0,n);if(""!==i){var o=t.slice(n),a=i.length-e,l=i.slice(0,a)+o,s=n-e;this.value=l,this.selectionPosition=s,this.inputEventHandler()}}},{key:"replaceValue",value:function(e){this.removeValue(),this.insertValue(e),this.inputEventHandler()}},{key:"clear",value:function(){this.value="",this.focus()}},{key:"validation",value:function(){var e=this.selectionStartPosition,t=this.value.replace(/[^0-9+*#]/g,""),n=e+t.length-this.value.length;this.value=t,this.selectionPosition=n}},{key:"inputEventHandler",value:function(){this.validation(),""===this.value?(console.log("value empty"),this.config.onValueEmpty()):(console.log("value non empty"),this.config.onValueNonEmpty())}},{key:"skeleton",get:function(){var e=document.createElement("input");return e.id=this.id,e.className="input-element",e.name="number",e.type="text",e.autofocus=!0,e.inputMode="none",e.autocomplete="off",e.addEventListener("input",this.inputEventHandler.bind(this)),e}},{key:"querySelector",get:function(){return document.getElementById(this.id)}},{key:"id",get:function(){return"input-"+this.config.namespace}},{key:"selectionStartPosition",get:function(){return this.makeSureFocused(),this.querySelector.selectionStart}},{key:"selectionEndPosition",get:function(){return this.makeSureFocused(),this.querySelector.selectionEnd}},{key:"selectionPosition",set:function(e){this.makeSureFocused(),this.querySelector.selectionStart=e,this.querySelector.selectionEnd=e}},{key:"focused",get:function(){return document.activeElement===this.querySelector}},{key:"value",get:function(){return this.querySelector.value},set:function(e){this.querySelector.value=e}}]),e}();n.default=o},{}],3:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});n.default=[{namespace:"one",ariaLabel:"One",title:"1"},{namespace:"two",ariaLabel:"Two",title:"2",subtitle:"ABC"},{namespace:"three",ariaLabel:"Three",title:"3",subtitle:"DEF"},{namespace:"four",ariaLabel:"Four",title:"4",subtitle:"GHI"},{namespace:"five",ariaLabel:"Five",title:"5",subtitle:"JKL"},{namespace:"six",ariaLabel:"Six",title:"6",subtitle:"MNO"},{namespace:"seven",ariaLabel:"Seven",title:"7",subtitle:"PQRS"},{namespace:"eight",ariaLabel:"Eight",title:"8",subtitle:"TUV"},{namespace:"nine",ariaLabel:"Nine",title:"9",subtitle:"WXYZ"},{namespace:"star",ariaLabel:"Star",title:"*"},{namespace:"zero",ariaLabel:"Zero",title:"0",subtitle:"+"},{namespace:"hash",ariaLabel:"Hash",title:"#"}]},{}],4:[function(e,t,n){"use strict";var i=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:1,t=this.value,n=this.selectionStartPosition,i=t.slice(0,n);if(""!==i){var o=t.slice(n),a=i.length-e,l=i.slice(0,a)+o,s=n-e;this.value=l,this.selectionPosition=s,this.inputEventHandler()}}},{key:"replaceValue",value:function(e){this.removeValue(),this.insertValue(e),this.inputEventHandler()}},{key:"clear",value:function(){this.value="",this.focus()}},{key:"validation",value:function(){var e=this.value,t=this.selectionStartPosition,n=e.replace(/[^0-9+*#]/g,""),i=t+n.length-e.length;this.value=n,this.selectionPosition=i}},{key:"inputEventHandler",value:function(){this.validation(),""===this.value?(console.log("value empty"),this.config.onValueEmpty()):(console.log("value non empty"),this.config.onValueNonEmpty())}},{key:"skeleton",get:function(){var e=document.createElement("input");return e.id=this.id,e.className="input-element",e.name="number",e.type="text",e.autofocus=!0,e.inputMode="none",e.autocomplete="off",e.addEventListener("input",this.inputEventHandler.bind(this)),e}},{key:"querySelector",get:function(){return document.getElementById(this.id)}},{key:"id",get:function(){return"input-"+this.config.namespace}},{key:"selectionStartPosition",get:function(){return this.makeSureFocused(),this.querySelector.selectionStart}},{key:"selectionEndPosition",get:function(){return this.makeSureFocused(),this.querySelector.selectionEnd}},{key:"selectionPosition",set:function(e){this.makeSureFocused(),this.querySelector.selectionStart=e,this.querySelector.selectionEnd=e}},{key:"focused",get:function(){return document.activeElement===this.querySelector}},{key:"value",get:function(){return this.querySelector.value},set:function(e){this.querySelector.value=e}}]),e}();n.default=o},{}],3:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});n.default=[{namespace:"one",ariaLabel:"One",title:"1"},{namespace:"two",ariaLabel:"Two",title:"2",subtitle:"ABC"},{namespace:"three",ariaLabel:"Three",title:"3",subtitle:"DEF"},{namespace:"four",ariaLabel:"Four",title:"4",subtitle:"GHI"},{namespace:"five",ariaLabel:"Five",title:"5",subtitle:"JKL"},{namespace:"six",ariaLabel:"Six",title:"6",subtitle:"MNO"},{namespace:"seven",ariaLabel:"Seven",title:"7",subtitle:"PQRS"},{namespace:"eight",ariaLabel:"Eight",title:"8",subtitle:"TUV"},{namespace:"nine",ariaLabel:"Nine",title:"9",subtitle:"WXYZ"},{namespace:"star",ariaLabel:"Star",title:"*"},{namespace:"zero",ariaLabel:"Zero",title:"0",subtitle:"+"},{namespace:"hash",ariaLabel:"Hash",title:"#"}]},{}],4:[function(e,t,n){"use strict";var i=function(){function e(e,t){for(var n=0;n {\n this.config.onLongPress!(this.config.subtitle!);\n },\n onPressStart: () => {\n this.config.onClick(this.config.title);\n },\n onLongPressCancel: this.config.onLongPressCancel,\n });\n }\n\n /**\n *\n * Apply click event listener on button element\n *\n * @param button\n *\n */\n addClickEventListener(button: HTMLButtonElement) {\n button.addEventListener('click', () => {\n this.config.onClick(this.config.title);\n });\n }\n\n /**\n *\n * If onLongPress callback is exist in config apply long press event\n * Else apply click event listener on button\n *\n * @param button\n *\n */\n configureButtonEvents(button: HTMLButtonElement) {\n if (this.config.onLongPress !== undefined) {\n // apply long press event\n this.applyLongPressEvent(button);\n } else {\n // add click event listener\n this.addClickEventListener(button);\n }\n }\n\n /**\n *\n * Dialpad button tile element\n *\n */\n get titleElement(): HTMLHeadingElement {\n const title = document.createElement('h1');\n title.classList.add('dialpad-btn__title');\n title.innerText = this.config.title;\n return title;\n }\n\n /**\n *\n * Dialpad button subtile\n *\n */\n get subtitleElement(): HTMLParagraphElement {\n const subtitle = document.createElement('p');\n subtitle.classList.add('dialpad-btn__subtitle');\n subtitle.innerText = this.config.subtitle ?? '';\n return subtitle;\n }\n\n /**\n *\n * Unique id for dialpad button\n *\n */\n get id() {\n return `dialpad-btn-${this.config.namespace}`;\n }\n\n /**\n *\n * Dialpad button query selector for dom manipulation\n *\n */\n get querySelector(): HTMLButtonElement {\n return document.getElementById(this.id)! as HTMLButtonElement;\n }\n\n /**\n *\n * Appends dialpad button to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n}\n","import { InputElementConfig } from './types';\r\n\r\n/**\r\n *\r\n * Input Element\r\n *\r\n */\r\nexport default class InputElement {\r\n // input element config\r\n private config: InputElementConfig;\r\n\r\n /**\r\n *\r\n * construct InputElement instance\r\n *\r\n */\r\n constructor(config: InputElementConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * input element skeleton\r\n *\r\n */\r\n get skeleton(): HTMLInputElement {\r\n const input = document.createElement('input');\r\n input.id = this.id;\r\n input.className = 'input-element';\r\n input.name = 'number';\r\n input.type = 'text';\r\n input.autofocus = true;\r\n input.inputMode = 'none';\r\n input.autocomplete = 'off';\r\n\r\n // add input event listener\r\n input.addEventListener('input', this.inputEventHandler.bind(this));\r\n\r\n return input;\r\n }\r\n\r\n /**\r\n *\r\n * Input element query selector for dom manipulation\r\n *\r\n */\r\n get querySelector(): HTMLInputElement {\r\n return document.getElementById(this.id) as HTMLInputElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for input element\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `input-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * Appends input element to a specified parent element.\r\n *\r\n * @param parentElement\r\n *\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton);\r\n }\r\n\r\n /**\r\n *\r\n * Make sure input element is focused\r\n *\r\n */\r\n makeSureFocused() {\r\n if (!this.focused) {\r\n this.focus();\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * Get input element selection start position\r\n *\r\n */\r\n get selectionStartPosition(): number {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n return this.querySelector.selectionStart!;\r\n }\r\n\r\n /**\r\n *\r\n * Get input element caret end position\r\n *\r\n */\r\n get selectionEndPosition(): number {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n return this.querySelector.selectionEnd!;\r\n }\r\n\r\n /**\r\n *\r\n * Set input element selection start and end positions\r\n *\r\n * @param position - desired caret position index value\r\n *\r\n */\r\n set selectionPosition(position: number) {\r\n // make sure the input is focused\r\n this.makeSureFocused();\r\n\r\n // set selection\r\n this.querySelector.selectionStart = position;\r\n this.querySelector.selectionEnd = position;\r\n }\r\n\r\n /**\r\n *\r\n * To check whether the input element is focused or not\r\n *\r\n */\r\n get focused(): boolean {\r\n return document.activeElement === this.querySelector;\r\n }\r\n\r\n /**\r\n *\r\n * To get input element value\r\n *\r\n */\r\n get value(): string {\r\n return this.querySelector.value;\r\n }\r\n\r\n /**\r\n *\r\n * To set input element value\r\n *\r\n * @param value - The value to set\r\n *\r\n */\r\n set value(value: string) {\r\n this.querySelector.value = value;\r\n }\r\n\r\n /**\r\n *\r\n * Focus input element\r\n *\r\n */\r\n focus() {\r\n this.querySelector.focus();\r\n }\r\n\r\n /**\r\n *\r\n * Insert a value at the caret position\r\n *\r\n * @param value - The value to be inserted\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n insertValue(value: string) {\r\n // capture current state\r\n const currentValue = this.value;\r\n const { selectionStartPosition, selectionEndPosition } = this;\r\n\r\n // prepare updated state\r\n let updatedValue;\r\n\r\n if (selectionStartPosition !== selectionEndPosition) {\r\n // if input selection start value is not equal to selection end position\r\n // it means input value is selected, in this case remove selected value\r\n // and insert new value at the selection start (caret) position\r\n updatedValue =\r\n currentValue.slice(0, selectionStartPosition) +\r\n value +\r\n currentValue.slice(selectionEndPosition);\r\n } else {\r\n // else input selection start value is same as selection end value\r\n // insert new value at selection start (caret) position\r\n updatedValue =\r\n currentValue.slice(0, selectionStartPosition) +\r\n value +\r\n currentValue.slice(selectionStartPosition);\r\n }\r\n const updatedCaretPosition = selectionStartPosition + value.length;\r\n\r\n // update state\r\n this.value = updatedValue;\r\n this.selectionPosition = updatedCaretPosition;\r\n }\r\n\r\n /**\r\n *\r\n * Remove characters from the value starting at the caret position\r\n *\r\n * @param count - The number of characters, default value is 1\r\n *\r\n */\r\n removeValue(count: number = 1) {\r\n // capture current state\r\n const currentValue = this.value;\r\n const currentCaretPosition = this.selectionStartPosition;\r\n\r\n // prepare updated state\r\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\r\n\r\n // if before caret value is empty do nothing\r\n if (beforeCaretValue !== '') {\r\n const afterCaretValue = currentValue.slice(currentCaretPosition);\r\n const endIndex = beforeCaretValue.length - count;\r\n const updatedValue =\r\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\r\n const updatedCaretPosition = currentCaretPosition - count;\r\n\r\n // update state\r\n this.value = updatedValue;\r\n this.selectionPosition = updatedCaretPosition;\r\n\r\n // check empty or not\r\n this.inputEventHandler();\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * @param value - The value to be replaced current last value\r\n *\r\n */\r\n replaceValue(value: string) {\r\n this.removeValue();\r\n this.insertValue(value);\r\n this.inputEventHandler();\r\n }\r\n\r\n /**\r\n *\r\n * Clear the value of input element\r\n *\r\n */\r\n clear() {\r\n this.value = '';\r\n this.focus();\r\n }\r\n\r\n /**\r\n *\r\n * Remove unwanted symbols and allow only digits, +, *, and #\r\n *\r\n */\r\n validation() {\r\n // prepare updated state\r\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\r\n const updatedValueAfterRemoveUnwantedChars = this.value.replace(\r\n /[^0-9+*#]/g,\r\n ''\r\n );\r\n const updatedCaretPosition =\r\n caretPositionBeforeRemoveUnwantedChars +\r\n updatedValueAfterRemoveUnwantedChars.length -\r\n this.value.length;\r\n\r\n // update state\r\n this.value = updatedValueAfterRemoveUnwantedChars;\r\n this.selectionPosition = updatedCaretPosition;\r\n }\r\n\r\n /**\r\n *\r\n * `input` event handler\r\n *\r\n */\r\n inputEventHandler() {\r\n // Remove unwanted symbols and allow only digits, +, *, and #\r\n this.validation();\r\n\r\n // check whether the value is empty or not, and act accordingly.\r\n if (this.value === '') {\r\n // eslint-disable-next-line no-console\r\n console.log('value empty');\r\n this.config.onValueEmpty();\r\n } else {\r\n // eslint-disable-next-line no-console\r\n console.log('value non empty');\r\n this.config.onValueNonEmpty();\r\n }\r\n }\r\n}\r\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\r\nimport Keypad from '../layout/keypad';\r\nimport DialpadConfig from './types';\r\n\r\n/**\r\n *\r\n * Dialpad Page\r\n *\r\n */\r\nexport default class Dialpad {\r\n // dialpad configuration\r\n private config: DialpadConfig;\r\n\r\n // input field instance\r\n private inputField!: InputElement;\r\n\r\n // keypad instance\r\n private keypad!: Keypad;\r\n\r\n // recent call number\r\n private recentCallOnNumber?: string;\r\n\r\n /**\r\n *\r\n * construct Dialpad instance\r\n *\r\n */\r\n constructor(config: DialpadConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * page skeleton\r\n *\r\n * @returns {HTMLDivElement}\r\n *\r\n */\r\n private skeleton(): HTMLElement {\r\n const dialpad = document.createElement('section');\r\n dialpad.id = this.id;\r\n dialpad.className = 'dialpad';\r\n\r\n // build input field\r\n this.inputFieldLayout.build(dialpad);\r\n\r\n // build keypad layout\r\n this.keypadLayout.build(dialpad);\r\n\r\n // append copyright text\r\n dialpad.appendChild(this.copyrightText);\r\n\r\n return dialpad;\r\n }\r\n\r\n /**\r\n *\r\n * To get Input Element\r\n *\r\n */\r\n private get inputFieldLayout(): InputElement {\r\n this.inputField = new InputElement({\r\n namespace: this.config.namespace,\r\n onValueEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('disable backspace button');\r\n this.keypad.disableBackspaceButton();\r\n },\r\n onValueNonEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('enable backspace button');\r\n this.keypad.enableBackspaceButton();\r\n },\r\n });\r\n return this.inputField;\r\n }\r\n\r\n /**\r\n *\r\n * To get Keypad Layout\r\n *\r\n */\r\n private get keypadLayout() {\r\n this.keypad = new Keypad({\r\n namespace: this.config.namespace,\r\n onKeypadBtnClick: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on button', value);\r\n\r\n // insert value\r\n this.inputField.insertValue(value);\r\n this.keypad.enableBackspaceButton();\r\n },\r\n onZeroBtnLongPress: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('long pressed on zero button');\r\n\r\n // insert zero button subtitle value `+`\r\n this.inputField.replaceValue(value);\r\n },\r\n onZeroBtnLongPressCancel: () => {\r\n // focus input field\r\n this.inputField.focus();\r\n },\r\n onCallBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on call button');\r\n\r\n // check whether try to call on recent number\r\n if (\r\n this.inputField.value === '' &&\r\n this.recentCallOnNumber !== undefined\r\n ) {\r\n // eslint-disable-next-line no-console\r\n console.log('fill last call on number in input field value');\r\n this.inputField.value = this.recentCallOnNumber;\r\n this.inputField.focus();\r\n this.keypad.enableBackspaceButton();\r\n }\r\n // if value is not empty (or add check for valid phone number)\r\n else if (this.inputField.value !== '') {\r\n // update last call on number\r\n this.recentCallOnNumber = this.inputField.value;\r\n\r\n // eslint-disable-next-line no-console\r\n console.log('placing call on ', this.inputField.value);\r\n }\r\n },\r\n onBackspaceBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on clear button');\r\n this.inputField.removeValue();\r\n },\r\n onBackspaceBtnLongPress: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('long press on backspace button');\r\n\r\n // clear input value and hide backspace button\r\n this.inputField.value = '';\r\n this.inputField.focus();\r\n this.keypad.disableBackspaceButton();\r\n },\r\n });\r\n\r\n return this.keypad;\r\n }\r\n\r\n /**\r\n *\r\n * append copyright text element\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n get copyrightText(): HTMLParagraphElement {\r\n // create copyright text\r\n const copyrightTextElement = document.createElement('p');\r\n copyrightTextElement.className = 'copyright-text';\r\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\r\n\r\n return copyrightTextElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for page\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `dialpad-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * querySelect for app\r\n *\r\n * @returns {HTMLElement}\r\n *\r\n */\r\n get element(): HTMLElement {\r\n return document.getElementById(this.id)!;\r\n }\r\n\r\n /**\r\n *\r\n * Remove dialpad skeleton from dom\r\n *\r\n */\r\n destroy(): void {\r\n this.element.remove();\r\n }\r\n\r\n /**\r\n *\r\n * Append dialpad skeleton into parent element\r\n *\r\n * @param parentElement {HTMLElement}\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton());\r\n }\r\n}\r\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","bundle.js","src/ts/components/buttons/buttons.ts","src/ts/components/forms/inputs.ts","src/ts/layout/data.ts","src/ts/layout/keypad.ts","src/ts/main.ts","src/ts/pages/dialpad.ts","src/ts/utilities/longPress.ts"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","module","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","Constructor","protoProps","staticProps","prototype","__importDefault","mod","__esModule","default","value","longPress_1","DialpadButton","config","instance","TypeError","_classCallCheck","this","undefined","subtitle","onLongPress","button","_this","apply","onLongPressCallback","onPressStart","onClick","title","onLongPressCancel","_this2","addEventListener","applyLongPressEvent","addClickEventListener","parentElement","appendChild","skeleton","get","document","createElement","id","classList","add","setAttribute","ariaLabel","titleElement","subtitleElement","configureButtonEvents","innerText","_a","namespace","getElementById","InputElement","focused","focus","querySelector","currentValue","selectionStartPosition","selectionEndPosition","updatedValue","slice","updatedCaretPosition","selectionPosition","count","arguments","currentCaretPosition","beforeCaretValue","afterCaretValue","endIndex","inputEventHandler","removeValue","insertValue","caretPositionBeforeRemoveUnwantedChars","updatedValueAfterRemoveUnwantedChars","replace","validation","console","log","onValueEmpty","onValueNonEmpty","input","className","name","type","autofocus","inputMode","autocomplete","bind","makeSureFocused","selectionStart","selectionEnd","set","position","activeElement","buttons_1","data_1","Keypad","iconName","iconElement","backspaceButtonElement","disabled","keypad","forEach","onKeypadBtnClick","onZeroBtnLongPress","onZeroBtnLongPressCancel","build","callButton","backspaceButton","callBtn","getMaterialIcon","onCallBtnClick","backspaceBtn","onBackspaceBtnClick","onBackspaceBtnLongPress","dialpad_1","window","onload","body","inputs_1","keypad_1","Dialpad","dialpad","inputFieldLayout","keypadLayout","copyrightText","element","remove","inputField","disableBackspaceButton","enableBackspaceButton","replaceValue","recentCallOnNumber","copyrightTextElement","Date","getFullYear","LongPressEvent","isHeld","activeHoldTimeoutId","longPressTimeout","onHoldStart","onHoldEnd","setTimeout","clearTimeout"],"mappings":"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","file":"../bundle.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i {\n this.config.onLongPress!(this.config.subtitle!);\n },\n onPressStart: () => {\n this.config.onClick(this.config.title);\n },\n onLongPressCancel: this.config.onLongPressCancel,\n });\n }\n\n /**\n *\n * Apply click event listener on button element\n *\n * @param button\n *\n */\n addClickEventListener(button: HTMLButtonElement) {\n button.addEventListener('click', () => {\n this.config.onClick(this.config.title);\n });\n }\n\n /**\n *\n * If onLongPress callback is exist in config apply long press event\n * Else apply click event listener on button\n *\n * @param button\n *\n */\n configureButtonEvents(button: HTMLButtonElement) {\n if (this.config.onLongPress !== undefined) {\n // apply long press event\n this.applyLongPressEvent(button);\n } else {\n // add click event listener\n this.addClickEventListener(button);\n }\n }\n\n /**\n *\n * Dialpad button tile element\n *\n */\n get titleElement(): HTMLHeadingElement {\n const title = document.createElement('h1');\n title.classList.add('dialpad-btn__title');\n title.innerText = this.config.title;\n return title;\n }\n\n /**\n *\n * Dialpad button subtile\n *\n */\n get subtitleElement(): HTMLParagraphElement {\n const subtitle = document.createElement('p');\n subtitle.classList.add('dialpad-btn__subtitle');\n subtitle.innerText = this.config.subtitle ?? '';\n return subtitle;\n }\n\n /**\n *\n * Unique id for dialpad button\n *\n */\n get id() {\n return `dialpad-btn-${this.config.namespace}`;\n }\n\n /**\n *\n * Dialpad button query selector for dom manipulation\n *\n */\n get querySelector(): HTMLButtonElement {\n return document.getElementById(this.id)! as HTMLButtonElement;\n }\n\n /**\n *\n * Appends dialpad button to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n}\n","import { InputElementConfig } from './types';\n\n/**\n *\n * Input Element\n *\n */\nexport default class InputElement {\n // input element config\n private config: InputElementConfig;\n\n /**\n *\n * construct InputElement instance\n *\n */\n constructor(config: InputElementConfig) {\n this.config = config;\n }\n\n /**\n *\n * input element skeleton\n *\n */\n get skeleton(): HTMLInputElement {\n const input = document.createElement('input');\n input.id = this.id;\n input.className = 'input-element';\n input.name = 'number';\n input.type = 'text';\n input.autofocus = true;\n input.inputMode = 'none';\n input.autocomplete = 'off';\n\n // add input event listener\n input.addEventListener('input', this.inputEventHandler.bind(this));\n\n return input;\n }\n\n /**\n *\n * Input element query selector for dom manipulation\n *\n */\n get querySelector(): HTMLInputElement {\n return document.getElementById(this.id) as HTMLInputElement;\n }\n\n /**\n *\n * Unique id for input element\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `input-${this.config.namespace}`;\n }\n\n /**\n *\n * Appends input element to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Make sure input element is focused\n *\n */\n makeSureFocused() {\n if (!this.focused) {\n this.focus();\n }\n }\n\n /**\n *\n * Get input element selection start position\n *\n */\n get selectionStartPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionStart!;\n }\n\n /**\n *\n * Get input element caret end position\n *\n */\n get selectionEndPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionEnd!;\n }\n\n /**\n *\n * Set input element selection start and end positions\n *\n * @param position - desired caret position index value\n *\n */\n set selectionPosition(position: number) {\n // make sure the input is focused\n this.makeSureFocused();\n\n // set selection\n this.querySelector.selectionStart = position;\n this.querySelector.selectionEnd = position;\n }\n\n /**\n *\n * To check whether the input element is focused or not\n *\n */\n get focused(): boolean {\n return document.activeElement === this.querySelector;\n }\n\n /**\n *\n * To get input element value\n *\n */\n get value(): string {\n return this.querySelector.value;\n }\n\n /**\n *\n * To set input element value\n *\n * @param value - The value to set\n *\n */\n set value(value: string) {\n this.querySelector.value = value;\n }\n\n /**\n *\n * Focus input element\n *\n */\n focus() {\n this.querySelector.focus();\n }\n\n /**\n *\n * Insert a value at the caret position\n *\n * @param value - The value to be inserted\n *\n */\n // eslint-disable-next-line class-methods-use-this\n insertValue(value: string) {\n // capture current state\n const currentValue = this.value;\n const { selectionStartPosition, selectionEndPosition } = this;\n\n // prepare updated state\n let updatedValue;\n\n if (selectionStartPosition !== selectionEndPosition) {\n // if input selection start value is not equal to selection end position\n // it means input value is selected, in this case remove selected value\n // and insert new value at the selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionEndPosition);\n } else {\n // else input selection start value is same as selection end value\n // insert new value at selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionStartPosition);\n }\n const updatedCaretPosition = selectionStartPosition + value.length;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * Remove characters from the value starting at the caret position\n *\n * @param count - The number of characters, default value is 1\n *\n */\n removeValue(count: number = 1) {\n // capture current state\n const currentValue = this.value;\n const currentCaretPosition = this.selectionStartPosition;\n\n // prepare updated state\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\n\n // if before caret value is empty do nothing\n if (beforeCaretValue !== '') {\n const afterCaretValue = currentValue.slice(currentCaretPosition);\n const endIndex = beforeCaretValue.length - count;\n const updatedValue =\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\n const updatedCaretPosition = currentCaretPosition - count;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n\n // check empty or not\n this.inputEventHandler();\n }\n }\n\n /**\n *\n * @param value - The value to be replaced current last value\n *\n */\n replaceValue(value: string) {\n this.removeValue();\n this.insertValue(value);\n this.inputEventHandler();\n }\n\n /**\n *\n * Clear the value of input element\n *\n */\n clear() {\n this.value = '';\n this.focus();\n }\n\n /**\n *\n * Remove unwanted symbols and allow only digits, +, *, and #\n *\n */\n validation() {\n // prepare updated state\n const { value } = this;\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\n const updatedValueAfterRemoveUnwantedChars = value.replace(\n /[^0-9+*#]/g,\n ''\n );\n const updatedCaretPosition =\n caretPositionBeforeRemoveUnwantedChars +\n updatedValueAfterRemoveUnwantedChars.length -\n value.length;\n\n // update state\n this.value = updatedValueAfterRemoveUnwantedChars;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * `input` event handler\n *\n */\n inputEventHandler() {\n // Remove unwanted symbols and allow only digits, +, *, and #\n this.validation();\n\n // check whether the value is empty or not, and act accordingly.\n if (this.value === '') {\n // eslint-disable-next-line no-console\n console.log('value empty');\n this.config.onValueEmpty();\n } else {\n // eslint-disable-next-line no-console\n console.log('value non empty');\n this.config.onValueNonEmpty();\n }\n }\n}\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\r\nimport Keypad from '../layout/keypad';\r\nimport DialpadConfig from './types';\r\n\r\n/**\r\n *\r\n * Dialpad Page\r\n *\r\n */\r\nexport default class Dialpad {\r\n // dialpad configuration\r\n private config: DialpadConfig;\r\n\r\n // input field instance\r\n private inputField!: InputElement;\r\n\r\n // keypad instance\r\n private keypad!: Keypad;\r\n\r\n // recent call number\r\n private recentCallOnNumber?: string;\r\n\r\n /**\r\n *\r\n * construct Dialpad instance\r\n *\r\n */\r\n constructor(config: DialpadConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * page skeleton\r\n *\r\n * @returns {HTMLDivElement}\r\n *\r\n */\r\n private skeleton(): HTMLElement {\r\n const dialpad = document.createElement('section');\r\n dialpad.id = this.id;\r\n dialpad.className = 'dialpad';\r\n\r\n // build input field\r\n this.inputFieldLayout.build(dialpad);\r\n\r\n // build keypad layout\r\n this.keypadLayout.build(dialpad);\r\n\r\n // append copyright text\r\n dialpad.appendChild(this.copyrightText);\r\n\r\n return dialpad;\r\n }\r\n\r\n /**\r\n *\r\n * To get Input Element\r\n *\r\n */\r\n private get inputFieldLayout(): InputElement {\r\n this.inputField = new InputElement({\r\n namespace: this.config.namespace,\r\n onValueEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('disable backspace button');\r\n this.keypad.disableBackspaceButton();\r\n },\r\n onValueNonEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('enable backspace button');\r\n this.keypad.enableBackspaceButton();\r\n },\r\n });\r\n return this.inputField;\r\n }\r\n\r\n /**\r\n *\r\n * To get Keypad Layout\r\n *\r\n */\r\n private get keypadLayout() {\r\n this.keypad = new Keypad({\r\n namespace: this.config.namespace,\r\n onKeypadBtnClick: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on button', value);\r\n\r\n // insert value\r\n this.inputField.insertValue(value);\r\n this.keypad.enableBackspaceButton();\r\n },\r\n onZeroBtnLongPress: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('long pressed on zero button');\r\n\r\n // insert zero button subtitle value `+`\r\n this.inputField.replaceValue(value);\r\n },\r\n onZeroBtnLongPressCancel: () => {\r\n // focus input field\r\n this.inputField.focus();\r\n },\r\n onCallBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on call button');\r\n\r\n // check whether try to call on recent number\r\n if (\r\n this.inputField.value === '' &&\r\n this.recentCallOnNumber !== undefined\r\n ) {\r\n // eslint-disable-next-line no-console\r\n console.log('fill last call on number in input field value');\r\n this.inputField.value = this.recentCallOnNumber;\r\n this.inputField.focus();\r\n this.keypad.enableBackspaceButton();\r\n }\r\n // if value is not empty (or add check for valid phone number)\r\n else if (this.inputField.value !== '') {\r\n // update last call on number\r\n this.recentCallOnNumber = this.inputField.value;\r\n\r\n // eslint-disable-next-line no-console\r\n console.log('placing call on ', this.inputField.value);\r\n }\r\n },\r\n onBackspaceBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on clear button');\r\n this.inputField.removeValue();\r\n },\r\n onBackspaceBtnLongPress: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('long press on backspace button');\r\n\r\n // clear input value and hide backspace button\r\n this.inputField.value = '';\r\n this.inputField.focus();\r\n this.keypad.disableBackspaceButton();\r\n },\r\n });\r\n\r\n return this.keypad;\r\n }\r\n\r\n /**\r\n *\r\n * append copyright text element\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n get copyrightText(): HTMLParagraphElement {\r\n // create copyright text\r\n const copyrightTextElement = document.createElement('p');\r\n copyrightTextElement.className = 'copyright-text';\r\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\r\n\r\n return copyrightTextElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for page\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `dialpad-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * querySelect for app\r\n *\r\n * @returns {HTMLElement}\r\n *\r\n */\r\n get element(): HTMLElement {\r\n return document.getElementById(this.id)!;\r\n }\r\n\r\n /**\r\n *\r\n * Remove dialpad skeleton from dom\r\n *\r\n */\r\n destroy(): void {\r\n this.element.remove();\r\n }\r\n\r\n /**\r\n *\r\n * Append dialpad skeleton into parent element\r\n *\r\n * @param parentElement {HTMLElement}\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton());\r\n }\r\n}\r\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file From 6f5ca85d1ee5d2115eb7163b51533e31e894b19c Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Tue, 14 Nov 2023 01:33:05 +0530 Subject: [PATCH 21/28] input element validation should update correct value and selection position after removing unwanted characters --- tests/components/inputs.ts | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 99bfff7..59673d6 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -346,4 +346,44 @@ describe('Test Input Element', () => { // Assert that the focusSpy called once expect(focusSpy.calledOnce).to.be.true; }); + + it('validation should update correct value and selection position after removing unwanted characters', () => { + // Create InputElement instance + const input = new InputElement(config); + + // build InputElement + input.build(document.body); + + // Set initial state + input.value = '123abc*456#'; + + // Create spy for value getter and setter + const valueSpy = sinon.spy(input, 'value', ['get', 'set']); + + // Create spy for selectionStartPosition getter + const selectionStartPositionGetterSpy = sinon.spy( + input, + 'selectionStartPosition', + ['get'] + ); + + // Create spy for selectionStartPosition setter + const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); + + // Call the validation method + input.validation(); + + // Assert that the focusSpy called once + expect(valueSpy.get.calledOnce).to.be.true; + + // Assert that the selectionStartPositionGetterStub called once + expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the value is updated correctly + expect(valueSpy.set.calledOnceWith('123*456#')).to.be.true; + + // Assert that the selection position is updated correctly - 13 + 8 -13 = 8 + expect(selectionPositionSpy.set.calledOnceWith('123*456#'.length)).to.be + .true; + }); }); From 5d01482b6c979cbb4950424b7056e50255c3b59b Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Fri, 17 Nov 2023 02:21:57 +0530 Subject: [PATCH 22/28] input element inputEventHandler should call validation and trigger callbacks based on value --- tests/components/inputs.ts | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 59673d6..d7ae1ac 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -354,8 +354,8 @@ describe('Test Input Element', () => { // build InputElement input.build(document.body); - // Set initial state - input.value = '123abc*456#'; + // Set input element value + input.querySelector.value = '123abc*456#'; // Create spy for value getter and setter const valueSpy = sinon.spy(input, 'value', ['get', 'set']); @@ -386,4 +386,58 @@ describe('Test Input Element', () => { expect(selectionPositionSpy.set.calledOnceWith('123*456#'.length)).to.be .true; }); + + it('inputEventHandler should call validation and trigger callbacks based on value', () => { + // Create spy for onValueEmpty + const onValueEmptySpy = sinon.spy(); + + // Create spy for onValueNonEmptySpy + const onValueNonEmptySpy = sinon.spy(); + + // Create InputElement instance + const input = new InputElement({ + namespace: 'test-namespace', + onValueEmpty: onValueEmptySpy, + onValueNonEmpty: onValueNonEmptySpy, + }); + + // Create spy for validation + const validationSpy = sinon.spy(input, 'validation'); + + // build InputElement + input.build(document.body); + + // Call inputEventHandler method + input.inputEventHandler(); + + // Assert that validationSpy is called + expect(validationSpy.calledOnce).to.be.true; + + // Assert onValueEmptySpy is called + expect(onValueEmptySpy.calledOnce).to.be.true; + + // Assert onValueNonEmptySpy is not called + expect(onValueNonEmptySpy.calledOnce).to.be.false; + + // Reset spies + validationSpy.resetHistory(); + onValueEmptySpy.resetHistory(); + onValueNonEmptySpy.resetHistory(); + + // Set input element value + input.querySelector.value = 'hello'; + + // Call inputEventHandler method + input.inputEventHandler(); + + // Assert that validationSpy is called + expect(validationSpy.calledOnce).to.be.true; + + // TODO: fix this + // // Assert onValueEmptySpy is not called + // expect(onValueEmptySpy.calledOnce).to.be.false; + + // // Assert onValueNonEmptySpy is called + // expect(onValueNonEmptySpy.calledOnce).to.be.true; + }); }); From 185c43183845bb13f562e322f9e72400530c4990 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 3 Dec 2023 21:06:33 +0530 Subject: [PATCH 23/28] fix input element inputEventHandler should call validation and trigger callbacks based on value test --- tests/components/inputs.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index d7ae1ac..d5dd380 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -425,7 +425,7 @@ describe('Test Input Element', () => { onValueNonEmptySpy.resetHistory(); // Set input element value - input.querySelector.value = 'hello'; + input.querySelector.value = '1234'; // Call inputEventHandler method input.inputEventHandler(); @@ -433,11 +433,10 @@ describe('Test Input Element', () => { // Assert that validationSpy is called expect(validationSpy.calledOnce).to.be.true; - // TODO: fix this - // // Assert onValueEmptySpy is not called - // expect(onValueEmptySpy.calledOnce).to.be.false; + // Assert onValueEmptySpy is not called + expect(onValueEmptySpy.calledOnce).to.be.false; - // // Assert onValueNonEmptySpy is called - // expect(onValueNonEmptySpy.calledOnce).to.be.true; + // Assert onValueNonEmptySpy is called + expect(onValueNonEmptySpy.calledOnce).to.be.true; }); }); From 08840b55c2738d29fd7e7f5c672c3d104640cf9e Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 3 Dec 2023 21:18:04 +0530 Subject: [PATCH 24/28] relocate input element focus --- dist/js/maps/bundle.js.map | 2 +- src/ts/components/forms/inputs.ts | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dist/js/maps/bundle.js.map b/dist/js/maps/bundle.js.map index 6044b2b..4fb107b 100644 --- a/dist/js/maps/bundle.js.map +++ b/dist/js/maps/bundle.js.map @@ -1 +1 @@ -{"version":3,"sources":["node_modules/browser-pack/_prelude.js","bundle.js","src/ts/components/buttons/buttons.ts","src/ts/components/forms/inputs.ts","src/ts/layout/data.ts","src/ts/layout/keypad.ts","src/ts/main.ts","src/ts/pages/dialpad.ts","src/ts/utilities/longPress.ts"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","module","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","Constructor","protoProps","staticProps","prototype","__importDefault","mod","__esModule","default","value","longPress_1","DialpadButton","config","instance","TypeError","_classCallCheck","this","undefined","subtitle","onLongPress","button","_this","apply","onLongPressCallback","onPressStart","onClick","title","onLongPressCancel","_this2","addEventListener","applyLongPressEvent","addClickEventListener","parentElement","appendChild","skeleton","get","document","createElement","id","classList","add","setAttribute","ariaLabel","titleElement","subtitleElement","configureButtonEvents","innerText","_a","namespace","getElementById","InputElement","focused","focus","querySelector","currentValue","selectionStartPosition","selectionEndPosition","updatedValue","slice","updatedCaretPosition","selectionPosition","count","arguments","currentCaretPosition","beforeCaretValue","afterCaretValue","endIndex","inputEventHandler","removeValue","insertValue","caretPositionBeforeRemoveUnwantedChars","updatedValueAfterRemoveUnwantedChars","replace","validation","console","log","onValueEmpty","onValueNonEmpty","input","className","name","type","autofocus","inputMode","autocomplete","bind","makeSureFocused","selectionStart","selectionEnd","set","position","activeElement","buttons_1","data_1","Keypad","iconName","iconElement","backspaceButtonElement","disabled","keypad","forEach","onKeypadBtnClick","onZeroBtnLongPress","onZeroBtnLongPressCancel","build","callButton","backspaceButton","callBtn","getMaterialIcon","onCallBtnClick","backspaceBtn","onBackspaceBtnClick","onBackspaceBtnLongPress","dialpad_1","window","onload","body","inputs_1","keypad_1","Dialpad","dialpad","inputFieldLayout","keypadLayout","copyrightText","element","remove","inputField","disableBackspaceButton","enableBackspaceButton","replaceValue","recentCallOnNumber","copyrightTextElement","Date","getFullYear","LongPressEvent","isHeld","activeHoldTimeoutId","longPressTimeout","onHoldStart","onHoldEnd","setTimeout","clearTimeout"],"mappings":"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","file":"../bundle.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i {\n this.config.onLongPress!(this.config.subtitle!);\n },\n onPressStart: () => {\n this.config.onClick(this.config.title);\n },\n onLongPressCancel: this.config.onLongPressCancel,\n });\n }\n\n /**\n *\n * Apply click event listener on button element\n *\n * @param button\n *\n */\n addClickEventListener(button: HTMLButtonElement) {\n button.addEventListener('click', () => {\n this.config.onClick(this.config.title);\n });\n }\n\n /**\n *\n * If onLongPress callback is exist in config apply long press event\n * Else apply click event listener on button\n *\n * @param button\n *\n */\n configureButtonEvents(button: HTMLButtonElement) {\n if (this.config.onLongPress !== undefined) {\n // apply long press event\n this.applyLongPressEvent(button);\n } else {\n // add click event listener\n this.addClickEventListener(button);\n }\n }\n\n /**\n *\n * Dialpad button tile element\n *\n */\n get titleElement(): HTMLHeadingElement {\n const title = document.createElement('h1');\n title.classList.add('dialpad-btn__title');\n title.innerText = this.config.title;\n return title;\n }\n\n /**\n *\n * Dialpad button subtile\n *\n */\n get subtitleElement(): HTMLParagraphElement {\n const subtitle = document.createElement('p');\n subtitle.classList.add('dialpad-btn__subtitle');\n subtitle.innerText = this.config.subtitle ?? '';\n return subtitle;\n }\n\n /**\n *\n * Unique id for dialpad button\n *\n */\n get id() {\n return `dialpad-btn-${this.config.namespace}`;\n }\n\n /**\n *\n * Dialpad button query selector for dom manipulation\n *\n */\n get querySelector(): HTMLButtonElement {\n return document.getElementById(this.id)! as HTMLButtonElement;\n }\n\n /**\n *\n * Appends dialpad button to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n}\n","import { InputElementConfig } from './types';\n\n/**\n *\n * Input Element\n *\n */\nexport default class InputElement {\n // input element config\n private config: InputElementConfig;\n\n /**\n *\n * construct InputElement instance\n *\n */\n constructor(config: InputElementConfig) {\n this.config = config;\n }\n\n /**\n *\n * input element skeleton\n *\n */\n get skeleton(): HTMLInputElement {\n const input = document.createElement('input');\n input.id = this.id;\n input.className = 'input-element';\n input.name = 'number';\n input.type = 'text';\n input.autofocus = true;\n input.inputMode = 'none';\n input.autocomplete = 'off';\n\n // add input event listener\n input.addEventListener('input', this.inputEventHandler.bind(this));\n\n return input;\n }\n\n /**\n *\n * Input element query selector for dom manipulation\n *\n */\n get querySelector(): HTMLInputElement {\n return document.getElementById(this.id) as HTMLInputElement;\n }\n\n /**\n *\n * Unique id for input element\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `input-${this.config.namespace}`;\n }\n\n /**\n *\n * Appends input element to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Make sure input element is focused\n *\n */\n makeSureFocused() {\n if (!this.focused) {\n this.focus();\n }\n }\n\n /**\n *\n * Get input element selection start position\n *\n */\n get selectionStartPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionStart!;\n }\n\n /**\n *\n * Get input element caret end position\n *\n */\n get selectionEndPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionEnd!;\n }\n\n /**\n *\n * Set input element selection start and end positions\n *\n * @param position - desired caret position index value\n *\n */\n set selectionPosition(position: number) {\n // make sure the input is focused\n this.makeSureFocused();\n\n // set selection\n this.querySelector.selectionStart = position;\n this.querySelector.selectionEnd = position;\n }\n\n /**\n *\n * To check whether the input element is focused or not\n *\n */\n get focused(): boolean {\n return document.activeElement === this.querySelector;\n }\n\n /**\n *\n * To get input element value\n *\n */\n get value(): string {\n return this.querySelector.value;\n }\n\n /**\n *\n * To set input element value\n *\n * @param value - The value to set\n *\n */\n set value(value: string) {\n this.querySelector.value = value;\n }\n\n /**\n *\n * Focus input element\n *\n */\n focus() {\n this.querySelector.focus();\n }\n\n /**\n *\n * Insert a value at the caret position\n *\n * @param value - The value to be inserted\n *\n */\n // eslint-disable-next-line class-methods-use-this\n insertValue(value: string) {\n // capture current state\n const currentValue = this.value;\n const { selectionStartPosition, selectionEndPosition } = this;\n\n // prepare updated state\n let updatedValue;\n\n if (selectionStartPosition !== selectionEndPosition) {\n // if input selection start value is not equal to selection end position\n // it means input value is selected, in this case remove selected value\n // and insert new value at the selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionEndPosition);\n } else {\n // else input selection start value is same as selection end value\n // insert new value at selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionStartPosition);\n }\n const updatedCaretPosition = selectionStartPosition + value.length;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * Remove characters from the value starting at the caret position\n *\n * @param count - The number of characters, default value is 1\n *\n */\n removeValue(count: number = 1) {\n // capture current state\n const currentValue = this.value;\n const currentCaretPosition = this.selectionStartPosition;\n\n // prepare updated state\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\n\n // if before caret value is empty do nothing\n if (beforeCaretValue !== '') {\n const afterCaretValue = currentValue.slice(currentCaretPosition);\n const endIndex = beforeCaretValue.length - count;\n const updatedValue =\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\n const updatedCaretPosition = currentCaretPosition - count;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n\n // check empty or not\n this.inputEventHandler();\n }\n }\n\n /**\n *\n * @param value - The value to be replaced current last value\n *\n */\n replaceValue(value: string) {\n this.removeValue();\n this.insertValue(value);\n this.inputEventHandler();\n }\n\n /**\n *\n * Clear the value of input element\n *\n */\n clear() {\n this.value = '';\n this.focus();\n }\n\n /**\n *\n * Remove unwanted symbols and allow only digits, +, *, and #\n *\n */\n validation() {\n // prepare updated state\n const { value } = this;\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\n const updatedValueAfterRemoveUnwantedChars = value.replace(\n /[^0-9+*#]/g,\n ''\n );\n const updatedCaretPosition =\n caretPositionBeforeRemoveUnwantedChars +\n updatedValueAfterRemoveUnwantedChars.length -\n value.length;\n\n // update state\n this.value = updatedValueAfterRemoveUnwantedChars;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * `input` event handler\n *\n */\n inputEventHandler() {\n // Remove unwanted symbols and allow only digits, +, *, and #\n this.validation();\n\n // check whether the value is empty or not, and act accordingly.\n if (this.value === '') {\n // eslint-disable-next-line no-console\n console.log('value empty');\n this.config.onValueEmpty();\n } else {\n // eslint-disable-next-line no-console\n console.log('value non empty');\n this.config.onValueNonEmpty();\n }\n }\n}\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\r\nimport Keypad from '../layout/keypad';\r\nimport DialpadConfig from './types';\r\n\r\n/**\r\n *\r\n * Dialpad Page\r\n *\r\n */\r\nexport default class Dialpad {\r\n // dialpad configuration\r\n private config: DialpadConfig;\r\n\r\n // input field instance\r\n private inputField!: InputElement;\r\n\r\n // keypad instance\r\n private keypad!: Keypad;\r\n\r\n // recent call number\r\n private recentCallOnNumber?: string;\r\n\r\n /**\r\n *\r\n * construct Dialpad instance\r\n *\r\n */\r\n constructor(config: DialpadConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * page skeleton\r\n *\r\n * @returns {HTMLDivElement}\r\n *\r\n */\r\n private skeleton(): HTMLElement {\r\n const dialpad = document.createElement('section');\r\n dialpad.id = this.id;\r\n dialpad.className = 'dialpad';\r\n\r\n // build input field\r\n this.inputFieldLayout.build(dialpad);\r\n\r\n // build keypad layout\r\n this.keypadLayout.build(dialpad);\r\n\r\n // append copyright text\r\n dialpad.appendChild(this.copyrightText);\r\n\r\n return dialpad;\r\n }\r\n\r\n /**\r\n *\r\n * To get Input Element\r\n *\r\n */\r\n private get inputFieldLayout(): InputElement {\r\n this.inputField = new InputElement({\r\n namespace: this.config.namespace,\r\n onValueEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('disable backspace button');\r\n this.keypad.disableBackspaceButton();\r\n },\r\n onValueNonEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('enable backspace button');\r\n this.keypad.enableBackspaceButton();\r\n },\r\n });\r\n return this.inputField;\r\n }\r\n\r\n /**\r\n *\r\n * To get Keypad Layout\r\n *\r\n */\r\n private get keypadLayout() {\r\n this.keypad = new Keypad({\r\n namespace: this.config.namespace,\r\n onKeypadBtnClick: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on button', value);\r\n\r\n // insert value\r\n this.inputField.insertValue(value);\r\n this.keypad.enableBackspaceButton();\r\n },\r\n onZeroBtnLongPress: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('long pressed on zero button');\r\n\r\n // insert zero button subtitle value `+`\r\n this.inputField.replaceValue(value);\r\n },\r\n onZeroBtnLongPressCancel: () => {\r\n // focus input field\r\n this.inputField.focus();\r\n },\r\n onCallBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on call button');\r\n\r\n // check whether try to call on recent number\r\n if (\r\n this.inputField.value === '' &&\r\n this.recentCallOnNumber !== undefined\r\n ) {\r\n // eslint-disable-next-line no-console\r\n console.log('fill last call on number in input field value');\r\n this.inputField.value = this.recentCallOnNumber;\r\n this.inputField.focus();\r\n this.keypad.enableBackspaceButton();\r\n }\r\n // if value is not empty (or add check for valid phone number)\r\n else if (this.inputField.value !== '') {\r\n // update last call on number\r\n this.recentCallOnNumber = this.inputField.value;\r\n\r\n // eslint-disable-next-line no-console\r\n console.log('placing call on ', this.inputField.value);\r\n }\r\n },\r\n onBackspaceBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on clear button');\r\n this.inputField.removeValue();\r\n },\r\n onBackspaceBtnLongPress: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('long press on backspace button');\r\n\r\n // clear input value and hide backspace button\r\n this.inputField.value = '';\r\n this.inputField.focus();\r\n this.keypad.disableBackspaceButton();\r\n },\r\n });\r\n\r\n return this.keypad;\r\n }\r\n\r\n /**\r\n *\r\n * append copyright text element\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n get copyrightText(): HTMLParagraphElement {\r\n // create copyright text\r\n const copyrightTextElement = document.createElement('p');\r\n copyrightTextElement.className = 'copyright-text';\r\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\r\n\r\n return copyrightTextElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for page\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `dialpad-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * querySelect for app\r\n *\r\n * @returns {HTMLElement}\r\n *\r\n */\r\n get element(): HTMLElement {\r\n return document.getElementById(this.id)!;\r\n }\r\n\r\n /**\r\n *\r\n * Remove dialpad skeleton from dom\r\n *\r\n */\r\n destroy(): void {\r\n this.element.remove();\r\n }\r\n\r\n /**\r\n *\r\n * Append dialpad skeleton into parent element\r\n *\r\n * @param parentElement {HTMLElement}\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton());\r\n }\r\n}\r\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file +{"version":3,"sources":["node_modules/browser-pack/_prelude.js","bundle.js","src/ts/components/buttons/buttons.ts","src/ts/components/forms/inputs.ts","src/ts/layout/data.ts","src/ts/layout/keypad.ts","src/ts/main.ts","src/ts/pages/dialpad.ts","src/ts/utilities/longPress.ts"],"names":["r","e","n","t","o","i","f","c","require","u","a","Error","code","p","exports","call","length","module","_createClass","defineProperties","target","props","descriptor","enumerable","configurable","writable","Object","defineProperty","key","Constructor","protoProps","staticProps","prototype","__importDefault","mod","__esModule","default","value","longPress_1","DialpadButton","config","instance","TypeError","_classCallCheck","this","undefined","subtitle","onLongPress","button","_this","apply","onLongPressCallback","onPressStart","onClick","title","onLongPressCancel","_this2","addEventListener","applyLongPressEvent","addClickEventListener","parentElement","appendChild","skeleton","get","document","createElement","id","classList","add","setAttribute","ariaLabel","titleElement","subtitleElement","configureButtonEvents","innerText","_a","namespace","getElementById","InputElement","focused","focus","querySelector","currentValue","selectionStartPosition","selectionEndPosition","updatedValue","slice","updatedCaretPosition","selectionPosition","count","arguments","currentCaretPosition","beforeCaretValue","afterCaretValue","endIndex","inputEventHandler","removeValue","insertValue","caretPositionBeforeRemoveUnwantedChars","updatedValueAfterRemoveUnwantedChars","replace","validation","console","log","onValueEmpty","onValueNonEmpty","input","className","name","type","autofocus","inputMode","autocomplete","bind","makeSureFocused","selectionStart","selectionEnd","set","position","activeElement","buttons_1","data_1","Keypad","iconName","iconElement","backspaceButtonElement","disabled","keypad","forEach","onKeypadBtnClick","onZeroBtnLongPress","onZeroBtnLongPressCancel","build","callButton","backspaceButton","callBtn","getMaterialIcon","onCallBtnClick","backspaceBtn","onBackspaceBtnClick","onBackspaceBtnLongPress","dialpad_1","window","onload","body","inputs_1","keypad_1","Dialpad","dialpad","inputFieldLayout","keypadLayout","copyrightText","element","remove","inputField","disableBackspaceButton","enableBackspaceButton","replaceValue","recentCallOnNumber","copyrightTextElement","Date","getFullYear","LongPressEvent","isHeld","activeHoldTimeoutId","longPressTimeout","onHoldStart","onHoldEnd","setTimeout","clearTimeout"],"mappings":"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","file":"../bundle.js","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i {\n this.config.onLongPress!(this.config.subtitle!);\n },\n onPressStart: () => {\n this.config.onClick(this.config.title);\n },\n onLongPressCancel: this.config.onLongPressCancel,\n });\n }\n\n /**\n *\n * Apply click event listener on button element\n *\n * @param button\n *\n */\n addClickEventListener(button: HTMLButtonElement) {\n button.addEventListener('click', () => {\n this.config.onClick(this.config.title);\n });\n }\n\n /**\n *\n * If onLongPress callback is exist in config apply long press event\n * Else apply click event listener on button\n *\n * @param button\n *\n */\n configureButtonEvents(button: HTMLButtonElement) {\n if (this.config.onLongPress !== undefined) {\n // apply long press event\n this.applyLongPressEvent(button);\n } else {\n // add click event listener\n this.addClickEventListener(button);\n }\n }\n\n /**\n *\n * Dialpad button tile element\n *\n */\n get titleElement(): HTMLHeadingElement {\n const title = document.createElement('h1');\n title.classList.add('dialpad-btn__title');\n title.innerText = this.config.title;\n return title;\n }\n\n /**\n *\n * Dialpad button subtile\n *\n */\n get subtitleElement(): HTMLParagraphElement {\n const subtitle = document.createElement('p');\n subtitle.classList.add('dialpad-btn__subtitle');\n subtitle.innerText = this.config.subtitle ?? '';\n return subtitle;\n }\n\n /**\n *\n * Unique id for dialpad button\n *\n */\n get id() {\n return `dialpad-btn-${this.config.namespace}`;\n }\n\n /**\n *\n * Dialpad button query selector for dom manipulation\n *\n */\n get querySelector(): HTMLButtonElement {\n return document.getElementById(this.id)! as HTMLButtonElement;\n }\n\n /**\n *\n * Appends dialpad button to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n}\n","import { InputElementConfig } from './types';\n\n/**\n *\n * Input Element\n *\n */\nexport default class InputElement {\n // input element config\n private config: InputElementConfig;\n\n /**\n *\n * construct InputElement instance\n *\n */\n constructor(config: InputElementConfig) {\n this.config = config;\n }\n\n /**\n *\n * input element skeleton\n *\n */\n get skeleton(): HTMLInputElement {\n const input = document.createElement('input');\n input.id = this.id;\n input.className = 'input-element';\n input.name = 'number';\n input.type = 'text';\n input.autofocus = true;\n input.inputMode = 'none';\n input.autocomplete = 'off';\n\n // add input event listener\n input.addEventListener('input', this.inputEventHandler.bind(this));\n\n return input;\n }\n\n /**\n *\n * Input element query selector for dom manipulation\n *\n */\n get querySelector(): HTMLInputElement {\n return document.getElementById(this.id) as HTMLInputElement;\n }\n\n /**\n *\n * Unique id for input element\n *\n * @returns {string}\n *\n */\n get id(): string {\n return `input-${this.config.namespace}`;\n }\n\n /**\n *\n * Appends input element to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Make sure input element is focused\n *\n */\n makeSureFocused() {\n if (!this.focused) {\n this.focus();\n }\n }\n\n /**\n *\n * Get input element selection start position\n *\n */\n get selectionStartPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionStart!;\n }\n\n /**\n *\n * Get input element caret end position\n *\n */\n get selectionEndPosition(): number {\n // make sure the input is focused\n this.makeSureFocused();\n return this.querySelector.selectionEnd!;\n }\n\n /**\n *\n * Set input element selection start and end positions\n *\n * @param position - desired caret position index value\n *\n */\n set selectionPosition(position: number) {\n // make sure the input is focused\n this.makeSureFocused();\n\n // set selection\n this.querySelector.selectionStart = position;\n this.querySelector.selectionEnd = position;\n }\n\n /**\n *\n * Focus input element\n *\n */\n focus() {\n this.querySelector.focus();\n }\n\n /**\n *\n * To check whether the input element is focused or not\n *\n */\n get focused(): boolean {\n return document.activeElement === this.querySelector;\n }\n\n /**\n *\n * To get input element value\n *\n */\n get value(): string {\n return this.querySelector.value;\n }\n\n /**\n *\n * To set input element value\n *\n * @param value - The value to set\n *\n */\n set value(value: string) {\n this.querySelector.value = value;\n }\n\n /**\n *\n * Insert a value at the caret position\n *\n * @param value - The value to be inserted\n *\n */\n // eslint-disable-next-line class-methods-use-this\n insertValue(value: string) {\n // capture current state\n const currentValue = this.value;\n const { selectionStartPosition, selectionEndPosition } = this;\n\n // prepare updated state\n let updatedValue;\n\n if (selectionStartPosition !== selectionEndPosition) {\n // if input selection start value is not equal to selection end position\n // it means input value is selected, in this case remove selected value\n // and insert new value at the selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionEndPosition);\n } else {\n // else input selection start value is same as selection end value\n // insert new value at selection start (caret) position\n updatedValue =\n currentValue.slice(0, selectionStartPosition) +\n value +\n currentValue.slice(selectionStartPosition);\n }\n const updatedCaretPosition = selectionStartPosition + value.length;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * Remove characters from the value starting at the caret position\n *\n * @param count - The number of characters, default value is 1\n *\n */\n removeValue(count: number = 1) {\n // capture current state\n const currentValue = this.value;\n const currentCaretPosition = this.selectionStartPosition;\n\n // prepare updated state\n const beforeCaretValue = currentValue.slice(0, currentCaretPosition);\n\n // if before caret value is empty do nothing\n if (beforeCaretValue !== '') {\n const afterCaretValue = currentValue.slice(currentCaretPosition);\n const endIndex = beforeCaretValue.length - count;\n const updatedValue =\n beforeCaretValue.slice(0, endIndex) + afterCaretValue;\n const updatedCaretPosition = currentCaretPosition - count;\n\n // update state\n this.value = updatedValue;\n this.selectionPosition = updatedCaretPosition;\n\n // check empty or not\n this.inputEventHandler();\n }\n }\n\n /**\n *\n * @param value - The value to be replaced current last value\n *\n */\n replaceValue(value: string) {\n this.removeValue();\n this.insertValue(value);\n this.inputEventHandler();\n }\n\n /**\n *\n * Clear the value of input element\n *\n */\n clear() {\n this.value = '';\n this.focus();\n }\n\n /**\n *\n * Remove unwanted symbols and allow only digits, +, *, and #\n *\n */\n validation() {\n // prepare updated state\n const { value } = this;\n const caretPositionBeforeRemoveUnwantedChars = this.selectionStartPosition;\n const updatedValueAfterRemoveUnwantedChars = value.replace(\n /[^0-9+*#]/g,\n ''\n );\n const updatedCaretPosition =\n caretPositionBeforeRemoveUnwantedChars +\n updatedValueAfterRemoveUnwantedChars.length -\n value.length;\n\n // update state\n this.value = updatedValueAfterRemoveUnwantedChars;\n this.selectionPosition = updatedCaretPosition;\n }\n\n /**\n *\n * `input` event handler\n *\n */\n inputEventHandler() {\n // Remove unwanted symbols and allow only digits, +, *, and #\n this.validation();\n\n // check whether the value is empty or not, and act accordingly.\n if (this.value === '') {\n // eslint-disable-next-line no-console\n console.log('value empty');\n this.config.onValueEmpty();\n } else {\n // eslint-disable-next-line no-console\n console.log('value non empty');\n this.config.onValueNonEmpty();\n }\n }\n}\n","import { KeypadButtonData } from './types';\n\n/**\n *\n * Keypad buttons data\n *\n */\nconst KEYPAD_BUTTONS_DATA: Array = [\n {\n namespace: 'one',\n ariaLabel: 'One',\n title: '1',\n },\n {\n namespace: 'two',\n ariaLabel: 'Two',\n title: '2',\n subtitle: 'ABC',\n },\n {\n namespace: 'three',\n ariaLabel: 'Three',\n title: '3',\n subtitle: 'DEF',\n },\n {\n namespace: 'four',\n ariaLabel: 'Four',\n title: '4',\n subtitle: 'GHI',\n },\n {\n namespace: 'five',\n ariaLabel: 'Five',\n title: '5',\n subtitle: 'JKL',\n },\n {\n namespace: 'six',\n ariaLabel: 'Six',\n title: '6',\n subtitle: 'MNO',\n },\n {\n namespace: 'seven',\n ariaLabel: 'Seven',\n title: '7',\n subtitle: 'PQRS',\n },\n {\n namespace: 'eight',\n ariaLabel: 'Eight',\n title: '8',\n subtitle: 'TUV',\n },\n {\n namespace: 'nine',\n ariaLabel: 'Nine',\n title: '9',\n subtitle: 'WXYZ',\n },\n {\n namespace: 'star',\n ariaLabel: 'Star',\n title: '*',\n },\n {\n namespace: 'zero',\n ariaLabel: 'Zero',\n title: '0',\n subtitle: '+',\n },\n {\n namespace: 'hash',\n ariaLabel: 'Hash',\n title: '#',\n },\n];\n\nexport default KEYPAD_BUTTONS_DATA;\n","import DialpadButton from '../components/buttons/buttons';\nimport LongPressEvent from '../utilities/longPress';\nimport KEYPAD_BUTTONS_DATA from './data';\nimport { KeypadButtonData, KeypadConfig } from './types';\n\n/**\n *\n * Keypad\n *\n */\nexport default class Keypad {\n // keypad config\n private config: KeypadConfig;\n\n /**\n *\n * construct Keypad instance\n *\n */\n constructor(config: KeypadConfig) {\n this.config = config;\n }\n\n /**\n *\n * Keypad skeleton\n *\n */\n private get skeleton(): HTMLDivElement {\n const keypad = document.createElement('div');\n keypad.id = this.id;\n keypad.classList.add('keypad');\n\n // append digits buttons\n KEYPAD_BUTTONS_DATA.forEach((config: KeypadButtonData) => {\n // create button instance\n const btn = new DialpadButton({\n namespace: config.namespace,\n ariaLabel: config.ariaLabel,\n title: config.title,\n subtitle: config.subtitle,\n onClick: this.config.onKeypadBtnClick,\n onLongPress:\n config.title === '0' ? this.config.onZeroBtnLongPress : undefined,\n onLongPressCancel:\n config.title === '0'\n ? this.config.onZeroBtnLongPressCancel\n : undefined,\n });\n\n // append button\n btn.build(keypad);\n });\n\n // append dummy element\n keypad.appendChild(document.createElement('span'));\n\n // append call button\n keypad.appendChild(this.callButton);\n\n // append backspace button\n keypad.appendChild(this.backspaceButton);\n\n return keypad;\n }\n\n // eslint-disable-next-line class-methods-use-this\n getMaterialIcon(iconName: string) {\n const iconElement = document.createElement('span');\n iconElement.className = 'material-symbols-outlined';\n iconElement.innerText = iconName;\n return iconElement;\n }\n\n /**\n *\n * Keypad call button\n *\n */\n private get callButton(): HTMLElement {\n const callBtn = document.createElement('button');\n callBtn.classList.add('keypad__call-btn');\n callBtn.setAttribute('aria-label', 'call button');\n\n // append call icon\n callBtn.appendChild(this.getMaterialIcon('call'));\n\n // add click event listener\n callBtn.addEventListener('click', this.config.onCallBtnClick);\n\n return callBtn;\n }\n\n /**\n *\n * Keypad backspace button\n *\n */\n private get backspaceButton(): HTMLElement {\n const backspaceBtn = document.createElement('button');\n backspaceBtn.classList.add('keypad__backspace-btn');\n backspaceBtn.setAttribute('aria-label', 'Backspace button');\n backspaceBtn.disabled = true;\n\n // append backspace icon\n backspaceBtn.appendChild(this.getMaterialIcon('backspace'));\n\n // add click event listener\n backspaceBtn.addEventListener('click', this.config.onBackspaceBtnClick);\n\n // apply long press event\n LongPressEvent.apply({\n target: backspaceBtn,\n onLongPressCallback: this.config.onBackspaceBtnLongPress,\n });\n\n return backspaceBtn;\n }\n\n /**\n *\n * To get backspace button element for dom manipulations\n *\n */\n get backspaceButtonElement(): HTMLButtonElement {\n return this.querySelector.querySelector(\n '.keypad__backspace-btn'\n ) as HTMLButtonElement;\n }\n\n /**\n *\n * Unique id for keypad\n *\n */\n get id() {\n return `keypad-${this.config.namespace}`;\n }\n\n /**\n *\n * Keypad query selector for dom manipulation\n *\n */\n get querySelector(): HTMLDivElement {\n return document.getElementById(this.id)! as HTMLDivElement;\n }\n\n /**\n *\n * Appends keypad to a specified parent element.\n *\n * @param parentElement\n *\n */\n build(parentElement: HTMLElement) {\n parentElement.appendChild(this.skeleton);\n }\n\n /**\n *\n * Enable backspace button\n *\n */\n enableBackspaceButton() {\n this.backspaceButtonElement.disabled = false;\n }\n\n /**\n *\n * Disable backspace button\n *\n */\n disableBackspaceButton() {\n this.backspaceButtonElement.disabled = true;\n }\n}\n","import Dialpad from './pages/dialpad';\n\nwindow.onload = () => {\n // create dialpad instance\n const dialpad = new Dialpad({ namespace: 'demo' });\n\n // build dialpad\n dialpad.build(document.body);\n};\n","import InputElement from '../components/forms/inputs';\r\nimport Keypad from '../layout/keypad';\r\nimport DialpadConfig from './types';\r\n\r\n/**\r\n *\r\n * Dialpad Page\r\n *\r\n */\r\nexport default class Dialpad {\r\n // dialpad configuration\r\n private config: DialpadConfig;\r\n\r\n // input field instance\r\n private inputField!: InputElement;\r\n\r\n // keypad instance\r\n private keypad!: Keypad;\r\n\r\n // recent call number\r\n private recentCallOnNumber?: string;\r\n\r\n /**\r\n *\r\n * construct Dialpad instance\r\n *\r\n */\r\n constructor(config: DialpadConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n *\r\n * page skeleton\r\n *\r\n * @returns {HTMLDivElement}\r\n *\r\n */\r\n private skeleton(): HTMLElement {\r\n const dialpad = document.createElement('section');\r\n dialpad.id = this.id;\r\n dialpad.className = 'dialpad';\r\n\r\n // build input field\r\n this.inputFieldLayout.build(dialpad);\r\n\r\n // build keypad layout\r\n this.keypadLayout.build(dialpad);\r\n\r\n // append copyright text\r\n dialpad.appendChild(this.copyrightText);\r\n\r\n return dialpad;\r\n }\r\n\r\n /**\r\n *\r\n * To get Input Element\r\n *\r\n */\r\n private get inputFieldLayout(): InputElement {\r\n this.inputField = new InputElement({\r\n namespace: this.config.namespace,\r\n onValueEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('disable backspace button');\r\n this.keypad.disableBackspaceButton();\r\n },\r\n onValueNonEmpty: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('enable backspace button');\r\n this.keypad.enableBackspaceButton();\r\n },\r\n });\r\n return this.inputField;\r\n }\r\n\r\n /**\r\n *\r\n * To get Keypad Layout\r\n *\r\n */\r\n private get keypadLayout() {\r\n this.keypad = new Keypad({\r\n namespace: this.config.namespace,\r\n onKeypadBtnClick: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on button', value);\r\n\r\n // insert value\r\n this.inputField.insertValue(value);\r\n this.keypad.enableBackspaceButton();\r\n },\r\n onZeroBtnLongPress: (value: string) => {\r\n // eslint-disable-next-line no-console\r\n console.log('long pressed on zero button');\r\n\r\n // insert zero button subtitle value `+`\r\n this.inputField.replaceValue(value);\r\n },\r\n onZeroBtnLongPressCancel: () => {\r\n // focus input field\r\n this.inputField.focus();\r\n },\r\n onCallBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on call button');\r\n\r\n // check whether try to call on recent number\r\n if (\r\n this.inputField.value === '' &&\r\n this.recentCallOnNumber !== undefined\r\n ) {\r\n // eslint-disable-next-line no-console\r\n console.log('fill last call on number in input field value');\r\n this.inputField.value = this.recentCallOnNumber;\r\n this.inputField.focus();\r\n this.keypad.enableBackspaceButton();\r\n }\r\n // if value is not empty (or add check for valid phone number)\r\n else if (this.inputField.value !== '') {\r\n // update last call on number\r\n this.recentCallOnNumber = this.inputField.value;\r\n\r\n // eslint-disable-next-line no-console\r\n console.log('placing call on ', this.inputField.value);\r\n }\r\n },\r\n onBackspaceBtnClick: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('clicked on clear button');\r\n this.inputField.removeValue();\r\n },\r\n onBackspaceBtnLongPress: () => {\r\n // eslint-disable-next-line no-console\r\n console.log('long press on backspace button');\r\n\r\n // clear input value and hide backspace button\r\n this.inputField.value = '';\r\n this.inputField.focus();\r\n this.keypad.disableBackspaceButton();\r\n },\r\n });\r\n\r\n return this.keypad;\r\n }\r\n\r\n /**\r\n *\r\n * append copyright text element\r\n *\r\n */\r\n // eslint-disable-next-line class-methods-use-this\r\n get copyrightText(): HTMLParagraphElement {\r\n // create copyright text\r\n const copyrightTextElement = document.createElement('p');\r\n copyrightTextElement.className = 'copyright-text';\r\n copyrightTextElement.innerText = `Made by • Satyam Seth Ⓒ ${new Date().getFullYear()}`;\r\n\r\n return copyrightTextElement;\r\n }\r\n\r\n /**\r\n *\r\n * Unique id for page\r\n *\r\n * @returns {string}\r\n *\r\n */\r\n get id(): string {\r\n return `dialpad-${this.config.namespace}`;\r\n }\r\n\r\n /**\r\n *\r\n * querySelect for app\r\n *\r\n * @returns {HTMLElement}\r\n *\r\n */\r\n get element(): HTMLElement {\r\n return document.getElementById(this.id)!;\r\n }\r\n\r\n /**\r\n *\r\n * Remove dialpad skeleton from dom\r\n *\r\n */\r\n destroy(): void {\r\n this.element.remove();\r\n }\r\n\r\n /**\r\n *\r\n * Append dialpad skeleton into parent element\r\n *\r\n * @param parentElement {HTMLElement}\r\n */\r\n build(parentElement: HTMLElement) {\r\n parentElement.appendChild(this.skeleton());\r\n }\r\n}\r\n","import LongPressEventConfig from './types';\n\n/**\n *\n * LongPress event\n *\n */\nexport default class LongPressEvent {\n // target html element\n private config: LongPressEventConfig;\n\n // state to detect press and hold\n private isHeld: boolean = false;\n\n // setTimeout Id\n private activeHoldTimeoutId: number | null = null;\n\n // long press timeout in milliseconds\n private longPressTimeout = 500;\n\n /**\n *\n * construct LongPressEvent instance and add event listener\n *\n */\n constructor(config: LongPressEventConfig) {\n this.config = config;\n\n // start timer\n this.config.target.addEventListener(\n 'mousedown',\n this.onHoldStart.bind(this)\n );\n this.config.target.addEventListener(\n 'touchstart',\n this.onHoldStart.bind(this)\n );\n\n // stop timer\n this.config.target.addEventListener('mouseup', this.onHoldEnd.bind(this));\n this.config.target.addEventListener('mouseout', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'mouseleave',\n this.onHoldEnd.bind(this)\n );\n this.config.target.addEventListener('touchend', this.onHoldEnd.bind(this));\n this.config.target.addEventListener(\n 'touchcancel',\n this.onHoldEnd.bind(this)\n );\n }\n\n /**\n *\n * start set timeout timer on long press event for callback execution\n *\n */\n private onHoldStart() {\n // eslint-disable-next-line no-console\n console.log('Start Pressing');\n this.isHeld = true;\n\n // call on press start handler\n if (this.config.onPressStart) {\n this.config.onPressStart();\n }\n\n this.activeHoldTimeoutId = window.setTimeout(() => {\n if (this.isHeld === true) {\n // eslint-disable-next-line no-console\n console.log('long press detected');\n this.config.onLongPressCallback();\n }\n }, this.longPressTimeout);\n }\n\n /**\n *\n * clear set timeout timer on long press event\n *\n */\n private onHoldEnd() {\n // eslint-disable-next-line no-console\n console.log('Stop Pressing');\n this.isHeld = false;\n\n // call on press start handler\n if (this.config.onLongPressCancel) {\n this.config.onLongPressCancel();\n }\n\n if (this.activeHoldTimeoutId !== null) {\n window.clearTimeout(this.activeHoldTimeoutId);\n this.activeHoldTimeoutId = null;\n }\n }\n\n /**\n *\n * Apply long press event without using `new` keyword\n *\n */\n static apply(config: LongPressEventConfig) {\n // eslint-disable-next-line no-new\n new LongPressEvent(config);\n }\n}\n\n// Example how to use\n// window.onload = () => {\n// // const demoButton = document.querySelector('button');\n// // Using new keyword\n// // new ClickAndHold(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// // // Without using new keyword\n// // ClickAndHold.apply(demoButton, () => {\n// // console.log('Long Press');\n// // alert('Long Press Detected');\n// // });\n// };\n"]} \ No newline at end of file diff --git a/src/ts/components/forms/inputs.ts b/src/ts/components/forms/inputs.ts index 87bcd31..df511d3 100644 --- a/src/ts/components/forms/inputs.ts +++ b/src/ts/components/forms/inputs.ts @@ -119,6 +119,15 @@ export default class InputElement { this.querySelector.selectionEnd = position; } + /** + * + * Focus input element + * + */ + focus() { + this.querySelector.focus(); + } + /** * * To check whether the input element is focused or not @@ -148,15 +157,6 @@ export default class InputElement { this.querySelector.value = value; } - /** - * - * Focus input element - * - */ - focus() { - this.querySelector.focus(); - } - /** * * Insert a value at the caret position From 8593f464b440c4582defcc52da435b696b8bf101 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 10 Dec 2023 01:09:23 +0530 Subject: [PATCH 25/28] input element removeValue should remove characters from the value starting at the caret position --- tests/components/inputs.ts | 80 +++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index d5dd380..ad41f2a 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -376,7 +376,7 @@ describe('Test Input Element', () => { // Assert that the focusSpy called once expect(valueSpy.get.calledOnce).to.be.true; - // Assert that the selectionStartPositionGetterStub called once + // Assert that the selectionStartPositionGetterSpy called once expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; // Assert that the value is updated correctly @@ -439,4 +439,82 @@ describe('Test Input Element', () => { // Assert onValueNonEmptySpy is called expect(onValueNonEmptySpy.calledOnce).to.be.true; }); + + it.only('removeValue should remove characters from the value starting at the caret position', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for value getter and setter + const valueSpy = sinon.spy(input, 'value', ['get', 'set']); + + // Create spy for selectionStartPosition getter and setter + const selectionStartPositionGetterSpy = sinon.spy( + input, + 'selectionStartPosition', + ['get'] + ); + + // Create spy for selectionPosition setter + const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); + + // Create spy for inputEventHandler + const inputEventHandlerSpy = sinon.spy(input, 'inputEventHandler'); + + // build InputElement + input.build(document.body); + + // Call removeValue method + input.removeValue(); + + // Assert that the focusSpy called once + expect(valueSpy.get.calledOnce).to.be.true; + + // Assert that the selectionStartPositionGetterSpy called once + expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the valueSpy setter not called + expect(valueSpy.set.called).to.be.false; + + // Assert that the selectionPositionSpy setter not called + expect(selectionPositionSpy.set.called).to.be.false; + + // Assert that the inputEventHandlerSpy not called + expect(inputEventHandlerSpy.called).to.be.false; + + // Reset spies + valueSpy.get.resetHistory(); + valueSpy.set.resetHistory(); + selectionStartPositionGetterSpy.get.resetHistory(); + selectionPositionSpy.set.resetHistory(); + inputEventHandlerSpy.resetHistory(); + + // Set input element value + input.querySelector.value = '123456789'; + + input.querySelector.selectionStart = 6; + + // Call removeValue method + input.removeValue(2); + + // Assert that the focusSpy called thrice + expect(valueSpy.get.calledThrice).to.be.true; + + // Assert that the selectionStartPositionGetterSpy called twice + expect(selectionStartPositionGetterSpy.get.calledTwice).to.be.true; + + // Assert that the valueSpy setter first time called twice + expect(valueSpy.set.calledTwice).to.be.true; + + // Assert that the valueSpy setter first time called with correct args + expect(valueSpy.set.getCall(0).calledWithExactly('1234789')).to.be.true; + + // Assert that the selectionPositionSpy setter called twice + expect(selectionPositionSpy.set.calledTwice).to.be.true; + + // Assert that the selectionPositionSpy setter first time called with correct args + expect(selectionPositionSpy.set.getCall(0).calledWithExactly(4)).to.be.true; + + // Assert that the inputEventHandlerSpy called once + expect(inputEventHandlerSpy.calledOnce).to.be.true; + }); }); From 328650e0e9a74b14af883189ceb4925dd1964858 Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 10 Dec 2023 01:11:32 +0530 Subject: [PATCH 26/28] remove only annotation form input element removeValue method unit test --- tests/components/inputs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index ad41f2a..ff0d2f3 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -440,7 +440,7 @@ describe('Test Input Element', () => { expect(onValueNonEmptySpy.calledOnce).to.be.true; }); - it.only('removeValue should remove characters from the value starting at the caret position', () => { + it('removeValue should remove characters from the value starting at the caret position', () => { // Create InputElement instance const input = new InputElement(config); From b74f837405c8048f3ad48ca371f6ae3484f5a68c Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 10 Dec 2023 13:20:48 +0530 Subject: [PATCH 27/28] add unit test for input component inserValue method --- tests/components/inputs.ts | 115 ++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index ff0d2f3..1ce84c4 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -447,7 +447,7 @@ describe('Test Input Element', () => { // Create spy for value getter and setter const valueSpy = sinon.spy(input, 'value', ['get', 'set']); - // Create spy for selectionStartPosition getter and setter + // Create spy for selectionStartPosition getter const selectionStartPositionGetterSpy = sinon.spy( input, 'selectionStartPosition', @@ -491,12 +491,13 @@ describe('Test Input Element', () => { // Set input element value input.querySelector.value = '123456789'; + // Set selection start position input.querySelector.selectionStart = 6; // Call removeValue method input.removeValue(2); - // Assert that the focusSpy called thrice + // Assert that the valueSpy getter called thrice expect(valueSpy.get.calledThrice).to.be.true; // Assert that the selectionStartPositionGetterSpy called twice @@ -517,4 +518,114 @@ describe('Test Input Element', () => { // Assert that the inputEventHandlerSpy called once expect(inputEventHandlerSpy.calledOnce).to.be.true; }); + + it('insertValue should insert a value at the caret position', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for value getter and setter + const valueSpy = sinon.spy(input, 'value', ['get', 'set']); + + // Create spy for selectionStartPosition getter and setter + const selectionStartPositionGetterSpy = sinon.spy( + input, + 'selectionStartPosition', + ['get'] + ); + + // Create spy for selectionEndPosition getter and setter + const selectionEndPositionGetterSpy = sinon.spy( + input, + 'selectionEndPosition', + ['get'] + ); + + // Create spy for selectionPosition setter + const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); + + // build InputElement + input.build(document.body); + + // Set input element value + input.querySelector.value = '123789'; + + // Set selection start position + input.querySelector.selectionStart = 3; + + // Set selection end position + input.querySelector.selectionEnd = 3; + + // Call insertValue method - Insert '456' at the caret position + input.insertValue('456'); + + // Assert that the valueSpy getter called once + expect(valueSpy.get.calledOnce).to.be.true; + + // Assert that the selectionStartPositionGetterSpy called once + expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the selectionEndPositionGetterSpy called once + expect(selectionEndPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the valueSpy setter called once with correct args + expect(valueSpy.set.calledOnceWithExactly('123456789')).to.be.true; + + // Assert that the selectionPositionSpy setter called once with correct args + expect(selectionPositionSpy.set.calledOnceWithExactly(6)).to.be.true; + }); + + it('insertValue should replace selected value with the new value', () => { + // Create InputElement instance + const input = new InputElement(config); + + // Create spy for value getter and setter + const valueSpy = sinon.spy(input, 'value', ['get', 'set']); + + // Create spy for selectionStartPosition getter and setter + const selectionStartPositionGetterSpy = sinon.spy( + input, + 'selectionStartPosition', + ['get'] + ); + + // Create spy for selectionEndPosition getter and setter + const selectionEndPositionGetterSpy = sinon.spy( + input, + 'selectionEndPosition', + ['get'] + ); + + // Create spy for selectionPosition setter + const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); + + // build InputElement + input.build(document.body); + + // Set input element value + input.querySelector.value = '123789'; + + // Set selection start position + input.querySelector.selectionStart = 3; + + // Set selection end position + input.querySelector.selectionEnd = 6; + + // Call insertValue method - Insert '456' at the caret position + input.insertValue('456'); + + // Assert that the valueSpy getter called once + expect(valueSpy.get.calledOnce).to.be.true; + + // Assert that the selectionStartPositionGetterSpy called once + expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the selectionEndPositionGetterSpy called once + expect(selectionEndPositionGetterSpy.get.calledOnce).to.be.true; + + // Assert that the valueSpy setter called once with correct args + expect(valueSpy.set.calledOnceWithExactly('123456')).to.be.true; + + // Assert that the selectionPositionSpy setter called once with correct args + expect(selectionPositionSpy.set.calledOnceWithExactly(6)).to.be.true; + }); }); From 623c1600c9760bf05d890588607109e6bf2ba22b Mon Sep 17 00:00:00 2001 From: satyam-seth Date: Sun, 10 Dec 2023 13:23:38 +0530 Subject: [PATCH 28/28] rename spyGetter to spy --- tests/components/buttons.ts | 4 +-- tests/components/inputs.ts | 58 +++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/tests/components/buttons.ts b/tests/components/buttons.ts index 1e6d8af..666bca7 100644 --- a/tests/components/buttons.ts +++ b/tests/components/buttons.ts @@ -324,7 +324,7 @@ describe('Test Dialpad Button', () => { const button = new DialpadButton(validConfig); // Create spy for skeleton getter - const skeletonGetterSpy = sinon.spy(button, 'skeleton', ['get']); + const skeletonSpy = sinon.spy(button, 'skeleton', ['get']); // Create spy for appendChild method const appendChildSpy = sinon.spy(document.body, 'appendChild'); @@ -333,7 +333,7 @@ describe('Test Dialpad Button', () => { button.build(document.body); // Assert that the skeleton getter was accessed - expect(skeletonGetterSpy.get.calledOnce).to.be.true; + expect(skeletonSpy.get.calledOnce).to.be.true; // Assert that the parentElement appendChild was called once with the correct argument expect(appendChildSpy.calledOnceWith(sinon.match.instanceOf(HTMLElement))) diff --git a/tests/components/inputs.ts b/tests/components/inputs.ts index 1ce84c4..9329c4e 100644 --- a/tests/components/inputs.ts +++ b/tests/components/inputs.ts @@ -81,7 +81,7 @@ describe('Test Input Element', () => { const input = new InputElement(config); // Create spy for skeleton getter - const skeletonGetterSpy = sinon.spy(input, 'skeleton', ['get']); + const skeletonSpy = sinon.spy(input, 'skeleton', ['get']); // Create spy for appendChild method const appendChildSpy = sinon.spy(document.body, 'appendChild'); @@ -90,7 +90,7 @@ describe('Test Input Element', () => { input.build(document.body); // Assert that the skeleton getter was accessed - expect(skeletonGetterSpy.get.calledOnce).to.be.true; + expect(skeletonSpy.get.calledOnce).to.be.true; // Assert that the parentElement appendChild was called once with the correct argument expect(appendChildSpy.calledOnceWith(sinon.match.instanceOf(HTMLElement))) @@ -361,7 +361,7 @@ describe('Test Input Element', () => { const valueSpy = sinon.spy(input, 'value', ['get', 'set']); // Create spy for selectionStartPosition getter - const selectionStartPositionGetterSpy = sinon.spy( + const selectionStartPositionSpy = sinon.spy( input, 'selectionStartPosition', ['get'] @@ -376,8 +376,8 @@ describe('Test Input Element', () => { // Assert that the focusSpy called once expect(valueSpy.get.calledOnce).to.be.true; - // Assert that the selectionStartPositionGetterSpy called once - expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionStartPositionSpy called once + expect(selectionStartPositionSpy.get.calledOnce).to.be.true; // Assert that the value is updated correctly expect(valueSpy.set.calledOnceWith('123*456#')).to.be.true; @@ -448,7 +448,7 @@ describe('Test Input Element', () => { const valueSpy = sinon.spy(input, 'value', ['get', 'set']); // Create spy for selectionStartPosition getter - const selectionStartPositionGetterSpy = sinon.spy( + const selectionStartPositionSpy = sinon.spy( input, 'selectionStartPosition', ['get'] @@ -469,8 +469,8 @@ describe('Test Input Element', () => { // Assert that the focusSpy called once expect(valueSpy.get.calledOnce).to.be.true; - // Assert that the selectionStartPositionGetterSpy called once - expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionStartPositionSpy called once + expect(selectionStartPositionSpy.get.calledOnce).to.be.true; // Assert that the valueSpy setter not called expect(valueSpy.set.called).to.be.false; @@ -484,7 +484,7 @@ describe('Test Input Element', () => { // Reset spies valueSpy.get.resetHistory(); valueSpy.set.resetHistory(); - selectionStartPositionGetterSpy.get.resetHistory(); + selectionStartPositionSpy.get.resetHistory(); selectionPositionSpy.set.resetHistory(); inputEventHandlerSpy.resetHistory(); @@ -500,8 +500,8 @@ describe('Test Input Element', () => { // Assert that the valueSpy getter called thrice expect(valueSpy.get.calledThrice).to.be.true; - // Assert that the selectionStartPositionGetterSpy called twice - expect(selectionStartPositionGetterSpy.get.calledTwice).to.be.true; + // Assert that the selectionStartPositionSpy called twice + expect(selectionStartPositionSpy.get.calledTwice).to.be.true; // Assert that the valueSpy setter first time called twice expect(valueSpy.set.calledTwice).to.be.true; @@ -527,18 +527,16 @@ describe('Test Input Element', () => { const valueSpy = sinon.spy(input, 'value', ['get', 'set']); // Create spy for selectionStartPosition getter and setter - const selectionStartPositionGetterSpy = sinon.spy( + const selectionStartPositionSpy = sinon.spy( input, 'selectionStartPosition', ['get'] ); // Create spy for selectionEndPosition getter and setter - const selectionEndPositionGetterSpy = sinon.spy( - input, - 'selectionEndPosition', - ['get'] - ); + const selectionEndPositionSpy = sinon.spy(input, 'selectionEndPosition', [ + 'get', + ]); // Create spy for selectionPosition setter const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); @@ -561,11 +559,11 @@ describe('Test Input Element', () => { // Assert that the valueSpy getter called once expect(valueSpy.get.calledOnce).to.be.true; - // Assert that the selectionStartPositionGetterSpy called once - expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionStartPositionSpy called once + expect(selectionStartPositionSpy.get.calledOnce).to.be.true; - // Assert that the selectionEndPositionGetterSpy called once - expect(selectionEndPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionEndPositionSpy called once + expect(selectionEndPositionSpy.get.calledOnce).to.be.true; // Assert that the valueSpy setter called once with correct args expect(valueSpy.set.calledOnceWithExactly('123456789')).to.be.true; @@ -582,18 +580,16 @@ describe('Test Input Element', () => { const valueSpy = sinon.spy(input, 'value', ['get', 'set']); // Create spy for selectionStartPosition getter and setter - const selectionStartPositionGetterSpy = sinon.spy( + const selectionStartPositionSpy = sinon.spy( input, 'selectionStartPosition', ['get'] ); // Create spy for selectionEndPosition getter and setter - const selectionEndPositionGetterSpy = sinon.spy( - input, - 'selectionEndPosition', - ['get'] - ); + const selectionEndPositionSpy = sinon.spy(input, 'selectionEndPosition', [ + 'get', + ]); // Create spy for selectionPosition setter const selectionPositionSpy = sinon.spy(input, 'selectionPosition', ['set']); @@ -616,11 +612,11 @@ describe('Test Input Element', () => { // Assert that the valueSpy getter called once expect(valueSpy.get.calledOnce).to.be.true; - // Assert that the selectionStartPositionGetterSpy called once - expect(selectionStartPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionStartPositionSpy called once + expect(selectionStartPositionSpy.get.calledOnce).to.be.true; - // Assert that the selectionEndPositionGetterSpy called once - expect(selectionEndPositionGetterSpy.get.calledOnce).to.be.true; + // Assert that the selectionEndPositionSpy called once + expect(selectionEndPositionSpy.get.calledOnce).to.be.true; // Assert that the valueSpy setter called once with correct args expect(valueSpy.set.calledOnceWithExactly('123456')).to.be.true;