@steroidsjs/core 3.0.0-beta.81 → 3.0.0-beta.83

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 (53) hide show
  1. package/docs-autogen-result.json +398 -43
  2. package/en.json +24 -2
  3. package/hooks/index.d.ts +2 -1
  4. package/hooks/index.js +3 -1
  5. package/hooks/useAbsolutePositioning.d.ts +56 -7
  6. package/hooks/useAbsolutePositioning.js +39 -131
  7. package/hooks/useApplication.js +2 -0
  8. package/hooks/useDataSelect.js +3 -0
  9. package/hooks/useList.d.ts +10 -4
  10. package/hooks/useList.js +59 -14
  11. package/hooks/useSaveCursorPosition.d.ts +6 -0
  12. package/hooks/useSaveCursorPosition.js +29 -0
  13. package/package.json +1 -1
  14. package/ui/content/Chart/Chart.d.ts +37 -0
  15. package/ui/content/Chart/Chart.js +19 -0
  16. package/ui/content/Chart/index.d.ts +2 -0
  17. package/ui/content/Chart/index.js +7 -0
  18. package/ui/content/DropDown/DropDown.d.ts +1 -1
  19. package/ui/content/DropDown/DropDown.js +1 -1
  20. package/ui/content/index.d.ts +2 -1
  21. package/ui/content/index.js +3 -1
  22. package/ui/form/CheckboxListField/CheckboxListField.d.ts +1 -1
  23. package/ui/form/CheckboxListField/CheckboxListField.js +1 -1
  24. package/ui/form/DateField/useDateRange.d.ts +2 -1
  25. package/ui/form/DateField/useDateRange.js +23 -0
  26. package/ui/form/DateRangeField/DateRangeField.js +3 -1
  27. package/ui/form/DateTimeRangeField/DateTimeRangeField.js +3 -1
  28. package/ui/form/Field/fieldWrapper.d.ts +6 -5
  29. package/ui/form/InputField/InputField.d.ts +1 -2
  30. package/ui/form/InputField/InputField.js +9 -4
  31. package/ui/form/NumberField/NumberField.d.ts +6 -2
  32. package/ui/form/NumberField/NumberField.js +32 -4
  33. package/ui/form/NumberField/hooks/useInputTypeNumber.d.ts +10 -0
  34. package/ui/form/NumberField/hooks/useInputTypeNumber.js +45 -0
  35. package/ui/form/PasswordField/PasswordField.js +3 -2
  36. package/ui/form/RadioListField/RadioListField.js +1 -1
  37. package/ui/form/TextField/TextField.js +4 -3
  38. package/ui/layout/Sidebar/Sidebar.d.ts +0 -5
  39. package/ui/layout/Sidebar/Sidebar.js +1 -4
  40. package/ui/layout/Tooltip/Tooltip.d.ts +3 -7
  41. package/ui/layout/Tooltip/Tooltip.js +17 -49
  42. package/ui/list/Grid/Grid.js +2 -1
  43. package/ui/list/TreeTable/TreeTable.d.ts +35 -0
  44. package/ui/list/TreeTable/TreeTable.js +69 -0
  45. package/ui/list/TreeTable/index.d.ts +2 -0
  46. package/ui/list/TreeTable/index.js +7 -0
  47. package/ui/nav/Tree/Tree.js +4 -4
  48. package/utils/calculateComponentAbsolutePosition.d.ts +6 -0
  49. package/{ui/layout/Tooltip/calculate.js → utils/calculateComponentAbsolutePosition.js} +64 -58
  50. package/utils/calendar.js +2 -0
  51. package/utils/list.d.ts +1 -0
  52. package/utils/list.js +5 -0
  53. package/ui/layout/Tooltip/calculate.d.ts +0 -6
package/en.json CHANGED
@@ -782,5 +782,27 @@
782
782
  " Элементы меню": " Menu Items",
783
783
  "При указании данного свойства, после нажатия на кнопку и до выполнения действия будет отображено нативное\nокно с текстом подтверждения - `window.confirm('Ваш текст')`.": "When this property is specified, after clicking the button and before performing the action, a native \nwindow with the confirmation text will be displayed - `window.confirm('Your text')`.",
784
784
  "Контент, который отобразиться, если элемент навигации будет активен": "The content that will be displayed if the navigation element is active",
