@true-engineering/true-react-common-ui-kit 4.0.0-alpha0 → 4.0.0-alpha10

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 (156) hide show
  1. package/README.md +11 -567
  2. package/dist/components/Button/Button.styles.d.ts +1 -1
  3. package/dist/components/Checkbox/Checkbox.styles.d.ts +1 -1
  4. package/dist/components/ControlWrapper/ControlWrapper.styles.d.ts +1 -1
  5. package/dist/components/DatePicker/DatePicker.d.ts +2 -2
  6. package/dist/components/DatePicker/helpers.d.ts +3 -0
  7. package/dist/components/DatePicker/types.d.ts +3 -1
  8. package/dist/components/FiltersPane/FiltersPane.d.ts +4 -2
  9. package/dist/components/FiltersPane/FiltersPane.stories.d.ts +3 -3
  10. package/dist/components/FiltersPane/components/Filter/Filter.d.ts +2 -2
  11. package/dist/components/FiltersPane/components/Filter/helpers.d.ts +4 -0
  12. package/dist/components/FiltersPane/components/FilterInterval/FilterInterval.styles.d.ts +1 -1
  13. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +1 -1
  14. package/dist/components/FiltersPane/components/FilterValueView/FilterValueView.d.ts +3 -1
  15. package/dist/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.d.ts +2 -2
  16. package/dist/components/FiltersPane/components/FilterWrapper/FilterWrapper.d.ts +2 -2
  17. package/dist/components/FiltersPane/types.d.ts +15 -5
  18. package/dist/components/Flag/customFlags/customFlags.d.ts +10 -0
  19. package/dist/components/Flag/customFlags/index.d.ts +1 -0
  20. package/dist/components/FlexibleTable/FlexibleTable.d.ts +4 -2
  21. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +6 -6
  22. package/dist/components/FlexibleTable/constants.d.ts +18 -2
  23. package/dist/components/FlexibleTable/types.d.ts +1 -1
  24. package/dist/components/Input/Input.d.ts +3 -3
  25. package/dist/components/Input/Input.stories.d.ts +2 -2
  26. package/dist/components/Input/Input.styles.d.ts +3 -3
  27. package/dist/components/List/List.d.ts +1 -1
  28. package/dist/components/List/index.d.ts +2 -1
  29. package/dist/components/List/types.d.ts +4 -0
  30. package/dist/components/SearchInput/SearchInput.d.ts +2 -2
  31. package/dist/components/SearchInput/SearchInput.stories.d.ts +3 -12
  32. package/dist/components/SearchInput/SearchInput.styles.d.ts +3 -5
  33. package/dist/components/Select/Select.d.ts +5 -3
  34. package/dist/components/Select/Select.styles.d.ts +8 -8
  35. package/dist/components/Select/index.d.ts +1 -1
  36. package/dist/components/TextArea/TextArea.d.ts +2 -2
  37. package/dist/components/TextArea/TextArea.styles.d.ts +3 -3
  38. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  39. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  40. package/dist/components/WithMessages/WithMessages.d.ts +10 -0
  41. package/dist/components/{ControlGroup/ControlGroup.stories.d.ts → WithMessages/WithMessages.stories.d.ts} +2 -2
  42. package/dist/components/WithMessages/WithMessages.styles.d.ts +3 -0
  43. package/dist/components/WithMessages/index.d.ts +2 -0
  44. package/dist/components/WithPopup/WithPopup.d.ts +21 -6
  45. package/dist/components/WithPopup/WithPopup.styles.d.ts +1 -1
  46. package/dist/components/WithPopup/helpers.d.ts +2 -0
  47. package/dist/components/WithPopup/types.d.ts +3 -0
  48. package/dist/components/WithTooltip/WithTooltip.styles.d.ts +1 -0
  49. package/dist/components/index.d.ts +1 -1
  50. package/dist/hooks/index.d.ts +1 -0
  51. package/dist/hooks/use-merge.d.ts +1 -0
  52. package/dist/hooks/use-mixed-styles.d.ts +3 -1
  53. package/dist/hooks/use-tweak-styles.d.ts +5 -5
  54. package/dist/theme/Provider.d.ts +6 -3
  55. package/dist/theme/common.d.ts +4 -2
  56. package/dist/theme/create-themed-styles.d.ts +2 -0
  57. package/dist/theme/helpers.d.ts +9 -3
  58. package/dist/theme/index.d.ts +2 -0
  59. package/dist/theme/true-jss/ThemedStylesManager.d.ts +18 -0
  60. package/dist/theme/true-jss/TweakStylesManager.d.ts +34 -0
  61. package/dist/theme/true-jss/index.d.ts +2 -0
  62. package/dist/theme/true-jss/jss-context.d.ts +9 -0
  63. package/dist/theme/types.d.ts +6 -4
  64. package/dist/true-react-common-ui-kit.js +7304 -6380
  65. package/dist/true-react-common-ui-kit.js.map +1 -1
  66. package/dist/true-react-common-ui-kit.umd.cjs +7071 -6148
  67. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  68. package/dist/types.d.ts +2 -1
  69. package/package.json +1 -1
  70. package/src/components/ControlWrapper/ControlWrapper.styles.ts +5 -5
  71. package/src/components/ControlWrapper/ControlWrapper.tsx +1 -1
  72. package/src/components/DatePicker/DatePicker.tsx +9 -4
  73. package/src/components/DatePicker/components/DatePickerHeader/DatePickerHeader.styles.ts +1 -1
  74. package/src/components/DatePicker/helpers.ts +13 -1
  75. package/src/components/DatePicker/types.ts +4 -1
  76. package/src/components/FiltersPane/FiltersPane.stories.tsx +4 -2
  77. package/src/components/FiltersPane/FiltersPane.tsx +14 -9
  78. package/src/components/FiltersPane/components/Filter/Filter.tsx +24 -17
  79. package/src/components/FiltersPane/components/Filter/helpers.ts +18 -0
  80. package/src/components/FiltersPane/components/FilterInterval/FilterInterval.styles.ts +1 -1
  81. package/src/components/FiltersPane/components/FilterInterval/FilterInterval.tsx +6 -1
  82. package/src/components/FiltersPane/components/FilterSelect/FilterSelect.styles.ts +2 -2
  83. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +27 -22
  84. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +1 -0
  85. package/src/components/FiltersPane/components/FilterWithPeriod/FilterWithPeriod.tsx +1 -1
  86. package/src/components/FiltersPane/components/FilterWrapper/FilterWrapper.tsx +7 -5
  87. package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.styles.ts +8 -2
  88. package/src/components/FiltersPane/types.ts +23 -5
  89. package/src/components/Flag/Flag.stories.tsx +2 -1
  90. package/src/components/Flag/Flag.styles.ts +4 -0
  91. package/src/components/Flag/Flag.tsx +23 -9
  92. package/src/components/Flag/customFlags/AB.svg +1 -0
  93. package/src/components/Flag/customFlags/OS.svg +1 -0
  94. package/src/components/Flag/customFlags/augment.d.ts +1 -0
  95. package/src/components/Flag/customFlags/customFlags.ts +13 -0
  96. package/src/components/Flag/customFlags/index.ts +1 -0
  97. package/src/components/FlexibleTable/FlexibleTable.tsx +13 -12
  98. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +9 -8
  99. package/src/components/FlexibleTable/constants.ts +6 -3
  100. package/src/components/FlexibleTable/types.ts +1 -5
  101. package/src/components/IncrementInput/IncrementInput.styles.ts +1 -1
  102. package/src/components/Input/Input.styles.ts +5 -2
  103. package/src/components/Input/Input.tsx +9 -8
  104. package/src/components/Input/InputBase.tsx +2 -1
  105. package/src/components/List/List.tsx +5 -2
  106. package/src/components/List/index.ts +2 -1
  107. package/src/components/List/types.ts +5 -0
  108. package/src/components/MultiSelectList/MultiSelectList.styles.ts +1 -1
  109. package/src/components/PhoneInput/PhoneInput.stories.tsx +2 -1
  110. package/src/components/PhoneInput/PhoneInput.styles.ts +2 -2
  111. package/src/components/PhoneInput/PhoneInput.tsx +5 -2
  112. package/src/components/PhoneInput/components/PhoneInputCountryList/PhoneInputCountryList.styles.ts +1 -1
  113. package/src/components/SearchInput/SearchInput.styles.ts +17 -30
  114. package/src/components/SearchInput/SearchInput.tsx +17 -30
  115. package/src/components/Select/CustomSelect.stories.tsx +2 -2
  116. package/src/components/Select/Select.styles.ts +8 -2
  117. package/src/components/Select/Select.tsx +19 -31
  118. package/src/components/Select/components/SelectList/SelectList.tsx +1 -1
  119. package/src/components/Select/index.ts +1 -1
  120. package/src/components/TextArea/TextArea.styles.ts +7 -3
  121. package/src/components/TextArea/TextArea.tsx +16 -11
  122. package/src/components/Tooltip/Tooltip.styles.ts +2 -0
  123. package/src/components/Tooltip/Tooltip.tsx +1 -1
  124. package/src/components/{ControlGroup/ControlGroup.stories.tsx → WithMessages/WithMessages.stories.tsx} +10 -10
  125. package/src/components/{ControlGroup/ControlGroup.styles.ts → WithMessages/WithMessages.styles.ts} +4 -3
  126. package/src/components/{ControlGroup/ControlGroup.tsx → WithMessages/WithMessages.tsx} +13 -9
  127. package/src/components/WithMessages/index.ts +2 -0
  128. package/src/components/WithPopup/WithPopup.stories.tsx +1 -0
  129. package/src/components/WithPopup/WithPopup.styles.ts +2 -0
  130. package/src/components/WithPopup/WithPopup.tsx +64 -16
  131. package/src/components/WithPopup/helpers.ts +9 -0
  132. package/src/components/WithPopup/types.ts +7 -0
  133. package/src/components/WithTooltip/WithTooltip.styles.ts +6 -0
  134. package/src/components/WithTooltip/WithTooltip.tsx +7 -2
  135. package/src/components/index.ts +1 -1
  136. package/src/constants/phone-info.ts +20 -33
  137. package/src/helpers/phone.ts +19 -15
  138. package/src/hooks/index.ts +1 -0
  139. package/src/hooks/use-merge.ts +8 -0
  140. package/src/hooks/use-mixed-styles.ts +9 -11
  141. package/src/hooks/use-tweak-styles.ts +49 -27
  142. package/src/theme/Provider.tsx +10 -5
  143. package/src/theme/common.ts +5 -2
  144. package/src/theme/create-themed-styles.ts +78 -0
  145. package/src/theme/helpers.ts +39 -39
  146. package/src/theme/index.ts +2 -0
  147. package/src/theme/true-jss/ThemedStylesManager.ts +92 -0
  148. package/src/theme/true-jss/TweakStylesManager.ts +157 -0
  149. package/src/theme/true-jss/index.ts +2 -0
  150. package/src/theme/true-jss/jss-context.tsx +34 -0
  151. package/src/theme/types.ts +6 -4
  152. package/src/types.ts +2 -1
  153. package/dist/components/ControlGroup/ControlGroup.d.ts +0 -10
  154. package/dist/components/ControlGroup/ControlGroup.styles.d.ts +0 -3
  155. package/dist/components/ControlGroup/index.d.ts +0 -2
  156. package/src/components/ControlGroup/index.ts +0 -2
