@superdispatch/phones 0.16.0-alpha.0 → 0.16.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,149 +0,0 @@
1
- import _objectSpread from "@babel/runtime/helpers/objectSpread2";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["value", "onBlur", "onFocus", "onChange"],
4
- _excluded2 = ["label", "fullWidth", "helperText", "suspenseFallback"];
5
- import { TextField } from '@mui/material';
6
- import { v5 } from '@superdispatch/ui';
7
- import { forwardRef, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
- import { usePhoneService } from "../phone-service/PhoneService.js";
9
- import { PhoneFieldMenu } from "./PhoneFieldMenu.js";
10
- import { PhoneFieldStartAdornment } from "./PhoneFieldStartAdornment.js";
11
- import { jsx as _jsx } from "react/jsx-runtime";
12
- import { Fragment as _Fragment } from "react/jsx-runtime";
13
- import { jsxs as _jsxs } from "react/jsx-runtime";
14
- var {
15
- mergeRefs
16
- } = v5;
17
-
18
- function normalizeValue(value) {
19
- return typeof value === 'string' ? value : '';
20
- }
21
-
22
- export var PhoneField = /*#__PURE__*/forwardRef((_ref, ref) => {
23
- var {
24
- value: valueProp,
25
- onBlur: _onBlur,
26
- onFocus: _onFocus,
27
- onChange: _onChange
28
- } = _ref,
29
- props = _objectWithoutProperties(_ref, _excluded);
30
-
31
- var rootRef = useRef(null);
32
- var inputRef = useRef(null);
33
- var [menuAnchor, setMenuAnchor] = useState(null);
34
- var phoneService = usePhoneService();
35
- var createState = useCallback(value => _objectSpread({
36
- value
37
- }, phoneService.getInfo(value)), [phoneService]);
38
- var value = useMemo(() => normalizeValue(valueProp), [valueProp]);
39
- var [{
40
- country,
41
- nationalNumber
42
- }, setValue] = useState(() => createState(value));
43
- var placeholder = useMemo(() => phoneService.APN.getExample(country).getNumber('national'), [country, phoneService.APN]);
44
-
45
- function handleChange(fn, nextCountry, nextNationalNumber) {
46
- if (fn) {
47
- var nextValue = phoneService.format(nextNationalNumber, {
48
- country: nextCountry
49
- });
50
- setValue({
51
- value: nextValue,
52
- country: nextCountry,
53
- nationalNumber: nextNationalNumber
54
- });
55
- fn(nextValue);
56
- }
57
- }
58
-
59
- function handleChangeEvent(fn, _ref2) {
60
- var {
61
- target: {
62
- value: nextValue
63
- }
64
- } = _ref2;
65
-
66
- if (fn) {
67
- handleChange(fn, country, phoneService.format(nextValue, {
68
- country,
69
- format: 'national'
70
- }));
71
- }
72
- }
73
-
74
- useEffect(() => {
75
- setValue(prev => // Ignore `props.value` changes when they were performed from inside.
76
- prev.value === value ? prev : createState(value));
77
- }, [value, createState]);
78
- return /*#__PURE__*/_jsxs(_Fragment, {
79
- children: [/*#__PURE__*/_jsx(PhoneFieldMenu, {
80
- value: country,
81
- anchorEl: menuAnchor,
82
- onClose: () => {
83
- setMenuAnchor(null);
84
- },
85
- onChange: nextRegion => {
86
- handleChange(_onChange, nextRegion, nationalNumber);
87
- }
88
- }), /*#__PURE__*/_jsx(TextField, _objectSpread(_objectSpread({}, props), {}, {
89
- type: "tel",
90
- variant: "outlined",
91
- autoComplete: "off",
92
- value: nationalNumber,
93
- placeholder: placeholder,
94
- ref: mergeRefs(ref, rootRef),
95
- inputRef: inputRef,
96
- onBlur: event => {
97
- handleChangeEvent(_onBlur, event);
98
- },
99
- onFocus: event => {
100
- handleChangeEvent(_onFocus, event);
101
- },
102
- onChange: event => {
103
- handleChangeEvent(_onChange, event);
104
- },
105
- InputProps: {
106
- startAdornment: /*#__PURE__*/_jsx(PhoneFieldStartAdornment, {
107
- country: country,
108
- isExpanded: !!menuAnchor,
109
- onClick: () => {
110
- var _inputRef$current;
111
-
112
- // `FocusTrap` inside of `Menu` will restore focus to
113
- // the last focused element. We want to manually focus input
114
- // to trick the `FocusTrap`.
115
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
116
- setMenuAnchor(rootRef.current);
117
- }
118
- })
119
- }
120
- }))]
121
- });
122
- });
123
- if (process.env.NODE_ENV !== "production") PhoneField.displayName = "PhoneField";
124
- export var SuspendedPhoneField = /*#__PURE__*/forwardRef((_ref3, ref) => {
125
- var {
126
- label,
127
- fullWidth,
128
- helperText,
129
- suspenseFallback = /*#__PURE__*/_jsx(TextField, {
130
- disabled: true,
131
- label: label,
132
- fullWidth: fullWidth,
133
- helperText: helperText,
134
- placeholder: "Loading\u2026"
135
- })
136
- } = _ref3,
137
- props = _objectWithoutProperties(_ref3, _excluded2);
138
-
139
- return /*#__PURE__*/_jsx(Suspense, {
140
- fallback: suspenseFallback,
141
- children: /*#__PURE__*/_jsx(PhoneField, _objectSpread(_objectSpread({}, props), {}, {
142
- ref: ref,
143
- label: label,
144
- fullWidth: fullWidth,
145
- helperText: helperText
146
- }))
147
- });
148
- });
149
- if (process.env.NODE_ENV !== "production") SuspendedPhoneField.displayName = "SuspendedPhoneField";
@@ -1,46 +0,0 @@
1
- import _objectSpread from "@babel/runtime/helpers/objectSpread2";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["country", "alt", "className"];
4
- import { Typography } from '@mui/material';
5
- import { makeStyles } from '@mui/styles';
6
- import clsx from 'clsx';
7
- import { forwardRef } from 'react';
8
- import { jsx as _jsx } from "react/jsx-runtime";
9
- var useStyles = /*#__PURE__*/makeStyles(theme => ({
10
- root: {
11
- minHeight: theme.spacing(2),
12
- minWidth: theme.spacing(2.75)
13
- }
14
- }), {
15
- name: 'SD-PhoneFieldFlag'
16
- });
17
- export var PhoneFieldFlag = /*#__PURE__*/forwardRef((_ref, ref) => {
18
- var {
19
- country,
20
- alt = country,
21
- className
22
- } = _ref,
23
- props = _objectWithoutProperties(_ref, _excluded);
24
-
25
- var styles = useStyles();
26
-
27
- if (country === 'AC' || country === 'BQ' || country === 'EH' || country === 'TA') {
28
- return /*#__PURE__*/_jsx(Typography, {
29
- ref: ref,
30
- variant: "h6",
31
- align: "center",
32
- component: "span",
33
- color: "textSecondary",
34
- className: clsx(styles.root, className),
35
- children: country
36
- });
37
- }
38
-
39
- return /*#__PURE__*/_jsx("img", _objectSpread(_objectSpread({}, props), {}, {
40
- alt: alt,
41
- ref: ref,
42
- className: clsx(styles.root, className),
43
- src: "https://cdn.jsdelivr.net/gh/madebybowtie/FlagKit@2.2/Assets/SVG/".concat(country, ".svg")
44
- }));
45
- });
46
- if (process.env.NODE_ENV !== "production") PhoneFieldFlag.displayName = "PhoneFieldFlag";
@@ -1,40 +0,0 @@
1
- import { Divider, Menu } from '@mui/material';
2
- import { makeStyles } from '@mui/styles';
3
- import { forwardRef } from 'react';
4
- import { listCountries } from "../country-code-metadata/CountryCodeMetadata.js";
5
- import { PhoneFieldMenuItem } from "./PhoneFieldMenuItem.js";
6
- import { jsx as _jsx } from "react/jsx-runtime";
7
- var useStyles = /*#__PURE__*/makeStyles(theme => ({
8
- paper: {
9
- maxHeight: theme.spacing(30)
10
- }
11
- }), {
12
- name: 'SD-PhoneFieldMenu'
13
- });
14
- export var PhoneFieldMenu = /*#__PURE__*/forwardRef((_ref, ref) => {
15
- var {
16
- anchorEl,
17
- value,
18
- onClose,
19
- onChange
20
- } = _ref;
21
- var styles = useStyles();
22
- return /*#__PURE__*/_jsx(Menu, {
23
- ref: ref,
24
- open: !!anchorEl,
25
- onClose: onClose,
26
- anchorEl: anchorEl,
27
- classes: {
28
- paper: styles.paper
29
- },
30
- children: listCountries().map(country => [/*#__PURE__*/_jsx(PhoneFieldMenuItem, {
31
- country: country,
32
- selected: country === value,
33
- onClick: () => {
34
- onChange(country);
35
- onClose();
36
- }
37
- }, country), country === 'NZ' && /*#__PURE__*/_jsx(Divider, {}, "divider")])
38
- });
39
- });
40
- if (process.env.NODE_ENV !== "production") PhoneFieldMenu.displayName = "PhoneFieldMenu";
@@ -1,54 +0,0 @@
1
- import _objectSpread from "@babel/runtime/helpers/objectSpread2";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["country", "classes"],
4
- _excluded2 = ["flag"];
5
- import { MenuItem, Typography } from '@mui/material';
6
- import { makeStyles } from '@mui/styles';
7
- import { forwardRef, useMemo } from 'react';
8
- import { formatCountry, getCountryCode } from "../country-code-metadata/CountryCodeMetadata.js";
9
- import { PhoneFieldFlag } from "./PhoneFieldFlag.js";
10
- import { jsx as _jsx } from "react/jsx-runtime";
11
- import { jsxs as _jsxs } from "react/jsx-runtime";
12
- var useStyles = /*#__PURE__*/makeStyles(theme => ({
13
- dense: {},
14
- gutters: {},
15
- root: {},
16
- selected: {},
17
- disabled: {},
18
- divider: {},
19
- focusVisible: {},
20
- flag: {
21
- marginRight: theme.spacing(1)
22
- }
23
- }), {
24
- name: 'SD-PhoneFieldMenuItem'
25
- });
26
- export var PhoneFieldMenuItem = /*#__PURE__*/forwardRef((_ref, ref) => {
27
- var {
28
- country,
29
- classes
30
- } = _ref,
31
- props = _objectWithoutProperties(_ref, _excluded);
32
-
33
- var _useStyles = useStyles({
34
- classes
35
- }),
36
- {
37
- flag: flagClassName
38
- } = _useStyles,
39
- styles = _objectWithoutProperties(_useStyles, _excluded2);
40
-
41
- var countryCode = useMemo(() => getCountryCode(country), [country]);
42
- return /*#__PURE__*/_jsxs(MenuItem, _objectSpread(_objectSpread({}, props), {}, {
43
- ref: ref,
44
- classes: styles,
45
- children: [/*#__PURE__*/_jsx(PhoneFieldFlag, {
46
- country: country,
47
- className: flagClassName
48
- }), formatCountry(country), "\xA0", /*#__PURE__*/_jsx(Typography, {
49
- color: "textSecondary",
50
- children: countryCode
51
- })]
52
- }));
53
- });
54
- if (process.env.NODE_ENV !== "production") PhoneFieldMenuItem.displayName = "PhoneFieldMenuItem";
@@ -1,62 +0,0 @@
1
- import { ArrowDropDown, ArrowDropUp } from '@mui/icons-material';
2
- import { ButtonBase, InputAdornment, Typography } from '@mui/material';
3
- import { makeStyles } from '@mui/styles';
4
- import { v5 } from '@superdispatch/ui';
5
- import { forwardRef, useMemo } from 'react';
6
- import { formatCountry, getCountryCode } from "../country-code-metadata/CountryCodeMetadata.js";
7
- import { PhoneFieldFlag } from "./PhoneFieldFlag.js";
8
- import { jsx as _jsx } from "react/jsx-runtime";
9
- import { jsxs as _jsxs } from "react/jsx-runtime";
10
- var {
11
- Color
12
- } = v5;
13
- var useStyles = /*#__PURE__*/makeStyles(theme => ({
14
- root: {
15
- marginLeft: theme.spacing(-1),
16
- marginRight: 0
17
- },
18
- button: {
19
- color: Color.Blue300,
20
- padding: theme.spacing(0.5, 0.5, 0.5, 1),
21
- borderRadius: theme.spacing(0.5, 0, 0, 0.5),
22
- '&:hover, &:focus': {
23
- backgroundColor: Color.Blue50
24
- }
25
- }
26
- }), {
27
- name: 'SD-PhoneFieldStartAdornment'
28
- });
29
- export var PhoneFieldStartAdornment = /*#__PURE__*/forwardRef((_ref, ref) => {
30
- var {
31
- country,
32
- onClick,
33
- isExpanded
34
- } = _ref;
35
- var styles = useStyles();
36
- var [title, countryCode] = useMemo(() => {
37
- var code = "+".concat(getCountryCode(country));
38
- return ["".concat(formatCountry(country), ": ").concat(code), code];
39
- }, [country]);
40
- return /*#__PURE__*/_jsx(InputAdornment, {
41
- ref: ref,
42
- position: "start",
43
- className: styles.root,
44
- children: /*#__PURE__*/_jsxs(ButtonBase, {
45
- title: title,
46
- onClick: onClick,
47
- className: styles.button,
48
- "aria-expanded": isExpanded,
49
- children: [/*#__PURE__*/_jsx(PhoneFieldFlag, {
50
- country: country
51
- }), isExpanded ? /*#__PURE__*/_jsx(ArrowDropUp, {
52
- htmlColor: Color.Dark200
53
- }) : /*#__PURE__*/_jsx(ArrowDropDown, {
54
- htmlColor: Color.Dark200
55
- }), /*#__PURE__*/_jsx(Typography, {
56
- color: "textPrimary",
57
- children: countryCode
58
- })]
59
- })
60
- });
61
- });
62
- if (process.env.NODE_ENV !== "production") PhoneFieldStartAdornment.displayName = "PhoneFieldStartAdornment";
@@ -1,56 +0,0 @@
1
- import _objectSpread from "@babel/runtime/helpers/objectSpread2";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["phone", "country", "fallback", "format"],
4
- _excluded2 = ["suspenseFallback"];
5
- import { Link } from '@mui/material';
6
- import { v5 } from '@superdispatch/ui';
7
- import { forwardRef, Suspense, useMemo } from 'react';
8
- import { usePhoneService } from "../phone-service/PhoneService.js";
9
- import { jsx as _jsx } from "react/jsx-runtime";
10
- var {
11
- renderChildren
12
- } = v5;
13
- export var PhoneLink = /*#__PURE__*/forwardRef((_ref, ref) => {
14
- var {
15
- phone,
16
- country,
17
- fallback,
18
- format = 'international'
19
- } = _ref,
20
- props = _objectWithoutProperties(_ref, _excluded);
21
-
22
- var service = usePhoneService();
23
- var [text, href] = useMemo(() => {
24
- if (service.checkPossibility(phone) !== 'is-possible') {
25
- return [undefined, undefined];
26
- }
27
-
28
- return [service.format(phone, {
29
- country,
30
- format
31
- }), service.format(phone, {
32
- country,
33
- format: 'rfc3966'
34
- })];
35
- }, [phone, country, format, service]);
36
- return !href ? renderChildren(fallback) : /*#__PURE__*/_jsx(Link, _objectSpread(_objectSpread({}, props), {}, {
37
- ref: ref,
38
- href: href,
39
- children: text
40
- }));
41
- });
42
- if (process.env.NODE_ENV !== "production") PhoneLink.displayName = "PhoneLink";
43
- export var SuspendedPhoneLink = /*#__PURE__*/forwardRef((_ref2, ref) => {
44
- var {
45
- suspenseFallback = null
46
- } = _ref2,
47
- props = _objectWithoutProperties(_ref2, _excluded2);
48
-
49
- return /*#__PURE__*/_jsx(Suspense, {
50
- fallback: suspenseFallback,
51
- children: /*#__PURE__*/_jsx(PhoneLink, _objectSpread(_objectSpread({}, props), {}, {
52
- ref: ref
53
- }))
54
- });
55
- });
56
- if (process.env.NODE_ENV !== "production") SuspendedPhoneLink.displayName = "SuspendedPhoneLink";
@@ -1,185 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { getAPN, loadAPN } from "../apn/APN.js";
3
- import { DEFAULT_COUNTRY, getCountryCode, toCountryISO } from "../country-code-metadata/CountryCodeMetadata.js";
4
- var PLUS_SIGN = '+';
5
- var NON_PHONE_SYMBOLS_PATTERN = /[^+\d]/g;
6
- var PHONE_PATTERN = /^\+?\d+/g;
7
-
8
- function getPrefix(country) {
9
- return PLUS_SIGN + String(getCountryCode(toCountryISO(country)));
10
- }
11
-
12
- function trim(input) {
13
- return typeof input == 'string' ? input.trim() : undefined;
14
- }
15
-
16
- function normalize(input) {
17
- if (typeof input == 'string') {
18
- var matches = input.replace(NON_PHONE_SYMBOLS_PATTERN, '').match(PHONE_PATTERN);
19
- if (matches !== null && matches !== void 0 && matches[0]) return matches[0];
20
- }
21
-
22
- return '';
23
- }
24
-
25
- function normalizeNational(country, input) {
26
- var phone = normalize(input);
27
- var prefix = getPrefix(country);
28
-
29
- if (phone.startsWith(PLUS_SIGN)) {
30
- return phone.slice(prefix.length);
31
- }
32
-
33
- return phone;
34
- }
35
-
36
- export class PhoneService {
37
- static load() {
38
- return loadAPN().then(APN => new PhoneService(APN));
39
- }
40
-
41
- static checkPossibility(input) {
42
- return this.load().then(phoneService => phoneService.checkPossibility(input));
43
- }
44
-
45
- static validate(input, rules) {
46
- return this.load().then(phoneService => phoneService.validate(input, rules));
47
- }
48
-
49
- constructor(APN) {
50
- this.APN = APN;
51
- }
52
-
53
- createAPN(phone, country) {
54
- var asYouType = this.APN.getAsYouType(toCountryISO(country));
55
- asYouType.reset(normalize(phone));
56
- return asYouType.getPhoneNumber();
57
- }
58
-
59
- getJSON(phone, country) {
60
- return this.createAPN(phone, country).toJSON();
61
- }
62
-
63
- checkPossibility(input) {
64
- var phone = trim(input);
65
-
66
- if (!phone) {
67
- return 'unknown';
68
- }
69
-
70
- var apn = phone.startsWith(PLUS_SIGN) ? new this.APN(phone) : new this.APN(phone, DEFAULT_COUNTRY);
71
- var {
72
- valid,
73
- possible,
74
- possibility
75
- } = apn.toJSON(); // Avoid false positive short phone numbers.
76
-
77
- if (!valid && possible) {
78
- return 'invalid-number';
79
- }
80
-
81
- return possibility;
82
- }
83
-
84
- validate(input) {
85
- var {
86
- required,
87
- requiredMessage = 'This field is required',
88
- invalidMessage = 'Invalid phone number',
89
- tooLongMessage = 'Phone number is too long',
90
- tooShortMessage = 'Phone number is too short'
91
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
92
- var phone = trim(input);
93
-
94
- if (!phone) {
95
- if (required) {
96
- return requiredMessage;
97
- }
98
-
99
- return undefined;
100
- }
101
-
102
- switch (this.checkPossibility(phone)) {
103
- case 'is-possible':
104
- return undefined;
105
-
106
- case 'too-long':
107
- return tooLongMessage;
108
-
109
- case 'too-short':
110
- return tooShortMessage;
111
- }
112
-
113
- return invalidMessage;
114
- }
115
-
116
- getInfo(phone) {
117
- var {
118
- regionCode,
119
- number: {
120
- input,
121
- national: nationalNumber
122
- }
123
- } = this.getJSON(phone);
124
- var country = toCountryISO(regionCode);
125
-
126
- if (!nationalNumber) {
127
- nationalNumber = normalizeNational(country, input);
128
- }
129
-
130
- return {
131
- country,
132
- nationalNumber
133
- };
134
- }
135
-
136
- format(input) {
137
- var {
138
- country,
139
- format = 'e164',
140
- fallback = ''
141
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
142
- var phone = normalize(input);
143
-
144
- if (!phone) {
145
- return fallback;
146
- }
147
-
148
- var apn = this.createAPN(phone, country);
149
- var formatted = apn.getNumber(format);
150
-
151
- if (!formatted) {
152
- country = toCountryISO(apn.getRegionCode());
153
- var nationalNumber = normalizeNational(country, phone);
154
-
155
- if (format === 'national') {
156
- return nationalNumber;
157
- }
158
-
159
- var prefix = '';
160
- var separator = '';
161
-
162
- if (format === 'rfc3966') {
163
- prefix = 'tel:';
164
- separator = '-';
165
- }
166
-
167
- if (format === 'international') {
168
- separator = ' ';
169
- }
170
-
171
- formatted = prefix + getPrefix(country);
172
-
173
- if (nationalNumber) {
174
- formatted += separator + nationalNumber;
175
- }
176
- }
177
-
178
- return formatted;
179
- }
180
-
181
- }
182
- export function usePhoneService() {
183
- var APN = getAPN();
184
- return useMemo(() => new PhoneService(APN), [APN]);
185
- }
@@ -1,34 +0,0 @@
1
- import _objectSpread from "@babel/runtime/helpers/objectSpread2";
2
- import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
- var _excluded = ["suspenseFallback"];
4
- import { v5 } from '@superdispatch/ui';
5
- import { Suspense } from 'react';
6
- import { useFormattedPhoneNumber } from "../formatted-phone-number/FormattedPhoneNumber.js";
7
- import { jsx as _jsx } from "react/jsx-runtime";
8
- var {
9
- renderChildren
10
- } = v5;
11
- export function PhoneText(_ref) {
12
- var {
13
- phone,
14
- country,
15
- fallback,
16
- format = 'international'
17
- } = _ref;
18
- var children = useFormattedPhoneNumber(phone, {
19
- format,
20
- country
21
- });
22
- return renderChildren(children || fallback);
23
- }
24
- export function SuspendedPhoneText(_ref2) {
25
- var {
26
- suspenseFallback = null
27
- } = _ref2,
28
- props = _objectWithoutProperties(_ref2, _excluded);
29
-
30
- return /*#__PURE__*/_jsx(Suspense, {
31
- fallback: suspenseFallback,
32
- children: /*#__PURE__*/_jsx(PhoneText, _objectSpread({}, props))
33
- });
34
- }