@steroidsjs/core 3.0.0-beta.82 → 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.
package/en.json CHANGED
@@ -783,26 +783,26 @@
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
785
  " Используется для управления раскрытием всех элементов в дереве": " Used to control the expansion of all items in the tree",
786
- "\nСписок с чекбоксами. Используется в формах для выбора нескольких значений.\n": "",
787
- "Разница времени с бекендом (в микросекундах)": "",
788
- "Временная зона бекенда": "",
789
- "Исходный язык": "",
790
- "Переводы сообщений": "",
791
- "Показывать ли компонент сразу после рендера страницы": "",
792
- "Объект стилей для позиционирования стрелки": "",
793
- "Позиция компонента слева": "",
794
- "Позиция компонента справа": "",
795
- "Позиция компонента сверху": "",
796
- "Обработчик события загрузки файлов": "",
797
- "Если в форме есть элементы \\<input\\>, то произойдет автоматическая фокусировка на первом из них": "",
798
- "Варианты абсолютного позиционирования": "",
799
- "Получение экземпляра `dayjs` с учетом временной зоны бекенда": "",
800
- "Алиас для метода `translate`": "",
801
- "Перевод сообщения": "",
802
- "Компонент графика из библиотеки nivo": "",
803
- "Конфигурация, настройки отображения графика": "",
804
- "Данные для графика": "",
805
- "Фиксированная высота графика в пикселях": "",
806
- "Использовать ли дефолтную конфигурацию для графика типа line": "",
807
- "Этот компонент позволяет создавать в проекте графики разных типов. Под капотом для графиков используется библиотека nivo.\nДля работы этого компонента необходимо установить в проекте зависимости @nivo/core и пакет конкретного графика nivo, например @nivo/line.\nКомпонент графика nivo нужно передать в пропс chartComponent": ""
808
- }
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,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.82",
3
+ "version": "3.0.0-beta.83",
4
4
  "description": "",
5
5
  "author": "Vladimir Kozhin <hello@kozhindev.com>",
6
6
  "repository": {
@@ -72,6 +72,11 @@ export interface IFieldWrapperInputProps {
72
72
  isRenderWithoutFieldLayout?: boolean;
73
73
  [key: string]: any;
74
74
  }
75
+ export interface IInputParams {
76
+ name?: string;
77
+ value?: any;
78
+ onChange: (value: any) => void;
79
+ }
75
80
  export interface IFieldWrapperOutputProps {
76
81
  /**
77
82
  * Id формы
@@ -88,11 +93,7 @@ export interface IFieldWrapperOutputProps {
88
93
  /**
89
94
  * Параметры для input элемента
90
95
  */
91
- input?: {
92
- name?: string;
93
- value?: any;
94
- onChange: (value: any) => void;
95
- };
96
+ input?: IInputParams;
96
97
  }
97
98
  interface IFieldWrapperOptions {
98
99
  label?: boolean;
@@ -85,7 +85,7 @@ export interface IInputFieldViewProps extends IInputFieldProps, IFieldWrapperOut
85
85
  inputProps: {
86
86
  type: string;
87
87
  name: string;
88
- onChange: (value: any) => void;
88
+ onInput: (event: any, value?: string) => void;
89
89
  value: string | number;
90
90
  placeholder: string;
91
91
  disabled: boolean;
@@ -94,7 +94,6 @@ export interface IInputFieldViewProps extends IInputFieldProps, IFieldWrapperOut
94
94
  onFocus?: (e: Event | React.FocusEvent) => void;
95
95
  onBlur?: (e: Event | React.FocusEvent) => void;
96
96
  onMouseDown?: (e: Event | React.MouseEvent) => void;
97
- maskedInputRef?: React.RefCallback<HTMLElement>;
98
97
  defaultValue?: string;
99
98
  }
100
99
  declare const _default: import("../Field/fieldWrapper").FieldWrapperComponent<IInputFieldProps>;
@@ -87,17 +87,22 @@ function InputField(props) {
87
87
  var maskedInputRef = (0, react_2.useMaskito)({
88
88
  options: props.maskOptions
89
89
  });
90
+ var _a = (0, hooks_1.useSaveCursorPosition)(props.input), inputRef = _a.inputRef, onChange = _a.onChange;
91
+ React.useEffect(function () {
92
+ if (inputRef.current) {
93
+ maskedInputRef(inputRef.current);
94
+ }
95
+ }, [inputRef, maskedInputRef]);
90
96
  var onClear = React.useCallback(function () { return props.input.onChange(''); }, [props.input]);
91
- props.onClear = onClear;
92
97
  var inputProps = (0, react_1.useMemo)(function () {
93
98
  var _a;
94
- return (__assign({ type: props.type, name: props.input.name, defaultValue: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: function (value) { return props.input.onChange(value); }, placeholder: props.placeholder, disabled: props.disabled }, props.inputProps));
95
- }, [props.disabled, props.input, props.inputProps, props.placeholder, props.type]);
99
+ return (__assign({ type: props.type, name: props.input.name, value: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onInput: onChange, placeholder: props.placeholder, disabled: props.disabled }, props.inputProps));
100
+ }, [onChange, props.disabled, props.input.name, props.input.value, props.inputProps, props.placeholder, props.type]);
96
101
  // No render for hidden input
97
102
  if (props.type === 'hidden') {
98
103
  return null;
99
104
  }
100
- return components.ui.renderView(props.view || 'form.InputFieldView', __assign(__assign(__assign({}, props), props.viewProps), { inputProps: inputProps, maskedInputRef: maskedInputRef }));
105
+ return components.ui.renderView(props.view || 'form.InputFieldView', __assign(__assign(__assign({}, props), props.viewProps), { inputProps: inputProps, inputRef: inputRef, onClear: onClear }));
101
106
  }
