@azure/communication-react
Version:
React library for building modern communication user experiences utilizing Azure Communication Services
217 lines • 10.9 kB
JavaScript
// 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