@@ -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,
@@ -18,7 +19,6 @@ import { debounce } from 'ts-debounce';
18
19
  import {
19
20
  createFilter,
20
21
  getTestId,
21
- isEmpty,
22
22
  isNotEmpty,
23
23
  isReactNodeNotEmpty,
24
24
  isStringNotEmpty,
@@ -26,10 +26,10 @@ import {
26
26
  import { hasExactParent } from '../../helpers';
27
27
  import { useDropdown, useIsMounted, useOnClickOutsideWithRef, useTweakStyles } from '../../hooks';
28
28
  import { ICommonProps, IDropdownWithPopperOptions } from '../../types';
29
- import { ControlGroup } from '../ControlGroup';
30
29
  import { IIcon, renderIcon } from '../Icon';
31
30
  import { IChangeInputEvent, IInputProps, InputBase } from '../Input';
32
31
  import { ISearchInputProps, SearchInput } from '../SearchInput';
32
+ import { WithMessages } from '../WithMessages';
33
33
  import { SelectList } from './components';
34
34
  import { ALL_OPTION_INDEX, DEFAULT_OPTION_INDEX } from './constants';
35
35
  import {
@@ -64,7 +64,11 @@ export interface ISelectProps<Value>
64
64
  /** @default true */
65
65
  shouldScrollToList?: boolean;
66
66
  isMultiSelect?: false;
67
- searchInput?: { shouldRenderInList: true } & Pick<ISearchInputProps, 'placeholder'>;
67
+ searchInput?: {
68
+ /** @default false */
69
+ shouldRenderInList?: boolean;
70
+ ref?: Ref<HTMLInputElement>;
71
+ } & Pick<ISearchInputProps, 'placeholder' | 'shouldFocusOnMount'>;
68
72
  isOptionDisabled?: (option: Value) => boolean;
69
73
  onChange: (value: Value | undefined, event: IChangeSelectEvent) => void; // подумать как возвращать индекс
70
74
  onBlur?: (event: Event | SyntheticEvent) => void;
@@ -142,9 +146,9 @@ export function Select<Value>(
142
146
  const hasSearchInputInList = optionsMode !== 'normal' && shouldRenderSearchInputInList;
143
147
  const hasReadonlyInput = isReadonly || optionsMode === 'normal' || shouldRenderSearchInputInList;
144
148
 
145
- const tweakControlGroupStyles = useTweakStyles({
149
+ const tweakWithMessagesStyles = useTweakStyles({
146
150
  tweakStyles,
147
- className: 'tweakControlGroup',
151
+ className: 'tweakWithMessages',
148
152
  currentComponentName: 'Select',
149
153
  });
150
154
 
@@ -241,44 +245,27 @@ export function Select<Value>(
241
245
  [convertValueToId, convertValueToString],
242
246
  );
243
247
 
244
- const getDropdownOffset = () => {
245
- if (isEmpty(input.current)) {
246
- return 0;
247
- }
248
-
249
- // Высота элемента inputWrapper у компонента Input
250
- return input.current.parentElement?.offsetHeight ?? 0;
251
- };
252
-
253
248
  const closeList = useCallback(() => {
254
249
  setIsListOpen(false);
255
250
  setSearchValue('');
256
251
  setShouldShowDefaultOption(true);
257
-
258
- if (!dropdownOptions?.shouldUsePopper) {
259
- root.current?.style.removeProperty('--dropdown-offset');
260
- }
261
252
  }, [dropdownOptions?.shouldUsePopper]);
262
253
 
263
254
  const handleListClose = useCallback(
264
255
  (event: Event | SyntheticEvent) => {
256
+ if (!isListOpen) {
257
+ return;
258
+ }
259
+
265
260
  closeList();
266
261
  onBlur?.(event);
267
262
  },
268
- [closeList, onBlur],
263
+ [isListOpen, closeList, onBlur],
269
264
  );
270
265
 
271
266
  const handleListOpen = () => {
272
- if (isReadonly) {
273
- return;
274
- }
275
-
276
- if (!isListOpen) {
267
+ if (!isReadonly && !isListOpen) {
277
268
  setIsListOpen(true);
278
-
279
- if (!dropdownOptions?.shouldUsePopper) {
280
- root.current?.style.setProperty('--dropdown-offset', `${getDropdownOffset()}px`);
281
- }
282
269
  }
283
270
  };
284
271
 
@@ -599,10 +586,11 @@ export function Select<Value>(
599
586
  ) : undefined;
600
587
 
601
588
  return (
602
- <ControlGroup
589
+ <WithMessages
603
590
  errorMessage={errorMessage}
604
591
  infoMessage={infoMessage}
605
- tweakStyles={tweakControlGroupStyles}
592
+ tweakStyles={tweakWithMessagesStyles}
593
+ testId={getTestId(testId, 'wrapper')}
606
594
  >
607
595
  <div className={classes.root} onKeyDown={handleKeyDown} ref={root}>
608
596
  <div
@@ -645,6 +633,6 @@ export function Select<Value>(
645
633
  <>{isOpen && listEl}</>
646
634
  )}
647
635
  </div>
648
- </ControlGroup>
636
+ </WithMessages>
649
637
  );
650
638
  }
@@ -84,7 +84,7 @@ export function SelectList<Value>({
84
84
  })}
85
85
  >
86
86
  {isHeaderNotEmpty && <div className={classes.listHeader}>{customListHeader}</div>}
87
- <div className={classes.list} {...addDataTestId(testId)}>
87
+ <div className={classes.list} {...addDataTestId(testId)} tabIndex={-1}>
88
88
  {isLoading ? (
89
89
  <div className={clsx(classes.cell, classes.loading)}>{loadingLabel}</div>
90
90
  ) : (
@@ -1,4 +1,4 @@
1
1
  export * from './Select';
2
2
  export * from './types';
3
3
  export type { ISelectStyles } from './Select.styles';
4
- export type { ISelectListStyles } from './components';
4
+ export { type ISelectListStyles, SelectList } from './components';
@@ -1,6 +1,6 @@
1
1
  import { animations, createThemedStyles, helpers, ITweakStyles } from '../../theme';
2
- import { IControlGroupStyles } from '../ControlGroup';
3
2
  import { IControlWrapperStyles } from '../ControlWrapper';
3
+ import { IWithMessagesStyles } from '../WithMessages';
4
4
 
5
5
  export const useStyles = createThemedStyles('TextArea', {
6
6
  textarea: {
@@ -32,12 +32,16 @@ export const useStyles = createThemedStyles('TextArea', {
32
32
  },
33
33
  },
34
34
 
35
+ wrapper: {
36
+ width: '100%',
37
+ },
38
+
35
39
  autoSized: {
36
40
  display: 'grid',
37
41
  gridTemplateRows: 'minmax(0, 100%)',
38
42
 
39
43
  '& > $textarea, &::after': {
40
- gridArea: '1 / 2',
44
+ gridArea: '1 / 1',
41
45
  },
42
46
 
43
47
  '&::after': {
@@ -72,7 +76,7 @@ export const controlWrapperStyles: IControlWrapperStyles = {
72
76
  export type ITextAreaStyles = ITweakStyles<
73
77
  typeof useStyles,
74
78
  {
75
- tweakControlGroup: IControlGroupStyles;
79
+ tweakWithMessages: IWithMessagesStyles;
76
80
  tweakControlWrapper: IControlWrapperStyles;
77
81
  }
78
82
  >;
@@ -2,21 +2,22 @@ import { ChangeEvent, FocusEvent, FormEvent, forwardRef, useState } from 'react'
2
2
  import clsx from 'clsx';
3
3
  import {
4
4
  addDataAttributes,
5
+ getTestId,
5
6
  isNotEmpty,
6
7
  isReactNodeNotEmpty,
7
8
  isStringNotEmpty,
8
9
  } from '@true-engineering/true-react-platform-helpers';
9
10
  import { useTweakStyles } from '../../hooks';
10
11
  import { ICommonProps } from '../../types';
11
- import { ControlGroup, IControlGroupProps } from '../ControlGroup';
12
12
  import { ControlWrapper, IControlWrapperProps } from '../ControlWrapper';
13
+ import { IWithMessagesProps, WithMessages } from '../WithMessages';
13
14
  import { ITextAreaHTMLBaseProps } from './types';
14
15
  import { controlWrapperStyles, ITextAreaStyles, useStyles } from './TextArea.styles';
15
16
 
16
17
  export interface ITextAreaProps
17
18
  extends ICommonProps<ITextAreaStyles>,
18
19
  Pick<IControlWrapperProps, 'label' | 'isInvalid' | 'isRequired' | 'isDisabled'>,
19
- Pick<IControlGroupProps, 'infoMessage' | 'errorMessage'>,
20
+ Pick<IWithMessagesProps, 'infoMessage' | 'errorMessage'>,
20
21
  Pick<ITextAreaHTMLBaseProps, 'name' | 'maxLength' | 'rows' | 'onPaste' | 'onFocus' | 'onBlur'> {
21
22
  value?: string;
22
23
  placeholder?: string;
@@ -73,9 +74,9 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
73
74
  ) => {
74
75
  const classes = useStyles({ theme: tweakStyles });
75
76
 
76
- const tweakControlGroupStyles = useTweakStyles({
77
+ const tweakWithMessagesStyles = useTweakStyles({
77
78
  tweakStyles,
78
- className: 'tweakControlGroup',
79
+ className: 'tweakWithMessages',
79
80
  currentComponentName: 'TextArea',
80
81
  });
81
82
 
@@ -92,7 +93,8 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
92
93
  // в hasValue нельзя использовать isStringNotEmpty из-за того что isStringNotEmpty делает trim
93
94
  const hasValue = value !== undefined && value !== '';
94
95
  const hasLabel = isReactNodeNotEmpty(label);
95
- const hasPlaceholder = (!hasLabel || hasFocus || shouldAlwaysShowPlaceholder) && isStringNotEmpty(placeholder);
96
+ const hasPlaceholder =
97
+ (!hasLabel || hasFocus || shouldAlwaysShowPlaceholder) && isStringNotEmpty(placeholder);
96
98
 
97
99
  const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
98
100
  onChange(event.currentTarget.value, event);
@@ -109,10 +111,11 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
109
111
  };
110
112
 
111
113
  return (
112
- <ControlGroup
114
+ <WithMessages
113
115
  errorMessage={errorMessage}
114
116
  infoMessage={infoMessage}
115
- tweakStyles={tweakControlGroupStyles}
117
+ tweakStyles={tweakWithMessagesStyles}
118
+ testId={getTestId(testId, 'wrapper')}
116
119
  >
117
120
  <ControlWrapper
118
121
  label={label}
@@ -121,10 +124,12 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
121
124
  isDisabled={isDisabled}
122
125
  hasValue={hasValue}
123
126
  isFullWidth
124
- testId={testId}
125
127
  {...controlProps}
126
128
  >
127
- <div {...(isAutoSized && { className: classes.autoSized, 'data-value': value })}>
129
+ <div
130
+ className={clsx(classes.wrapper, { [classes.autoSized]: isAutoSized })}
131
+ {...(isAutoSized && { 'data-value': value })}
132
+ >
128
133
  <textarea
129
134
  ref={ref}
130
135
  className={classes.textarea}
@@ -138,7 +143,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
138
143
  onFocus={handleFocus}
139
144
  onBlur={handleBlur}
140
145
  onChange={handleChange}
141
- {...addDataAttributes(data, testId, 'control')}
146
+ {...addDataAttributes(data, testId)}
142
147
  />
143
148
  </div>
144
149
  </ControlWrapper>
@@ -151,7 +156,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, ITextAreaProps>(
151
156
  {value.length} / {maxLength}
152
157
  </span>
153
158
  )}
154
- </ControlGroup>
159
+ </WithMessages>
155
160
  );
156
161
  },
157
162
  );
@@ -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,26 +1,26 @@
1
1
  import { doNothing } from '@true-engineering/true-react-platform-helpers';
2
2
  import { ComponentStory } from '@storybook/react';
3
3
  import { IInputProps, Input } from '../Input';
4
- import { ControlGroup } from './ControlGroup';
4
+ import { WithMessages } from './WithMessages';
5
5
 
6
6
  export default {
7
- title: 'ControlGroup',
8
- component: ControlGroup,
7
+ title: 'WithMessages',
8
+ component: WithMessages,
9
9
  };
10
10
 
11
- const Template: ComponentStory<typeof ControlGroup> = ({ direction, ...args }) => {
11
+ const Template: ComponentStory<typeof WithMessages> = ({ controlsDirection, ...args }) => {
12
12
  let groupPlacement: Array<IInputProps['groupPlacement']> = [];
13
- if (direction === 'horizontal') {
13
+ if (controlsDirection === 'horizontal') {
14
14
  groupPlacement = ['left', 'right'];
15
15
  }
16
- if (direction === 'vertical') {
16
+ if (controlsDirection === 'vertical') {
17
17
  groupPlacement = ['top', 'bottom'];
18
18
  }
19
19
  return (
20
- <ControlGroup {...args} direction={direction}>
20
+ <WithMessages {...args} controlsDirection={controlsDirection}>
21
21
  <Input value="value" groupPlacement={groupPlacement[0]} onChange={doNothing} />
22
22
  <Input value="value" groupPlacement={groupPlacement[1]} onChange={doNothing} />
23
- </ControlGroup>
23
+ </WithMessages>
24
24
  );
25
25
  };
26
26
 
@@ -29,11 +29,11 @@ export const Default = Template.bind({});
29
29
  Default.args = {
30
30
  infoMessage: 'Message Info',
31
31
  errorMessage: 'Error Text',
32
- direction: undefined,
32
+ controlsDirection: undefined,
33
33
  };
34
34
 
35
35
  Default.argTypes = {
36
- direction: {
36
+ controlsDirection: {
37
37
  options: [undefined, 'horizontal', 'vertical'],
38
38
  control: 'radio',
39
39
  },
@@ -1,10 +1,11 @@
1
1
  import { createThemedStyles, ITweakStyles } from '../../theme';
2
2
 
3
- export const useStyles = createThemedStyles('ControlGroup', {
4
- root: {
3
+ export const useStyles = createThemedStyles('WithMessages', {
4
+ withMessages: {
5
5
  display: 'flex',
6
6
  flexDirection: 'column',
7
7
  width: '100%',
8
+ position: 'relative',
8
9
  },
9
10
 
10
11
  error: {
@@ -43,4 +44,4 @@ export const useStyles = createThemedStyles('ControlGroup', {
43
44
  children: {},
44
45
  });
45
46
 
46
- export type IControlGroupStyles = ITweakStyles<typeof useStyles>;
47
+ export type IWithMessagesStyles = ITweakStyles<typeof useStyles>;
@@ -1,18 +1,22 @@
1
1
  import { forwardRef, ReactNode } from 'react';
2
2
  import clsx from 'clsx';
3
- import { addDataAttributes, addDataTestId, isReactNodeNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
+ import {
4
+ addDataAttributes,
5
+ addDataTestId,
6
+ isReactNodeNotEmpty,
7
+ } from '@true-engineering/true-react-platform-helpers';
4
8
  import { ICommonProps } from '../../types';
5
- import { IControlGroupStyles, useStyles } from './ControlGroup.styles';
9
+ import { IWithMessagesStyles, useStyles } from './WithMessages.styles';
6
10
 
7
- export interface IControlGroupProps extends ICommonProps<IControlGroupStyles> {
11
+ export interface IWithMessagesProps extends ICommonProps<IWithMessagesStyles> {
8
12
  children: ReactNode;
9
13
  infoMessage?: ReactNode;
10
14
  errorMessage?: ReactNode;
11
- direction?: 'horizontal' | 'vertical';
15
+ controlsDirection?: 'horizontal' | 'vertical';
12
16
  }
13
17
 
14
- export const ControlGroup = forwardRef<HTMLDivElement, IControlGroupProps>(
15
- ({ children, infoMessage, errorMessage, direction, tweakStyles, testId, data }, ref) => {
18
+ export const WithMessages = forwardRef<HTMLDivElement, IWithMessagesProps>(
19
+ ({ children, infoMessage, errorMessage, controlsDirection, tweakStyles, testId, data }, ref) => {
16
20
  const classes = useStyles({ theme: tweakStyles });
17
21
 
18
22
  const shouldShowError = isReactNodeNotEmpty(errorMessage);
@@ -21,15 +25,15 @@ export const ControlGroup = forwardRef<HTMLDivElement, IControlGroupProps>(
21
25
  return (
22
26
  <div
23
27
  ref={ref}
24
- className={classes.root}
28
+ className={classes.withMessages}
25
29
  data-invalid={shouldShowError ? true : undefined}
26
30
  {...addDataAttributes(data, testId)}
27
31
  >
28
32
  {isReactNodeNotEmpty(children) && (
29
33
  <div
30
34
  className={clsx(classes.children, {
31
- [classes.horizontal]: direction === 'horizontal',
32
- [classes.vertical]: direction === 'vertical',
35
+ [classes.horizontal]: controlsDirection === 'horizontal',
36
+ [classes.vertical]: controlsDirection === 'vertical',
33
37
  })}
34
38
  >
35
39
  {children}
@@ -0,0 +1,2 @@
1
+ export * from './WithMessages';
2
+ export type { IWithMessagesStyles } from './WithMessages.styles';
@@ -91,6 +91,7 @@ Default.args = {
91
91
  shouldRenderInBody: true,
92
92
  shouldHideOnScroll: false,
93
93
  shouldStopPropagation: true,
94
+ shouldShowArrow: false,
94
95
  };
95
96
 
96
97
  Default.parameters = {
@@ -20,6 +20,8 @@ export const useStyles = createThemedStyles('WithPopup', {
20
20
  outline: 'none',
21
21
  },
22
22
 
23
+ arrow: {},
24
+
23
25
  animationEnd: {},
24
26
 
25
27
  animationStart: {},
@@ -1,7 +1,6 @@
1
- import { FC, useState } from 'react';
1
+ import { FC, useRef, useState } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import {
4
- addDataTestId,
5
4
  applyAction,
6
5
  stopPropagation,
7
6
  addDataAttributes,
@@ -23,10 +22,18 @@ import {
23
22
  UseHoverProps,
24
23
  safePolygon,
25
24
  useFocus,
25
+ FloatingArrow,
26
+ arrow,
27
+ UseClickProps,
28
+ UseFocusProps,
29
+ UseDismissProps,
30
+ UseTransitionStatusProps,
26
31
  } from '@floating-ui/react';
27
- import { ICommonProps, IRenderNode } from '../../types';
32
+ import { ICommonProps, IDataAttributes, IRenderNode } from '../../types';
28
33
  import { DEFAULT_OFFSET } from './constants';
34
+ import { minWidthRelativeToTrigger } from './helpers';
29
35
  import {
36
+ IPopupArrowProps,
30
37
  IPopupEventType,
31
38
  IReferenceProps,
32
39
  IWithPopupChildrenProps,
@@ -43,11 +50,16 @@ export interface IWithPopupProps extends ICommonProps<IWithPopupStyles> {
43
50
  placement?: Placement;
44
51
  /** @default 'click' */
45
52
  eventType?: IPopupEventType;
46
- /** @default 0 */
47
- hoverDelay?: UseHoverProps['delay'];
53
+ /**
54
+ * @deprecated Используйте hoverOptions
55
+ * @default 0
56
+ */
57
+ hoverDelay?: number;
48
58
  /** @default 6 */
49
59
  popupOffset?: OffsetOptions;
50
- /** @default true */
60
+ arrowProps?: IPopupArrowProps;
61
+ popupData?: IDataAttributes;
62
+ /** @default true, if eventType === click */
51
63
  shouldStopPropagation?: boolean;
52
64
  /** @default false */
53
65
  shouldHideOnScroll?: boolean;
@@ -60,6 +72,16 @@ export interface IWithPopupProps extends ICommonProps<IWithPopupStyles> {
60
72
  canBeFlipped?: boolean;
61
73
  /** @default false */
62
74
  isDisabled?: boolean;
75
+ /** @default false */
76
+ shouldShowArrow?: boolean;
77
+ /** Должна ли минимальная ширина попапа быть равна ширине триггера
78
+ * @default false */
79
+ isMinWidthSameAsTrigger?: boolean;
80
+ hoverOptions?: UseHoverProps;
81
+ clickOptions?: UseClickProps;
82
+ focusOptions?: UseFocusProps;
83
+ dismissOptions?: UseDismissProps;
84
+ transitionOptions?: UseTransitionStatusProps;
63
85
  onToggle?: (isActive: boolean, event?: IWithPopupToggleEvent) => void;
64
86
  }
65
87
 
@@ -71,12 +93,21 @@ export const WithPopup: FC<IWithPopupProps> = ({
71
93
  placement = eventType === 'click' ? 'bottom-end' : 'top',
72
94
  hoverDelay = 0,
73
95
  popupOffset = DEFAULT_OFFSET,
74
- shouldStopPropagation = true,
96
+ arrowProps,
97
+ popupData,
98
+ shouldStopPropagation = eventType === 'click',
75
99
  shouldHideOnScroll = false,
76
100
  shouldRenderInBody = true,
77
101
  canBeFlipped = true,
78
102
  isTriggerWrapped = eventType === 'hover',
79
103
  isDisabled = false,
104
+ shouldShowArrow = false,
105
+ isMinWidthSameAsTrigger = false,
106
+ hoverOptions,
107
+ clickOptions,
108
+ focusOptions,
109
+ dismissOptions,
110
+ transitionOptions,
80
111
  tweakStyles,
81
112
  data,
82
113
  testId,
@@ -86,6 +117,8 @@ export const WithPopup: FC<IWithPopupProps> = ({
86
117
 
87
118
  const [isOpen, setIsOpen] = useState(false);
88
119
 
120
+ const arrowRef = useRef<SVGSVGElement>(null);
121
+
89
122
  const handleToggle = (isActive: boolean, event?: IWithPopupToggleEvent) => {
90
123
  event?.stopPropagation();
91
124
  if (!isDisabled) {
@@ -103,7 +136,9 @@ export const WithPopup: FC<IWithPopupProps> = ({
103
136
  middleware: [
104
137
  offset(popupOffset),
105
138
  canBeFlipped && flip({ fallbackAxisSideDirection: 'start' }),
139
+ isMinWidthSameAsTrigger && minWidthRelativeToTrigger,
106
140
  ...middlewares,
141
+ shouldShowArrow && arrow({ element: arrowRef }),
107
142
  ],
108
143
  whileElementsMounted: autoUpdate,
109
144
  placement,
@@ -112,22 +147,27 @@ export const WithPopup: FC<IWithPopupProps> = ({
112
147
 
113
148
  const hover = useHover(context, {
114
149
  enabled: eventType === 'hover',
115
- delay: typeof hoverDelay === 'number' ? { open: hoverDelay, close: 0 } : hoverDelay,
150
+ delay: { open: hoverDelay, close: 0 },
116
151
  handleClose: safePolygon(),
152
+ ...hoverOptions,
117
153
  });
118
154
 
119
- const focus = useFocus(context, { enabled: eventType === 'hover' });
155
+ const focus = useFocus(context, { enabled: eventType === 'hover', ...focusOptions });
120
156
 
121
- const click = useClick(context, { enabled: eventType === 'click' });
157
+ const click = useClick(context, { enabled: eventType === 'click', ...clickOptions });
122
158
 
123
159
  const dismiss = useDismiss(context, {
124
160
  enabled: eventType === 'click',
125
161
  ancestorScroll: shouldHideOnScroll,
162
+ ...dismissOptions,
126
163
  });
127
164
 
128
165
  const { getFloatingProps, getReferenceProps } = useInteractions([hover, click, focus, dismiss]);
129
166
 
130
- const { isMounted, status } = useTransitionStatus(context, { duration: { close: 500 } });
167
+ const { isMounted, status } = useTransitionStatus(context, {
168
+ duration: { close: 500 },
169
+ ...transitionOptions,
170
+ });
131
171
 
132
172
  const referenceProps: IReferenceProps = getReferenceProps({
133
173
  ref: refs.setReference,
@@ -155,8 +195,7 @@ export const WithPopup: FC<IWithPopupProps> = ({
155
195
  [classes.active]: isOpen,
156
196
  })}
157
197
  {...referenceProps}
158
- {...addDataTestId(testId)}
159
- {...addDataAttributes(data)}
198
+ {...addDataAttributes(data, testId)}
160
199
  >
161
200
  {triggerElement}
162
201
  </div>
@@ -165,16 +204,25 @@ export const WithPopup: FC<IWithPopupProps> = ({
165
204
  )}
166
205
  {isMounted && (
167
206
  <FloatingPortal
168
- root={!shouldRenderInBody ? (refs.reference.current as HTMLDivElement) : undefined}
207
+ root={shouldRenderInBody ? document.body : (refs.reference.current as HTMLElement)}
169
208
  >
170
209
  <div
210
+ ref={refs.setFloating}
171
211
  style={floatingStyles}
172
212
  className={classes.popup}
173
- ref={refs.setFloating}
174
213
  {...getFloatingProps()}
214
+ {...addDataAttributes(popupData, testId, 'popup')}
175
215
  >
176
216
  <div className={classes[`dropdown-${status}`]}>
177
- {applyAction(children, { onClose: handleClose })}
217
+ {shouldShowArrow && (
218
+ <FloatingArrow
219
+ {...arrowProps}
220
+ ref={arrowRef}
221
+ context={context}
222
+ className={classes.arrow}
223
+ />
224
+ )}
225
+ {applyAction(children, { floatingContext: context, onClose: handleClose })}
178
226
  </div>
179
227
  </div>
180
228
  </FloatingPortal>
@@ -0,0 +1,9 @@
1
+ import { Middleware, size } from '@floating-ui/react';
2
+
3
+ export const minWidthRelativeToTrigger: Middleware = size({
4
+ apply({ rects, elements }) {
5
+ Object.assign(elements.floating.style, {
6
+ minWidth: `${rects.reference.width}px`,
7
+ });
8
+ },
9
+ });
@@ -1,4 +1,5 @@
1
1
  import type { MouseEvent, KeyboardEvent } from 'react';
2
+ import type { FloatingArrowProps, UseFloatingReturn } from '@floating-ui/react';
2
3
  import type { IDataAttributesProps, IDomElementInteractions } from '../../types';
3
4
  import type { POPUP_EVENT_TYPES } from './constants';
4
5
 
@@ -21,5 +22,11 @@ export interface IWithPopupTriggerProps {
21
22
  }
22
23
 
23
24
  export interface IWithPopupChildrenProps {
25
+ floatingContext: UseFloatingReturn['context'];
24
26
  onClose: (event?: IWithPopupToggleEvent) => void;
25
27
  }
28
+
29
+ export type IPopupArrowProps = Pick<
30
+ FloatingArrowProps,
31
+ 'width' | 'height' | 'tipRadius' | 'staticOffset' | 'd' | 'stroke' | 'strokeWidth'
32
+ >;
@@ -1,6 +1,12 @@
1
1
  import { ITooltipStyles } from '../Tooltip';
2
2
  import { IWithPopupStyles } from '../WithPopup';
3
3
 
4
+ export const withPopupStyles: IWithPopupStyles = {
5
+ popup: {
6
+ zIndex: 9999,
7
+ },
8
+ };
9
+
4
10
  export interface IWithTooltipStyles {
5
11
  tweakWithPopup?: IWithPopupStyles;
6
12
  tweakTooltip?: ITooltipStyles;