102
107
  InputField.defaultProps = {
103
108
  type: 'text',
@@ -1,4 +1,4 @@
1
- import { ChangeEvent } from 'react';
1
+ import React, { ChangeEvent } from 'react';
2
2
  import { IBaseFieldProps } from '../InputField/InputField';
3
3
  import { IFieldWrapperOutputProps } from '../Field/fieldWrapper';
4
4
  /**
@@ -21,7 +21,7 @@ export interface INumberFieldProps extends IBaseFieldProps {
21
21
  * Шаг увеличения/уменьшения значения
22
22
  * @example 5
23
23
  */
24
- step?: string | number;
24
+ step?: number;
25
25
  }
26
26
  export interface INumberFieldViewProps extends INumberFieldProps, IFieldWrapperOutputProps {
27
27
  inputProps: {
@@ -35,6 +35,10 @@ export interface INumberFieldViewProps extends INumberFieldProps, IFieldWrapperO
35
35
  max: number;
36
36
  step: string | number;
37
37
  };
38
+ inputRef: React.MutableRefObject<any>;
39
+ onStepUp: VoidFunction;
40
+ onStepDown: VoidFunction;
41
+ onKeyDown: VoidFunction;
38
42
  }
39
43
  declare const _default: import("../Field/fieldWrapper").FieldWrapperComponent<INumberFieldProps>;
40
44
  export default _default;
@@ -14,16 +14,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
16
  exports.__esModule = true;
17
+ /* eslint-disable max-len */
17
18
  var react_1 = require("react");
18
19
  var hooks_1 = require("../../../hooks");
19
20
  var fieldWrapper_1 = __importDefault(require("../Field/fieldWrapper"));
