UNPKG

@azure/communication-react

Version:

React library for building modern communication user experiences utilizing Azure Communication Services

217 lines • 10.9 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import React, { useEffect, useRef } from 'react'; import { IconButton } from '@fluentui/react'; import { concatStyleSets, DefaultButton, FocusZone, mergeStyles, Stack, Text, TextField, useTheme } from '@fluentui/react'; import { useState } from 'react'; import { useLocale } from '../../localization'; import { buttonStyles, containerStyles, iconButtonStyles, digitStyles, letterStyles, textFieldStyles } from '../styles/Dialpad.styles'; import { _formatPhoneNumber } from '../utils/formatPhoneNumber'; import useLongPress from '../utils/useLongPress'; import { dtmfFrequencies, Tone } from './DTMFToneGenerator'; const dialPadButtonsDefault = [[{ digit: '1' }, { digit: '2', letter: 'ABC' }, { digit: '3', letter: 'DEF' }], [{ digit: '4', letter: 'GHI' }, { digit: '5', letter: 'JKL' }, { digit: '6', letter: 'MNO' }], [{ digit: '7', letter: 'PQRS' }, { digit: '8', letter: 'TUV' }, { digit: '9', letter: 'WXYZ' }], [{ digit: '*' }, { digit: '0', letter: '+' }, { digit: '#' }]]; const DtmfTones = ['Num1', 'Num2', 'Num3', 'Num4', 'Num5', 'Num6', 'Num7', 'Num8', 'Num9', 'Star', 'Num0', 'Pound']; const DialpadButton = (props) => { var _a, _b, _c, _d; const theme = useTheme(); const { digit, index, onClick, onLongPress, longPressTrigger, dtmfToneAudioContext, disableDtmfPlayback } = props; const [buttonPressed, setButtonPressed] = useState(false); const dtmfToneSound = useRef(new Tone(dtmfToneAudioContext, dtmfFrequencies[digit].f1, dtmfFrequencies[digit].f2)); const useLongPressProps = React.useMemo(() => ({ onClick: () => __awaiter(void 0, void 0, void 0, function* () { onClick(digit, index); }), onLongPress: () => __awaiter(void 0, void 0, void 0, function* () { onLongPress(digit, index); }), touchEventsOnly: longPressTrigger === 'touch' }), [digit, index, longPressTrigger, onClick, onLongPress]); const longPressHandlers = useLongPress(useLongPressProps); return React.createElement(DefaultButton, Object.assign({ "data-test-id": `dialpad-button-${props.index}`, styles: concatStyleSets(buttonStyles(theme), (_a = props.styles) === null || _a === void 0 ? void 0 : _a.button) }, longPressHandlers, { onKeyDown: e => { if ((e.key === 'Enter' || e.key === ' ') && !buttonPressed) { if (!disableDtmfPlayback) { dtmfToneSound.current.play(); } longPressHandlers.onKeyDown(); setButtonPressed(true); return; } if (e.key === 'Tab' || e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'ArrowUp' || e.key === 'ArrowDown') { dtmfToneSound.current.stop(); return; } longPressHandlers.onKeyDown(); }, onKeyUp: e => { if ((e.key === 'Enter' || e.key === ' ') && buttonPressed) { dtmfToneSound.current.stop(); longPressHandlers.onKeyUp(); setButtonPressed(false); } longPressHandlers.onKeyUp(); }, onMouseDown: () => { if (!disableDtmfPlayback) { dtmfToneSound.current.play(); } longPressHandlers.onMouseDown(); }, onMouseUp: () => { dtmfToneSound.current.stop(); longPressHandlers.onMouseUp(); }, onMouseLeave: () => { dtmfToneSound.current.stop(); }, onTouchStart: () => { if (!disableDtmfPlayback) { dtmfToneSound.current.play(); } longPressHandlers.onTouchStart(); }, onTouchEnd: () => { dtmfToneSound.current.stop(); longPressHandlers.onTouchEnd(); } }), React.createElement(Stack, null, React.createElement(Text, { className: mergeStyles(digitStyles(theme), (_b = props.styles) === null || _b === void 0 ? void 0 : _b.digit) }, props.digit), React.createElement(Text, { className: mergeStyles(letterStyles(theme), (_c = props.styles) === null || _c === void 0 ? void 0 : _c.letter) }, (_d = props.letter) !== null && _d !== void 0 ? _d : ' '))); }; const DialpadContainer = (props) => { var _a, _b; const theme = useTheme(); const { onSendDtmfTone, onClickDialpadButton, textFieldValue, onChange, showDeleteButton = true, longPressTrigger = 'mouseAndTouch', disableDtmfPlayback, dialpadMode = 'dialer', dtmfAudioContext } = props; const dtmfToneAudioContext = useRef(dtmfAudioContext ? dtmfAudioContext : new AudioContext()); const [plainTextValue, setPlainTextValue] = useState(textFieldValue !== null && textFieldValue !== void 0 ? textFieldValue : ''); const plainTextValuePreviousRenderValue = useRef(plainTextValue); useEffect(() => { if (plainTextValuePreviousRenderValue.current !== plainTextValue) { onChange === null || onChange === void 0 ? void 0 : onChange(plainTextValue); } plainTextValuePreviousRenderValue.current = plainTextValue; }, [plainTextValuePreviousRenderValue, plainTextValue, onChange]); useEffect(() => { setText(textFieldValue !== null && textFieldValue !== void 0 ? textFieldValue : ''); }, [textFieldValue]); const onClickDialpad = (input, index) => { setText(plainTextValue + input); const tone = DtmfTones[index]; if (onSendDtmfTone && tone) { onSendDtmfTone(tone); } if (onClickDialpadButton) { onClickDialpadButton(input, index); } }; const onLongPressDialpad = (input, index) => { if (input === '0' && index === 10) { setText(plainTextValue + '+'); } else { setText(plainTextValue + input); } const tone = DtmfTones[index]; if (onSendDtmfTone && tone) { onSendDtmfTone(tone); } if (onClickDialpadButton) { onClickDialpadButton(input, index); } }; const setText = (input) => { // remove non-valid characters from input: letters,special characters excluding +, *,# const plainInput = sanitizeInput(input); setPlainTextValue(plainInput); }; const deleteNumbers = () => { const modifiedInput = plainTextValue.substring(0, plainTextValue.length - 1); setText(modifiedInput); }; return React.createElement(Stack, { className: mergeStyles(containerStyles(theme), (_a = props.styles) === null || _a === void 0 ? void 0 : _a.root), "data-test-id": "dialpadContainer", "data-ui-id": "dialpadContainer", horizontalAlign: 'center' }, dialpadMode === 'dialer' && React.createElement(TextField, { styles: concatStyleSets(textFieldStyles(theme, plainTextValue !== ''), (_b = props.styles) === null || _b === void 0 ? void 0 : _b.textField), value: textFieldValue ? textFieldValue : _formatPhoneNumber(plainTextValue), // eslint-disable-next-line @typescript-eslint/no-explicit-any onChange: (e) => { setText(e.target.value); }, onClick: e => { e.preventDefault(); }, placeholder: props.strings.placeholderText, "data-test-id": "dialpad-input", onRenderSuffix: () => { var _a; return React.createElement(React.Fragment, null, showDeleteButton && plainTextValue.length !== 0 && React.createElement(IconButton, { ariaLabel: props.strings.deleteButtonAriaLabel, onClick: deleteNumbers, styles: concatStyleSets(iconButtonStyles(theme), (_a = props.styles) === null || _a === void 0 ? void 0 : _a.deleteIcon), iconProps: { iconName: 'DialpadBackspace' } })); } }), React.createElement(FocusZone, null, dialPadButtonsDefault.map((rows, rowIndex) => { return React.createElement(Stack, { horizontal: true, key: `row_${rowIndex}`, horizontalAlign: "stretch", tokens: { childrenGap: '1rem' } }, rows.map((button, columnIndex) => React.createElement(DialpadButton, { key: `button_${columnIndex}`, /* row index = 0 columnIndex: (0,1,2) => (0,1,2) row index = 1 columnIndex: (0,1,2)=> (3,4,5) row index = 2 columnIndex: (0,1,2)=> (6,7,8) row index = 3 columnIndex: (0,1,2)=> (9,10,11) columnIndex + rowIndex*rows.length calculates the corresponding index for each button dialpad index: 0 1 2 3 4 5 6 7 8 9 10 11 then use this index to locate the corresponding dtmf tones DtmfTones[index] */ index: columnIndex + rowIndex * rows.length, digit: button.digit, letter: button.letter, styles: props.styles, onClick: onClickDialpad, onLongPress: onLongPressDialpad, longPressTrigger: longPressTrigger, dtmfToneAudioContext: dtmfToneAudioContext.current, disableDtmfPlayback: disableDtmfPlayback }))); }))); }; /** * A component to allow users to enter phone number through clicking on dialpad/using keyboard * It will return empty component for stable builds * * @public */ export const Dialpad = (props) => { const localeStrings = useLocale().strings.dialpad; const strings = Object.assign(Object.assign({}, localeStrings), props.strings); return React.createElement(DialpadContainer, Object.assign({ strings: strings }, props)); }; const sanitizeInput = (input) => { // remove non-valid characters from input: letters,special characters excluding +, *,# return input.replace(/[^\d*#+]/g, ''); }; //# sourceMappingURL=Dialpad.js.map