react-native-transformer-text-input 0.2.0 → 0.4.0

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.
Files changed (28) hide show
  1. package/android/src/main/jni/CMakeLists.txt +27 -2
  2. package/lib/module/TransformerTextInput.js.map +1 -1
  3. package/lib/module/TransformerTextInput.types.js +4 -0
  4. package/lib/module/TransformerTextInput.types.js.map +1 -0
  5. package/lib/module/TransformerTextInput.web.js +134 -0
  6. package/lib/module/TransformerTextInput.web.js.map +1 -0
  7. package/lib/module/formatters/phone-data.js +208 -0
  8. package/lib/module/formatters/phone-data.js.map +1 -1
  9. package/lib/module/formatters/phone-number.js +145 -1
  10. package/lib/module/formatters/phone-number.js.map +1 -1
  11. package/lib/module/index.js +1 -1
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/typescript/src/TransformerTextInput.d.ts +3 -44
  14. package/lib/typescript/src/TransformerTextInput.d.ts.map +1 -1
  15. package/lib/typescript/src/TransformerTextInput.types.d.ts +40 -0
  16. package/lib/typescript/src/TransformerTextInput.types.d.ts.map +1 -0
  17. package/lib/typescript/src/TransformerTextInput.web.d.ts +206 -0
  18. package/lib/typescript/src/TransformerTextInput.web.d.ts.map +1 -0
  19. package/lib/typescript/src/formatters/phone-data.d.ts +1 -0
  20. package/lib/typescript/src/formatters/phone-data.d.ts.map +1 -1
  21. package/lib/typescript/src/formatters/phone-number.d.ts +17 -1
  22. package/lib/typescript/src/formatters/phone-number.d.ts.map +1 -1
  23. package/package.json +2 -2
  24. package/src/TransformerTextInput.tsx +11 -44
  25. package/src/TransformerTextInput.types.ts +40 -0
  26. package/src/TransformerTextInput.web.tsx +161 -0
  27. package/src/formatters/phone-data.ts +235 -0
  28. package/src/formatters/phone-number.ts +168 -1
@@ -12,13 +12,29 @@ export type PhoneNumberTransformerOptions = {
12
12
  * @default true
13
13
  */
14
14
  includeCallingCode?: boolean;
15
+ /**
16
+ * International mode: the calling code is part of the editable text and the
17
+ * country is detected from it as you type (e.g. typing "+44" switches
18
+ * formatting to the UK). `country` is ignored in this mode. Use
19
+ * {@link detectCountry} to drive a flag/country indicator from the value.
20
+ * @default false
21
+ */
22
+ international?: boolean;
15
23
  /**
16
24
  * Enable debug logging for transformer operations.
17
25
  * @default false
18
26
  */
19
27
  debug?: boolean;
20
28
  };
29
+ /**
30
+ * Detect the ISO 3166-1 alpha-2 country for a phone value by its leading
31
+ * international calling code. Returns the primary country for the code (e.g.
32
+ * "US" for "+1"), or undefined if no calling code is recognized yet. Runs on
33
+ * the JS thread — use it to drive a flag/country indicator alongside an
34
+ * `international` PhoneNumberTransformer.
35
+ */
36
+ export declare function detectCountry(value: string): string | undefined;
21
37
  export declare class PhoneNumberTransformer extends Transformer {
22
- constructor({ country, includeCallingCode, debug, }?: PhoneNumberTransformerOptions);
38
+ constructor({ country, includeCallingCode, international, debug, }?: PhoneNumberTransformerOptions);
23
39
  }
24
40
  //# sourceMappingURL=phone-number.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../../../src/formatters/phone-number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,gBAAgB,CAAC;AAG7D,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAuOF,qBAAa,sBAAuB,SAAQ,WAAW;gBACzC,EACV,OAAc,EACd,kBAAyB,EACzB,KAAa,GACd,GAAE,6BAAkC;CAyKtC"}