21
+ var useInputTypeNumber_1 = __importDefault(require("./hooks/useInputTypeNumber"));
22
+ var DEFAULT_STEP = 1;
20
23
  function NumberField(props) {
21
24
  var components = (0, hooks_1.useComponents)();
22
- props.inputProps = (0, react_1.useMemo)(function () {
25
+ var _a = (0, hooks_1.useSaveCursorPosition)(props.input), currentInputRef = _a.inputRef, onChange = _a.onChange;
26
+ var onInputChange = (0, useInputTypeNumber_1["default"])(currentInputRef, {
27
+ max: props.max,
28
+ min: props.min,
29
+ value: props.input.value
30
+ }, onChange).onInputChange;
31
+ var onStep = (0, react_1.useCallback)(function (isIncrement) {
23
32
  var _a;
24
- return (__assign({ name: props.input.name, defaultValue: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: function (value) { return props.input.onChange(value); }, type: 'number', min: props.min, max: props.max, step: props.step, placeholder: props.placeholder, disabled: props.disabled }, props.inputProps));
25
- }, [props.disabled, props.input, props.inputProps, props.placeholder, props.min, props.max, props.step]);
26
- return components.ui.renderView(props.view || 'form.NumberFieldView', props);
33
+ var step = props.step || DEFAULT_STEP;
34
+ onChange(null, String(Number((_a = currentInputRef === null || currentInputRef === void 0 ? void 0 : currentInputRef.current) === null || _a === void 0 ? void 0 : _a.value) + (isIncrement ? step : -step)));
35
+ }, [currentInputRef, onChange, props.step]);
36
+ var onStepUp = (0, react_1.useCallback)(function () {
37
+ onStep(true);
38
+ }, [onStep]);
39
+ var onStepDown = (0, react_1.useCallback)(function () {
40
+ onStep(false);
41
+ }, [onStep]);
42
+ var onKeyDown = (0, react_1.useCallback)(function (event) {
43
+ if (event.key === 'ArrowUp') {
44
+ onStepUp();
45
+ }
46
+ else if (event.key === 'ArrowDown') {
47
+ onStepDown();
48
+ }
49
+ }, [onStepDown, onStepUp]);
50
+ var inputProps = (0, react_1.useMemo)(function () {
51
+ var _a;
52
+ return (__assign({ name: props.input.name, value: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: onInputChange, type: 'text', min: props.min, max: props.max, step: props.step, placeholder: props.placeholder, disabled: props.disabled, autoComplete: 'off', onKeyDown: onKeyDown }, props.inputProps));
53
+ }, [props.input.name, props.input.value, props.min, props.max, props.step, props.placeholder, props.disabled, props.inputProps, onInputChange, onKeyDown]);
54
+ return components.ui.renderView(props.view || 'form.NumberFieldView', __assign(__assign({}, props), { inputProps: inputProps, onStepUp: onStepUp, onStepDown: onStepDown, inputRef: currentInputRef }));
27
55
  }