785
- " Используется для управления раскрытием всех элементов в дереве": " Used to control the expansion of all items in the tree"
786
- }
785
+ " Используется для управления раскрытием всех элементов в дереве": " Used to control the expansion of all items in the tree",
786
+ "\nСписок с чекбоксами. Используется в формах для выбора нескольких значений.\n": "\nCheckbox list. Used in forms to select multiple values.\n",
787
+ "Разница времени с бекендом (в микросекундах)": "Time difference with the backend (in microseconds)",
788
+ "Временная зона бекенда": "Backend time zone",
789
+ "Исходный язык": "Source language",
790
+ "Переводы сообщений": "Message translations",
791
+ "Показывать ли компонент сразу после рендера страницы": "Show the component immediately after page rendering",
792
+ "Объект стилей для позиционирования стрелки": "Style object for arrow positioning",
793
+ "Позиция компонента слева": "Component position on the left",
794
+ "Позиция компонента справа": "Component position on the right",
795
+ "Позиция компонента сверху": "Component position on top",
796
+ "Обработчик события загрузки файлов": "File upload event handler",
797
+ "Если в форме есть элементы \\<input\\>, то произойдет автоматическая фокусировка на первом из них": "If there are \\<input\\> elements in the form, automatic focus will be on the first one",
798
+ "Варианты абсолютного позиционирования": "Absolute positioning options",
799
+ "Получение экземпляра `dayjs` с учетом временной зоны бекенда": "Getting an instance of `dayjs` considering the backend time zone",
800
+ "Алиас для метода `translate`": "Alias for the `translate` method",
801
+ "Перевод сообщения": "Message translation",
802
+ "Компонент графика из библиотеки nivo": "Chart component from the nivo library",
803
+ "Конфигурация, настройки отображения графика": "Chart display configuration and settings",
804
+ "Данные для графика": "Data for the chart",
805
+ "Фиксированная высота графика в пикселях": "Fixed chart height in pixels",
806
+ "Использовать ли дефолтную конфигурацию для графика типа line": "Use default configuration for the line chart",
807
+ "Этот компонент позволяет создавать в проекте графики разных типов. Под капотом для графиков используется библиотека nivo.\nДля работы этого компонента необходимо установить в проекте зависимости @nivo/core и пакет конкретного графика nivo, например @nivo/line.\nКомпонент графика nivo нужно передать в пропс chartComponent": "This component allows you to create various types of graphics in your project. Under the hood, the nivo library is used for charts. \nTo use this component, you need to install dependencies @nivo/core and a specific nivo chart package, for example, @nivo/line. \nPass the nivo chart component as the chartComponent prop"
808
+ }
package/hooks/index.d.ts CHANGED
@@ -18,4 +18,5 @@ import useSelector from './useSelector';
18
18
  import useSsr from './useSsr';
19
19
  import useUniqueId from './useUniqueId';
20
20
  import useTheme from './useTheme';
21
- export { useAbsolutePositioning, useAddressBar, useApplication, useBem, useComponents, useDataProvider, useDataSelect, useDispatch, useFetch, useFile, useForm, useInitial, useLayout, useList, useModel, useScreen, useSelector, useSsr, useUniqueId, useTheme, };
21
+ import useSaveCursorPosition from './useSaveCursorPosition';
22
+ export { useAbsolutePositioning, useAddressBar, useApplication, useBem, useComponents, useDataProvider, useDataSelect, useDispatch, useFetch, useFile, useForm, useInitial, useLayout, useList, useModel, useScreen, useSelector, useSsr, useUniqueId, useTheme, useSaveCursorPosition, };
package/hooks/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  exports.__esModule = true;
6
- exports.useTheme = exports.useUniqueId = exports.useSsr = exports.useSelector = exports.useScreen = exports.useModel = exports.useList = exports.useLayout = exports.useInitial = exports.useForm = exports.useFile = exports.useFetch = exports.useDispatch = exports.useDataSelect = exports.useDataProvider = exports.useComponents = exports.useBem = exports.useApplication = exports.useAddressBar = exports.useAbsolutePositioning = void 0;
6
+ exports.useSaveCursorPosition = exports.useTheme = exports.useUniqueId = exports.useSsr = exports.useSelector = exports.useScreen = exports.useModel = exports.useList = exports.useLayout = exports.useInitial = exports.useForm = exports.useFile = exports.useFetch = exports.useDispatch = exports.useDataSelect = exports.useDataProvider = exports.useComponents = exports.useBem = exports.useApplication = exports.useAddressBar = exports.useAbsolutePositioning = void 0;
7
7
  var useAbsolutePositioning_1 = __importDefault(require("./useAbsolutePositioning"));
8
8
  exports.useAbsolutePositioning = useAbsolutePositioning_1["default"];
9
9
  var useAddressBar_1 = __importDefault(require("./useAddressBar"));
@@ -44,3 +44,5 @@ var useUniqueId_1 = __importDefault(require("./useUniqueId"));
44
44
  exports.useUniqueId = useUniqueId_1["default"];
45
45
  var useTheme_1 = __importDefault(require("./useTheme"));
46
46
  exports.useTheme = useTheme_1["default"];
47
+ var useSaveCursorPosition_1 = __importDefault(require("./useSaveCursorPosition"));
48
+ exports.useSaveCursorPosition = useSaveCursorPosition_1["default"];
@@ -1,14 +1,55 @@
1
1
  import * as React from 'react';