1
+ {"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../../../src/formatters/phone-number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,gBAAgB,CAAC;AAO7D,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAmBF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI/D;AAuOD,qBAAa,sBAAuB,SAAQ,WAAW;gBACzC,EACV,OAAc,EACd,kBAAyB,EACzB,aAAqB,EACrB,KAAa,GACd,GAAE,6BAAkC;CAqStC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-transformer-text-input",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "TextInput component that allows transforming text synchronously with a worklet",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -57,7 +57,7 @@
57
57
  "prepare": "bob build",
58
58
  "generate:phone-data": "tsx scripts/generate-phone-data.ts",
59
59
  "test": "jest",
60
- "lint": "yarn lint:eslint && yarn lint:ts && yarn lint:android",
60
+ "lint": "yarn lint:eslint && yarn lint:ts",
61
61
  "lint:ts": "tsc",
62
62
  "lint:eslint": "eslint \"**/*.{js,ts,tsx}\"",
63
63
  "lint:android": "cd example/android && RNTTI_WARNINGS_AS_ERRORS=true ./gradlew :react-native-transformer-text-input:lint --rerun-tasks",
@@ -7,57 +7,24 @@ import {
7
7
  type ElementRef,
8
8
  type Ref,
9
9
  } from 'react';
10
- import {
11
- StyleSheet,
12
- TextInput,
13
- type HostInstance,
14
- type TextInputProps,
15
- } from 'react-native';
16
- import { type Selection, type Transformer } from './Transformer';
10
+ import { StyleSheet, TextInput, type HostInstance } from 'react-native';
11
+ import { type Selection } from './Transformer';
17
12
  import TransformerTextInputDecoratorViewNativeComponent, {
18
13
  Commands,
19
14
  } from './TransformerTextInputDecoratorViewNativeComponent';
20
15
  import { registerTransformer, unregisterTransformer } from './registry';
21
16
  import useMergeRefs from './utils/useMergeRefs';
22
17
  import { validateSelection } from './selection';
18
+ import {
19
+ type TransformerTextInputInstance,
20
+ type TransformerTextInputInstanceMethods,
21
+ type TransformerTextInputProps,
22
+ } from './TransformerTextInput.types';
23
23
 
24
- type TransformerTextInputInstanceMethods = {
25
- /**
26
- * Get the current text value.
27
- */
28
- getValue: () => string;
29
- /**
30
- * Update the value and/or selection, optionally running the transformer.
31
- */
32
- update: (options: {
33
- /**
34
- * New value to apply.
35
- */
36
- value?: string | null;
37
- /**
38
- * Optional selection to apply alongside the value.
39
- */
40
- selection?: { start: number; end: number };
41
- /**
42
- * Whether to run the transformer on update. Defaults to true.
43
- */
44
- transform?: boolean;
45
- }) => void;
46
- /**
47
- * Clear the input value without running the transformer.
48
- */
49
- clear: () => void;
50
- };
51
-
52
- export type TransformerTextInputInstance = HostInstance &
53
- TransformerTextInputInstanceMethods;
54
-
55
- export type TransformerTextInputProps = Omit<TextInputProps, 'value'> & {
56
- /**
57
- * Transformer instance used to sync text changes on the UI thread.
58
- */
59
- transformer: Transformer;
60
- };
24
+ export type {
25
+ TransformerTextInputInstance,
26
+ TransformerTextInputProps,
27
+ } from './TransformerTextInput.types';
61
28
 
62
29
  export const TransformerTextInput = forwardRef(
63
30
  (
@@ -0,0 +1,40 @@
1
+ import { type HostInstance, type TextInputProps } from 'react-native';
2
+ import { type Transformer } from './Transformer';
3
+
4
+ export type TransformerTextInputInstanceMethods = {
5
+ /**
6
+ * Get the current text value.
7
+ */
8
+ getValue: () => string;
9
+ /**
10
+ * Update the value and/or selection, optionally running the transformer.
11
+ */
12
+ update: (options: {
13
+ /**
14
+ * New value to apply.
15
+ */
16
+ value?: string | null;
17
+ /**
18
+ * Optional selection to apply alongside the value.
19
+ */
20
+ selection?: { start: number; end: number };
21
+ /**
22
+ * Whether to run the transformer on update. Defaults to true.
23
+ */
24
+ transform?: boolean;
25
+ }) => void;
26
+ /**
27
+ * Clear the input value without running the transformer.
28
+ */
29
+ clear: () => void;
30
+ };
31
+
32
+ export type TransformerTextInputInstance = HostInstance &
33
+ TransformerTextInputInstanceMethods;
34
+
35
+ export type TransformerTextInputProps = Omit<TextInputProps, 'value'> & {
36
+ /**
37
+ * Transformer instance used to sync text changes on the UI thread.
38
+ */
39
+ transformer: Transformer;
40
+ };
@@ -0,0 +1,161 @@
1
+ import { forwardRef, useCallback, useMemo, useRef, type Ref } from 'react';
2
+ import { TextInput } from 'react-native';
3
+ import { type Selection } from './Transformer';
4
+ import { computeUncontrolledSelection, validateSelection } from './selection';
5
+ import useMergeRefs from './utils/useMergeRefs';
6
+ import {
7
+ type TransformerTextInputInstance,
8
+ type TransformerTextInputInstanceMethods,
9
+ type TransformerTextInputProps,
10
+ } from './TransformerTextInput.types';
11
+
12
+ export type {
13
+ TransformerTextInputInstance,
14
+ TransformerTextInputProps,
15
+ } from './TransformerTextInput.types';
16
+
17
+ // The web host node is a DOM input; type only the bits we use so the library's
18
+ // tsconfig doesn't need the `dom` lib.
19
+ type WebInputNode = {
20
+ value: string;
21
+ selectionStart: number | null;
22
+ selectionEnd: number | null;
23
+ setSelectionRange: (start: number, end: number) => void;
24
+ };
25
+
26
+ // Web implementation. There is no UI thread, so instead of the native decorator
27
+ // running the transformer worklet on the UI runtime, we run the same transformer
28
+ // synchronously in JS on every change. The input is uncontrolled: the formatted
29
+ // value and caret are written straight to the DOM node in the change handler
30
+ // (mirroring the native side's imperative update), so there's no React
31
+ // re-render and no intermediate paint where the caret jumps to the end. Web is
32
+ // single-threaded, so value and selection land together in one step.
33
+ export const TransformerTextInput = forwardRef(
34
+ (
35
+ {
36
+ transformer,
37
+ onChangeText,
38
+ defaultValue,
39
+ ...others
40
+ }: TransformerTextInputProps,
41
+ forwardedRef: Ref<TransformerTextInputInstance>,
42
+ ) => {
43
+ const transformedDefaultValue = useMemo(() => {
44
+ if (defaultValue == null) {
45
+ return '';
46
+ }
47
+ const result = transformer.worklet({
48
+ value: defaultValue,
49
+ previousValue: defaultValue,
50
+ selection: { start: defaultValue.length, end: defaultValue.length },
51
+ previousSelection: { start: 0, end: 0 },
52
+ });
53
+ return result?.value ?? defaultValue;
54
+ }, [defaultValue, transformer]);
55
+
56
+ const valueRef = useRef(transformedDefaultValue);
57
+ const previousRef = useRef<{ value: string; selection: Selection }>({
58
+ value: transformedDefaultValue,
59
+ selection: {
60
+ start: transformedDefaultValue.length,
61
+ end: transformedDefaultValue.length,
62
+ },
63
+ });
64
+ const nodeRef = useRef<WebInputNode | null>(null);
65
+
66
+ const applyTransform = useCallback(
67
+ (
68
+ rawValue: string,
69
+ rawSelection: Selection,
70
+ transform: boolean,
71
+ ): string => {
72
+ const prev = previousRef.current;
73
+ const result = transform
74
+ ? transformer.worklet({
75
+ value: rawValue,
76
+ previousValue: prev.value,
77
+ selection: rawSelection,
78
+ previousSelection: prev.selection,
79
+ })
80
+ : null;
81
+ const newValue = result?.value ?? rawValue;
82
+ let newSelection: Selection;
83
+ if (result?.selection != null) {
84
+ newSelection = result.selection;
85
+ validateSelection(newSelection, newValue.length);
86
+ } else {
87
+ newSelection = computeUncontrolledSelection(
88
+ rawValue,
89
+ newValue,
90
+ rawSelection.start,
91
+ rawSelection.end,
92
+ );
93
+ }
94
+ previousRef.current = { value: newValue, selection: newSelection };
95
+ valueRef.current = newValue;
96
+ // Write straight to the DOM node — uncontrolled, no React re-render.
97
+ const node = nodeRef.current;
98
+ if (node != null) {
99
+ node.value = newValue;
100
+ if (typeof node.setSelectionRange === 'function') {
101
+ node.setSelectionRange(newSelection.start, newSelection.end);
102
+ }
103
+ }
104
+ return newValue;
105
+ },
106
+ [transformer],
107
+ );
108
+
109
+ const handleChangeText = useCallback(
110
+ (text: string) => {
111
+ // On web the DOM caret sits right after the edit when onChangeText
112
+ // fires; read it so the transformer can map it like the native side.
113
+ const node = nodeRef.current;
114
+ const rawSelection: Selection =
115
+ node != null && typeof node.selectionStart === 'number'
116
+ ? {
117
+ start: node.selectionStart,
118
+ end: node.selectionEnd ?? node.selectionStart,
119
+ }
120
+ : { start: text.length, end: text.length };
121
+ const newValue = applyTransform(text, rawSelection, true);
122
+ onChangeText?.(newValue);
123
+ },
124
+ [applyTransform, onChangeText],
125
+ );
126
+
127
+ const setInputRef = useCallback(
128
+ (instance: TransformerTextInputInstance | null) => {
129
+ nodeRef.current = instance as unknown as WebInputNode | null;
130
+ if (instance != null) {
131
+ Object.assign(instance, {
132
+ getValue() {
133
+ return valueRef.current;
134
+ },
135
+ update({ value: nextValue, selection, transform }) {
136
+ const base = nextValue ?? valueRef.current;
137
+ const sel = selection ?? { start: base.length, end: base.length };
138
+ applyTransform(base, sel, transform ?? true);
139
+ },
140
+ clear() {
141
+ this.update({ value: '', transform: false });
142
+ },
143
+ } satisfies TransformerTextInputInstanceMethods);
144
+ }
145
+ },
146
+ [applyTransform],
147
+ );
148
+
149
+ const inputRef = useMergeRefs(setInputRef, forwardedRef);
150
+
151
+ return (
152
+ <TextInput
153
+ // @ts-expect-error web host node carries the instance methods
154
+ ref={inputRef}
155
+ defaultValue={transformedDefaultValue}
156
+ onChangeText={handleChangeText}
157
+ {...others}
158
+ />
159
+ );
160
+ },
161
+ );
@@ -5793,3 +5793,238 @@ export const COUNTRY_LIST: CountryPhoneData[] = [
5793
5793
  COUNTRY_PHONE_DATA['ZM']!,
5794
5794
  COUNTRY_PHONE_DATA['ZW']!,
5795
5795
  ];
5796
+
5797
+ export const COUNTRY_CALLING_CODES: Record<string, string[]> = {
5798
+ '1': [
5799
+ 'US',
5800
+ 'AG',
5801
+ 'AI',
5802
+ 'AS',
5803
+ 'BB',
5804
+ 'BM',
5805
+ 'BS',
5806
+ 'CA',
5807
+ 'DM',
5808
+ 'DO',
5809
+ 'GD',
5810
+ 'GU',
5811
+ 'JM',
5812
+ 'KN',
5813
+ 'KY',
5814
+ 'LC',
5815
+ 'MP',
5816
+ 'MS',
5817
+ 'PR',
5818
+ 'SX',
5819
+ 'TC',
5820
+ 'TT',
5821
+ 'VC',
5822
+ 'VG',
5823
+ 'VI',
5824
+ ],
5825
+ '7': ['RU', 'KZ'],
5826
+ '20': ['EG'],
5827
+ '27': ['ZA'],
5828
+ '30': ['GR'],
5829
+ '31': ['NL'],
5830
+ '32': ['BE'],
5831
+ '33': ['FR'],
5832
+ '34': ['ES'],
5833
+ '36': ['HU'],
5834
+ '39': ['IT', 'VA'],
5835
+ '40': ['RO'],
5836
+ '41': ['CH'],
5837
+ '43': ['AT'],
5838
+ '44': ['GB', 'GG', 'IM', 'JE'],
5839
+ '45': ['DK'],
5840
+ '46': ['SE'],
5841
+ '47': ['NO', 'SJ'],
5842
+ '48': ['PL'],
5843
+ '49': ['DE'],
5844
+ '51': ['PE'],
5845
+ '52': ['MX'],
5846
+ '53': ['CU'],
5847
+ '54': ['AR'],
5848
+ '55': ['BR'],
5849
+ '56': ['CL'],
5850
+ '57': ['CO'],
5851
+ '58': ['VE'],
5852
+ '60': ['MY'],
5853
+ '61': ['AU', 'CC', 'CX'],
5854
+ '62': ['ID'],
5855
+ '63': ['PH'],
5856
+ '64': ['NZ'],
5857
+ '65': ['SG'],
5858
+ '66': ['TH'],
5859
+ '81': ['JP'],
5860
+ '82': ['KR'],
5861
+ '84': ['VN'],
5862
+ '86': ['CN'],
5863
+ '90': ['TR'],
5864
+ '91': ['IN'],
5865
+ '92': ['PK'],
5866
+ '93': ['AF'],
5867
+ '94': ['LK'],
5868
+ '95': ['MM'],
5869
+ '98': ['IR'],
5870
+ '211': ['SS'],
5871
+ '212': ['MA', 'EH'],
5872
+ '213': ['DZ'],
5873
+ '216': ['TN'],
5874
+ '218': ['LY'],
5875
+ '220': ['GM'],
5876
+ '221': ['SN'],
5877
+ '222': ['MR'],
5878
+ '223': ['ML'],
5879
+ '224': ['GN'],
5880
+ '225': ['CI'],
5881
+ '226': ['BF'],
5882
+ '227': ['NE'],
5883
+ '228': ['TG'],
5884
+ '229': ['BJ'],
5885
+ '230': ['MU'],
5886
+ '231': ['LR'],
5887
+ '232': ['SL'],
5888
+ '233': ['GH'],
5889
+ '234': ['NG'],
5890
+ '235': ['TD'],
5891
+ '236': ['CF'],
5892
+ '237': ['CM'],
5893
+ '238': ['CV'],
5894
+ '239': ['ST'],
5895
+ '240': ['GQ'],
5896
+ '241': ['GA'],
5897
+ '242': ['CG'],
5898
+ '243': ['CD'],
5899
+ '244': ['AO'],
5900
+ '245': ['GW'],
5901
+ '246': ['IO'],
5902
+ '247': ['AC'],
5903
+ '248': ['SC'],
5904
+ '249': ['SD'],
5905
+ '250': ['RW'],
5906
+ '251': ['ET'],
5907
+ '252': ['SO'],
5908
+ '253': ['DJ'],
5909
+ '254': ['KE'],
5910
+ '255': ['TZ'],
5911
+ '256': ['UG'],
5912
+ '257': ['BI'],
5913
+ '258': ['MZ'],
5914
+ '260': ['ZM'],
5915
+ '261': ['MG'],
5916
+ '262': ['RE', 'YT'],
5917
+ '263': ['ZW'],
5918
+ '264': ['NA'],
5919
+ '265': ['MW'],
5920
+ '266': ['LS'],
5921
+ '267': ['BW'],
5922
+ '268': ['SZ'],
5923
+ '269': ['KM'],
5924
+ '290': ['SH', 'TA'],
5925
+ '291': ['ER'],
5926
+ '297': ['AW'],
5927
+ '298': ['FO'],
5928
+ '299': ['GL'],
5929
+ '350': ['GI'],
5930
+ '351': ['PT'],
5931
+ '352': ['LU'],
5932
+ '353': ['IE'],
5933
+ '354': ['IS'],
5934
+ '355': ['AL'],
5935
+ '356': ['MT'],
5936
+ '357': ['CY'],
5937
+ '358': ['FI', 'AX'],
5938
+ '359': ['BG'],
5939
+ '370': ['LT'],
5940
+ '371': ['LV'],
5941
+ '372': ['EE'],
5942
+ '373': ['MD'],
5943
+ '374': ['AM'],
5944
+ '375': ['BY'],
5945
+ '376': ['AD'],
5946
+ '377': ['MC'],
5947
+ '378': ['SM'],
5948
+ '380': ['UA'],
5949
+ '381': ['RS'],
5950
+ '382': ['ME'],
5951
+ '383': ['XK'],
5952
+ '385': ['HR'],
5953
+ '386': ['SI'],
5954
+ '387': ['BA'],
5955
+ '389': ['MK'],
5956
+ '420': ['CZ'],
5957
+ '421': ['SK'],
5958
+ '423': ['LI'],
5959
+ '500': ['FK'],
5960
+ '501': ['BZ'],
5961
+ '502': ['GT'],
5962
+ '503': ['SV'],
5963
+ '504': ['HN'],
5964
+ '505': ['NI'],
5965
+ '506': ['CR'],
5966
+ '507': ['PA'],
5967
+ '508': ['PM'],
5968
+ '509': ['HT'],
5969
+ '590': ['GP', 'BL', 'MF'],
5970
+ '591': ['BO'],
5971
+ '592': ['GY'],
5972
+ '593': ['EC'],
5973
+ '594': ['GF'],
5974
+ '595': ['PY'],
5975
+ '596': ['MQ'],
5976
+ '597': ['SR'],
5977
+ '598': ['UY'],
5978
+ '599': ['CW', 'BQ'],
5979
+ '670': ['TL'],
5980
+ '672': ['NF'],
5981
+ '673': ['BN'],
5982
+ '674': ['NR'],
5983
+ '675': ['PG'],
5984
+ '676': ['TO'],
5985
+ '677': ['SB'],
5986
+ '678': ['VU'],
5987
+ '679': ['FJ'],
5988
+ '680': ['PW'],
5989
+ '681': ['WF'],
5990
+ '682': ['CK'],
5991
+ '683': ['NU'],
5992
+ '685': ['WS'],
5993
+ '686': ['KI'],
5994
+ '687': ['NC'],
5995
+ '688': ['TV'],
5996
+ '689': ['PF'],
5997
+ '690': ['TK'],
5998
+ '691': ['FM'],
5999
+ '692': ['MH'],
6000
+ '850': ['KP'],
6001
+ '852': ['HK'],
6002
+ '853': ['MO'],
6003
+ '855': ['KH'],
6004
+ '856': ['LA'],
6005
+ '880': ['BD'],
6006
+ '886': ['TW'],
6007
+ '960': ['MV'],
6008
+ '961': ['LB'],
6009
+ '962': ['JO'],
6010
+ '963': ['SY'],
6011
+ '964': ['IQ'],
6012
+ '965': ['KW'],
6013
+ '966': ['SA'],
6014
+ '967': ['YE'],
6015
+ '968': ['OM'],
6016
+ '970': ['PS'],
6017
+ '971': ['AE'],
6018
+ '972': ['IL'],
6019
+ '973': ['BH'],
6020
+ '974': ['QA'],
6021
+ '975': ['BT'],
6022
+ '976': ['MN'],
6023
+ '977': ['NP'],
6024
+ '992': ['TJ'],
6025
+ '993': ['TM'],
6026
+ '994': ['AZ'],
6027
+ '995': ['GE'],
6028
+ '996': ['KG'],
6029
+ '998': ['UZ'],
6030
+ };