28
56
  NumberField.defaultProps = {
29
57
  disabled: false,
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface IInputTypeNumberProps {
3
+ max: any;
4
+ min: any;
5
+ value: any;
6
+ }
7
+ declare const useInputTypeNumber: (currentInputRef: React.MutableRefObject<HTMLInputElement>, inputTypeNumberProps: IInputTypeNumberProps, onChange: (event: React.ChangeEvent<HTMLInputElement>, value?: any) => void) => {
8
+ onInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
9
+ };
10
+ export default useInputTypeNumber;
@@ -0,0 +1,45 @@
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
+ var react_1 = __importDefault(require("react"));
7
+ var useInputTypeNumber = function (currentInputRef, inputTypeNumberProps, onChange) {
8
+ var setValidity = react_1["default"].useCallback(function (message) {
9
+ var _a;
10
+ (_a = currentInputRef.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(message);
11
+ }, [currentInputRef]);
12
+ var checkIsValueFalsy = function (value) { return value === '' || value === null || value === undefined; };
13
+ react_1["default"].useEffect(function () {
14
+ var defaultValidity = 'The number is not included in the range';
15
+ // eslint-disable-next-line no-unused-expressions
16
+ inputTypeNumberProps.value > inputTypeNumberProps.max
17
+ || inputTypeNumberProps.value < inputTypeNumberProps.min
18
+ || checkIsValueFalsy(inputTypeNumberProps.value)
19
+ ? setValidity(__(defaultValidity) + " ".concat(inputTypeNumberProps.min, " \u2014 ").concat(inputTypeNumberProps.max))
20
+ : setValidity('');
21
+ }, [currentInputRef, inputTypeNumberProps.value, inputTypeNumberProps.max, inputTypeNumberProps.min, setValidity]);
22
+ var isValueNumeric = function (value) {
23
+ if (checkIsValueFalsy(value)) {
24
+ return true;
25
+ }
26
+ /**
27
+ * Подходят как отрицательные так и положительные числа
28
+ * @example -1
29
+ * @example 1
30
+ */
31
+ var numericRegex = /^-?\d*\.?\d+$/;
32
+ return numericRegex.test(value);
33
+ };
34
+ var onInputChange = function (event) {
35
+ var _a;
36
+ var value = (_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.value;
37
+ if (isValueNumeric(value)) {
38
+ onChange(event);
39
+ }
40
+ };
41
+ return {
42
+ onInputChange: onInputChange
43
+ };
44
+ };
45
+ exports["default"] = useInputTypeNumber;
@@ -55,14 +55,15 @@ exports.checkPassword = checkPassword;
55
55
  function PasswordField(props) {
56
56
  var _a = (0, react_1.useState)(InputType.PASSWORD), type = _a[0], setType = _a[1];
57
57
  var components = (0, hooks_1.useComponents)();
58
+ var _b = (0, hooks_1.useSaveCursorPosition)(props.input), inputRef = _b.inputRef, onChange = _b.onChange;
58
59
  var onClear = (0, react_1.useCallback)(function () { return props.input.onChange(''); }, [props.input]);
59
60
  var onShowButtonClick = (0, react_1.useCallback)(function () {
60
61
  type === InputType.PASSWORD ? setType(InputType.TEXT) : setType(InputType.PASSWORD);
61
62
  }, [type]);
62
63
  var inputProps = (0, react_1.useMemo)(function () {
63
64
  var _a;
64
- return (__assign({ name: props.input.name, defaultValue: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: function (value) { return props.input.onChange(value); }, type: type, placeholder: props.placeholder, disabled: props.disabled }, props.inputProps));
65
- }, [props.disabled, props.input, props.inputProps, props.placeholder, type]);
65
+ return (__assign({ name: props.input.name, defaultValue: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: onChange, type: type, placeholder: props.placeholder, disabled: props.disabled, ref: inputRef }, props.inputProps));
66
+ }, [inputRef, onChange, props.disabled, props.input.name, props.input.value, props.inputProps, props.placeholder, type]);
66
67
  return components.ui.renderView(props.view || 'form.PasswordFieldView' || 'form.InputFieldView', __assign(__assign({}, props), { inputProps: inputProps, securityLevel: props.showSecurityBar ? (0, exports.checkPassword)(props.input.value) : null, onClear: onClear, onShowButtonClick: onShowButtonClick }));
67
68
  }
68
69
  PasswordField.defaultProps = {
@@ -20,6 +20,7 @@ var fieldWrapper_1 = __importDefault(require("../Field/fieldWrapper"));
20
20
  function TextField(props) {
21
21
  // const dispatch = useDispatch();
22
22
  var components = (0, hooks_1.useComponents)();
23
+ var _a = (0, hooks_1.useSaveCursorPosition)(props.input), inputRef = _a.inputRef, onChange = _a.onChange;
23
24
  var onKeyUp = (0, react_1.useCallback)(function (e) {
24
25
  if (props.submitOnEnter
25
26
  && props.formId
@@ -30,12 +31,12 @@ function TextField(props) {
30
31
  // dispatch(submit(props.formId));
31
32
  }
32
33
  }, [props.formId, props.submitOnEnter]);
33
- var onChange = (0, react_1.useCallback)(function (e) { return props.input.onChange.call(null, e.target ? e.target.value : e.nativeEvent.text); }, [props.input.onChange]);
34
+ var handleChange = (0, react_1.useCallback)(function (event) { return onChange(event, event.target ? event.target.value : event.nativeEvent.text); }, [onChange]);
34
35
  var onClear = (0, react_1.useCallback)(function () { return props.input.onChange(''); }, [props.input]);
35
36
  var inputProps = (0, react_1.useMemo)(function () {
36
37
  var _a;
37
- return (__assign({ name: props.input.name, defaultValue: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: onChange, onKeyUp: onKeyUp, placeholder: props.placeholder, disabled: props.disabled }, props.inputProps));
38
- }, [onKeyUp, onChange, props.disabled, props.input.name, props.input.value, props.inputProps, props.placeholder]);
38
+ return (__assign({ name: props.input.name, value: (_a = props.input.value) !== null && _a !== void 0 ? _a : '', onChange: handleChange, onKeyUp: onKeyUp, placeholder: props.placeholder, disabled: props.disabled, ref: inputRef }, props.inputProps));
39
+ }, [props.input.name, props.input.value, props.placeholder, props.disabled, props.inputProps, handleChange, onKeyUp, inputRef]);
39
40
  return components.ui.renderView(props.view || 'form.TextFieldView', __assign(__assign({}, props), { inputProps: inputProps, onClear: onClear }));
40
41
  }
41
42
  TextField.defaultProps = {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { Position } from '../../../hooks/useAbsolutePositioning';
2
+ import { PositionType } from '../../../hooks/useAbsolutePositioning';
3
3
  export interface ITooltipArrowPosition {
4
4
  /**
5
5
  * Позиция стрелки слева
@@ -50,7 +50,7 @@ export interface ITooltipProps {
50
50
  /**
51
51
  * Позиционирование подсказки, относительно целевого элемента
52
52
  */
53
- position?: Position;
53
+ position?: PositionType;
54
54
  /**
55
55
  * Показывать ли подсказку сразу после рендера страницы
56
56
  * @example true
@@ -84,7 +84,7 @@ export interface ITooltipProps {
84
84
  export interface ITooltipViewProps extends ITooltipProps {
85
85
  isTooltipVisible: boolean;
86
86
  content: string | any;
87
- position: Position;
87
+ position: PositionType;
88
88
  style: ITooltipStylePosition;
89
89
  }
90
90
  declare function Tooltip(props: ITooltipProps): JSX.Element;
@@ -69,7 +69,8 @@ function Grid(props) {
69
69
  items: props.items,
70
70
  initialItems: props.initialItems,
71
71
  initialTotal: props.initialTotal,
72
- autoFetchOnFormChanges: props.autoFetchOnFormChanges
72
+ autoFetchOnFormChanges: props.autoFetchOnFormChanges,
73
+ hasTreeItems: props.hasTreeItems
73
74
  }), list = _a.list, model = _a.model, searchModel = _a.searchModel, paginationPosition = _a.paginationPosition, paginationSizePosition = _a.paginationSizePosition, layoutNamesPosition = _a.layoutNamesPosition, renderList = _a.renderList, renderEmpty = _a.renderEmpty, renderPagination = _a.renderPagination, renderPaginationSize = _a.renderPaginationSize, renderLayoutNames = _a.renderLayoutNames, renderSearchForm = _a.renderSearchForm, onFetch = _a.onFetch, onSort = _a.onSort;
74
75
  var renderLabel = (0, react_1.useCallback)(function (column) {
75
76
  if (column.headerView) {
@@ -0,0 +1,35 @@
1
+ /// <reference types="react" />
2
+ import { IColumnViewProps, IGridColumn, IGridProps } from '../Grid/Grid';
3
+ export interface ITreeColumnViewProps extends IColumnViewProps {
4
+ item: {
5
+ onTreeItemClick?: (uniqueId: string, item: {
6
+ [key: string]: any;
7
+ }) => void;
8
+ [key: string]: any;
9
+ };
10
+ }
11
+ export interface ITreeTableItem {
12
+ /**
13
+ * Идентификатор узла
14
+ */
15
+ id?: string | number;
16
+ /**
17
+ * Вложенные элементы
18
+ * @example items: [{id: 3, name: 'Ivan'}]
19
+ */
20
+ items?: any[];
21
+ /**
22
+ * Уникальный идентификатор,
23
+ * используется для сохранения состояния открыта или закрыта ячейка
24
+ */
25
+ uniqueId?: string;
26
+ }
27
+ export interface ITreeTableProps extends Omit<IGridProps, 'items'> {
28
+ /**
29
+ * Элементы коллекции
30
+ * @example [{id: 1, name: 'Jane'}, {id: 2, name: 'John', items: [...]}]
31
+ */
32
+ items?: ITreeTableItem[];
33
+ }
34
+ export declare const addTreeColumnFieldsToFirstColumn: (columns: IGridColumn[]) => IGridColumn[];
35
+ export default function TreeTable(props: ITreeTableProps): JSX.Element;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
37
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
38
+ if (ar || !(i in from)) {
39
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
40
+ ar[i] = from[i];
41
+ }
42
+ }
43
+ return to.concat(ar || Array.prototype.slice.call(from));
44
+ };
45
+ var __importDefault = (this && this.__importDefault) || function (mod) {
46
+ return (mod && mod.__esModule) ? mod : { "default": mod };
47
+ };
48
+ exports.__esModule = true;
49
+ exports.addTreeColumnFieldsToFirstColumn = void 0;
50
+ var React = __importStar(require("react"));
51
+ var react_1 = require("react");
52
+ var merge_1 = __importDefault(require("lodash-es/merge"));
53
+ var Grid_1 = __importDefault(require("../Grid"));
54
+ var TREE_COLUMN_VIEW_FIELDS = {
55
+ valueView: 'TreeColumnView',
56
+ headerClassName: 'TreeColumnHeader'
57
+ };
58
+ var addTreeColumnFieldsToFirstColumn = function (columns) {
59
+ var newColumns = __spreadArray([], columns, true);
60
+ // Add tree view to the first column
61
+ (0, merge_1["default"])(newColumns[0], TREE_COLUMN_VIEW_FIELDS);
62
+ return newColumns;
63
+ };
64
+ exports.addTreeColumnFieldsToFirstColumn = addTreeColumnFieldsToFirstColumn;
65
+ function TreeTable(props) {
66
+ var columns = (0, react_1.useMemo)(function () { return (0, exports.addTreeColumnFieldsToFirstColumn)(props.columns); }, [props.columns]);
67
+ return (React.createElement(Grid_1["default"], __assign({}, props, { columns: columns, items: props.items, itemsIndexing: false, hasTreeItems: true })));
68
+ }
69
+ exports["default"] = TreeTable;
@@ -0,0 +1,2 @@
1
+ import TreeTable from './TreeTable';
2
+ export default TreeTable;
@@ -0,0 +1,7 @@
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
+ var TreeTable_1 = __importDefault(require("./TreeTable"));
7
+ exports["default"] = TreeTable_1["default"];
@@ -43,9 +43,9 @@ var omit_1 = __importDefault(require("lodash-es/omit"));
43
43
  var isEqual_1 = __importDefault(require("lodash-es/isEqual"));
