@steroidsjs/core 3.0.4 → 3.0.6

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.
@@ -157,6 +157,7 @@ export default class MetaComponent implements IMetaComponent {
157
157
  time: {
158
158
  jsType: string;
159
159
  field: string;
160
+ formatter: string;
160
161
  };
161
162
  };
162
163
  }
@@ -166,7 +166,8 @@ var MetaComponent = /** @class */ (function () {
166
166
  },
167
167
  time: {
168
168
  jsType: 'string',
169
- field: 'TimeField'
169
+ field: 'TimeField',
170
+ formatter: 'TimeFormatter'
170
171
  }
171
172
  };
172
173
  };
@@ -4581,6 +4581,14 @@
4581
4581
  "type": "number",
4582
4582
  "example": "1"
4583
4583
  },
4584
+ {
4585
+ "name": "clientStorageId",
4586
+ "decorators": [],
4587
+ "description": "Идентификатор (ключ) для сохранения в LocalStorage коллекции.",
4588
+ "required": false,
4589
+ "type": "string",
4590
+ "example": "'exampleTree'"
4591
+ },
4584
4592
  {
4585
4593
  "name": "currentPage",
4586
4594
  "decorators": [],
@@ -4629,6 +4637,14 @@
4629
4637
  "type": "any",
4630
4638
  "example": null
4631
4639
  },
4640
+ {
4641
+ "name": "saveInClientStorage",
4642
+ "decorators": [],
4643
+ "description": "Сохранение в localStorage уровней вложенности.",
4644
+ "required": false,
4645
+ "type": "boolean",
4646
+ "example": "true"
4647
+ },
4632
4648
  {
4633
4649
  "name": "selectedItemId",
4634
4650
  "decorators": [],
@@ -15312,7 +15328,7 @@
15312
15328
  "'items'>",
15313
15329
  "IUiComponent",
15314
15330
  "Pick<ITreeProps",
15315
- "'levelPadding' | 'alwaysOpened' | 'customIcon'>"
15331
+ "'levelPadding' | 'alwaysOpened' | 'hasIconExpandOnly' | 'customIcon' | 'saveInClientStorage'>"
15316
15332
  ],
