@true-engineering/true-react-common-ui-kit 4.0.0-alpha71 → 4.0.0-alpha73
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/dist/components/ControlWrapper/types.d.ts +3 -2
- package/dist/components/DatePicker/components/DatePickerHeader/DatePickerHeader.d.ts +2 -1
- package/dist/components/DatePicker/components/DatePickerHeader/constants.d.ts +3 -0
- package/dist/components/FiltersPane/types.d.ts +7 -1
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/true-react-common-ui-kit.js +53 -29
- package/dist/true-react-common-ui-kit.js.map +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs +1 -1
- package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ControlWrapper/ControlWrapper.tsx +22 -8
- package/src/components/ControlWrapper/types.ts +3 -2
- package/src/components/DatePicker/DatePicker.stories.tsx +7 -18
- package/src/components/DatePicker/DatePicker.tsx +27 -4
- package/src/components/DatePicker/components/DatePickerHeader/DatePickerHeader.tsx +12 -14
- package/src/components/DatePicker/components/DatePickerHeader/constants.ts +5 -0
- package/src/components/FiltersPane/types.ts +8 -0
- package/src/components/Select/Select.stories.tsx +5 -0
- package/src/components/Select/Select.tsx +18 -16
package/package.json
CHANGED
|
@@ -70,6 +70,7 @@ export const ControlWrapper: FC<IControlWrapperProps> = ({
|
|
|
70
70
|
const classes = useStyles({ tweakStyles });
|
|
71
71
|
|
|
72
72
|
const [startControlsWidth, setStartControlsWidth] = useState<number>();
|
|
73
|
+
const [endControlsWidth, setEndControlsWidth] = useState<number>();
|
|
73
74
|
|
|
74
75
|
const startIcons = getArray(startIcon).map(convertToControlWrapperIcon);
|
|
75
76
|
const endIcons = getArray(endIcon).map(convertToControlWrapperIcon);
|
|
@@ -89,24 +90,41 @@ export const ControlWrapper: FC<IControlWrapperProps> = ({
|
|
|
89
90
|
onTargetChange: (target) => setStartControlsWidth(target.clientWidth),
|
|
90
91
|
});
|
|
91
92
|
|
|
93
|
+
const endControlsRef = useResizeRef({
|
|
94
|
+
onTargetChange: (target) => setEndControlsWidth(target.clientWidth),
|
|
95
|
+
});
|
|
96
|
+
|
|
92
97
|
const renderIconControl = (
|
|
93
|
-
{
|
|
98
|
+
{
|
|
99
|
+
key,
|
|
100
|
+
iconComponent,
|
|
101
|
+
onClick,
|
|
102
|
+
shouldResetSize = false,
|
|
103
|
+
isActiveIcon = isNotEmpty(onClick),
|
|
104
|
+
...iconContainerProps
|
|
105
|
+
}: IControlWrapperIcon,
|
|
94
106
|
iconType: 'start' | 'end' | 'clear',
|
|
95
107
|
index?: number,
|
|
96
108
|
) => (
|
|
97
109
|
<div
|
|
98
110
|
key={key ?? index}
|
|
99
111
|
className={clsx(classes.icon, classes[`${iconType}Icon`], {
|
|
100
|
-
[classes.activeIcon]: !isDisabled &&
|
|
112
|
+
[classes.activeIcon]: !isDisabled && isActiveIcon,
|
|
101
113
|
[classes.customIcon]: shouldResetSize,
|
|
102
114
|
})}
|
|
103
115
|
{...addClickHandler(onClick, !isDisabled)}
|
|
104
116
|
{...addDataTestId(testId, `${iconType}-icon`)}
|
|
117
|
+
{...iconContainerProps}
|
|
105
118
|
>
|
|
106
119
|
<div className={classes.iconInner}>{renderIcon(iconComponent)}</div>
|
|
107
120
|
</div>
|
|
108
121
|
);
|
|
109
122
|
|
|
123
|
+
const style = {
|
|
124
|
+
'--start-controls-width': hasStartIcons ? `${startControlsWidth}px` : undefined,
|
|
125
|
+
'--end-controls-width': hasEndControls ? `${endControlsWidth}px` : undefined,
|
|
126
|
+
} as CSSProperties;
|
|
127
|
+
|
|
110
128
|
return (
|
|
111
129
|
<div
|
|
112
130
|
className={clsx(
|
|
@@ -123,11 +141,7 @@ export const ControlWrapper: FC<IControlWrapperProps> = ({
|
|
|
123
141
|
[classes.withStartControls]: hasStartIcons,
|
|
124
142
|
},
|
|
125
143
|
)}
|
|
126
|
-
style={
|
|
127
|
-
hasStartIcons
|
|
128
|
-
? ({ '--start-controls-width': `${startControlsWidth}px` } as CSSProperties)
|
|
129
|
-
: undefined
|
|
130
|
-
}
|
|
144
|
+
style={style}
|
|
131
145
|
{...addDataAttributes(data, testId)}
|
|
132
146
|
>
|
|
133
147
|
{isReactNodeNotEmpty(label) && (
|
|
@@ -151,7 +165,7 @@ export const ControlWrapper: FC<IControlWrapperProps> = ({
|
|
|
151
165
|
{children}
|
|
152
166
|
|
|
153
167
|
{hasEndControls && (
|
|
154
|
-
<div className={classes.controls}>
|
|
168
|
+
<div className={classes.controls} ref={endControlsRef}>
|
|
155
169
|
{hasClearButton &&
|
|
156
170
|
renderIconControl({ iconComponent: 'close', onClick: onClear }, 'clear')}
|
|
157
171
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Key } from 'react';
|
|
1
|
+
import { HTMLAttributes, type Key } from 'react';
|
|
2
2
|
import { IClickHandlerEvent } from '../../types';
|
|
3
3
|
import { IIcon } from '../Icon';
|
|
4
4
|
import { GROUP_PLACEMENTS } from './constants';
|
|
@@ -11,9 +11,10 @@ export type IControlWrapperSize = keyof IControlWrapperSizes;
|
|
|
11
11
|
export type IGroupPlacement = (typeof GROUP_PLACEMENTS)[number];
|
|
12
12
|
|
|
13
13
|
// подумать над extend HTMLAttributes<HTMLDivElement>
|
|
14
|
-
export interface IControlWrapperIcon {
|
|
14
|
+
export interface IControlWrapperIcon extends Pick<HTMLAttributes<HTMLDivElement>, 'tabIndex'> {
|
|
15
15
|
key?: Key;
|
|
16
16
|
iconComponent: IIcon;
|
|
17
17
|
onClick?: (event: IClickHandlerEvent) => void;
|
|
18
18
|
shouldResetSize?: boolean;
|
|
19
|
+
isActiveIcon?: boolean;
|
|
19
20
|
}
|
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
import { FC, useState } from 'react';
|
|
2
|
-
import { ru } from 'date-fns/locale';
|
|
3
2
|
import { type Meta } from '@storybook/react';
|
|
4
3
|
import { DatePicker, IDatePickerProps } from './DatePicker';
|
|
5
4
|
|
|
6
|
-
const months = [
|
|
7
|
-
'Январь',
|
|
8
|
-
'Февраль',
|
|
9
|
-
'Март',
|
|
10
|
-
'Апрель',
|
|
11
|
-
'Май',
|
|
12
|
-
'Июнь',
|
|
13
|
-
'Июль',
|
|
14
|
-
'Август',
|
|
15
|
-
'Сентябрь',
|
|
16
|
-
'Октябрь',
|
|
17
|
-
'Ноябрь',
|
|
18
|
-
'Декабрь',
|
|
19
|
-
];
|
|
20
|
-
|
|
21
5
|
const Story: FC<IDatePickerProps> = (args) => {
|
|
22
6
|
const [date, setDate] = useState<Date | null>(null);
|
|
23
7
|
|
|
@@ -36,9 +20,7 @@ const Story: FC<IDatePickerProps> = (args) => {
|
|
|
36
20
|
<div style={{ display: 'flex', height: 48 }}>
|
|
37
21
|
<DatePicker
|
|
38
22
|
{...args}
|
|
39
|
-
locale={ru}
|
|
40
23
|
testId="datepicker"
|
|
41
|
-
months={months}
|
|
42
24
|
selectedDate={date}
|
|
43
25
|
minDate={new Date()}
|
|
44
26
|
onChangeDate={setDate}
|
|
@@ -57,6 +39,13 @@ const meta: Meta<typeof Story> = {
|
|
|
57
39
|
isClearable: true,
|
|
58
40
|
shouldRenderPopperInBody: false,
|
|
59
41
|
popperPlacement: 'bottom-start',
|
|
42
|
+
locale: 'ru',
|
|
43
|
+
},
|
|
44
|
+
argTypes: {
|
|
45
|
+
locale: {
|
|
46
|
+
control: 'inline-radio',
|
|
47
|
+
options: ['ru', 'en'],
|
|
48
|
+
},
|
|
60
49
|
},
|
|
61
50
|
};
|
|
62
51
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
FocusEvent,
|
|
4
4
|
SyntheticEvent,
|
|
5
5
|
forwardRef,
|
|
6
|
+
useCallback,
|
|
6
7
|
useEffect,
|
|
7
8
|
useMemo,
|
|
8
9
|
useRef,
|
|
@@ -11,9 +12,10 @@ import {
|
|
|
11
12
|
import type ReactDatePicker from 'react-datepicker';
|
|
12
13
|
import 'react-datepicker/dist/react-datepicker.css';
|
|
13
14
|
import clsx from 'clsx';
|
|
14
|
-
import { isAfter, isBefore, isValid } from 'date-fns';
|
|
15
|
+
import { isAfter, isBefore, isValid, Month } from 'date-fns';
|
|
15
16
|
import {
|
|
16
17
|
addDataAttributes,
|
|
18
|
+
getArray,
|
|
17
19
|
isEmpty,
|
|
18
20
|
isNotEmpty,
|
|
19
21
|
isString,
|
|
@@ -140,13 +142,18 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(function
|
|
|
140
142
|
? isStringNotEmpty(startDateValue) || isStringNotEmpty(endDateValue)
|
|
141
143
|
: isStringNotEmpty(dateValue);
|
|
142
144
|
|
|
145
|
+
const endIcon =
|
|
146
|
+
isClearable && hasDateInputValue
|
|
147
|
+
? inputProps.endIcon
|
|
148
|
+
: getArray(inputProps.endIcon).concat('calendar');
|
|
149
|
+
|
|
143
150
|
const dateInputProps: IDateInputProps = {
|
|
144
151
|
...inputProps,
|
|
145
152
|
isRange,
|
|
146
153
|
isDisabled,
|
|
147
154
|
isClearable,
|
|
148
155
|
isActive: isOpen,
|
|
149
|
-
|
|
156
|
+
endIcon,
|
|
150
157
|
tweakStyles: tweakDateInputStyles,
|
|
151
158
|
...(isRange ? { startDate: startDateValue, endDate: endDateValue } : { date: dateValue }),
|
|
152
159
|
};
|
|
@@ -267,13 +274,26 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(function
|
|
|
267
274
|
OUTSIDE_CLICK_IGNORE_CLASS,
|
|
268
275
|
);
|
|
269
276
|
|
|
277
|
+
const resolvedLocale = isString(locale) ? LocalesMap[locale] : locale;
|
|
278
|
+
|
|
279
|
+
const getLocalizedMonth = useCallback(
|
|
280
|
+
(month: Month) => {
|
|
281
|
+
if (isNotEmpty(months?.[month])) {
|
|
282
|
+
return months[month];
|
|
283
|
+
}
|
|
284
|
+
const localizedMonth = resolvedLocale.localize.month(month);
|
|
285
|
+
return localizedMonth.charAt(0).toUpperCase().concat(localizedMonth.slice(1));
|
|
286
|
+
},
|
|
287
|
+
[resolvedLocale, months],
|
|
288
|
+
);
|
|
289
|
+
|
|
270
290
|
return (
|
|
271
291
|
<div className={classes.root} {...addDataAttributes(data)}>
|
|
272
292
|
<DatePickerBase
|
|
273
293
|
ref={componentRef}
|
|
274
294
|
minDate={minDate}
|
|
275
295
|
maxDate={maxDate}
|
|
276
|
-
locale={
|
|
296
|
+
locale={resolvedLocale}
|
|
277
297
|
dateFormat={dateFormat}
|
|
278
298
|
placeholderText={placeholder}
|
|
279
299
|
calendarStartDay={calendarStartDay}
|
|
@@ -302,7 +322,10 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(function
|
|
|
302
322
|
customInput={<CustomInput {...dateInputProps} />}
|
|
303
323
|
renderDayContents={(day) => <div className={classes.dayInner}>{day}</div>}
|
|
304
324
|
renderCustomHeader={
|
|
305
|
-
renderCustomHeader ??
|
|
325
|
+
renderCustomHeader ??
|
|
326
|
+
((baseProps) => (
|
|
327
|
+
<DatePickerHeader {...baseProps} getMonthSelectString={getLocalizedMonth} />
|
|
328
|
+
))
|
|
306
329
|
}
|
|
307
330
|
todayButton={todayButton}
|
|
308
331
|
highlightDates={highlightDates}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { FC
|
|
1
|
+
import { FC } from 'react';
|
|
2
2
|
import { ReactDatePickerCustomHeaderProps as BaseProps } from 'react-datepicker';
|
|
3
|
-
import { getMonth, getYear } from 'date-fns';
|
|
3
|
+
import { getMonth, getYear, Month } from 'date-fns';
|
|
4
|
+
import { isNotEmpty } from '@true-engineering/true-react-platform-helpers';
|
|
4
5
|
import { useTweakStyles } from '../../../../hooks';
|
|
5
6
|
import { ITweakStylesProps } from '../../../../types';
|
|
6
7
|
import { Icon } from '../../../Icon';
|
|
7
8
|
import { Select } from '../../../Select';
|
|
9
|
+
import { MONTH_SELECT_OPTIONS, YEARS_SELECT_OPTIONS } from './constants';
|
|
8
10
|
import { IDatePickerHeaderStyles, selectStyles, useStyles } from './DatePickerHeader.styles';
|
|
9
11
|
|
|
10
12
|
export interface IDatePickerHeaderProps
|
|
11
13
|
extends BaseProps,
|
|
12
14
|
ITweakStylesProps<IDatePickerHeaderStyles> {
|
|
13
|
-
|
|
15
|
+
getMonthSelectString: (value: Month) => string;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const DatePickerHeader: FC<IDatePickerHeaderProps> = ({
|
|
17
19
|
date,
|
|
18
|
-
|
|
20
|
+
getMonthSelectString,
|
|
19
21
|
tweakStyles,
|
|
20
22
|
prevMonthButtonDisabled,
|
|
21
23
|
nextMonthButtonDisabled,
|
|
@@ -33,28 +35,24 @@ export const DatePickerHeader: FC<IDatePickerHeaderProps> = ({
|
|
|
33
35
|
currentComponentName: 'DatePickerHeader',
|
|
34
36
|
});
|
|
35
37
|
|
|
36
|
-
const years = useMemo(
|
|
37
|
-
() => Array.from(Array(41)).map((_, i) => getYear(new Date()) - 30 + i),
|
|
38
|
-
[],
|
|
39
|
-
);
|
|
40
|
-
|
|
41
38
|
return (
|
|
42
39
|
<div className={classes.header}>
|
|
43
40
|
<Select
|
|
44
|
-
value={
|
|
45
|
-
options={
|
|
41
|
+
value={getMonth(date) as Month}
|
|
42
|
+
options={MONTH_SELECT_OPTIONS}
|
|
43
|
+
convertValueToString={getMonthSelectString}
|
|
46
44
|
dropdownIcon="chevron-down-small"
|
|
47
45
|
isAutoSized
|
|
48
46
|
tweakStyles={tweakSelectStyles}
|
|
49
|
-
onChange={(value) =>
|
|
47
|
+
onChange={(value) => isNotEmpty(value) && changeMonth(value)}
|
|
50
48
|
/>
|
|
51
49
|
<Select
|
|
52
50
|
value={getYear(date)}
|
|
53
|
-
options={
|
|
51
|
+
options={YEARS_SELECT_OPTIONS}
|
|
54
52
|
dropdownIcon="chevron-down-small"
|
|
55
53
|
isAutoSized
|
|
56
54
|
tweakStyles={tweakSelectStyles}
|
|
57
|
-
onChange={(value) =>
|
|
55
|
+
onChange={(value) => isNotEmpty(value) && changeYear(value)}
|
|
58
56
|
/>
|
|
59
57
|
|
|
60
58
|
<div className={classes.buttons}>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { getYear, Month } from 'date-fns';
|
|
2
|
+
import { indexMap } from '@true-engineering/true-react-platform-helpers';
|
|
3
|
+
|
|
4
|
+
export const MONTH_SELECT_OPTIONS = indexMap(12, (idx) => idx as Month);
|
|
5
|
+
export const YEARS_SELECT_OPTIONS = indexMap(41, (idx) => getYear(new Date()) - 30 + idx);
|
|
@@ -117,6 +117,13 @@ export interface ICustomRangeConfigItem<Value> extends IConfigItemBasicBase<Valu
|
|
|
117
117
|
valueViewType?: 'range';
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
export interface ICustomSingularConfigItem<Value> extends IConfigItemBasicBase<Value> {
|
|
121
|
+
[key: string & {}]: any;
|
|
122
|
+
type: 'custom';
|
|
123
|
+
component: CustomComponent<Value>;
|
|
124
|
+
getSelectedValue?: (v: Value) => ReactNode;
|
|
125
|
+
}
|
|
126
|
+
|
|
120
127
|
export interface ICustomMultipleConfigItem<Value> extends IConfigItemBasicBase<Value> {
|
|
121
128
|
[key: string & {}]: any;
|
|
122
129
|
type: 'custom';
|
|
@@ -127,6 +134,7 @@ export interface ICustomMultipleConfigItem<Value> extends IConfigItemBasicBase<V
|
|
|
127
134
|
|
|
128
135
|
export type ICustomConfigItem<Value> =
|
|
129
136
|
| ICustomRangeConfigItem<Value>
|
|
137
|
+
| ICustomSingularConfigItem<Value>
|
|
130
138
|
| ICustomMultipleConfigItem<Value>;
|
|
131
139
|
|
|
132
140
|
export type ConfigItem<Value> =
|
|
@@ -193,6 +193,7 @@ const meta: Meta<typeof Story> = {
|
|
|
193
193
|
isClearable: false,
|
|
194
194
|
isLoading: false,
|
|
195
195
|
debounceTime: 400,
|
|
196
|
+
minSymbolsCountToOpenList: 0,
|
|
196
197
|
endIcon: undefined,
|
|
197
198
|
// custom options
|
|
198
199
|
shouldUseReactNodes: false,
|
|
@@ -222,6 +223,10 @@ const meta: Meta<typeof Story> = {
|
|
|
222
223
|
options: ['strings', 'objects'],
|
|
223
224
|
},
|
|
224
225
|
endIcon: { control: 'select', options: icons },
|
|
226
|
+
size: {
|
|
227
|
+
control: 'inline-radio',
|
|
228
|
+
options: [undefined, 'micro'],
|
|
229
|
+
},
|
|
225
230
|
},
|
|
226
231
|
};
|
|
227
232
|
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { hasExactParent } from '../../helpers';
|
|
29
29
|
import { useDropdown, useIsMounted, useOnClickOutsideWithRef, useTweakStyles } from '../../hooks';
|
|
30
30
|
import { IDropdownWithPopperOptions, IReplaceTweakStylesProps } from '../../types';
|
|
31
|
+
import { IControlWrapperIcon } from '../ControlWrapper';
|
|
31
32
|
import { IIcon, renderIcon } from '../Icon';
|
|
32
33
|
import { IChangeInputEvent, IInputProps, InputBase } from '../Input';
|
|
33
34
|
import { ISearchInputProps, SearchInput } from '../SearchInput';
|
|
@@ -62,7 +63,7 @@ export interface ISelectProps<Value>
|
|
|
62
63
|
minSymbolsCountToOpenList?: number;
|
|
63
64
|
dropdownOptions?: IDropdownWithPopperOptions;
|
|
64
65
|
/** @default 'chevron-down' */
|
|
65
|
-
dropdownIcon?: IIcon;
|
|
66
|
+
dropdownIcon?: IIcon | null;
|
|
66
67
|
options: Value[] | readonly Value[];
|
|
67
68
|
value: Value | undefined;
|
|
68
69
|
/** @default true */
|
|
@@ -611,28 +612,29 @@ export function Select<Value>(
|
|
|
611
612
|
isLoading={areOptionsLoading}
|
|
612
613
|
tweakStyles={tweakInputStyles}
|
|
613
614
|
testId={testId}
|
|
614
|
-
|
|
615
|
+
endIcon={[
|
|
615
616
|
isMultiSelect && shouldShowMultiSelectCounter
|
|
616
|
-
? {
|
|
617
|
+
? ({
|
|
617
618
|
key: 'counter',
|
|
618
|
-
iconComponent: (
|
|
619
|
-
<div key="counter" className={classes.counter}>
|
|
620
|
-
(+{value.length - 1})
|
|
621
|
-
</div>
|
|
622
|
-
),
|
|
619
|
+
iconComponent: <div className={classes.counter}>(+{value.length - 1})</div>,
|
|
623
620
|
shouldResetSize: true,
|
|
624
|
-
}
|
|
621
|
+
} satisfies IControlWrapperIcon)
|
|
625
622
|
: undefined,
|
|
626
623
|
|
|
627
624
|
...getArray(endIcon),
|
|
628
625
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
626
|
+
isNotEmpty(dropdownIcon)
|
|
627
|
+
? ({
|
|
628
|
+
key: 'arrow',
|
|
629
|
+
onClick: onArrowClick,
|
|
630
|
+
tabIndex: undefined,
|
|
631
|
+
iconComponent: (
|
|
632
|
+
<div className={clsx(classes.arrow, { [classes.activeArrow]: isOpen })}>
|
|
633
|
+
{renderIcon(dropdownIcon)}
|
|
634
|
+
</div>
|
|
635
|
+
),
|
|
636
|
+
} satisfies IControlWrapperIcon)
|
|
637
|
+
: undefined,
|
|
636
638
|
].filter(isNotEmpty)}
|
|
637
639
|
{...inputProps}
|
|
638
640
|
/>
|