@true-engineering/true-react-common-ui-kit 3.24.0 → 3.25.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-engineering/true-react-common-ui-kit",
3
- "version": "3.24.0",
3
+ "version": "3.25.0",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@floating-ui/react": "0.26.12",
46
- "@true-engineering/true-react-platform-helpers": "0.1.0",
46
+ "@true-engineering/true-react-platform-helpers": "0.2.2",
47
47
  "clsx": "1.2.1",
48
48
  "country-flag-icons": "1.5.5",
49
49
  "date-fns": "2.29.3",
@@ -1,11 +1,6 @@
1
1
  import { ReactNode, useState, memo, MouseEvent } from 'react';
2
2
  import clsx from 'clsx';
3
- import {
4
- isEmpty,
5
- isFunction,
6
- isNotEmpty,
7
- isReactNodeNotEmpty,
8
- } from '@true-engineering/true-react-platform-helpers';
3
+ import { applyAction, isEmpty, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
9
4
  import { addDataAttributes } from '../../../../helpers';
10
5
  import { useTweakStyles } from '../../../../hooks';
11
6
  import { ICommonProps, IDataAttributes } from '../../../../types';
@@ -68,19 +63,9 @@ function FlexibleTableRowInner<Row extends ITableRow>({
68
63
  });
69
64
 
70
65
  const [isFocused, setFocused] = useState(false);
71
- const [nestedComponent, setNestedComponent] = useState<INestedComponent>(() => {
72
- const isOpen = isFunction(isExpandableRowComponentInitiallyOpen)
73
- ? isExpandableRowComponentInitiallyOpen(item, index)
74
- : isExpandableRowComponentInitiallyOpen;
75
-
76
- const component = isOpen
77
- ? expandableRowComponent?.(item, true, () => {
78
- setNestedComponent({ isOpen: false });
79
- })
80
- : undefined;
81
-
82
- return isReactNodeNotEmpty(component) ? { isOpen: true, component } : { isOpen: false };
83
- });
66
+ const [nestedComponent, setNestedComponent] = useState<INestedComponent>(() => ({
67
+ isOpen: applyAction(isExpandableRowComponentInitiallyOpen, item, index),
68
+ }));
84
69
 
85
70
  const isActive = activeRows?.includes(index) ?? false;
86
71
  const isEditable = !isLoading && (isNotEmpty(onRowClick) || isNotEmpty(onRowHover));
@@ -108,33 +93,30 @@ function FlexibleTableRowInner<Row extends ITableRow>({
108
93
  setFocused(false);
109
94
  };
110
95
 
111
- const updateNestedComponent = (component?: ReactNode, cellKey?: string) => {
112
- setNestedComponent(
113
- component === undefined ? { isOpen: false } : { isOpen: true, component, cellKey },
114
- );
115
- };
116
-
117
96
  const closeNestedComponent = () => {
118
97
  setNestedComponent({ isOpen: false });
119
98
  };
120
99
 
100
+ const updateNestedComponent = (component: ReactNode, cellKey: string) => {
101
+ if (isEmpty(component)) {
102
+ closeNestedComponent();
103
+ } else {
104
+ setNestedComponent({ isOpen: true, component, cellKey });
105
+ }
106
+ };
107
+
121
108
  const handleRowClick = () => {
122
109
  if (isNotEmpty(uniqueField)) {
123
110
  onRowClick?.(item[uniqueField]);
124
111
  }
112
+ if (isEmpty(expandableRowComponent)) {
113
+ return;
114
+ }
125
115
 
126
- if (isNotEmpty(expandableRowComponent)) {
127
- const newNestedComponent = expandableRowComponent(item, true, closeNestedComponent);
128
-
129
- if (!isNestedComponentExpanded && newNestedComponent !== null) {
130
- updateNestedComponent(newNestedComponent);
131
- return;
132
- }
133
-
134
- if (isNestedComponentExpanded && isEmpty(nestedComponentCellKey)) {
135
- closeNestedComponent();
136
- return;
137
- }
116
+ if (!isNestedComponentExpanded) {
117
+ setNestedComponent({ isOpen: true });
118
+ } else if (isEmpty(nestedComponentCellKey)) {
119
+ closeNestedComponent();
138
120
  }
139
121
  };
140
122
 
@@ -185,7 +167,8 @@ function FlexibleTableRowInner<Row extends ITableRow>({
185
167
  {isNestedComponentExpanded && (
186
168
  <TableRow className={classes.root}>
187
169
  <TableCell className={classes.nestedComponent} colSpan={columns.length}>
188
- {nestedComponent.component}
170
+ {nestedComponent.component ??
171
+ expandableRowComponent?.(item, true, closeNestedComponent)}
189
172
  </TableCell>
190
173
  </TableRow>
191
174
  )}
@@ -7,7 +7,7 @@ import { Input, type IInputStyles } from '../Input';
7
7
  import { TextButton } from '../TextButton';
8
8
  import { Select, type ISelectProps } from './Select';
9
9
 
10
- interface ISelectWithCustomProps<T> extends ISelectProps<T> {
10
+ interface ISelectWithCustomProps<Option> extends ISelectProps<Option> {
11
11
  shouldUsePopper?: boolean;
12
12
  shouldRenderInBody?: boolean;
13
13
  shouldHideOnScroll?: boolean;
@@ -25,14 +25,10 @@ const useSelectStyles = createUseStyles({
25
25
  },
26
26
  });
27
27
 
28
- const useDefaultOptionStyles = createUseStyles({
29
- customDefaultOption: {
30
- width: '100%',
28
+ const useCustomListHeaderStyles = createUseStyles({
29
+ customListHeader: {
31
30
  padding: [10, 20],
32
- justifySelf: 'stretch',
33
- alignSelf: 'stretch',
34
- backgroundColor: '#ffffff',
35
- cursor: 'default',
31
+ boxSizing: 'border-box',
36
32
  },
37
33
 
38
34
  defaultView: {
@@ -47,6 +43,16 @@ const useDefaultOptionStyles = createUseStyles({
47
43
  },
48
44
  });
49
45
 
46
+ const useCustomListFooterStyles = createUseStyles({
47
+ customListFooter: {
48
+ gap: 10,
49
+ padding: [6, 20],
50
+ boxSizing: 'border-box',
51
+ display: 'flex',
52
+ justifyContent: 'center',
53
+ },
54
+ });
55
+
50
56
  const inputTweakStyles: IInputStyles = {
51
57
  inputWrapper: {
52
58
  height: 24,
@@ -67,12 +73,12 @@ const buttonTweakStyles: IButtonStyles = {
67
73
  },
68
74
  };
69
75
 
70
- interface ICustomDefaultOptionProps {
76
+ interface ICustomListHeaderProps {
71
77
  onAdd?: (option?: string) => void;
72
78
  }
73
79
 
74
- function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
75
- const classes = useDefaultOptionStyles();
80
+ function CustomListHeader({ onAdd }: ICustomListHeaderProps) {
81
+ const classes = useCustomListHeaderStyles();
76
82
 
77
83
  const [isAdding, setIsAdding] = useState(false);
78
84
  const [inputValue, setInputValue] = useState('');
@@ -84,7 +90,7 @@ function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
84
90
  };
85
91
 
86
92
  return (
87
- <div className={classes.customDefaultOption} onClick={(event) => event.stopPropagation()}>
93
+ <div className={classes.customListHeader} onClick={(event) => event.stopPropagation()}>
88
94
  {!isAdding && (
89
95
  <div className={classes.defaultView}>
90
96
  <TextButton icon="plus" hasCircleUnderIcon isBold onClick={() => setIsAdding(true)}>
@@ -109,6 +115,26 @@ function CustomDefaultOption({ onAdd }: ICustomDefaultOptionProps) {
109
115
  );
110
116
  }
111
117
 
118
+ interface ICustomListFooterProps {
119
+ onReset?: () => void;
120
+ onClear?: () => void;
121
+ }
122
+
123
+ function CustomListFooter({ onReset, onClear }: ICustomListFooterProps) {
124
+ const classes = useCustomListFooterStyles();
125
+
126
+ return (
127
+ <div className={classes.customListFooter}>
128
+ <Button view="secondary" size="s" onClick={onReset}>
129
+ Reset
130
+ </Button>
131
+ <Button view="secondary" size="s" onClick={onClear}>
132
+ Clear
133
+ </Button>
134
+ </div>
135
+ );
136
+ }
137
+
112
138
  function SelectWithCustomProps({
113
139
  noMatchesLabel,
114
140
  shouldUsePopper,
@@ -122,13 +148,22 @@ function SelectWithCustomProps({
122
148
  const [inputValue, setInputValue] = useState<string>();
123
149
  const [options, setOptions] = useState<string[]>([]);
124
150
 
125
- const onAdd = (option?: string) => {
151
+ const handleAdd = (option?: string) => {
126
152
  if (isStringNotEmpty(option)) {
127
153
  setOptions((prevOptions) => [...prevOptions, option]);
128
154
  }
129
155
  };
130
156
 
131
- const onRemove = (option: string) => {
157
+ const handleReset = () => {
158
+ setInputValue(undefined);
159
+ };
160
+
161
+ const handleClear = () => {
162
+ setInputValue(undefined);
163
+ setOptions([]);
164
+ };
165
+
166
+ const handleRemove = (option: string) => {
132
167
  setOptions((prevOptions) => prevOptions.filter((entry) => entry !== option));
133
168
  };
134
169
 
@@ -142,7 +177,7 @@ function SelectWithCustomProps({
142
177
  onClick={(event) => {
143
178
  event.stopPropagation();
144
179
 
145
- onRemove(value);
180
+ handleRemove(value);
146
181
  }}
147
182
  />
148
183
  </div>
@@ -159,7 +194,8 @@ function SelectWithCustomProps({
159
194
  return (
160
195
  <Select
161
196
  {...props}
162
- defaultOptionLabel={<CustomDefaultOption onAdd={onAdd} />}
197
+ header={<CustomListHeader onAdd={handleAdd} />}
198
+ footer={<CustomListFooter onReset={handleReset} onClear={handleClear} />}
163
199
  noMatchesLabel={isStringNotEmpty(noMatchesLabel) ? noMatchesLabel : undefined}
164
200
  tweakStyles={{
165
201
  tweakSelectList: {
@@ -1,7 +1,7 @@
1
1
  import { ReactNode, useEffect, useState } from 'react';
2
2
  import { isStringNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
3
  import { ComponentMeta, ComponentStory } from '@storybook/react';
4
- import { Select, ISelectProps } from './Select';
4
+ import { Select, ISelectProps, IMultipleSelectProps } from './Select';
5
5
 
6
6
  interface ObjectValue {
7
7
  name: string;
@@ -60,7 +60,7 @@ const objectOptions: ObjectValue[] = [
60
60
  const getRandomInt = (min: number, max: number) =>
61
61
  Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min))) + Math.ceil(min);
62
62
 
63
- interface ISelectWithCustomProps<T> extends ISelectProps<T> {
63
+ interface ISelectWithCustomProps<T> extends IMultipleSelectProps<T> {
64
64
  valuesType: 'strings' | 'objects';
65
65
  shouldUseReactNodes?: boolean;
66
66
  shouldUsePopper?: boolean;
@@ -160,7 +160,7 @@ function SelectWithCustomProps<T>({
160
160
  return (
161
161
  <Select
162
162
  {...rest}
163
- {...(props as unknown as ISelectProps<any>)}
163
+ {...(props as unknown as IMultipleSelectProps<any>)}
164
164
  {...(shouldRenderSearchInputInList && {
165
165
  searchInput: { shouldRenderInList: true },
166
166
  })}
@@ -1,8 +1,8 @@
1
1
  import { mergeStyles } from '@true-engineering/true-react-platform-helpers';
2
- import { ITweakStyles, createThemedStyles } from '../../theme';
3
- import { IInputStyles } from '../Input';
4
- import { ISearchInputStyles } from '../SearchInput';
5
- import { ISelectListStyles } from './components';
2
+ import { animations, createThemedStyles, type ITweakStyles } from '../../theme';
3
+ import { type IInputStyles } from '../Input';
4
+ import { type ISearchInputStyles } from '../SearchInput';
5
+ import { type ISelectListStyles } from './components';
6
6
 
7
7
  export const useStyles = createThemedStyles('Select', {
8
8
  root: {
@@ -26,7 +26,7 @@ export const useStyles = createThemedStyles('Select', {
26
26
 
27
27
  withoutPopper: {
28
28
  position: 'absolute',
29
- top: 'calc(100% + 6px)',
29
+ top: 'calc(var(--dropdown-offset, 100%) + 6px)',
30
30
  },
31
31
 
32
32
  listWrapperInBody: {
@@ -43,7 +43,8 @@ export const useStyles = createThemedStyles('Select', {
43
43
  height: 20,
44
44
  cursor: 'pointer',
45
45
  zIndex: 1,
46
- transition: 'transform 0.1s ease',
46
+ transition: animations.defaultTransition,
47
+ transitionProperty: 'transform',
47
48
  },
48
49
 
49
50
  activeArrow: {
@@ -111,7 +112,7 @@ export const getInputStyles = ({
111
112
  isMultiSelect,
112
113
  }: {
113
114
  hasReadonlyInput: boolean;
114
- isMultiSelect: boolean;
115
+ isMultiSelect?: boolean;
115
116
  }): IInputStyles => {
116
117
  if (hasReadonlyInput && isMultiSelect) {
117
118
  return readonlyMultiSelectStyles;