2
- interface IStyleObj {
2
+ export interface IComponentStylePosition {
3
+ /**
4
+ * Позиция компонента слева
5
+ */
3
6
  left: 'unset' | number;
7
+ /**
8
+ * Позиция компонента справа
9
+ */
4
10
  right: 'unset' | number;
11
+ /**
12
+ * Позиция компонента сверху
13
+ */
5
14
  top: 'unset' | number;
6
15
  }
16
+ export interface IComponentArrowPosition {
17
+ /**
18
+ * Позиция стрелки слева
19
+ */
20
+ left?: number | string;
21
+ /**
22
+ * Позиция стрелки справа
23
+ */
24
+ right?: number | string;
25
+ /**
26
+ * Позиция стрелки сверху
27
+ */
28
+ top?: number | string;
29
+ /**
30
+ * Позиция стрелки снизу
31
+ */
32
+ bottom?: number | string;
33
+ }
34
+ export declare const enum Position {
35
+ TOP = "top",
36
+ TOP_LEFT = "topLeft",
37
+ TOP_RIGHT = "topRight",
38
+ BOTTOM = "bottom",
39
+ BOTTOM_LEFT = "bottomLeft",
40
+ BOTTOM_RIGHT = "bottomRight",
41
+ LEFT = "left",
42
+ LEFT_TOP = "leftTop",
43
+ LEFT_BOTTOM = "leftBottom",
44
+ RIGHT = "right",
45
+ RIGHT_TOP = "rightTop",
46
+ RIGHT_BOTTOM = "rightBottom"
47
+ }
7
48
  /**
8
49
  * Варианты абсолютного позиционирования
9
50
  * @example 'top'
10
51
  */
11
- type Position = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom' | string;
52
+ export type PositionType = keyof typeof Position | string;
12
53
  export interface IAbsolutePositioningInputProps {
13
54
  /**
14
55
  * Включает "умное" позиционирование - если компонент не может быть помещен в промежуток между целевым компонентом
@@ -32,13 +73,17 @@ export interface IAbsolutePositioningInputProps {
32
73
  /**
33
74
  * Позиционирование компонента, относительно целевого элемента
34
75
  */
35
- position: Position;
76
+ position: PositionType;
36
77
  /**
37
78
  * Отобразить или скрыть компонент.
38
79
  * Включает "ручной режим", при котором можно задать логику отображения компонента извне,
39
80
  * через измененение данного свойства.
40
81
  */
41
82
  visible?: boolean;
83
+ /**
84
+ * Показывать ли компонент сразу после рендера страницы
85
+ */
86
+ defaultVisible?: boolean;
42
87
  /**
43
88
  * Обработчик изменения свойства isComponentVisible (отображение на странице).
44
89
  * Возвращает значение isComponentVisible.
@@ -62,15 +107,19 @@ export interface IAbsolutePositioningOutputProps {
62
107
  /**
63
108
  * Объект стилей для абсолютного позиционирования
64
109
  */
65
- style: IStyleObj;
110
+ style: IComponentStylePosition;
111
+ /**
112
+ * Объект стилей для позиционирования стрелки
113
+ */
114
+ arrowPosition?: IComponentArrowPosition;
66
115
  }
67
116
  export default function useAbsolutePositioning(props: IAbsolutePositioningInputProps): {
68
117
  isComponentExist: boolean;
69
118
  isComponentVisible: boolean;
70
- style: IStyleObj;
119
+ style: IComponentStylePosition;
71
120
  position: string;
72
- calculateAbsolutePosition: (newPosition: Position, childRef: any, componentSize: any) => void;
121
+ arrowPosition: IComponentArrowPosition;
122
+ calculateAbsolutePosition: (newPosition: PositionType, parentRef: any, componentSize: any, arrowSize?: any) => void;
73
123
  onShow: () => void;
74
124
  onHide: () => void;
75
125
  };
76
- export {};
@@ -1,146 +1,53 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  exports.__esModule = true;
6
+ exports.Position = void 0;
3
7
  var react_1 = require("react");
8
+ var calculateComponentAbsolutePosition_1 = __importDefault(require("../utils/calculateComponentAbsolutePosition"));
9
+ var Position;
10
+ (function (Position) {
11
+ Position["TOP"] = "top";
12
+ Position["TOP_LEFT"] = "topLeft";
13
+ Position["TOP_RIGHT"] = "topRight";
14
+ Position["BOTTOM"] = "bottom";
15
+ Position["BOTTOM_LEFT"] = "bottomLeft";
16
+ Position["BOTTOM_RIGHT"] = "bottomRight";
17
+ Position["LEFT"] = "left";
18
+ Position["LEFT_TOP"] = "leftTop";
19
+ Position["LEFT_BOTTOM"] = "leftBottom";
20
+ Position["RIGHT"] = "right";
21
+ Position["RIGHT_TOP"] = "rightTop";
22
+ Position["RIGHT_BOTTOM"] = "rightBottom";
23
+ })(Position = exports.Position || (exports.Position = {}));
24
+ var DEFAULT_AUTO_POSITIONING_VALUE = true;
4
25
  function useAbsolutePositioning(props) {
5
26
  var _a = (0, react_1.useState)(props.visible), isComponentExist = _a[0], setIsComponentExist = _a[1];
6
- var _b = (0, react_1.useState)(props.visible), isComponentVisible = _b[0], setIsComponentVisible = _b[1];
27
+ var _b = (0, react_1.useState)(props.visible || props.defaultVisible), isComponentVisible = _b[0], setIsComponentVisible = _b[1];
7
28
  var _c = (0, react_1.useState)(props.position), position = _c[0], setPosition = _c[1];
8
29
  var _d = (0, react_1.useState)({
30
+ left: null,
31
+ right: null,
32
+ top: null,
33
+ bottom: null
34
+ }), arrowPosition = _d[0], setArrowPosition = _d[1];
35
+ var _e = (0, react_1.useState)({
9
36
  left: null,
10
37
  right: null,
11
38
  top: null
12
- }), style = _d[0], setStyle = _d[1];
39
+ }), style = _e[0], setStyle = _e[1];
40
+ var hasAutoPositioning = (0, react_1.useMemo)(function () { var _a; return (_a = props.autoPositioning) !== null && _a !== void 0 ? _a : DEFAULT_AUTO_POSITIONING_VALUE; }, [props.autoPositioning]);
13
41
  var timerRef = (0, react_1.useRef)(null);
