@true-engineering/true-react-common-ui-kit 4.0.0-alpha5 → 4.0.0-alpha6

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 (40) hide show
  1. package/README.md +53 -0
  2. package/dist/components/FiltersPane/types.d.ts +15 -5
  3. package/dist/components/Flag/customFlags/customFlags.d.ts +10 -0
  4. package/dist/components/Flag/customFlags/index.d.ts +1 -0
  5. package/dist/components/FlexibleTable/FlexibleTable.d.ts +1 -1
  6. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +5 -4
  7. package/dist/components/FlexibleTable/constants.d.ts +18 -2
  8. package/dist/components/FlexibleTable/types.d.ts +1 -1
  9. package/dist/components/SearchInput/SearchInput.d.ts +2 -2
  10. package/dist/components/SearchInput/SearchInput.stories.d.ts +3 -2
  11. package/dist/components/Select/Select.d.ts +5 -3
  12. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  13. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  14. package/dist/true-react-common-ui-kit.js +112 -78
  15. package/dist/true-react-common-ui-kit.js.map +1 -1
  16. package/dist/true-react-common-ui-kit.umd.cjs +111 -77
  17. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  18. package/package.json +1 -1
  19. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +19 -17
  20. package/src/components/FiltersPane/types.ts +23 -5
  21. package/src/components/Flag/Flag.stories.tsx +2 -1
  22. package/src/components/Flag/Flag.styles.ts +4 -0
  23. package/src/components/Flag/Flag.tsx +23 -9
  24. package/src/components/Flag/customFlags/AB.svg +1 -0
  25. package/src/components/Flag/customFlags/OS.svg +1 -0
  26. package/src/components/Flag/customFlags/augment.d.ts +1 -0
  27. package/src/components/Flag/customFlags/customFlags.ts +13 -0
  28. package/src/components/Flag/customFlags/index.ts +1 -0
  29. package/src/components/FlexibleTable/FlexibleTable.tsx +1 -0
  30. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +6 -3
  31. package/src/components/FlexibleTable/constants.ts +6 -3
  32. package/src/components/FlexibleTable/types.ts +1 -5
  33. package/src/components/PhoneInput/PhoneInput.stories.tsx +2 -1
  34. package/src/components/PhoneInput/PhoneInput.tsx +5 -2
  35. package/src/components/SearchInput/SearchInput.tsx +23 -28
  36. package/src/components/Select/Select.tsx +11 -2
  37. package/src/components/Tooltip/Tooltip.styles.ts +2 -0
  38. package/src/components/Tooltip/Tooltip.tsx +1 -1
  39. package/src/constants/phone-info.ts +20 -33
  40. package/src/helpers/phone.ts +19 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-engineering/true-react-common-ui-kit",
3
- "version": "4.0.0-alpha5",
3
+ "version": "4.0.0-alpha6",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -29,7 +29,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
29
29
  return <></>;
30
30
  }
31
31
 