44
44
  var keys_1 = __importDefault(require("lodash-es/keys"));
45
45
  var react_1 = require("react");
46
+ var list_1 = require("../../../utils/list");
46
47
  var hooks_1 = require("../../../hooks");
47
48
  var router_1 = require("../../../reducers/router");
48
- var resolveId = function (item, index, parentId) { return (parentId ? parentId + '.' : '') + String(item.id || index); };
49
49
  function Tree(props) {
50
50
  var components = (0, hooks_1.useComponents)();
51
51
  var STORAGE_KEY_PREFIX = 'tree_';
@@ -90,7 +90,7 @@ function Tree(props) {
90
90
  return null;
91
91
  }
92
92
  (sourceItems || []).forEach(function (item, index) {
93
- var uniqId = resolveId(item, index, parentId);
93
+ var uniqId = (0, list_1.getTreeItemUniqId)(item, index, parentId);
94
94
  if (!foundItem && (item.id === itemId || uniqId === itemId)) {
95
95
  foundItem = __assign(__assign({}, item), { uniqId: uniqId, level: level });
96
96
  }
@@ -105,7 +105,7 @@ function Tree(props) {
105
105
  if (level === void 0) { level = 1; }
106
106
  var opened = {};
107
107
  (sourceItems || []).forEach(function (item, index) {
108
- var uniqId = resolveId(item, index, parentId);
108
+ var uniqId = (0, list_1.getTreeItemUniqId)(item, index, parentId);
109
109
  if (props.autoOpenLevels >= level) {
110
110
  opened[uniqId] = true;
111
111
  }
@@ -159,7 +159,7 @@ function Tree(props) {
159
159
  return [];
160
160
  }
161
161
  (sourceItems || []).forEach(function (item, index) {
162
- var uniqId = resolveId(item, index, parentId);
162
+ var uniqId = (0, list_1.getTreeItemUniqId)(item, index, parentId);
163
163
  var isOpened = props.alwaysOpened || !!openedItems[uniqId];
164
164
  var hasItems = item[props.itemsKey] && item[props.itemsKey].length > 0;
165
165
  if (props.level && (level === props.level - 1)) {
@@ -0,0 +1 @@
1
+ export declare const getTreeItemUniqId: (item: any, index: any, parentId: any) => string;
package/utils/list.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ exports.__esModule = true;
3
+ exports.getTreeItemUniqId = void 0;
4
+ var getTreeItemUniqId = function (item, index, parentId) { return (parentId || '0') + '.' + String(item.id || index); };
5
+ exports.getTreeItemUniqId = getTreeItemUniqId;