14
- var calculateAbsolutePosition = (0, react_1.useCallback)(function (newPosition, childRef, componentSize) {
15
- var newStyle = { left: null, right: null, top: null };
16
- var _a = childRef.getBoundingClientRect(), top = _a.top, right = _a.right, left = _a.left, width = _a.width, height = _a.height;
17
- var parentDimensions = { top: top, right: right, left: left, width: width, height: height };
18
- parentDimensions.top += window.scrollY;
19
- var useAutoPositioning = props.autoPositioning;
20
- // eslint-disable-next-line default-case
21
- switch (newPosition) {
22
- case 'top':
23
- case 'topLeft':
24
- case 'topRight':
25
- // Проверка - выходит ли tooltip за верхний край страницы?
26
- // Если да - меняем позицию на bottom
27
- if (useAutoPositioning
28
- && ((parentDimensions.top - window.scrollY) <= Math.round(componentSize.height + props.gap))) {
29
- newStyle.top = parentDimensions.top + parentDimensions.height;
30
- newPosition = newPosition.replace('top', 'bottom');
31
- }
32
- else {
33
- newStyle.top = parentDimensions.top - componentSize.height;
34
- }
35
- break;
36
- case 'bottom':
37
- case 'bottomLeft':
38
- case 'bottomRight':
39
- // Проверка - выходит ли tooltip за нижний край страницы?
40
- // Если да - меняем позицию на top
41
- if (useAutoPositioning
42
- && ((window.innerHeight - (parentDimensions.top + parentDimensions.height - window.scrollY))) <= Math.round(componentSize.height + props.gap)) {
43
- newStyle.top = parentDimensions.top - componentSize.height;
44
- newPosition = newPosition.replace('bottom', 'top');
45
- }
46
- else {
47
- newStyle.top = parentDimensions.top + parentDimensions.height;
48
- }
49
- break;
50
- case 'left':
51
- case 'leftTop':
52
- case 'leftBottom':
53
- // Проверка - выходит ли tooltip за левый край страницы?
54
- // Если да - меняем позицию на right
55
- if (useAutoPositioning && (parentDimensions.left <= Math.round(componentSize.width + props.gap))) {
56
- newStyle.left = parentDimensions.right;
57
- newPosition = newPosition.replace('left', 'right');
58
- }
59
- else {
60
- newStyle.left = parentDimensions.left - componentSize.width;
61
- }
62
- break;
63
- case 'right':
64
- case 'rightTop':
65
- case 'rightBottom':
66
- // Проверка - выходит ли tooltip за правый край страницы?
67
- // Если да - меняем позицию на left
68
- if (useAutoPositioning
69
- && (document.body.clientWidth - parentDimensions.right <= Math.round(componentSize.width + props.gap))) {
70
- newStyle.left = parentDimensions.left - componentSize.width;
71
- newPosition = newPosition.replace('right', 'left');
72
- }
73
- else {
74
- newStyle.left = parentDimensions.right;
75
- }
76
- break;
77
- }
78
- // eslint-disable-next-line default-case
79
- switch (newPosition) {
80
- case 'top':
81
- case 'bottom':
82
- // Выравнивание по середине
83
- newStyle.left = (parentDimensions.left + (parentDimensions.width / 2)) - (componentSize.width / 2);
84
- break;
85
- case 'topLeft':
86
- case 'bottomLeft':
87
- // Ширина tooltip больше родителя - стрелка на середину родителя
88
- newStyle.left = parentDimensions.left;
89
- break;
90
- case 'topRight':
91
- case 'bottomRight':
92
- // Ширина tooltip больше родителя - стрелка на середину родителя
93
- newStyle.right = document.body.clientWidth - parentDimensions.right;
94
- break;
95
- case 'left':
96
- case 'right':
97
- newStyle.top = (parentDimensions.top + (parentDimensions.height / 2)) - (componentSize.height / 2);
98
- break;
99
- case 'leftTop':
100
- case 'rightTop':
101
- newStyle.top = parentDimensions.top;
102
- break;
103
- case 'leftBottom':
104
- case 'rightBottom':
105
- newStyle.top = parentDimensions.top + parentDimensions.height - componentSize.height;
106
- break;
107
- }
108
- // Проверка - при позиционировании top/bottom tooltip не выходит за пределы страницы по горизонтали
109
- if (newPosition.includes('top') || newPosition.includes('bottom')) {
110
- if (!newPosition.includes('Left')
111
- && (newStyle.left < 0 || parentDimensions.left
112
- <= Math.round((componentSize.width - parentDimensions.width) + props.gap))) {
113
- newStyle.right = null;
114
- newPosition = newPosition.replace('Right', 'Left');
115
- newStyle.left = parentDimensions.left;
116
- }
117
- if (!newPosition.includes('Right')
118
- && (document.body.clientWidth - parentDimensions.right
119
- <= Math.round((componentSize.width - parentDimensions.width) + props.gap))) {
120
- newPosition = newPosition.replace('Left', 'Right');
121
- newStyle.left = null;
122
- newStyle.right = document.body.clientWidth - parentDimensions.right;
123
- }
124
- }
125
- // Проверка - при позиционировании left/right tooltip не выходит за пределы страницы по вертикали
126
- if (newPosition.includes('left') || newPosition.includes('right')) {
127
- if (!newPosition.includes('Top')
128
- && parentDimensions.top - window.scrollY
129
- <= Math.round((componentSize.height - parentDimensions.height) + props.gap)) {
130
- newPosition = newPosition.replace('Bottom', 'Top');
131
- newStyle.top = parentDimensions.top;
132
- }
133
- if (!newPosition.includes('Bottom')
134
- && (window.innerHeight - (parentDimensions.top + parentDimensions.height - window.scrollY)
135
- <= Math.round((componentSize.height - parentDimensions.height) + props.gap))) {
136
- newPosition = newPosition.replace('Top', 'Bottom');
137
- newStyle.top = parentDimensions.top + parentDimensions.height - componentSize.height;
138
- }
139
- }
42
+ var calculateAbsolutePosition = (0, react_1.useCallback)(function (newPosition, parentRef, componentSize, arrowSize) {
43
+ if (arrowSize === void 0) { arrowSize = null; }
44
+ var _a = (0, calculateComponentAbsolutePosition_1["default"])(props.gap, newPosition, parentRef, componentSize, arrowSize, hasAutoPositioning), newStyle = _a.style, calculatedPosition = _a.position, calculatedArrowPosition = _a.arrowPosition;
140
45
  setStyle(newStyle);
141
- setPosition(newPosition);
142
- // eslint-disable-next-line react-hooks/exhaustive-deps
143
- }, [props.gap]);
46
+ setPosition(calculatedPosition);
47
+ if (calculatedArrowPosition) {
48
+ setArrowPosition(calculatedArrowPosition);
49
+ }
50
+ }, [hasAutoPositioning, props.gap]);
144
51
  var onShow = (0, react_1.useCallback)(function () {
145
52
  if (timerRef.current) {
146
53
  clearTimeout(timerRef.current);
@@ -180,6 +87,7 @@ function useAbsolutePositioning(props) {
180
87
  isComponentVisible: isComponentVisible,
181
88
  style: style,
182
89
  position: position,
90
+ arrowPosition: arrowPosition,
183
91
  calculateAbsolutePosition: calculateAbsolutePosition,
184
92
  onShow: onShow,
185
93
  onHide: onHide
@@ -55,6 +55,7 @@ var merge_1 = __importDefault(require("lodash-es/merge"));
55
55
  var react_1 = require("react");
56
56
  var relativeTime_1 = __importDefault(require("dayjs/plugin/relativeTime"));
57
57
  var localizedFormat_1 = __importDefault(require("dayjs/plugin/localizedFormat"));
58
+ var customParseFormat_1 = __importDefault(require("dayjs/plugin/customParseFormat"));
58
59
  var dayjs_1 = __importDefault(require("dayjs"));
59
60
  var utc_1 = __importDefault(require("dayjs/plugin/utc"));
60
61
  var ThemeProvider_1 = __importDefault(require("../providers/ThemeProvider"));
@@ -99,6 +100,7 @@ function useApplication(config) {
99
100
  var useGlobal = config.useGlobal !== false;
100
101
  //Extending dayjs / day.js with modules that used in steroids
101
102
  dayjs_1["default"].extend(relativeTime_1["default"]);
103
+ dayjs_1["default"].extend(customParseFormat_1["default"]);
102
104
  dayjs_1["default"].extend(localizedFormat_1["default"]);
103
105
  dayjs_1["default"].extend(utc_1["default"]);
104
106
  var components = (0, useComponents_1["default"])();
@@ -102,6 +102,9 @@ function useDataSelect(config) {
102
102
  if (selectedIds.length !== 1 || selectedIds[0] !== id_1) {
103
103
  setSelectedIdsInternal([id_1]);
104
104
  }
105
+ else if (selectedIds.length === 1 && selectedIds[0] === id_1) {
106
+ setSelectedIdsInternal([]);
107
+ }
105
108
  setIsOpened(false);
106
109
  }
107
110
  }
@@ -1,3 +1,4 @@
1
+ import { ITreeTableItem } from '../ui/list/TreeTable/TreeTable';
1
2
  import { IAddressBarConfig } from '../hooks/useAddressBar';
2
3
  import { IList } from '../actions/list';
3
4
  import { ILayoutNamesProps } from '../ui/list/LayoutNames/LayoutNames';
@@ -118,8 +119,8 @@ export interface IListConfig {
118
119
  */
119
120
  addressBar?: boolean | IAddressBarConfig;
120
121
  /**
121
- * Параметр для загрузки данных списка с сервера
122
- */
122
+ * Параметр для загрузки данных списка с сервера
123
+ */
123
124
  scope?: string[];
124
125
  /**
125
126
  * Дополнительные параметры, значения которых нужно передавать в запросе для получения данных
@@ -127,8 +128,8 @@ export interface IListConfig {
127
128
  */
128
129
  query?: Record<string, unknown>;
129
130
  /**
130
- * Модель
131
- */
131
+ * Модель
132
+ */
132
133
  model?: string;
133
134
  /**
134
135
  * Модель для синхронизации значений формы с адресной строкой
@@ -148,6 +149,10 @@ export interface IListConfig {
148
149
  * Количество элементов всего в списке (для отрисовки пагинации), заданное вручную
149
150
  */
150
151
  initialTotal?: number;
152
+ /**
153
+ * Включает обработку вложенных данных из items вида [{id: 1, name: 'John', items: [...]}]
154
+ */
155
+ hasTreeItems?: boolean;
151
156
  }
152
157
  export interface IListOutput {
153
158
  list: IList;
@@ -206,6 +211,7 @@ export declare const createInitialValues: ({ paginationProps, paginationSizeProp
206
211
  initialQuery: any;
207
212
  configQuery: any;
208
213
  }) => any;
214
+ export declare const prepareItemsToTree: (sourceItems: ITreeTableItem[], openedTreeItems: Record<string, boolean>, currentPage: number | null, itemsOnPage: number | null, onTreeItemClick: (uniqueId: string, item: Record<string, any>) => void, parentId?: string, currentLevel?: number) => any[];
209
215
  /**
210
216
  * useList
211
217
  * Добавляет массу возможностей для взаимодействия с коллекциями. Коллекции можно получать как с бекенда,
package/hooks/useList.js CHANGED
@@ -37,18 +37,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
39
  exports.__esModule = true;
40
- exports.createInitialValues = exports.getDefaultSearchModel = exports.normalizeSortProps = exports.defaultConfig = void 0;
40
+ exports.prepareItemsToTree = exports.createInitialValues = exports.getDefaultSearchModel = exports.normalizeSortProps = exports.defaultConfig = void 0;
41
41
  var react_1 = require("react");
42
42
  var get_1 = __importDefault(require("lodash-es/get"));
43
43
  var union_1 = __importDefault(require("lodash-es/union"));
44
44
  var isEqual_1 = __importDefault(require("lodash-es/isEqual"));
45
45
  var React = __importStar(require("react"));
46
46
  var react_use_1 = require("react-use");
47
+ var list_1 = require("../utils/list");
47
48
  var useSelector_1 = __importDefault(require("../hooks/useSelector"));
48
- var list_1 = require("../reducers/list");
49
+ var list_2 = require("../reducers/list");
49
50
  var useModel_1 = __importDefault(require("../hooks/useModel"));
50
51
  var useAddressBar_1 = __importDefault(require("../hooks/useAddressBar"));
51
- var list_2 = require("../actions/list");
52
+ var list_3 = require("../actions/list");
52
53
  var useDispatch_1 = __importDefault(require("../hooks/useDispatch"));
53
54
  var form_1 = require("../actions/form");
54
55
  var form_2 = require("../reducers/form");
@@ -104,6 +105,27 @@ var createInitialValues = function (_a) {
104
105
  return (__assign(__assign((_b = {}, _b[paginationProps.attribute] = paginationProps.defaultValue, _b[paginationSizeProps.attribute] = paginationSizeProps.defaultValue, _b[sort.attribute] = sort.defaultValue, _b[layoutNamesProps.attribute] = layoutNamesProps.defaultValue, _b), initialQuery), configQuery));
105
106
  };
106
107
  exports.createInitialValues = createInitialValues;
108
+ var TOP_TREE_LEVEL_VALUE = 0;
109
+ var prepareItemsToTree = function (sourceItems, openedTreeItems, currentPage, itemsOnPage, onTreeItemClick, parentId, currentLevel) {
110
+ if (parentId === void 0) { parentId = ''; }
111
+ if (currentLevel === void 0) { currentLevel = TOP_TREE_LEVEL_VALUE; }
112
+ var treeItems = [];
113
+ if (currentPage && itemsOnPage && currentLevel === TOP_TREE_LEVEL_VALUE) {
114
+ var startIndex = (currentPage - 1) * itemsOnPage;
115
+ sourceItems = sourceItems.slice(startIndex, startIndex + itemsOnPage);
116
+ }
117
+ (sourceItems || []).forEach(function (item, index) {
118
+ var uniqueId = (0, list_1.getTreeItemUniqId)(item, index, parentId);
119
+ var isOpened = !!openedTreeItems[uniqueId];
120
+ var hasItems = !!(item.items && item.items.length > 0);
121
+ treeItems.push(__assign(__assign({}, item), { uniqueId: uniqueId, level: currentLevel, isOpened: isOpened, hasItems: hasItems, onTreeItemClick: onTreeItemClick }));
122
+ if (isOpened) {
123
+ treeItems = treeItems.concat((0, exports.prepareItemsToTree)(item.items, openedTreeItems, currentPage, itemsOnPage, onTreeItemClick, uniqueId, currentLevel + 1)).filter(Boolean);
124
+ }
125
+ });
126
+ return treeItems;
127
+ };
128
+ exports.prepareItemsToTree = prepareItemsToTree;
107
129
  /**
108
130
  * useList
109
131
  * Добавляет массу возможностей для взаимодействия с коллекциями. Коллекции можно получать как с бекенда,
@@ -113,7 +135,18 @@ exports.createInitialValues = createInitialValues;
113
135
  function useList(config) {
114
136
  var _a, _b;
115
137
  // Get list from redux state
116
- var list = (0, useSelector_1["default"])(function (state) { return (0, list_1.getList)(state, config.listId); });
138
+ var list = (0, useSelector_1["default"])(function (state) { return (0, list_2.getList)(state, config.listId); });
139
+ var _c = (0, react_1.useState)({}), openedTreeItems = _c[0], setOpenedTreeItems = _c[1];
140
+ var onTreeItemClick = (0, react_1.useCallback)(function (uniqueId, item) {
141
+ var _a;
142
+ if (((_a = item.items) === null || _a === void 0 ? void 0 : _a.length) > 0) {
143
+ setOpenedTreeItems(function (prevItems) {
144
+ var _a;
145
+ return (__assign(__assign({}, prevItems), (_a = {}, _a[uniqueId] = !prevItems[uniqueId], _a)));
146
+ });
147
+ }
148
+ }, []);
149
+ var getTreeItems = (0, react_1.useCallback)(function (sourceItems) { return (0, exports.prepareItemsToTree)(sourceItems, openedTreeItems, list === null || list === void 0 ? void 0 : list.page, list === null || list === void 0 ? void 0 : list.pageSize, onTreeItemClick); }, [onTreeItemClick, openedTreeItems, list]);
117
150
  // Normalize sort config
118
151
  var sort = (0, exports.normalizeSortProps)(config.sort);
119
152
  // Empty
@@ -152,7 +185,7 @@ function useList(config) {
152
185
  var model = (0, useModel_1["default"])(config.model);
153
186
  var searchModel = (0, useModel_1["default"])(config.searchModel || ((_a = config.searchForm) === null || _a === void 0 ? void 0 : _a.model), defaultSearchModel);
154
187
  // Address bar synchronization
155
- var _c = (0, useAddressBar_1["default"])(__assign({ enable: !!config.addressBar, model: searchModel }, (typeof config.addressBar === 'boolean' ? { enable: config.addressBar } : config.addressBar))), initialQuery = _c.initialQuery, updateQuery = _c.updateQuery;
188
+ var _d = (0, useAddressBar_1["default"])(__assign({ enable: !!config.addressBar, model: searchModel }, (typeof config.addressBar === 'boolean' ? { enable: config.addressBar } : config.addressBar))), initialQuery = _d.initialQuery, updateQuery = _d.updateQuery;
156
189
  // Outside search form
157
190
  var searchFormFields = (_b = config.searchForm) === null || _b === void 0 ? void 0 : _b.fields;
158
191
  var SearchForm = require('../ui/list/SearchForm')["default"];
@@ -183,8 +216,9 @@ function useList(config) {
183
216
  // Init list in redux store
184
217
  (0, react_use_1.useMount)(function () {
185
218
  if (!list) {
219
+ var items = config.hasTreeItems ? getTreeItems(config.items) : config.items;
186
220
  var toDispatch = [
187
- (0, list_2.listInit)(config.listId, {
221
+ (0, list_3.listInit)(config.listId, {
188
222
  listId: config.listId,
189
223
  action: config.action || config.action === '' ? config.action : null,
190
224
  actionMethod: config.actionMethod || exports.defaultConfig.actionMethod,
@@ -193,7 +227,7 @@ function useList(config) {
193
227
  condition: config.condition,
194
228
  scope: config.scope,
195
229
  items: null,
196
- sourceItems: config.items || null,
230
+ sourceItems: items || null,
197
231
  total: config.initialTotal,
198
232
  isRemote: !config.items,
199
233
  primaryKey: config.primaryKey || exports.defaultConfig.primaryKey,
@@ -205,11 +239,15 @@ function useList(config) {
205
239
  layoutAttribute: layoutNamesProps.attribute || null
206
240
  }),
207
241
  ];
208
- if (config.initialItems || config.items) {
209
- toDispatch.push((0, list_2.listSetItems)(config.listId, config.initialItems || config.items));
242
+ if (config.initialItems) {
243
+ var initialItems = config.hasTreeItems ? getTreeItems(config.initialItems) : config.initialItems;
244
+ toDispatch.push((0, list_3.listSetItems)(config.listId, initialItems));
245
+ }
246
+ else if (config.items) {
247
+ toDispatch.push((0, list_3.listSetItems)(config.listId, items));
210
248
  }
211
249
  if (!config.initialItems) {
212
- toDispatch.push((0, list_2.listLazyFetch)(config.listId));
250
+ toDispatch.push((0, list_3.listLazyFetch)(config.listId));
213
251
  }
214
252
  dispatch(toDispatch);
215
253
  }
@@ -232,7 +270,7 @@ function useList(config) {
232
270
  updateQuery(formValues);
233
271
  // Send request
234
272
  if (config.autoFetchOnFormChanges !== false) {
235
- dispatch((0, list_2.listLazyFetch)(config.listId));
273
+ dispatch((0, list_3.listLazyFetch)(config.listId));
236
274
  }
237
275
  }
238
276
  }, [config.autoFetchOnFormChanges, config.listId, dispatch, formId, formValues,
@@ -250,22 +288,29 @@ function useList(config) {
250
288
  // Check change items
251
289
  (0, react_use_1.useUpdateEffect)(function () {
252
290
  dispatch([
253
- (0, list_2.listSetItems)(config.listId, config.items),
291
+ (0, list_3.listSetItems)(config.listId, config.items),
254
292
  ]);
255
293
  }, [dispatch, config.items, config.listId]);
294
+ (0, react_use_1.useUpdateEffect)(function () {
295
+ if (config.hasTreeItems) {
296
+ dispatch([
297
+ (0, list_3.listSetItems)(config.listId, getTreeItems(config.items)),
298
+ ]);
299
+ }
300
+ }, [dispatch, config.items, config.listId, openedTreeItems, list === null || list === void 0 ? void 0 : list.pageSize, list === null || list === void 0 ? void 0 : list.page]);
256
301
  // Destroy
257
302
  (0, react_use_1.useUnmount)(function () {
258
303
  var autoDestroy = typeof config.autoDestroy === 'boolean' ? config.autoDestroy : exports.defaultConfig.autoDestroy;
259
304
  if (autoDestroy) {
260
305
  dispatch([
261
- (0, list_2.listDestroy)(config.listId),
306
+ (0, list_3.listDestroy)(config.listId),
262
307
  (0, form_1.formDestroy)(config.listId),
263
308
  ]);
264
309
  }
265
310
  });
266
311
  var onFetch = (0, react_1.useCallback)(function (params) {
267
312
  if (params === void 0) { params = {}; }
268
- dispatch((0, list_2.listFetch)(config.listId, params));
313
+ dispatch((0, list_3.listFetch)(config.listId, params));
269
314
  }, [config.listId, dispatch]);
270
315
  var onSort = (0, react_1.useCallback)(function (value) {
271
316
  dispatch((0, form_1.formChange)(formId, sort.attribute, value));
@@ -0,0 +1,6 @@
1
+ import React, { ChangeEvent } from 'react';
2
+ import { IInputParams } from 'src/ui/form/Field/fieldWrapper';
3
+ export default function useSaveCursorPosition(inputParams: IInputParams): {
4
+ inputRef: React.MutableRefObject<any>;
5
+ onChange: (event: ChangeEvent<HTMLInputElement>, value?: any) => void;
6
+ };
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ exports.__esModule = true;
6
+ /* eslint-disable no-return-assign */
7
+ /* eslint-disable no-unused-expressions */
8
+ var react_1 = __importDefault(require("react"));
9
+ function useSaveCursorPosition(inputParams) {
10
+ var _a = react_1["default"].useState(null), cursor = _a[0], setCursor = _a[1];
11
+ var inputRef = react_1["default"].useRef(null);
12
+ react_1["default"].useEffect(function () {
13
+ var inputElement = inputRef.current;
14
+ if (inputElement) {
15
+ inputElement.setSelectionRange(cursor, cursor);
16
+ }
17
+ }, [cursor, inputParams.value]);
18
+ var onChange = react_1["default"].useCallback(function (event, value) {
19
+ var _a, _b;
20
+ if (value === void 0) { value = null; }
21
+ setCursor((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.selectionStart);
22
+ inputParams.onChange(value || ((_b = event.target) === null || _b === void 0 ? void 0 : _b.value));
23
+ }, [inputParams]);
24
+ return {
25
+ inputRef: inputRef,
26
+ onChange: onChange
27
+ };
28
+ }
29
+ exports["default"] = useSaveCursorPosition;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steroidsjs/core",
3
- "version": "3.0.0-beta.81",
3
+ "version": "3.0.0-beta.83",
4
4
  "description": "",
5
5
  "author": "Vladimir Kozhin <hello@kozhindev.com>",
6
6
  "repository": {