32
- if (filter.getSelectedValueView !== undefined) {
32
+ if (isNotEmpty(filter.getSelectedValueView)) {
33
33
  return <span className={classes.text}>{filter.getSelectedValueView(value)}</span>;
34
34
  }
35
35
 
@@ -94,7 +94,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
94
94
  const intervalValueFrom = intervalValue[0];
95
95
  const intervalValueTo = intervalValue[1];
96
96
 
97
- const intervals = [];
97
+ const intervals: string[] = [];
98
98
  if (intervalValueFrom !== undefined) {
99
99
  intervals.push(`${translates.from.toLowerCase()} ${String(intervalValueFrom)}`);
100
100
  }
@@ -105,19 +105,6 @@ export function FilterValueView<Values, Key extends keyof Values>({
105
105
  return <span className={classes.text}>{intervals.join(' ')}</span>;
106
106
  }
107
107
 
108
- if (isMultiple) {
109
- return (
110
- <>
111
- {Array.isArray(value) && value.length > 0 && (
112
- <>
113
- <span className={classes.text}>{displayValue(value[0])}</span>
114
- <span className={classes.count}>{value.length > 1 && ` (+${value.length - 1})`}</span>
115
- </>
116
- )}
117
- </>
118
- );
119
- }
120
-
121
108
  if (isDate) {
122
109
  const { from, to, periodType, label } = value as unknown as IPeriod;
123
110
  const hasFrom = from !== undefined && from !== null;
@@ -127,7 +114,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
127
114
  return <span className={classes.text}>{displayValue(label)}</span>;
128
115
  }
129
116
 
130
- const range = [];
117
+ const range: string[] = [];
131
118
  if (hasFrom) {
132
119
  if (!hasTo) {
133
120
  range.push(translates.from.toLowerCase());
@@ -146,12 +133,27 @@ export function FilterValueView<Values, Key extends keyof Values>({
146
133
  return <span className={classes.text}>{range.join(' ')}</span>;
147
134
  }
148
135
 
136
+ if (isMultiple) {
137
+ const convertValue = filter.getSelectedValue ?? displayValue;
138
+
139
+ return (
140
+ <>
141
+ {Array.isArray(value) && value.length > 0 && (
142
+ <>
143
+ <span className={classes.text}>{convertValue(value[0])}</span>
144
+ <span className={classes.count}>{value.length > 1 && ` (+${value.length - 1})`}</span>
145
+ </>
146
+ )}
147
+ </>
148
+ );
149
+ }
150
+
149
151
  if (isRange && Array.isArray(value)) {
150
152
  const rangeValue = value as unknown as number[];
151
153
  const rangeValueFrom = rangeValue[0];
152
154
  const rangeValueTo = rangeValue[1];
153
155
 
154
- const range = [];
156
+ const range: string[] = [];
155
157
  if (rangeValueFrom !== undefined) {
156
158
  range.push(`${translates.from.toLowerCase()} ${String(rangeValueFrom)}`);
157
159
  }
@@ -84,22 +84,40 @@ export type IDateRangeConfigItem<Value> = IConfigItemBasicBase<Value> & {
84
84
  dateFormat?: string;
85
85
  } & Omit<IFilterWithPeriodProps, 'value' | 'onChange' | 'setIsOpen'>;
86
86
 
87
- export type CustomComponent<Value> = FC<{
87
+ export interface ICustomComponentProps<Value> {
88
88
  value?: Value;
89
89
  onChange: (v?: Value) => void;
90
90
  onClose?: () => void;
91
91
  filter: ICustomConfigItem<Value>;
92
92
  localeKey?: IFilterLocaleKey;
93
93
  locale?: IPartialFilterLocale;
94
- }>;
94
+ }
95
+
96
+ export type CustomComponent<Value> = FC<ICustomComponentProps<Value>>;
97
+
98
+ export type ICustomValue<V> = V extends Array<infer T> ? T : never;
99
+
100
+ export interface ICustomRangeConfigItem<Value> extends IConfigItemBasicBase<Value> {
101
+ // eslint-disable-next-line @typescript-eslint/ban-types
102
+ [key: string & {}]: any;
103
+ type: 'custom';
104
+ component: CustomComponent<Value>;
105
+ valueViewType?: 'range';
106
+ }
95
107
 
96
- export interface ICustomConfigItem<Value> extends IConfigItemBasicBase<Value> {
97
- [key: string]: any;
108
+ export interface ICustomMultipleConfigItem<Value> extends IConfigItemBasicBase<Value> {
109
+ // eslint-disable-next-line @typescript-eslint/ban-types
110
+ [key: string & {}]: any;
98
111
  type: 'custom';
99
112
  component: CustomComponent<Value>;
100
- valueViewType?: 'range' | 'multiple';
113
+ valueViewType?: 'multiple';
114
+ getSelectedValue?: (v: ICustomValue<Value>) => ReactNode;
101
115
  }
102
116
 
117
+ export type ICustomConfigItem<Value> =
118
+ | ICustomRangeConfigItem<Value>
119
+ | ICustomMultipleConfigItem<Value>;
120
+
103
121
  export type ConfigItem<Value> =
104
122
  | ISelectConfigItem<Value>
105
123
  | IMultiSelectConfigItem<Value>
@@ -1,12 +1,13 @@
1
1
  import { countries } from 'country-flag-icons';
2
2
  import { ComponentStory } from '@storybook/react';
3
3
  import { Flag } from './Flag';
4
+ import { customFlags } from './customFlags';
4
5
 
5
6
  export default {
6
7
  title: 'Data Display/Flag',
7
8
  component: Flag,
8
9
  argTypes: {
9
- countryCode: { control: 'select', options: countries },
10
+ countryCode: { control: 'select', options: [...countries, ...Object.keys(customFlags)] },
10
11
  },
11
12
  };
12
13
 
@@ -2,6 +2,10 @@ import { colors, ITweakStyles, createThemedStyles } from '../../theme';
2
2
 
3
3
  export const useStyles = createThemedStyles('Flag', {
4
4
  root: {
5
+ display: 'flex',
6
+ width: '100%',
7
+ height: '100%',
8
+ boxSizing: 'border-box',
5
9
  // приходится хардкодить в компоненте, тк либа Flags выдает флаги с 2-3 пиксельным отступом снизу
6
10
  // если будет нужно, то можно вынести border на уровень пропсов
7
11
  border: [1, 'solid', colors.BORDER_MAIN],
@@ -3,6 +3,7 @@ import { hasFlag } from 'country-flag-icons';
3
3
  import Flags from 'country-flag-icons/react/3x2';
4
4
  import { ICommonProps } from '../../types';
5
5
  import { Icon } from '../Icon';
6
+ import { customFlags } from './customFlags';
6
7
  import { useStyles, IFlagStyles } from './Flag.styles';
7
8
 
8
9
  export interface IFlagProps extends Pick<ICommonProps<IFlagStyles>, 'tweakStyles'> {
@@ -12,15 +13,28 @@ export interface IFlagProps extends Pick<ICommonProps<IFlagStyles>, 'tweakStyles
12
13
 
13
14
  export const Flag: FC<IFlagProps> = ({ countryCode = '', tweakStyles }) => {
14
15
  const classes = useStyles({ theme: tweakStyles });
15
- const CC = countryCode.toUpperCase();
16
+ const countryFlagKey = countryCode.toUpperCase();
16
17
 
17
- const TheFlag = hasFlag(CC)
18
- ? Flags[CC as keyof typeof Flags]
19
- : () => (
20
- <div className={classes.noFlag}>
21
- <Icon type="minus" />
22
- </div>
23
- );
18
+ const hasFlagInLibrary = hasFlag(countryFlagKey);
24
19
 
25
- return <TheFlag className={classes.root} />;
20
+ if (hasFlagInLibrary) {
21
+ const FlagComponent = Flags[countryFlagKey as keyof typeof Flags];
22
+ return (
23
+ <div className={classes.root}>
24
+ <FlagComponent />
25
+ </div>
26
+ );
27
+ }
28
+
29
+ const CustomFlag = customFlags[countryFlagKey as keyof typeof customFlags];
30
+
31
+ if (CustomFlag !== undefined) {
32
+ return <div className={classes.root} dangerouslySetInnerHTML={{ __html: CustomFlag }} />;
33
+ }
34
+
35
+ return (
36
+ <div className={classes.noFlag}>
37
+ <Icon type="minus" />
38
+ </div>
39
+ );
26
40
  };
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 341.3"><path fill="#FFF" d="M0 0h512v341.3H0z"/><g fill="#6DA544"><path d="M0 0h512v48.8H0zM0 97.5h512v48.8H0zM0 195h512v48.8H0zM0 292.6h512v48.8H0z"/></g><path fill="#D80027" d="M0 0h256v146.3H0z"/><path fill="#FFF" d="m116.9 114.4-7.5-14.8V69.9L128 59l18.6 10.9v22.3l7.4-7.4 4.2 3-4.2 11.8-14.9 14.8z"/><circle fill="#FFF" cx="82" cy="82.8" r="5.4"/><circle fill="#FFF" cx="90.8" cy="61.7" r="5.4"/><circle fill="#FFF" cx="106.6" cy="46.2" r="5.4"/><circle fill="#FFF" cx="128" cy="40.8" r="5.4"/><circle fill="#FFF" cx="149.4" cy="46.2" r="5.4"/><circle fill="#FFF" cx="165.2" cy="61.7" r="5.4"/><circle fill="#FFF" cx="174" cy="82.8" r="5.4"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 300"><path fill="#FFF" d="M0 0h450v300H0z"/><path fill="red" d="M0 100h450v200H0z"/><path fill="#FFDF00" d="M0 200h450v100H0z"/></svg>
@@ -0,0 +1 @@
1
+ declare module '*.svg?raw';
@@ -0,0 +1,13 @@
1
+ import AB from './AB.svg?raw';
2
+ import OS from './OS.svg?raw';
3
+
4
+ export const customFlags = {
5
+ /**
6
+ * Абхазия
7
+ */
8
+ AB,
9
+ /**
10
+ * Южная осетия
11
+ */
12
+ OS,
13
+ };
@@ -0,0 +1 @@
1
+ export * from './customFlags';
@@ -38,6 +38,7 @@ export interface IFlexibleTableProps<
38
38
  | 'expandableRowComponent'
39
39
  | 'onRowClick'
40
40
  | 'onRowHover'
41
+ | 'rowRef'
41
42
  > {
42
43
  content: Row[];
43
44
  /** @default 'table' */
@@ -1,9 +1,9 @@
1
- import { ReactNode, useState, memo, MouseEvent } from 'react';
1
+ import { ReactNode, useState, memo, MouseEvent, RefCallback } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { applyAction, isEmpty, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
4
4
  import { addDataAttributes } from '../../../../helpers';
5
5
  import { useTweakStyles } from '../../../../hooks';
6
- import { ICommonProps, IDataAttributes } from '../../../../types';
6
+ import { IDataAttributes, ITweakStylesProps } from '../../../../types';
7
7
  import { TableRenders } from '../../constants';
8
8
  import {
9
9
  ITableRow,
@@ -19,7 +19,7 @@ export interface IFlexibleTableRowProps<
19
19
  Row extends ITableRow,
20
20
  HeaderContent extends IHeaderContent<Row>,
21
21
  UniqueField extends keyof Row,
22
- > extends Pick<ICommonProps<IFlexibleTableRowStyles>, 'tweakStyles'> {
22
+ > extends ITweakStylesProps<IFlexibleTableRowStyles> {
23
23
  item: Row;
24
24
  index: number;
25
25
  uniqueField?: UniqueField;
@@ -38,6 +38,7 @@ export interface IFlexibleTableRowProps<
38
38
  rowAttributes?: Array<keyof Row>;
39
39
  /** @default false */
40
40
  isExpandableRowComponentInitiallyOpen?: boolean | ((row: Row, index: number) => boolean);
41
+ rowRef?: RefCallback<HTMLTableRowElement>;
41
42
  /** Возвращает React-элемент, который отрисуется под строкой при нажатии на неё */
42
43
  expandableRowComponent?: (item: Row, isOpen: boolean, close: () => void) => ReactNode;
43
44
  onRowHover?: (id?: Row[UniqueField]) => void;
@@ -61,6 +62,7 @@ function FlexibleTableRowInner<
61
62
  isLoading = false,
62
63
  rowAttributes,
63
64
  isExpandableRowComponentInitiallyOpen = false,
65
+ rowRef,
64
66
  tweakStyles,
65
67
  expandableRowComponent,
66
68
  onRowHover,
@@ -139,6 +141,7 @@ function FlexibleTableRowInner<
139
141
  return (
140
142
  <>
141
143
  <Table.Row
144
+ ref={rowRef}
142
145
  className={clsx(classes.root, {
143
146
  [classes.active]: isActive,
144
147
  [classes.editable]: isEditable,
@@ -1,8 +1,11 @@
1
- import { IFlexibleTableRenderMode, IFlexibleTableRender } from './types';
1
+ import { IFlexibleTableRenderElement, IFlexibleTableRenderMode } from './types';
2
2
 
3
3
  export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
4
4
 
5
- export const TableRenders: Record<IFlexibleTableRenderMode, IFlexibleTableRender> = {
5
+ export const TableRenders = {
6
6
  table: { Root: 'table', Head: 'thead', Body: 'tbody', Row: 'tr', Header: 'th', Cell: 'td' },
7
7
  divs: { Root: 'div', Head: 'div', Body: 'div', Row: 'div', Header: 'div', Cell: 'div' },
8
- };
8
+ } satisfies Record<
9
+ IFlexibleTableRenderMode,
10
+ Record<IFlexibleTableRenderElement, keyof JSX.IntrinsicElements>
11
+ >;
@@ -2,11 +2,7 @@ import { CSSProperties, MouseEvent, ReactNode } from 'react';
2
2
  import { IRenderNode } from '../../types';
3
3
 
4
4
  export type IFlexibleTableRenderMode = 'table' | 'divs';
5
-
6
- export type IFlexibleTableRender = Record<
7
- 'Root' | 'Head' | 'Body' | 'Row' | 'Header' | 'Cell',
8
- keyof JSX.IntrinsicElements
9
- >;
5
+ export type IFlexibleTableRenderElement = 'Root' | 'Head' | 'Body' | 'Row' | 'Header' | 'Cell';
10
6
 
11
7
  // TODO: Заменить Record<string, any> на Record<string, unknown>
12
8
  export type ITableRow = Record<string, any>;
@@ -26,7 +26,8 @@ export default {
26
26
  const Template: ComponentStory<typeof PhoneInput> = (args) => {
27
27
  const [value, setValue] = useState<IPhoneValue>({
28
28
  dialCode: '7',
29
- phoneNumber: '1234567890',
29
+ phoneNumber: '9134567890',
30
+ countryCode: 'RU',
30
31
  });
31
32
 
32
33
  return <PhoneInput {...args} value={value} onChange={setValue} />;
@@ -76,7 +76,7 @@ export const PhoneInput: FC<IPhoneInputProps> = ({
76
76
 
77
77
  const countryCode = useMemo(
78
78
  () => value?.countryCode ?? getCountryCodeFromPhone(phoneWithCode),
79
- [value.dialCode, value.phoneNumber],
79
+ [value.countryCode, value.dialCode, value.phoneNumber],
80
80
  );
81
81
 
82
82
  const handleClose = () => {
@@ -122,7 +122,10 @@ export const PhoneInput: FC<IPhoneInputProps> = ({
122
122
  if (newPhoneInfo.countryCode !== countryCode) {
123
123
  onChange(
124
124
  {
125
- phoneNumber: '',
125
+ phoneNumber:
126
+ newPhoneInfo.dialCode !== value.dialCode
127
+ ? ''
128
+ : getPhoneObjFromString(phoneWithCode).phoneNumber,
126
129
  dialCode: newPhoneInfo.dialCode,
127
130
  countryCode: newPhoneInfo.countryCode,
128
131
  },
@@ -1,4 +1,4 @@
1
- import { FC } from 'react';
1
+ import { forwardRef } from 'react';
2
2
  import { getTestId } from '@true-engineering/true-react-platform-helpers';
3
3
  import { useTweakStyles } from '../../hooks';
4
4
  import { ICommonProps } from '../../types';
@@ -11,31 +11,26 @@ export type ISearchInputProps = Omit<
11
11
  > &
12
12
  ICommonProps<ISearchInputStyles>;
13
13
 
14
- export const SearchInput: FC<ISearchInputProps> = ({
15
- isClearable = true,
16
- placeholder,
17
- value,
18
- testId,
19
- tweakStyles,
20
- data,
21
- ...props
22
- }) => {
23
- const tweakInputStyles = useTweakStyles({
24
- innerStyles: inputStyles,
25
- tweakStyles,
26
- className: 'tweakInput',
27
- currentComponentName: 'SearchInput',
28
- });
14
+ export const SearchInput = forwardRef<HTMLInputElement, ISearchInputProps>(
15
+ ({ isClearable = true, placeholder, value, testId, tweakStyles, data, ...props }, ref) => {
16
+ const tweakInputStyles = useTweakStyles({
17
+ innerStyles: inputStyles,
18
+ tweakStyles,
19
+ className: 'tweakInput',
20
+ currentComponentName: 'SearchInput',
21
+ });
29
22
 
30
- return (
31
- <Input
32
- value={value}
33
- placeholder={placeholder}
34
- icon="search"
35
- isClearable={isClearable}
36
- testId={getTestId(testId, 'input')}
37
- tweakStyles={tweakInputStyles}
38
- {...props}
39
- />
40
- );
41
- };
23
+ return (
24
+ <Input
25
+ ref={ref}
26
+ value={value}
27
+ placeholder={placeholder}
28
+ icon="search"
29
+ isClearable={isClearable}
30
+ testId={getTestId(testId, 'input')}
31
+ tweakStyles={tweakInputStyles}
32
+ {...props}
33
+ />
34
+ );
35
+ },
36
+ );
@@ -5,6 +5,7 @@ import {
5
5
  KeyboardEvent,
6
6
  MouseEvent,
7
7
  ReactNode,
8
+ Ref,
8
9
  SyntheticEvent,
9
10
  useCallback,
10
11
  useEffect,
@@ -63,7 +64,11 @@ export interface ISelectProps<Value>
63
64
  /** @default true */
64
65
  shouldScrollToList?: boolean;
65
66
  isMultiSelect?: false;
66
- searchInput?: { shouldRenderInList: true } & Pick<ISearchInputProps, 'placeholder'>;
67
+ searchInput?: {
68
+ /** @default false */
69
+ shouldRenderInList?: boolean;
70
+ ref?: Ref<HTMLInputElement>;
71
+ } & Pick<ISearchInputProps, 'placeholder' | 'shouldFocusOnMount'>;
67
72
  isOptionDisabled?: (option: Value) => boolean;
68
73
  onChange: (value: Value | undefined, event: IChangeSelectEvent) => void; // подумать как возвращать индекс
69
74
  onBlur?: (event: Event | SyntheticEvent) => void;
@@ -248,10 +253,14 @@ export function Select<Value>(
248
253
 
249
254
  const handleListClose = useCallback(
250
255
  (event: Event | SyntheticEvent) => {
256
+ if (!isListOpen) {
257
+ return;
258
+ }
259
+
251
260
  closeList();
252
261
  onBlur?.(event);
253
262
  },
254
- [closeList, onBlur],
263
+ [isListOpen, closeList, onBlur],
255
264
  );
256
265
 
257
266
  const handleListOpen = () => {
@@ -32,6 +32,8 @@ export const useStyles = createThemedStyles('Tooltip', {
32
32
  letterSpacing: 0.2,
33
33
  },
34
34
 
35
+ custom: {},
36
+
35
37
  info: {},
36
38
 
37
39
  error: {
@@ -8,7 +8,7 @@ import { useStyles, ITooltipStyles } from './Tooltip.styles';
8
8
  export interface ITooltipProps extends ICommonProps<ITooltipStyles> {
9
9
  text: ReactNode;
10
10
  /** @default 'tooltip' */
11
- view?: 'tooltip' | 'hint';
11
+ view?: 'tooltip' | 'hint' | 'custom';
12
12
  /** @default 'info' */
13
13
  type?: 'info' | 'error';
14
14
  }
@@ -1,6 +1,15 @@
1
1
  import type { IPhoneInfo } from '../components';
2
2
 
3
3
  export const phoneInfo: IPhoneInfo[] = [
4
+ {
5
+ countryEn: 'Abkhazia',
6
+ countryRu: 'Абхазия',
7
+ countryCode: 'AB',
8
+ phoneMask: '(999) 999-99-99',
9
+ dialCode: '7',
10
+ dialCodePriority: 1,
11
+ fullCodes: ['7'],
12
+ },
4
13
  {
5
14
  countryEn: 'Afghanistan',
6
15
  countryRu: 'Афганистан',
@@ -785,40 +794,9 @@ export const phoneInfo: IPhoneInfo[] = [
785
794
  countryRu: 'Казахстан',
786
795
  countryCode: 'KZ',
787
796
  dialCode: '7',
788
- phoneMask: '999 999-99-99',
797
+ phoneMask: '(999) 999-99-99',
789
798
  dialCodePriority: 1,
790
- areaCodes: [
791
- '310',
792
- '311',
793
- '312',
794
- '313',
795
- '315',
796
- '318',
797
- '321',
798
- '324',
799
- '325',
800
- '326',
801
- '327',
802
- '336',
803
- '7172',
804
- '73622',
805
- ],
806
- fullCodes: [
807
- '7310',
808
- '7311',
809
- '7312',
810
- '7313',
811
- '7315',
812
- '7318',
813
- '7321',
814
- '7324',
815
- '7325',
816
- '7326',
817
- '7327',
818
- '7336',
819
- '77172',
820
- '773622',
821
- ],
799
+ fullCodes: ['7'],
822
800
  },
823
801
  {
824
802
  countryEn: 'Kenya',
@@ -1404,6 +1382,15 @@ export const phoneInfo: IPhoneInfo[] = [
1404
1382
  phoneMask: '999 9999 9999',
1405
1383
  fullCodes: ['82'],
1406
1384
  },
1385
+ {
1386
+ countryCode: 'OS',
1387
+ countryEn: 'South Ossetia',
1388
+ countryRu: 'Южная Осетия',
1389
+ phoneMask: '(999) 999-99-99',
1390
+ dialCode: '7',
1391
+ dialCodePriority: 1,
1392
+ fullCodes: ['7'],
1393
+ },
1407
1394
  {
1408
1395
  countryEn: 'South Sudan',
1409
1396
  countryRu: 'Южный Судан',
@@ -1,4 +1,4 @@
1
- import { isNotEmpty, isEmpty } from '@true-engineering/true-react-platform-helpers';
1
+ import { isNotEmpty, isStringEmpty } from '@true-engineering/true-react-platform-helpers';
2
2
  import type { IPhoneInfo, IPhoneValue } from '../components';
3
3
  import { phoneInfo } from '../constants';
4
4
 
@@ -46,22 +46,26 @@ export const getFullPhone = (phone?: IPhoneValue): string =>
46
46
  (phone?.dialCode ?? '') + (phone?.phoneNumber ?? '');
47
47
 
48
48
  export const getCountryCodeFromPhone = (phoneWithCode: string): string | undefined => {
49
- // попробуем найти уникальный код страны fullCode (dialCode + arealCode)
50
- let countryCode = phoneInfo.find((info) =>
51
- info.fullCodes.some((code) => phoneWithCode.startsWith(code)),
52
- )?.countryCode;
53
-
54
- if (isEmpty(countryCode) && isNotEmpty(phoneWithCode)) {
55
- // если не нашли уникальный fullCode (dialCode + arealCode),
56
- // то пробуем найти dialCode и выбираем с наименьшим Priority
57
- countryCode = phoneInfo
58
- .filter((info) => phoneWithCode.startsWith(info.dialCode))
59
- .sort(
60
- (infoA, infoB) => (infoA.dialCodePriority ?? 1000) - (infoB.dialCodePriority ?? 1000),
61
- )[0]?.countryCode;
49
+ if (isStringEmpty(phoneWithCode)) {
50
+ return;
62
51
  }
63
52
 
64
- return countryCode;
53
+ // ищем страны, для которых phoneWithCode начинается с fullCode (dialCode + areaCode)
54
+ const matchedCountries = phoneInfo.filter((info) =>
55
+ info.fullCodes.some((fullCode) => phoneWithCode.startsWith(fullCode)),
56
+ );
57
+
58
+ // если нашлась всего одна — ок, выдаём её
59
+ if (matchedCountries.length === 1) {
60
+ return matchedCountries[0].countryCode;
61
+ }
62
+
63
+ // если нашлось несколько, выбираем страну с наименьшим dialCodePriority (0 — самая приоритетная)
64
+ const highestPriorityCountries = phoneInfo
65
+ .filter((info) => phoneWithCode.startsWith(info.dialCode))
66
+ .sort((a, b) => (a.dialCodePriority ?? 1000) - (b.dialCodePriority ?? 1000));
67
+
68
+ return highestPriorityCountries.at(0)?.countryCode;
65
69
  };
66
70
 
67
71
  export const getPhoneObjFromString = (fullPhone: string, countryCode?: string): IPhoneValue => {