15317
15333
  "properties": [
15318
15334
  {
@@ -15423,6 +15439,24 @@
15423
15439
  "example": "items",
15424
15440
  "defaultValue": null
15425
15441
  },
15442
+ {
15443
+ "name": "hasIconExpandOnly",
15444
+ "decorators": [],
15445
+ "description": "Флаг, определяющий раскрывать вложенные элементы по клику на весь элемент или только на иконку",
15446
+ "required": false,
15447
+ "type": "boolean",
15448
+ "example": "false",
15449
+ "defaultValue": null
15450
+ },
15451
+ {
15452
+ "name": "hasOnlyLeafCheckboxes",
15453
+ "decorators": [],
15454
+ "description": "Отображать чекбоксы только на узлах, не имеющих вложенных элементов",
15455
+ "required": false,
15456
+ "type": "boolean",
15457
+ "example": null,
15458
+ "defaultValue": null
15459
+ },
15426
15460
  {
15427
15461
  "name": "hint",
15428
15462
  "decorators": [],
@@ -15567,6 +15601,15 @@
15567
15601
  "example": "true",
15568
15602
  "defaultValue": null
15569
15603
  },
15604
+ {
15605
+ "name": "saveInClientStorage",
15606
+ "decorators": [],
15607
+ "description": "Сохранение в localStorage уровней вложенности.",
15608
+ "required": false,
15609
+ "type": "boolean",
15610
+ "example": "true",
15611
+ "defaultValue": null
15612
+ },
15570
15613
  {
15571
15614
  "name": "selectFirst",
15572
15615
  "decorators": [],
@@ -15662,7 +15705,7 @@
15662
15705
  "extends": [
15663
15706
  "IFieldWrapperOutputProps",
15664
15707
  "Pick<ITreeProps",
15665
- "'levelPadding' | 'customIcon'>"
15708
+ "'levelPadding' | 'hasIconExpandOnly' | 'customIcon'>"
15666
15709
  ],
15667
15710
  "properties": [
15668
15711
  {
@@ -15697,6 +15740,22 @@
15697
15740
  "type": "string",
15698
15741
  "example": null
15699
15742
  },
15743
+ {
15744
+ "name": "hasIconExpandOnly",
15745
+ "decorators": [],
15746
+ "description": "Флаг, определяющий раскрывать вложенные элементы по клику на весь элемент или только на иконку",
15747
+ "required": false,
15748
+ "type": "boolean",
15749
+ "example": "false"
15750
+ },
15751
+ {
15752
+ "name": "hasOnlyLeafCheckboxes",
15753
+ "decorators": [],
15754
+ "description": "",
15755
+ "required": false,
15756
+ "type": "boolean",
15757
+ "example": null
15758
+ },
15700
15759
  {
15701
15760
  "name": "input",
15702
15761
  "decorators": [],
@@ -31130,6 +31189,45 @@
31130
31189
  ],
31131
31190
  "methods": []
31132
31191
  },
31192
+ "ITimeFormatterProps": {
31193
+ "name": "ITimeFormatterProps",
31194
+ "moduleName": "ui/format/TimeFormatter/TimeFormatter",
31195
+ "title": "TimeFormatter",
31196
+ "description": "\nКомпонент TimeFormatter предназначен для форматирования времени с использованием заданного формата.\nОн позволяет кастомизировать отображение времени, используя переданный view React компонент.\n",
31197
+ "tags": {},
31198
+ "defaultProps": null,
31199
+ "extends": [],
31200
+ "properties": [
31201
+ {
31202
+ "name": "format",
31203
+ "decorators": [],
31204
+ "description": "Формат времени",
31205
+ "required": false,
31206
+ "type": "string",
31207
+ "example": "HH:mm",
31208
+ "defaultValue": null
31209
+ },
31210
+ {
31211
+ "name": "value",
31212
+ "decorators": [],
31213
+ "description": "Время",
31214
+ "required": false,
31215
+ "type": "string",
31216
+ "example": "16:15",
31217
+ "defaultValue": null
31218
+ },
31219
+ {
31220
+ "name": "view",
31221
+ "decorators": [],
31222
+ "description": "Переопределение view React компонента для кастомизации отображения колонки",
31223
+ "required": false,
31224
+ "type": "React.ReactNode | {}",
31225
+ "example": "MyCustomView",
31226
+ "defaultValue": null
31227
+ }
31228
+ ],
31229
+ "methods": []
31230
+ },
31133
31231
  "IHeaderProps": {
31134
31232
  "name": "IHeaderProps",
31135
31233
  "moduleName": "ui/layout/Header/Header",
@@ -36511,7 +36609,7 @@
36511
36609
  "extends": [
36512
36610
  "IColumnViewProps",
36513
36611
  "Pick<ITreeTableProps",
36514
- "'levelPadding' | 'customIcon'>"
36612
+ "'levelPadding' | 'customIcon' >"
36515
36613
  ],
36516
36614
  "properties": [
36517
36615
  {
@@ -36717,13 +36815,14 @@
36717
36815
  "tags": {},
36718
36816
  "defaultProps": {
36719
36817
  "alwaysOpened": "false",
36720
- "levelPadding": "32"
36818
+ "levelPadding": "32",
36819
+ "saveInClientStorage": "false"
36721
36820
  },
36722
36821
  "extends": [
36723
36822
  "Omit<IGridProps",
36724
36823
  "'items'>",
36725
36824
  "Pick<ITreeProps",
36726
- "'alwaysOpened' | 'levelPadding' | 'customIcon'>"
36825
+ "'alwaysOpened' | 'levelPadding' | 'customIcon' | 'saveInClientStorage'>"
36727
36826
  ],
36728
36827
  "properties": [
36729
36828
  {
@@ -36761,6 +36860,15 @@
36761
36860
  "type": "number",
36762
36861
  "example": "32",
36763
36862
  "defaultValue": "32"
36863
+ },
36864
+ {
36865
+ "name": "saveInClientStorage",
36866
+ "decorators": [],
36867
+ "description": "Сохранение в localStorage уровней вложенности.",
36868
+ "required": false,
36869
+ "type": "boolean",
36870
+ "example": "true",
36871
+ "defaultValue": "false"
36764
36872
  }
36765
36873
  ],
36766
36874
  "methods": []
@@ -38776,6 +38884,14 @@
38776
38884
  "type": "string",
38777
38885
  "example": null
38778
38886
  },
38887
+ {
38888
+ "name": "clientStorageId",
38889
+ "decorators": [],
38890
+ "description": "Идентификатор (ключ) для сохранения в LocalStorage коллекции.",
38891
+ "required": false,
38892
+ "type": "string",
38893
+ "example": "'exampleTree'"
38894
+ },
38779
38895
  {
38780
38896
  "name": "customIcon",
38781
38897
  "decorators": [],
@@ -38856,6 +38972,14 @@
38856
38972
  "type": "any",
38857
38973
  "example": null
38858
38974
  },
38975
+ {
38976
+ "name": "saveInClientStorage",
38977
+ "decorators": [],
38978
+ "description": "Сохранение в localStorage уровней вложенности.",
38979
+ "required": false,
38980
+ "type": "boolean",
38981
+ "example": "true"
38982
+ },
38859
38983
  {
38860
38984
  "name": "selectedItemId",
38861
38985
  "decorators": [],
@@ -38879,6 +39003,14 @@
38879
39003
  "required": false,
38880
39004
  "type": "React.ReactNode | {}",
38881
39005
  "example": "MyCustomView"
39006
+ },
39007
+ {
39008
+ "name": "withoutPointerOnLabel",
39009
+ "decorators": [],
39010
+ "description": "",
39011
+ "required": false,
39012
+ "type": "boolean",
39013
+ "example": null
38882
39014
  }
38883
39015
  ],
38884
39016
  "methods": [
@@ -38942,6 +39074,7 @@
38942
39074
  "itemsKey": "'items'",
38943
39075
  "level": "0",
38944
39076
  "levelPadding": "32",
39077
+ "saveInClientStorage": "false",
38945
39078
  "useSameSelectedItemId": "true"
38946
39079
  },
38947
39080
  "extends": [
@@ -38985,6 +39118,15 @@
38985
39118
  "example": null,
38986
39119
  "defaultValue": null
38987
39120
  },
39121
+ {
39122
+ "name": "clientStorageId",
39123
+ "decorators": [],
39124
+ "description": "Идентификатор (ключ) для сохранения в LocalStorage коллекции.",
39125
+ "required": false,
39126
+ "type": "string",
39127
+ "example": "'exampleTree'",
39128
+ "defaultValue": null
39129
+ },
38988
39130
  {
38989
39131
  "name": "customIcon",
38990
39132
  "decorators": [],
@@ -39066,6 +39208,15 @@
39066
39208
  "example": null,
39067
39209
  "defaultValue": null
39068
39210
  },
39211
+ {
39212
+ "name": "saveInClientStorage",
39213
+ "decorators": [],
39214
+ "description": "Сохранение в localStorage уровней вложенности.",
39215
+ "required": false,
39216
+ "type": "boolean",
39217
+ "example": "true",
39218
+ "defaultValue": "false"
39219
+ },
39069
39220
  {
39070
39221
  "name": "selectedItemId",
39071
39222
  "decorators": [],
@@ -39184,6 +39335,14 @@
39184
39335
  "type": "string",
39185
39336
  "example": null
39186
39337
  },
39338
+ {
39339
+ "name": "clientStorageId",
39340
+ "decorators": [],
39341
+ "description": "Идентификатор (ключ) для сохранения в LocalStorage коллекции.",
39342
+ "required": false,
39343
+ "type": "string",
39344
+ "example": "'exampleTree'"
39345
+ },
39187
39346
  {
39188
39347
  "name": "customIcon",
39189
39348
  "decorators": [],
@@ -39256,6 +39415,14 @@
39256
39415
  "type": "any",
39257
39416
  "example": null
39258
39417
  },
39418
+ {
39419
+ "name": "saveInClientStorage",
39420
+ "decorators": [],
39421
+ "description": "Сохранение в localStorage уровней вложенности.",
39422
+ "required": false,
39423
+ "type": "boolean",
39424
+ "example": "true"
39425
+ },
39259
39426
  {
39260
39427
  "name": "selectedItemId",
39261
39428
  "decorators": [],
@@ -43098,6 +43265,13 @@
43098
43265
  "description": "",
43099
43266
  "tags": {}
43100
43267
  },
43268
+ "ui/format/TimeFormatter/TimeFormatter": {
43269
+ "name": "default",
43270
+ "moduleName": "ui/format/TimeFormatter/TimeFormatter",
43271
+ "title": "",
43272
+ "description": "",
43273
+ "tags": {}
43274
+ },
43101
43275
  "ui/layout/Header/Header": {
43102
43276
  "name": "default",
43103
43277
  "moduleName": "ui/layout/Header/Header",
package/en.json CHANGED
@@ -955,23 +955,17 @@
955
955
  "Будет ли отображён item?": "",
956
956
  "Устанавливать ли фокус и показывать панель времени сразу после рендера страницы": "",
957
957
  "Флаг, определяющий раскрывать вложенные элементы по клику на весь элемент или только на иконку": "",
958
- "\nКомпонент `Kanban` позволяет создать доску для управления задачами.\nКоличество столбцов задается с помощью пропа `columns`.\nЗадачи на доске можно создавать, редактировать и перемещать с визуальным отображением.\n\nДля работы этого компонента необходимо установить в проекте зависимости `react-beautiful-dnd`\nи передать в пропсы `droppableComponent`, `draggableComponent` и `dndContext`\nкомпоненты `Droppable`, `Draggable` и `DragDropContext` соответственно.\n\nДля корректной работы функционала создания задач,\nнеобходимо установить в проекте зависимости `@ckeditor/ckeditor5-react` и `@steroidsjs/ckeditor5`,\nзатем импортировать `CKEditor` из `@ckeditor/ckeditor5-react` и `ClassicEditor` из `@steroidsjs/ckeditor5/packages/ckeditor5-build-classic`.\nИмпортированные компоненты нужно передать в проп `createTaskEditorConfig`,\nв поле `htmlComponent` передать `CKEditor`, а в `editorConstructor` передать `ClassicEditor`.\n": "",
959
- "Компонент для создания HTML-разметки, использующий WYSIWYG редактор.\n\nДля использования WYSIWYG редактора, необходимо установить в проекте зависимости `@ckeditor/ckeditor5-react` и `@steroidsjs/ckeditor5`,\nзатем импортировать `CKEditor` из `@ckeditor/ckeditor5-react` и `ClassicEditor` из `@steroidsjs/ckeditor5/packages/ckeditor5-build-classic`.\nКомпонент `CKEditor` нужно передать в проп `htmlComponent`, а конструктор `ClassicEditor` в проп `editorConstructor`.\n\nПри передаче `HtmlField` с бэкенда, необходимо переопределить `view` компонента, указав локальный.\nВ локальном компоненте добавить вместо пропсов `htmlComponent` и `editorConstructor` импорты `CKEditor` и `ClassicEditor` соотвественно.\n": "",
960
958
  "\nКомпонент для создания пошаговой формы. Предоставляет управление и синхронизацию состояния формы,\nа также список шагов формы для удобной навигации.\nПоля для шагов можно передавать как в виде компонентов, так и в виде объекта с названием поля.\nПозволяет выполнять отправку данных формы на сервер с возможностью валидации и перехода к неверно заполненным полям.\n\n": "",
959
+ "\nКомпонент TimeFormatter предназначен для форматирования времени с использованием заданного формата.\nОн позволяет кастомизировать отображение времени, используя переданный view React компонент.\n": "",
961
960
  "При повторном нажатии на выбранный элемент из дерева, он продолжит отображаться как активный.": "",
962
961
  "Дополнительные свойства, которые передаются во view компонента": "",
963
962
  "Пропсы для инпута загрузки файлов": "",
964
963
  "Переопределение view React компонента для кастомизации отображения элемента инпута": "",
965
964
  "Кастомный placeholder для инпута": "",
966
- "Компоненты для подключения wysiwyg редактора": "",
967
965
  "Если true, то location.pathname будет совпадать с теми путями, которые содержат слеш в конце.\nНапример, если указать путь '/catalog/', тогда совпадение будет с '/catalog/' и '/catalog/chapter', но не '/catalog'.": "",
968
966
  "Кастомная иконка, заменяющая иконку раскрытия по умолчанию": "",
969
967
  "Опции маски для полей": "",
970
- "Размер компонента и вложенных полей": "",
971
968
  "Дополнительные кнопки": "",
972
- "Параметры для кнопки отправки формы": "",
973
- "Конструктор редактора 'ckeditor5-react' из библиотеки @steroidsjs/ckeditor5/packages/ckeditor5-build-classic\nПримечание: для использования встроенного отображения 'HtmlField', данный компонент должен быть передан": "",
974
- "Компонент редактора 'ckeditor5-react' из библиотеки @ckeditor\nПримечание: для использования встроенного отображения 'HtmlField', данный компонент должен быть передан": "",
975
969
  "Свойства для Form": "",
976
970
  "Свойства для кнопки продолжить/отправить": "",
977
971
  "Свойства для кнопки возврата": "",
@@ -979,7 +973,19 @@
979
973
  "Ориентация списка шагов формы": "",
980
974
  "Коллекция полей и аттрибутов для каждого шага формы. Можно передавать как компонент, так и объект с полями.\nГлавное, чтобы внутри шага использовался один из способов.": "",
981
975
  "Свойства для Steps": "",
982
- "CSS-класс для элемента навигации.": "",
976
+ "Формат времени": "",
977
+ "Время": "",
983
978
  "Флаг, который позволяет использовать вложенные роуты c указанием вложенного пути": "",
984
- "Скрыть иконку c лева от элемента": ""
979
+ "Скрыть иконку c лева от элемента": "",
980
+ "\nКомпонент `Kanban` позволяет создать доску для управления задачами.\nКоличество столбцов задается с помощью пропа `columns`.\nЗадачи на доске можно создавать, редактировать и перемещать с визуальным отображением.\n\nДля работы этого компонента необходимо установить в проекте зависимости `react-beautiful-dnd`\nи передать в пропсы `droppableComponent`, `draggableComponent` и `dndContext`\nкомпоненты `Droppable`, `Draggable` и `DragDropContext` соответственно.\n\nДля корректной работы функционала создания задач,\nнеобходимо установить в проекте зависимости `@ckeditor/ckeditor5-react` и `@steroidsjs/ckeditor5`,\nзатем импортировать `CKEditor` из `@ckeditor/ckeditor5-react` и `ClassicEditor` из `@steroidsjs/ckeditor5/packages/ckeditor5-build-classic`.\nИмпортированные компоненты нужно передать в проп `createTaskEditorConfig`,\nв поле `htmlComponent` передать `CKEditor`, а в `editorConstructor` передать `ClassicEditor`.\n": "",
981
+ "Компонент для создания HTML-разметки, использующий WYSIWYG редактор.\n\nДля использования WYSIWYG редактора, необходимо установить в проекте зависимости `@ckeditor/ckeditor5-react` и `@steroidsjs/ckeditor5`,\nзатем импортировать `CKEditor` из `@ckeditor/ckeditor5-react` и `ClassicEditor` из `@steroidsjs/ckeditor5/packages/ckeditor5-build-classic`.\nКомпонент `CKEditor` нужно передать в проп `htmlComponent`, а конструктор `ClassicEditor` в проп `editorConstructor`.\n\nПри передаче `HtmlField` с бэкенда, необходимо переопределить `view` компонента, указав локальный.\nВ локальном компоненте добавить вместо пропсов `htmlComponent` и `editorConstructor` импорты `CKEditor` и `ClassicEditor` соотвественно.\n": "",
982
+ "Идентификатор (ключ) для сохранения в LocalStorage коллекции.": "",
983
+ "Сохранение в localStorage уровней вложенности.": "",
984
+ "Компоненты для подключения wysiwyg редактора": "",
985
+ "Отображать чекбоксы только на узлах, не имеющих вложенных элементов": "",
986
+ "Размер компонента и вложенных полей": "",
987
+ "Параметры для кнопки отправки формы": "",
988
+ "Конструктор редактора 'ckeditor5-react' из библиотеки @steroidsjs/ckeditor5/packages/ckeditor5-build-classic\nПримечание: для использования встроенного отображения 'HtmlField', данный компонент должен быть передан": "",
989
+ "Компонент редактора 'ckeditor5-react' из библиотеки @ckeditor\nПримечание: для использования встроенного отображения 'HtmlField', данный компонент должен быть передан": "",
990
+ "CSS-класс для элемента навигации.": ""
985
991
  }
@@ -100,5 +100,15 @@ export interface ITreeConfig {
100
100
  * @example true
101
101
  */
102
102
  useSameSelectedItemId?: boolean;
103
+ /**
104
+ * Сохранение в localStorage уровней вложенности.
105
+ * @example true
106
+ */
107
+ saveInClientStorage?: boolean;
108
+ /**
109
+ * Идентификатор (ключ) для сохранения в LocalStorage коллекции.
110
+ * @example 'exampleTree'
111
+ */
112
+ clientStorageId?: string;
103
113
  }
104
114
  export default function useTree(config: ITreeConfig): ITreeOutput;
package/hooks/useTree.js CHANGED
@@ -22,12 +22,15 @@ var isEqual_1 = __importDefault(require("lodash-es/isEqual"));
22
22
  var isEmpty_1 = __importDefault(require("lodash-es/isEmpty"));
23
23
  var isNil_1 = __importDefault(require("lodash-es/isNil"));
24
24
  var keys_1 = __importDefault(require("lodash-es/keys"));
25
+ var react_use_1 = require("react-use");
26
+ var useComponents_1 = __importDefault(require("./useComponents"));
25
27
  var useSelector_1 = __importDefault(require("./useSelector"));
26
28
  var router_1 = require("../reducers/router");
27
29
  var INITIAL_CURRENT_LEVEL = 0;
28
30
  var DOT_SEPARATOR = '.';
29
31
  var EMPTY_PARENT_ID = '';
30
32
  var FIRST_LEVEL_PARENT_ID = '0';
33
+ var CLIENT_STORAGE_KEY = '_tree';
31
34
  var defaultProps = {
32
35
  itemsKey: 'items',
33
36
  autoOpenLevels: 0
@@ -96,6 +99,7 @@ function useTree(config) {
96
99
  var primaryKey = config.itemsKey || defaultProps.itemsKey;
97
100
  var _a = (0, react_1.useState)(null), selectedUniqueId = _a[0], setSelectedUniqueId = _a[1];
98
101
  var _b = (0, react_1.useState)({}), expandedItems = _b[0], setExpandedItems = _b[1];
102
+ var clientStorage = (0, useComponents_1["default"])().clientStorage;
99
103
  //Redux connection
100
104
  var _c = (0, useSelector_1["default"])(function (state) { return ({
101
105
  routes: (0, isString_1["default"])(config.items) ? (0, router_1.getNavItems)(state, config.items) : null,
@@ -116,11 +120,32 @@ function useTree(config) {
116
120
  }, [config.items, config.routerParams, routes]);
117
121
  // Initial expanded items
118
122
  (0, react_1.useEffect)(function () {
119
- // TODO add clientStorage
120
123
  setExpandedItems(getAutoExpandedItems(items, selectedItemId, primaryKey, config.autoOpenLevels));
121
124
  var selectedItem = findChildById(items, selectedItemId, primaryKey);
122
125
  setSelectedUniqueId(selectedItem ? selectedItem.uniqueId : null);
123
126
  }, [items]);
127
+ var localTree = JSON.parse(clientStorage.get(CLIENT_STORAGE_KEY)) || {};
128
+ var saveInClientStorage = function () {
129
+ if (config.saveInClientStorage) {
130
+ localTree[config.clientStorageId] = expandedItems;
131
+ clientStorage.set(CLIENT_STORAGE_KEY, JSON.stringify(localTree));
132
+ }
133
+ };
134
+ (0, react_use_1.useMount)(function () {
135
+ if (config.saveInClientStorage) {
136
+ var treeData = localTree[config.clientStorageId];
137
+ if (treeData) {
138
+ setExpandedItems(treeData);
139
+ }
140
+ }
141
+ });
142
+ (0, react_use_1.useUnmount)(function () {
143
+ saveInClientStorage();
144
+ });
145
+ (0, react_use_1.useBeforeUnload)(function () {
146
+ saveInClientStorage();
147
+ return true;
148
+ });
124
149
  var onExpand = (0, react_1.useCallback)(function (e, uniqueId, item) {
125
150
  var _a;
126
151
  e.preventDefault();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steroidsjs/core",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "",
5
5
  "author": "Vladimir Kozhin <hello@kozhindev.com>",
6
6
  "repository": {
@@ -19,7 +19,7 @@ type CheckboxTreeItems = string | ({
19
19
  *
20
20
  * Список с вложенными чекбоксами. Используется в формах для создания иерархической структуры и выбора нескольких значений.
21
21
  */
22
- export interface ICheckboxTreeFieldProps extends IFieldWrapperInputProps, Omit<IDataProviderConfig, 'items'>, Omit<IDataSelectConfig, 'items'>, IUiComponent, Pick<ITreeProps, 'levelPadding' | 'alwaysOpened' | 'customIcon'> {
22
+ export interface ICheckboxTreeFieldProps extends IFieldWrapperInputProps, Omit<IDataProviderConfig, 'items'>, Omit<IDataSelectConfig, 'items'>, IUiComponent, Pick<ITreeProps, 'levelPadding' | 'alwaysOpened' | 'hasIconExpandOnly' | 'customIcon' | 'saveInClientStorage'> {
23
23
  /**
24
24
  * Свойства для элемента input
25
25
  * @example { onKeyDown: ... }
@@ -46,9 +46,13 @@ export interface ICheckboxTreeFieldProps extends IFieldWrapperInputProps, Omit<I
46
46
  * Первичный ключ для доступа к вложенным элементам
47
47
  */
48
48
  primaryKey?: string;
49
+ /**
50
+ * Отображать чекбоксы только на узлах, не имеющих вложенных элементов
51
+ */
52
+ hasOnlyLeafCheckboxes?: boolean;
49
53
  [key: string]: any;
50
54
  }
51
- export interface ICheckboxTreeFieldViewProps extends IFieldWrapperOutputProps, Pick<ITreeProps, 'levelPadding' | 'customIcon'> {
55
+ export interface ICheckboxTreeFieldViewProps extends IFieldWrapperOutputProps, Pick<ITreeProps, 'levelPadding' | 'hasIconExpandOnly' | 'customIcon'> {
52
56
  items: {
53
57
  id: number | string | boolean;
54
58
  label?: string;
@@ -61,7 +65,8 @@ export interface ICheckboxTreeFieldViewProps extends IFieldWrapperOutputProps, P
61
65
  onItemSelect: (checkbox: IPreparedTreeItem) => void;
62
66
  renderCheckbox: (checkboxProps: ICheckboxFieldViewProps) => JSX.Element;
63
67
  size?: Size;
68
+ hasOnlyLeafCheckboxes?: boolean;
64
69
  }
65
- export declare const getNestedItemsIds: (item: any, groupAttribute: any) => any[];
70
+ export declare const getNestedItemsIds: (item: any, groupAttribute: any, hasOnlyLeafCheckboxes?: boolean) => any[];
66
71
  declare const _default: import("../../../ui/form/Field/fieldWrapper").FieldWrapperComponent<ICheckboxTreeFieldProps>;
67
72
  export default _default;
@@ -41,14 +41,16 @@ exports.getNestedItemsIds = void 0;
41
41
  var react_1 = __importStar(require("react"));
42
42
  var react_use_1 = require("react-use");
43
43
  var isArray_1 = __importDefault(require("lodash-es/isArray"));
44
+ var isEqual_1 = __importDefault(require("lodash-es/isEqual"));
44
45
  var hooks_1 = require("../../../hooks");
45
46
  var fieldWrapper_1 = __importDefault(require("../../../ui/form/Field/fieldWrapper"));
46
- var getNestedItemsIds = function (item, groupAttribute) {
47
+ var getNestedItemsIds = function (item, groupAttribute, hasOnlyLeafCheckboxes) {
48
+ if (hasOnlyLeafCheckboxes === void 0) { hasOnlyLeafCheckboxes = false; }
47
49
  if (item.disabled) {
48
50
  return [];
49
51
  }
50
52
  var _a = item, _b = groupAttribute, _c = _a[_b], nestedItems = _c === void 0 ? [] : _c;
51
- var result = [item.id];
53
+ var result = hasOnlyLeafCheckboxes ? [] : [item.id];
52
54
  if (groupAttribute && (0, isArray_1["default"])(nestedItems)) {
53
55
  nestedItems.reduce(function (acc, nestedItem) {
54
56
  if ((0, isArray_1["default"])(nestedItem[groupAttribute])) {
@@ -68,7 +70,9 @@ function CheckboxTreeField(props) {
68
70
  var inputSelectedIds = (0, react_1.useMemo)(function () { return props.selectedIds || [].concat(props.input.value || []); }, [props.input.value, props.selectedIds]);
69
71
  // Data Provider
70
72
  var items = (0, hooks_1.useDataProvider)({
71
- items: props.items
73
+ items: props.items,
74
+ initialSelectedIds: inputSelectedIds,
75
+ dataProvider: props.dataProvider
72
76
  }).items;
73
77
  // Tree items
74
78
  var treeItems = (0, hooks_1.useTree)({
@@ -86,23 +90,26 @@ function CheckboxTreeField(props) {
86
90
  }), selectedIds = _a.selectedIds, setSelectedIds = _a.setSelectedIds;
87
91
  var onItemSelect = (0, react_1.useCallback)(function (checkbox) {
88
92
  if (checkbox.hasItems) {
89
- var selectedItemIds = (0, exports.getNestedItemsIds)(checkbox, props.primaryKey);
93
+ var selectedItemIds = (0, exports.getNestedItemsIds)(checkbox, props.primaryKey, props.hasOnlyLeafCheckboxes);
90
94
  setSelectedIds(selectedItemIds);
91
95
  }
92
- else {
96
+ else if (checkbox.id && !checkbox.hasItems) {
93
97
  setSelectedIds(checkbox.id);
94
98
  }
95
- }, [props.primaryKey, setSelectedIds]);
96
- // Sync with form
97
- (0, react_1.useEffect)(function () {
98
- props.input.onChange.call(null, selectedIds);
99
- if (props.onChange) {
100
- props.onChange(selectedIds);
101
- }
102
- }, [props, props.input.onChange, selectedIds]);
99
+ }, [props.hasOnlyLeafCheckboxes, props.primaryKey, setSelectedIds]);
103
100
  var onReset = (0, react_1.useCallback)(function () {
104
101
  setSelectedIds([]);
105
102
  }, [setSelectedIds]);
103
+ // Sync with form
104
+ var prevSelectedIds = (0, react_use_1.usePrevious)(selectedIds);
105
+ (0, react_1.useEffect)(function () {
106
+ if (!(0, isEqual_1["default"])(prevSelectedIds || [], selectedIds)) {
107
+ props.input.onChange.call(null, selectedIds);
108
+ if (props.onChange) {
109
+ props.onChange.call(null, selectedIds);
110
+ }
111
+ }
112
+ }, [prevSelectedIds, props.input.onChange, props.multiple, props.onChange, selectedIds]);
106
113
  // Reset selected ids on form reset
107
114
  var prevInputValue = (0, react_use_1.usePrevious)(props.input.value);
108
115
  (0, react_use_1.useUpdateEffect)(function () {
@@ -119,8 +126,10 @@ function CheckboxTreeField(props) {
119
126
  selectedIds: selectedIds,
120
127
  renderCheckbox: renderCheckbox,
121
128
  size: props.size,
122
- levelPadding: props.levelPadding
123
- }); }, [onItemSelect, props.levelPadding, props.size, renderCheckbox, selectedIds, treeItems]);
129
+ levelPadding: props.levelPadding,
130
+ hasOnlyLeafCheckboxes: props.hasOnlyLeafCheckboxes,
131
+ hasIconExpandOnly: props.hasIconExpandOnly
132
+ }); }, [treeItems, onItemSelect, selectedIds, renderCheckbox, props.size, props.levelPadding, props.hasOnlyLeafCheckboxes, props.hasIconExpandOnly]);
124
133
  return components.ui.renderView(props.view || 'form.CheckboxTreeFieldView', viewProps);
125
134
  }
126
135
  CheckboxTreeField.defaultProps = {
@@ -129,6 +138,9 @@ CheckboxTreeField.defaultProps = {
129
138
  className: '',
130
139
  levelPadding: 32,
131
140
  alwaysOpened: false,
132
- primaryKey: 'items'
141
+ primaryKey: 'items',
142
+ hasOnlyLeafCheckboxes: false,
143
+ hasIconExpandOnly: true,
144
+ saveInClientStorage: false
133
145
  };
134
146
  exports["default"] = (0, fieldWrapper_1["default"])('CheckboxTreeField', CheckboxTreeField);
@@ -50,7 +50,8 @@ function NumberField(props) {
50
50
  var onInputChange = (0, useInputTypeNumber_1["default"])(currentInputRef, {
51
51
  max: props.max,
52
52
  min: props.min,
53
- value: props.input.value
53
+ value: props.input.value,
54
+ required: props.required
54
55
  }, onChange).onInputChange;
55
56
  var onStep = (0, react_1.useCallback)(function (isIncrement) {
56
57
  var _a;
@@ -3,6 +3,7 @@ interface IInputTypeNumberProps {
3
3
  max: any;
4
4
  min: any;
5
5
  value: string | undefined | null;
6
+ required?: boolean;
6
7
  }
7
8
  declare const useInputTypeNumber: (currentInputRef: React.MutableRefObject<HTMLInputElement>, inputTypeNumberProps: IInputTypeNumberProps, onChange: (event: React.ChangeEvent<HTMLInputElement>, value?: any) => void) => {
8
9
  onInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
@@ -7,14 +7,15 @@ var react_1 = __importDefault(require("react"));
7
7
  var useInputTypeNumber = function (currentInputRef, inputTypeNumberProps, onChange) {
8
8
  react_1["default"].useEffect(function () {
9
9
  var _a;
10
- var defaultValidity = 'The number is not valid.';
11
- var errorMessage = inputTypeNumberProps.value > inputTypeNumberProps.max
12
- || inputTypeNumberProps.value < inputTypeNumberProps.min
13
- || !inputTypeNumberProps.value
14
- ? __(defaultValidity)
10
+ var defaultValidity = __('The number is not valid.');
11
+ var errorMessage = inputTypeNumberProps.required
12
+ && (inputTypeNumberProps.value > inputTypeNumberProps.max
13
+ || inputTypeNumberProps.value < inputTypeNumberProps.min
14
+ || !inputTypeNumberProps.value)
15
+ ? defaultValidity
15
16
  : '';
16
17
  (_a = currentInputRef.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(errorMessage);
17
- }, [currentInputRef, inputTypeNumberProps.value, inputTypeNumberProps.max, inputTypeNumberProps.min]);
18
+ }, [currentInputRef, inputTypeNumberProps.value, inputTypeNumberProps.max, inputTypeNumberProps.min, inputTypeNumberProps.required]);
18
19
  var isValueNumeric = function (value) {
19
20
  if (!value) {
20
21
  return true;
@@ -0,0 +1,26 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * TimeFormatter
4
+ *
5
+ * Компонент TimeFormatter предназначен для форматирования времени с использованием заданного формата.
6
+ * Он позволяет кастомизировать отображение времени, используя переданный view React компонент.
7
+ **/
8
+ export interface ITimeFormatterProps {
9
+ /**
10
+ * Формат времени
11
+ * @example HH:mm
12
+ */
13
+ format?: string;
14
+ /**
15
+ * Время
16
+ * @example 16:15
17
+ */
18
+ value?: string;
19
+ /**
20
+ * Переопределение view React компонента для кастомизации отображения колонки
21
+ * @example MyCustomView
22
+ */
23
+ view?: CustomView;
24
+ [key: string]: any;
25
+ }
26
+ export default function TimeFormatter(props: ITimeFormatterProps): JSX.Element;
@@ -0,0 +1,21 @@
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 hooks_1 = require("@steroidsjs/core/hooks");
7
+ var dayjs_1 = __importDefault(require("dayjs"));
8
+ var defaultProps = {
9
+ format: 'HH:mm'
10
+ };
11
+ function TimeFormatter(props) {
12
+ var components = (0, hooks_1.useComponents)();
13
+ if (!props.value) {
14
+ return null;
15
+ }
16
+ var time = (0, dayjs_1["default"])(props.value).format(props.format || defaultProps.format);
17
+ return components.ui.renderView(props.view || 'format.DefaultFormatterView', {
18
+ value: time
19
+ });
20
+ }
21
+ exports["default"] = TimeFormatter;
@@ -0,0 +1,2 @@
1
+ import TimeFormatter from './TimeFormatter';
2
+ export default TimeFormatter;
@@ -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 TimeFormatter_1 = __importDefault(require("./TimeFormatter"));
7
+ exports["default"] = TimeFormatter_1["default"];
@@ -10,7 +10,7 @@ export interface ITreeColumnViewProps extends IColumnViewProps, Pick<ITreeTableP
10
10
  *
11
11
  * Компонент для представления данных коллекции в виде иерархической структуры.
12
12
  */
13
- export interface ITreeTableProps extends Omit<IGridProps, 'items'>, Pick<ITreeProps, 'alwaysOpened' | 'levelPadding' | 'customIcon'> {
13
+ export interface ITreeTableProps extends Omit<IGridProps, 'items'>, Pick<ITreeProps, 'alwaysOpened' | 'levelPadding' | 'customIcon' | 'saveInClientStorage'> {
14
14
  /**
15
15
  * Элементы коллекции
16
16
  * @example
@@ -35,6 +35,7 @@ declare namespace TreeTable {
35
35
  var defaultProps: {
36
36
  levelPadding: number;
37
37
  alwaysOpened: boolean;
38
+ saveInClientStorage: boolean;
38
39
  };
39
40
  }
40
41
  export default TreeTable;
@@ -73,12 +73,15 @@ function TreeTable(props) {
73
73
  autoOpenLevels: 0,
74
74
  alwaysOpened: props.alwaysOpened,
75
75
  currentPage: list === null || list === void 0 ? void 0 : list.page,
76
- itemsOnPage: list === null || list === void 0 ? void 0 : list.pageSize
76
+ itemsOnPage: list === null || list === void 0 ? void 0 : list.pageSize,
77
+ saveInClientStorage: props.saveInClientStorage,
78
+ clientStorageId: props.listId
77
79
  }).treeItems;
78
80
  return (React.createElement(Grid_1["default"], __assign({}, props, { columns: columns, items: treeItems, itemsIndexing: false })));
79
81
  }
80
82
  exports["default"] = TreeTable;
81
83
  TreeTable.defaultProps = {
82
84
  levelPadding: 32,
83
- alwaysOpened: false
85
+ alwaysOpened: false,
86
+ saveInClientStorage: false
84
87
  };
@@ -6,6 +6,7 @@ export interface ITreeViewProps extends ITreeProps {
6
6
  export interface ITreeItemViewProps extends ITreeProps {
7
7
  item: IPreparedTreeItem;
8
8
  children?: JSX.Element;
9
+ withoutPointerOnLabel?: boolean;
9
10
  }
10
11
  /**
11
12
  * Tree
@@ -68,6 +69,7 @@ declare namespace Tree {
68
69
  hasIconExpandOnly: boolean;
69
70
  useSameSelectedItemId: boolean;
70
71
  hideIcon: boolean;
72
+ saveInClientStorage: boolean;
71
73
  };
72
74
  }
73
75
  export default Tree;
@@ -13,7 +13,9 @@ function Tree(props) {
13
13
  onExpand: props.onItemClick,
14
14
  level: props.level,
15
15
  alwaysOpened: props.alwaysOpened,
16
- useSameSelectedItemId: props.useSameSelectedItemId
16
+ useSameSelectedItemId: props.useSameSelectedItemId,
17
+ saveInClientStorage: props.autoSave,
18
+ clientStorageId: props.id
17
19
  }).treeItems;
18
20
  var viewProps = (0, react_1.useMemo)(function () { return ({
19
21
  items: treeItems,
@@ -34,5 +36,6 @@ Tree.defaultProps = {
34
36
  levelPadding: 32,
35
37
  hasIconExpandOnly: false,
36
38
  useSameSelectedItemId: true,
37
- hideIcon: false
39
+ hideIcon: false,
40
+ saveInClientStorage: false
38
41
  };