@iobroker/adapter-react-v5 7.0.1 → 7.0.2

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 (219) hide show
  1. package/Components/404.js +13 -13
  2. package/Components/FileBrowser.js +42 -16
  3. package/Components/Loader.js +223 -223
  4. package/Components/Loaders/PT.css +108 -108
  5. package/Components/Loaders/PT.js +103 -103
  6. package/Components/Loaders/Vendor.css +13 -13
  7. package/Components/Loaders/Vendor.js +7 -7
  8. package/Components/ObjectBrowser.d.ts +10 -0
  9. package/Components/ObjectBrowser.js +257 -109
  10. package/Components/UploadImage.js +305 -305
  11. package/Components/loader.css +221 -221
  12. package/Components/types.d.ts +82 -82
  13. package/GenericApp.js +49 -49
  14. package/LICENSE +22 -22
  15. package/Prompt.js +7 -7
  16. package/README.md +30 -21
  17. package/assets/devices/Alarm Systems.svg +18 -18
  18. package/assets/devices/Amplifier.svg +21 -21
  19. package/assets/devices/Awnings.svg +4 -4
  20. package/assets/devices/Battery Status.svg +4 -4
  21. package/assets/devices/Ceiling Spotlights.svg +15 -15
  22. package/assets/devices/Chandelier.svg +6 -6
  23. package/assets/devices/Climate.svg +11 -11
  24. package/assets/devices/Coffee Makers.svg +5 -5
  25. package/assets/devices/Cold Water.svg +31 -31
  26. package/assets/devices/Computer.svg +21 -21
  27. package/assets/devices/Consumption.svg +7 -7
  28. package/assets/devices/Curtains.svg +43 -43
  29. package/assets/devices/Dishwashers.svg +11 -11
  30. package/assets/devices/Doors.svg +5 -5
  31. package/assets/devices/Doorstep.svg +35 -35
  32. package/assets/devices/Dryer.svg +13 -13
  33. package/assets/devices/Fan.svg +20 -20
  34. package/assets/devices/Floor Lamps.svg +4 -4
  35. package/assets/devices/Garage Doors.svg +9 -9
  36. package/assets/devices/Gates.svg +32 -32
  37. package/assets/devices/Hairdryer.svg +23 -23
  38. package/assets/devices/Handle.svg +6 -6
  39. package/assets/devices/Hanging Lamps.svg +8 -8
  40. package/assets/devices/Heater.svg +44 -44
  41. package/assets/devices/Hoods.svg +11 -11
  42. package/assets/devices/Hot Water.svg +9 -9
  43. package/assets/devices/Humidity.svg +41 -41
  44. package/assets/devices/Iron.svg +4 -4
  45. package/assets/devices/Irrigation.svg +22 -22
  46. package/assets/devices/Led Strip.svg +30 -30
  47. package/assets/devices/Light.svg +29 -29
  48. package/assets/devices/Lightings.svg +46 -46
  49. package/assets/devices/Lock.svg +19 -19
  50. package/assets/devices/Louvre.svg +6 -6
  51. package/assets/devices/Mowing Machine.svg +8 -8
  52. package/assets/devices/Music.svg +12 -12
  53. package/assets/devices/Outdoor Blinds.svg +6 -6
  54. package/assets/devices/People.svg +19 -19
  55. package/assets/devices/Pool.svg +7 -7
  56. package/assets/devices/Power Consumption.svg +12 -12
  57. package/assets/devices/Printer.svg +9 -9
  58. package/assets/devices/Pump.svg +9 -9
  59. package/assets/devices/Receiver.svg +18 -18
  60. package/assets/devices/Sconces.svg +9 -9
  61. package/assets/devices/Security.svg +34 -34
  62. package/assets/devices/Shading.svg +4 -4
  63. package/assets/devices/Shutters.svg +10 -10
  64. package/assets/devices/SmokeDetector.svg +12 -12
  65. package/assets/devices/Sockets.svg +13 -13
  66. package/assets/devices/Speaker.svg +35 -35
  67. package/assets/devices/Stove.svg +11 -11
  68. package/assets/devices/Table Lamps.svg +11 -11
  69. package/assets/devices/Temperature Sensors.svg +28 -28
  70. package/assets/devices/Tv.svg +7 -7
  71. package/assets/devices/Vacuum Cleaner.svg +15 -15
  72. package/assets/devices/Ventilation.svg +12 -12
  73. package/assets/devices/Washing Machines.svg +15 -15
  74. package/assets/devices/Water Consumption.svg +5 -5
  75. package/assets/devices/Water Heater.svg +8 -8
  76. package/assets/devices/Water.svg +40 -40
  77. package/assets/devices/Weather.svg +28 -28
  78. package/assets/devices/Window.svg +7 -7
  79. package/assets/lamp_ceiling.svg +8 -8
  80. package/assets/lamp_table.svg +7 -7
  81. package/assets/no_icon.svg +9 -9
  82. package/assets/rooms/Anteroom.svg +52 -52
  83. package/assets/rooms/Attic.svg +21 -21
  84. package/assets/rooms/Balcony.svg +12 -12
  85. package/assets/rooms/Barn.svg +5 -5
  86. package/assets/rooms/Basement.svg +4 -4
  87. package/assets/rooms/Bathroom.svg +38 -38
  88. package/assets/rooms/Bedroom.svg +5 -5
  89. package/assets/rooms/Boiler Room.svg +12 -12
  90. package/assets/rooms/Carport.svg +17 -17
  91. package/assets/rooms/Cellar.svg +89 -89
  92. package/assets/rooms/Chamber.svg +9 -9
  93. package/assets/rooms/Corridor.svg +52 -52
  94. package/assets/rooms/Dining Area.svg +37 -37
  95. package/assets/rooms/Dining Room.svg +37 -37
  96. package/assets/rooms/Dining.svg +37 -37
  97. package/assets/rooms/Dressing Room.svg +4 -4
  98. package/assets/rooms/Driveway.svg +14 -14
  99. package/assets/rooms/Entrance.svg +44 -44
  100. package/assets/rooms/Equipment Room.svg +14 -14
  101. package/assets/rooms/Front Yard.svg +64 -64
  102. package/assets/rooms/Gallery.svg +13 -13
  103. package/assets/rooms/Garage.svg +20 -20
  104. package/assets/rooms/Garden.svg +12 -12
  105. package/assets/rooms/Ground Floor.svg +95 -95
  106. package/assets/rooms/Guest Bathroom.svg +32 -32
  107. package/assets/rooms/Guest Room.svg +5 -5
  108. package/assets/rooms/Gym.svg +4 -4
  109. package/assets/rooms/Hall.svg +19 -19
  110. package/assets/rooms/Home Theater.svg +7 -7
  111. package/assets/rooms/Kitchen.svg +17 -17
  112. package/assets/rooms/Laundry Room.svg +11 -11
  113. package/assets/rooms/Living Area.svg +10 -10
  114. package/assets/rooms/Living Room.svg +10 -10
  115. package/assets/rooms/Locker Room.svg +16 -16
  116. package/assets/rooms/Nursery.svg +4 -4
  117. package/assets/rooms/Office.svg +8 -8
  118. package/assets/rooms/Outdoors.svg +7 -7
  119. package/assets/rooms/Playroom.svg +5 -5
  120. package/assets/rooms/Pool.svg +7 -7
  121. package/assets/rooms/Rear Wall.svg +30 -30
  122. package/assets/rooms/Second Floor.svg +95 -95
  123. package/assets/rooms/Shed.svg +16 -16
  124. package/assets/rooms/Sleeping Area.svg +22 -22
  125. package/assets/rooms/Stairway.svg +4 -4
  126. package/assets/rooms/Stairwell.svg +15 -15
  127. package/assets/rooms/Storeroom.svg +4 -4
  128. package/assets/rooms/Summer House.svg +27 -27
  129. package/assets/rooms/Swimming Pool.svg +21 -21
  130. package/assets/rooms/Terrace.svg +6 -6
  131. package/assets/rooms/Toilet.svg +10 -10
  132. package/assets/rooms/Upstairs.svg +5 -5
  133. package/assets/rooms/Wardrobe.svg +60 -60
  134. package/assets/rooms/Washroom.svg +19 -19
  135. package/assets/rooms/Wc.svg +10 -10
  136. package/assets/rooms/Windscreen.svg +60 -60
  137. package/assets/rooms/Workshop.svg +22 -22
  138. package/assets/rooms/Workspace.svg +8 -8
  139. package/craco-module-federation.js +71 -71
  140. package/i18n/de.json +4 -1
  141. package/i18n/en.json +4 -1
  142. package/i18n/es.json +4 -1
  143. package/i18n/fr.json +4 -1
  144. package/i18n/it.json +4 -1
  145. package/i18n/nl.json +4 -1
  146. package/i18n/pl.json +4 -1
  147. package/i18n/pt.json +4 -1
  148. package/i18n/ru.json +4 -1
  149. package/i18n/uk.json +4 -1
  150. package/i18n/zh-cn.json +4 -1
  151. package/icons/IconFx.js +1 -1
  152. package/icons/IconLogout.js +1 -1
  153. package/index.css +54 -54
  154. package/modulefederation.admin.config.js +31 -31
  155. package/package.json +5 -5
  156. package/src/AdminConnection.tsx +3 -3
  157. package/src/Components/404.tsx +121 -121
  158. package/src/Components/ColorPicker.tsx +315 -315
  159. package/src/Components/ComplexCron.tsx +507 -507
  160. package/src/Components/CopyToClipboard.tsx +165 -165
  161. package/src/Components/CustomModal.tsx +163 -163
  162. package/src/Components/FileBrowser.tsx +2443 -2414
  163. package/src/Components/FileViewer.tsx +393 -393
  164. package/src/Components/Icon.tsx +210 -210
  165. package/src/Components/IconPicker.tsx +149 -149
  166. package/src/Components/IconSelector.tsx +2202 -2202
  167. package/src/Components/Image.tsx +176 -176
  168. package/src/Components/Loader.tsx +304 -304
  169. package/src/Components/Logo.tsx +166 -166
  170. package/src/Components/MDUtils.tsx +100 -100
  171. package/src/Components/ObjectBrowser.tsx +8215 -8032
  172. package/src/Components/Router.tsx +90 -90
  173. package/src/Components/SaveCloseButtons.tsx +113 -113
  174. package/src/Components/Schedule.tsx +1724 -1724
  175. package/src/Components/SelectWithIcon.tsx +197 -197
  176. package/src/Components/TabContainer.tsx +55 -55
  177. package/src/Components/TabContent.tsx +37 -37
  178. package/src/Components/TabHeader.tsx +19 -19
  179. package/src/Components/TableResize.tsx +259 -259
  180. package/src/Components/TextWithIcon.tsx +148 -148
  181. package/src/Components/ToggleThemeMenu.tsx +34 -34
  182. package/src/Components/TreeTable.tsx +919 -919
  183. package/src/Components/UploadImage.tsx +599 -599
  184. package/src/Components/Utils.tsx +1794 -1794
  185. package/src/Components/loader.css +221 -221
  186. package/src/Components/withWidth.tsx +21 -21
  187. package/src/Connection.tsx +7 -7
  188. package/src/Dialogs/ComplexCron.tsx +129 -129
  189. package/src/Dialogs/Confirm.tsx +162 -162
  190. package/src/Dialogs/Cron.tsx +182 -182
  191. package/src/Dialogs/Error.tsx +72 -72
  192. package/src/Dialogs/Message.tsx +71 -71
  193. package/src/Dialogs/SelectFile.tsx +270 -270
  194. package/src/Dialogs/SelectID.tsx +298 -298
  195. package/src/Dialogs/SimpleCron.tsx +100 -100
  196. package/src/Dialogs/TextInput.tsx +107 -107
  197. package/src/GenericApp.tsx +976 -976
  198. package/src/LegacyConnection.tsx +3589 -3589
  199. package/src/Prompt.tsx +20 -20
  200. package/src/Theme.tsx +479 -479
  201. package/src/icons/IconAdapter.tsx +20 -20
  202. package/src/icons/IconAlias.tsx +20 -20
  203. package/src/icons/IconChannel.tsx +21 -21
  204. package/src/icons/IconClearFilter.tsx +22 -22
  205. package/src/icons/IconClosed.tsx +17 -17
  206. package/src/icons/IconCopy.tsx +16 -16
  207. package/src/icons/IconDevice.tsx +27 -27
  208. package/src/icons/IconDocument.tsx +17 -17
  209. package/src/icons/IconDocumentReadOnly.tsx +18 -18
  210. package/src/icons/IconExpert.tsx +18 -18
  211. package/src/icons/IconFx.tsx +36 -36
  212. package/src/icons/IconInstance.tsx +20 -20
  213. package/src/icons/IconLogout.tsx +30 -30
  214. package/src/icons/IconNoIcon.tsx +19 -19
  215. package/src/icons/IconOpen.tsx +17 -17
  216. package/src/icons/IconProps.tsx +15 -15
  217. package/src/icons/IconState.tsx +17 -17
  218. package/src/index.css +54 -54
  219. package/types.d.ts +0 -134
@@ -1,919 +1,919 @@
1
- import React, { Component } from 'react';
2
-
3
- import { HexColorPicker as ColorPicker } from 'react-colorful';
4
-
5
- import {
6
- Fab,
7
- Table,
8
- TableBody,
9
- TableCell,
10
- TableHead,
11
- TableRow,
12
- TableSortLabel,
13
- IconButton,
14
- Select,
15
- MenuItem,
16
- TextField,
17
- Checkbox,
18
- Dialog,
19
- } from '@mui/material';
20
-
21
- import {
22
- Edit as IconEdit,
23
- Delete as IconDelete,
24
- NavigateNext as IconExpand,
25
- ExpandMore as IconCollapse,
26
- Check as IconCheck,
27
- Close as IconClose,
28
- Add as IconAdd,
29
- ViewHeadline as IconList,
30
- Colorize as IconColor,
31
- } from '@mui/icons-material';
32
-
33
- import type Connection from '../Connection';
34
-
35
- import DialogSelectID from '../Dialogs/SelectID';
36
- import Utils from './Utils';
37
- import { IobTheme } from '../types';
38
-
39
- function getAttr(
40
- obj: Record<string, any>,
41
- attr: string | string[],
42
- lookup?: Record<string, string>,
43
- ): any {
44
- if (typeof attr === 'string') {
45
- attr = attr.split('.');
46
- }
47
-
48
- if (!obj) {
49
- return null;
50
- }
51
-
52
- if (attr.length === 1) {
53
- if (lookup && lookup[obj[attr[0]]]) {
54
- return lookup[obj[attr[0]]];
55
- }
56
- return obj[attr[0]];
57
- }
58
-
59
- const name: string = attr.shift() as string;
60
- return getAttr(obj[name], attr);
61
- }
62
-
63
- function setAttr(
64
- obj: Record<string, any>,
65
- attr: string | string[],
66
- value: any,
67
- ): void {
68
- if (typeof attr === 'string') {
69
- attr = attr.split('.');
70
- }
71
-
72
- if (attr.length === 1) {
73
- return obj[attr[0]] = value;
74
- }
75
- const name: string = attr.shift() as string;
76
- if (obj[name] === null || obj[name] === undefined) {
77
- obj[name] = {};
78
- }
79
- return setAttr(obj[name], attr, value);
80
- }
81
-
82
- const styles: Record<string, any> = {
83
- tableContainer: {
84
- width: '100%',
85
- height: '100%',
86
- overflow: 'auto',
87
- },
88
- table: {
89
- width: '100%',
90
- minWidth: 800,
91
- maxWidth: 1920,
92
- },
93
- cell: {
94
- paddingTop: 0,
95
- paddingBottom: 0,
96
- paddingLeft: 4,
97
- paddingRight: 4,
98
- },
99
- rowMainWithChildren: {
100
-
101
- },
102
- rowMainWithoutChildren: {
103
-
104
- },
105
- rowNoEdit: {
106
- opacity: 0.3,
107
- },
108
- cellExpand: {
109
- width: 30,
110
- },
111
- cellButton: {
112
- width: 30,
113
- },
114
- cellHeader: {
115
- fontWeight: 'bold',
116
- background: (theme: IobTheme) => (theme.palette.mode === 'dark' ? '#888' : '#888'),
117
- color: (theme: IobTheme) => (theme.palette.mode === 'dark' ? '#EEE' : '#111'),
118
- height: 48,
119
- wordBreak: 'break-word',
120
- whiteSpace: 'pre',
121
- },
122
- width_name_nicknames: {
123
- maxWidth: 150,
124
- },
125
- width_ioType: {
126
- maxWidth: 100,
127
- },
128
- width_type: {
129
- maxWidth: 100,
130
- },
131
- width_displayTraits: {
132
- maxWidth: 100,
133
- },
134
- width_roomHint: {
135
- maxWidth: 100,
136
- },
137
- rowSecondary: {
138
- fontStyle: 'italic',
139
- },
140
- cellSecondary: {
141
- fontSize: 10,
142
- },
143
- visuallyHidden: {
144
- border: 0,
145
- clip: 'rect(0 0 0 0)',
146
- height: 1,
147
- margin: -1,
148
- overflow: 'hidden',
149
- padding: 0,
150
- position: 'absolute',
151
- top: 20,
152
- width: 1,
153
- },
154
- fieldEditWithButton: {
155
- width: 'calc(100% - 33px)',
156
- display: 'inline-block',
157
- },
158
- fieldEdit: {
159
- width: '100%',
160
- display: 'inline-block',
161
- lineHeight: '50px',
162
- verticalAlign: 'middle',
163
- },
164
- fieldButton: {
165
- width: 30,
166
- display: 'inline-block',
167
- },
168
- colorDialog: {
169
- overflow: 'hidden',
170
- padding: 15,
171
- },
172
- subText: {
173
- fontSize: 10,
174
- fontStyle: 'italic',
175
- },
176
- glow: {
177
- animation: 'glow 0.2s 2 alternate',
178
- },
179
- };
180
-
181
- function descendingComparator(
182
- a: Record<string, any>,
183
- b: Record<string, any>,
184
- orderBy: string,
185
- lookup?: Record<string, string>,
186
- ): number {
187
- const _a = getAttr(a, orderBy, lookup) || '';
188
- const _b = getAttr(b, orderBy, lookup) || '';
189
-
190
- if (_b < _a) {
191
- return -1;
192
- }
193
- if (_b > _a) {
194
- return 1;
195
- }
196
- return 0;
197
- }
198
-
199
- function getComparator(
200
- order: 'desc' | 'asc',
201
- orderBy: string,
202
- lookup?: Record<string, string>,
203
- ): (a: Record<string, any>, b: Record<string, any>) => number {
204
- return order === 'desc'
205
- ? (a, b) => descendingComparator(a, b, orderBy, lookup)
206
- : (a, b) => -descendingComparator(a, b, orderBy, lookup);
207
- }
208
-
209
- function stableSort(
210
- array: Record<string, any>[],
211
- comparator: (a: Record<string, any>, b: Record<string, any>) => number,
212
- ): Record<string, any>[] {
213
- const stabilizedThis: { e: Record<string, any>; i: number }[] =
214
- array.map((el, index) => ({ e: el, i: index }));
215
-
216
- stabilizedThis.sort((a, b) => {
217
- const order = comparator(a.e, b.e);
218
- if (order) {
219
- return order;
220
- }
221
- return a.i - b.i;
222
- });
223
-
224
- return stabilizedThis.map(item => item.e);
225
- }
226
-
227
- interface Column {
228
- cellStyle?: Record<string, any>;
229
- editComponent?: React.FC<{ value: any; rowData: Record<string, any>; onChange: (newValue: any) => void }>;
230
- field: string;
231
- headerStyle?: Record<string, any>;
232
- hidden?: boolean;
233
- lookup?: Record<string, string>;
234
- editable?: boolean | 'never';
235
- title?: string;
236
- type?: 'string' | 'boolean' | 'numeric' | 'icon' | 'oid' | 'color';
237
- subField?: string;
238
- subLookup?: Record<string, string>;
239
- subStyle?: Record<string, any>;
240
- }
241
-
242
- interface TreeTableProps {
243
- data: Record<string, any>[];
244
- className?: string;
245
- /** name of table to save settings in localStorage */
246
- name?: string;
247
- columns: Column[];
248
- noSort?: boolean;
249
- onUpdate?: ((newData: Record<string, any>, oldData: Record<string, any>) => void) | ((addNew: true) => void);
250
- onDelete?: (oldData: Record<string, any>) => void;
251
- /** hide add button */
252
- noAdd?: boolean;
253
- themeType?: string;
254
- glowOnChange?: boolean;
255
- /** only if an oid type is used */
256
- socket?: Connection;
257
- /** Shift in pixels for every level */
258
- levelShift?: number;
259
- adapterName: string;
260
- theme: IobTheme;
261
- }
262
-
263
- interface TreeTableState {
264
- opened: string[];
265
- editMode: number | false;
266
- deleteMode: number | false;
267
- editData: Record<string, any> | null;
268
- order: 'desc' | 'asc';
269
- update: string[] | null;
270
- orderBy: string;
271
- showSelectColor: boolean;
272
- selectIdValue?: string | null;
273
- showSelectId?: boolean;
274
- data?: Record<string, any>[];
275
- }
276
-
277
- class TreeTable extends Component<TreeTableProps, TreeTableState> {
278
- private selectCallback: ((selected: string) => void) | null = null;
279
-
280
- private updateTimeout: ReturnType<typeof setTimeout> | null = null;
281
-
282
- constructor(props: TreeTableProps) {
283
- super(props);
284
-
285
- let opened = ((window as any)._localStorage || window.localStorage).getItem(this.props.name || 'iob-table') || '[]';
286
- try {
287
- opened = JSON.parse(opened) || [];
288
- } catch (e) {
289
- opened = [];
290
- }
291
- if (!Array.isArray(opened)) {
292
- opened = [];
293
- }
294
-
295
- this.state = {
296
- opened,
297
- editMode: false,
298
- deleteMode: false,
299
- editData: null,
300
- order: 'asc',
301
- update: null,
302
- orderBy: this.props.columns[0].field,
303
- showSelectColor: false,
304
- };
305
- }
306
-
307
- static getDerivedStateFromProps(props: TreeTableProps, state: TreeTableState): Partial<TreeTableState> {
308
- if (props.glowOnChange) {
309
- const update: string[] = [];
310
- let count = 0;
311
- if (props.data && state.data) {
312
- props.data.forEach(line => {
313
- count++;
314
- const oldLine = state.data?.find(it => it.id === line.id);
315
- if (oldLine) {
316
- if (JSON.stringify(oldLine) !== JSON.stringify(line)) {
317
- update.push(line.id);
318
- }
319
- } else {
320
- update.push(line.id);
321
- }
322
- });
323
- }
324
-
325
- if (update.length && update.length !== count) {
326
- return { data: props.data, update };
327
- }
328
- return { data: props.data };
329
- }
330
-
331
- return { data: props.data };
332
- }
333
-
334
- renderCellEdit(
335
- item: Record<string, any>,
336
- col: Column,
337
- ) {
338
- let val = getAttr(item, col.field);
339
- if (Array.isArray(val)) {
340
- val = val[0];
341
- }
342
-
343
- if (col.lookup) {
344
- return this.renderCellEditSelect(col, val);
345
- } if (col.editComponent) {
346
- return this.renderCellEditCustom(col, val, item);
347
- }
348
- if (col.type === 'boolean' || (!col.type && typeof val === 'boolean')) {
349
- return this.renderCellEditBoolean(col, val);
350
- }
351
- if (col.type === 'color') {
352
- return this.renderCellEditColor(col, val);
353
- }
354
- if (col.type === 'oid') {
355
- return this.renderCellEditObjectID(col, val);
356
- }
357
- if (col.type === 'numeric') {
358
- return this.renderCellEditNumber(col, val);
359
- }
360
-
361
- return this.renderCellEditString(col, val);
362
- }
363
-
364
- onChange(col: Column, oldValue: string | number | boolean, newValue: string | number | boolean) {
365
- const editData = this.state.editData ? { ...this.state.editData } : {};
366
- if (newValue === oldValue) {
367
- delete editData[col.field];
368
- } else {
369
- editData[col.field] = newValue;
370
- }
371
- this.setState({ editData });
372
- }
373
-
374
- renderCellEditSelect(
375
- col: Column,
376
- val: string | number,
377
- ) {
378
- return <Select
379
- variant="standard"
380
- onChange={e => this.onChange(col, val, e.target.value)}
381
- value={(this.state.editData && this.state.editData[col.field]) || val}
382
- >
383
- {col.lookup && Object.keys(col.lookup)
384
- .map((v, i) => <MenuItem key={i} value={v}>{col.lookup?.[v]}</MenuItem>)}
385
- </Select>;
386
- }
387
-
388
- renderCellEditString(
389
- col: Column,
390
- val: string,
391
- ) {
392
- return <TextField
393
- variant="standard"
394
- style={styles.fieldEdit}
395
- fullWidth
396
- value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
397
- onChange={e => this.onChange(col, val, e.target.value)}
398
- />;
399
- }
400
-
401
- renderCellEditNumber(
402
- col: Column,
403
- val: number,
404
- ) {
405
- return <TextField
406
- variant="standard"
407
- style={styles.fieldEdit}
408
- type="number"
409
- fullWidth
410
- value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
411
- onChange={e => this.onChange(col, val, e.target.value)}
412
- />;
413
- }
414
-
415
- renderCellEditCustom(
416
- col: Column,
417
- val: any,
418
- item: Record<string, any>,
419
- ) {
420
- const EditComponent = col.editComponent;
421
-
422
- // use new value if exists
423
- if (this.state.editData && this.state.editData[col.field] !== undefined) {
424
- val = this.state.editData[col.field];
425
- item = JSON.parse(JSON.stringify(item));
426
- item[col.field] = val;
427
- }
428
-
429
- return EditComponent ? <EditComponent
430
- value={val}
431
- rowData={item}
432
- onChange={(newVal: any) => this.onChange(col, val, newVal as string | number)}
433
- /> : null;
434
- }
435
-
436
- renderCellEditBoolean(
437
- col: Column,
438
- val: boolean,
439
- ) {
440
- return <Checkbox
441
- checked={this.state.editData && this.state.editData[col.field] !== undefined ? !!this.state.editData[col.field] : !!val}
442
- onChange={e => this.onChange(col, !!val, e.target.checked)}
443
- inputProps={{ 'aria-label': 'checkbox' }}
444
- />;
445
- }
446
-
447
- renderSelectColorDialog() {
448
- return <Dialog
449
- sx={{
450
- '& .MuiPaper-root': styles.root,
451
- '& .MuiPaper-paper': styles.paper,
452
- }}
453
- onClose={() => {
454
- this.selectCallback = null;
455
- this.setState({ showSelectColor: false });
456
- }}
457
- open={this.state.showSelectColor}
458
- >
459
- <ColorPicker
460
- color={this.state.selectIdValue as string}
461
- onChange={color =>
462
- this.setState({ selectIdValue: color }, () =>
463
- this.selectCallback && this.selectCallback(color))}
464
- />
465
- </Dialog>;
466
- }
467
-
468
- renderCellEditColor(
469
- col: Column,
470
- val: string,
471
- ) {
472
- const _val = this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val;
473
- return <div style={styles.fieldEdit}>
474
- <TextField
475
- variant="standard"
476
- fullWidth
477
- style={styles.fieldEditWithButton}
478
- value={_val}
479
- inputProps={{ style: { backgroundColor: _val, color: Utils.isUseBright(_val) ? '#FFF' : '#000' } }}
480
- onChange={e => this.onChange(col, !!val, e.target.value)}
481
- />
482
-
483
- <IconButton
484
- style={styles.fieldButton}
485
- onClick={() => {
486
- this.selectCallback = newColor => this.onChange(col, val, newColor);
487
- this.setState({ showSelectColor: true, selectIdValue: val });
488
- }}
489
- size="large"
490
- >
491
- <IconColor />
492
- </IconButton>
493
- </div>;
494
- }
495
-
496
- renderSelectIdDialog() {
497
- if (this.state.showSelectId && this.props.socket) {
498
- return <DialogSelectID
499
- key="tableSelect"
500
- imagePrefix="../.."
501
- dialogName={this.props.adapterName}
502
- themeType={this.props.themeType}
503
- theme={this.props.theme}
504
- socket={this.props.socket as Connection}
505
- selected={this.state.selectIdValue as string}
506
- onClose={() => this.setState({ showSelectId: false })}
507
- onOk={(selected /* , name */) => {
508
- this.setState({ showSelectId: false, selectIdValue: null });
509
- this.selectCallback && this.selectCallback(selected as string);
510
- this.selectCallback = null;
511
- }}
512
- />;
513
- }
514
-
515
- return null;
516
- }
517
-
518
- renderCellEditObjectID(
519
- col: Column,
520
- val: string,
521
- ) {
522
- return <div style={styles.fieldEdit}>
523
- <TextField
524
- variant="standard"
525
- fullWidth
526
- style={styles.fieldEditWithButton}
527
- value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
528
- onChange={e => this.onChange(col, val, e.target.value)}
529
- />
530
-
531
- <IconButton
532
- style={styles.fieldButton}
533
- onClick={() => {
534
- this.selectCallback = selected => this.onChange(col, val, selected);
535
- this.setState({ showSelectId: true, selectIdValue: val });
536
- }}
537
- size="large"
538
- >
539
- <IconList />
540
- </IconButton>
541
- </div>;
542
- }
543
-
544
- static renderCellNonEdit(
545
- item: Record<string, any>,
546
- col: Column,
547
- ) {
548
- let val = getAttr(item, col.field, col.lookup);
549
- if (Array.isArray(val)) {
550
- val = val[0];
551
- }
552
-
553
- if (col.type === 'boolean') {
554
- return <Checkbox
555
- checked={!!val}
556
- disabled
557
- inputProps={{ 'aria-label': 'checkbox' }}
558
- />;
559
- }
560
-
561
- return val;
562
- }
563
-
564
- renderCell(
565
- item: Record<string, any>,
566
- col: Column,
567
- level: number,
568
- i: number,
569
- ) {
570
- if (this.state.editMode === i && col.editable !== 'never' && col.editable !== false) {
571
- return <TableCell
572
- key={col.field}
573
- style={{ ...styles.cell, ...(level ? styles.cellSecondary : undefined), ...col.cellStyle }}
574
- component="th"
575
- >
576
- {this.renderCellEdit(item, col)}
577
- </TableCell>;
578
- }
579
- return <TableCell
580
- key={col.field}
581
- style={{ ...styles.cell, ...(level ? styles.cellSecondary : undefined), ...col.cellStyle }}
582
- component="th"
583
- >
584
- {TreeTable.renderCellNonEdit(item, col)}
585
- </TableCell>;
586
- }
587
-
588
- static renderCellWithSubField(
589
- item: Record<string, any>,
590
- col: Column,
591
- ) {
592
- const main = getAttr(item, col.field, col.lookup);
593
- if (col.subField) {
594
- const sub = getAttr(item, col.subField, col.subLookup);
595
- return <div>
596
- <div style={styles.mainText}>{main}</div>
597
- <div style={{ ...styles.subText, ...(col.subStyle || undefined) }}>{sub}</div>
598
- </div>;
599
- }
600
- return <div>
601
- <div style={styles.mainText}>{main}</div>
602
- </div>;
603
- }
604
-
605
- renderLine(
606
- item: Record<string, any>,
607
- level?: number,
608
- ): React.JSX.Element | React.JSX.Element[] | null {
609
- const levelShift = this.props.levelShift === undefined ? 24 : this.props.levelShift;
610
-
611
- level = level || 0;
612
- const i = this.props.data.indexOf(item);
613
- if (!item) {
614
- return null;
615
- }
616
- if (!level && item.parentId) {
617
- return null;
618
- }
619
- if (level && !item.parentId) {
620
- return null; // should never happen
621
- }
622
- // try to find children
623
- const opened = this.state.opened.includes(item.id);
624
- const children = this.props.data.filter(it => it.parentId === item.id);
625
-
626
- const row = <TableRow
627
- key={item.id}
628
- className={`table-row-${(item.id || '').toString().replace(/[.$]/g, '_')}`}
629
- style={{
630
- ...((this.state.update && this.state.update.includes(item.id) && styles.glow) || undefined),
631
- ...styles.row,
632
- ...(level ? styles.rowSecondary : undefined),
633
- ...(!level && children.length ? styles.rowMainWithChildren : undefined),
634
- ...(!level && !children.length ? styles.rowMainWithoutChildren : undefined),
635
- ...(this.state.editMode !== false && this.state.editMode !== i ? styles.rowNoEdit : undefined),
636
- ...(this.state.deleteMode !== false && this.state.deleteMode !== i ? styles.rowNoEdit : undefined),
637
- }}
638
- >
639
- <TableCell
640
- style={{ ...styles.cell, ...styles.cellExpand, ...(level ? styles.cellSecondary : undefined) }}
641
- >
642
- {children.length ? <IconButton
643
- onClick={() => {
644
- const _opened = [...this.state.opened];
645
- const pos = _opened.indexOf(item.id);
646
- if (pos === -1) {
647
- _opened.push(item.id);
648
- _opened.sort();
649
- } else {
650
- _opened.splice(pos, 1);
651
- }
652
-
653
- ((window as any)._localStorage || window.localStorage).setItem(this.props.name || 'iob-table', JSON.stringify(_opened));
654
-
655
- this.setState({ opened: _opened });
656
- }}
657
- size="small"
658
- >
659
- {opened ? <IconCollapse /> : <IconExpand />}
660
- </IconButton> : null}
661
- </TableCell>
662
- <TableCell
663
- scope="row"
664
- style={{
665
- ...styles.cell,
666
- ...(level ? styles.cellSecondary : undefined),
667
- ...this.props.columns[0].cellStyle,
668
- paddingLeft: levelShift * level,
669
- }}
670
- >
671
- {this.props.columns[0].subField ?
672
- TreeTable.renderCellWithSubField(item, this.props.columns[0])
673
- :
674
- getAttr(item, this.props.columns[0].field, this.props.columns[0].lookup)}
675
- </TableCell>
676
-
677
- {this.props.columns.map((col, ii) =>
678
- (!ii && !col.hidden ? null : this.renderCell(item, col, level, i)))}
679
-
680
- {this.props.onUpdate ? <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
681
- {this.state.editMode === i || this.state.deleteMode === i ?
682
- <IconButton
683
- disabled={this.state.editMode !== false && (!this.state.editData || !Object.keys(this.state.editData).length)}
684
- onClick={() => {
685
- if (this.state.editMode !== false) {
686
- const newData = JSON.parse(JSON.stringify(item));
687
- this.state.editData && Object.keys(this.state.editData).forEach(attr =>
688
- setAttr(newData, attr, this.state.editData?.[attr]));
689
- this.setState({ editMode: false }, () => this.props.onUpdate && this.props.onUpdate(newData, item));
690
- } else {
691
- this.setState({ deleteMode: false }, () => this.props.onDelete && this.props.onDelete(item));
692
- }
693
- }}
694
- size="large"
695
- >
696
- <IconCheck />
697
- </IconButton>
698
- :
699
- <IconButton
700
- disabled={this.state.editMode !== false}
701
- onClick={() => this.setState({ editMode: i, editData: null })}
702
- size="large"
703
- >
704
- <IconEdit />
705
- </IconButton>}
706
- </TableCell> : null}
707
-
708
- {this.props.onDelete && !this.props.onUpdate ?
709
- <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
710
- {this.state.deleteMode === i ?
711
- <IconButton
712
- disabled={this.state.editMode !== false && (!this.state.editData || !Object.keys(this.state.editData).length)}
713
- onClick={() => this.setState({ deleteMode: false }, () => this.props.onDelete && this.props.onDelete(item))}
714
- size="large"
715
- >
716
- <IconCheck />
717
- </IconButton>
718
- :
719
- null}
720
- </TableCell> : null}
721
-
722
- {this.props.onUpdate || this.props.onDelete ? <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
723
- {this.state.editMode === i || this.state.deleteMode === i ?
724
- <IconButton
725
- onClick={() => this.setState({ editMode: false, deleteMode: false })}
726
- size="large"
727
- >
728
- <IconClose />
729
- </IconButton>
730
- :
731
- (this.props.onDelete ? <IconButton
732
- disabled={this.state.deleteMode !== false}
733
- onClick={() => this.setState({ deleteMode: i })}
734
- size="large"
735
- >
736
- <IconDelete />
737
- </IconButton> : null)}
738
- </TableCell> : null}
739
- </TableRow>;
740
-
741
- if (!level && opened) {
742
- const items: React.JSX.Element[] = children.map(it =>
743
- this.renderLine(it, level + 1)) as React.JSX.Element[];
744
- items.unshift(row);
745
- return items;
746
- }
747
- return row;
748
- }
749
-
750
- handleRequestSort(property: string) {
751
- const isAsc = this.state.orderBy === property && this.state.order === 'asc';
752
- this.setState({ order: isAsc ? 'desc' : 'asc', orderBy: property });
753
- }
754
-
755
- renderHead() {
756
- return <TableHead>
757
- <TableRow key="headerRow">
758
- <TableCell
759
- component="th"
760
- sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellExpand)}
761
- />
762
- <TableCell
763
- component="th"
764
- sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles[`width_${this.props.columns[0].field.replace(/\./g, '_')}`])}
765
- style={this.props.columns[0].headerStyle || this.props.columns[0].cellStyle}
766
- sortDirection={this.props.noSort ? false : (this.state.orderBy === this.props.columns[0].field ? this.state.order : false)}
767
- >
768
- {this.props.noSort ? null : <TableSortLabel
769
- active={this.state.orderBy === this.props.columns[0].field}
770
- direction={this.state.orderBy === this.props.columns[0].field ? this.state.order : 'asc'}
771
- onClick={() => this.handleRequestSort(this.props.columns[0].field)}
772
- >
773
- {this.props.columns[0].title || this.props.columns[0].field}
774
- {this.state.orderBy === this.props.columns[0].field ?
775
- <span style={styles.visuallyHidden}>
776
- {this.state.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
777
- </span> : null}
778
- </TableSortLabel>}
779
- </TableCell>
780
- {this.props.columns.map((col, i) =>
781
- (!i && !col.hidden ? null : <TableCell
782
- key={col.field}
783
- sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles[`width_${col.field.replace(/\./g, '_')}`])}
784
- style={col.headerStyle || col.cellStyle}
785
- component="th"
786
- >
787
- {this.props.noSort ? null : <TableSortLabel
788
- active={this.state.orderBy === col.field}
789
- direction={this.state.orderBy === col.field ? this.state.order : 'asc'}
790
- onClick={() => this.handleRequestSort(col.field)}
791
- >
792
- {col.title || col.field}
793
- {this.state.orderBy === col.field ?
794
- <span style={styles.visuallyHidden}>
795
- {this.state.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
796
- </span> : null}
797
- </TableSortLabel> }
798
- </TableCell>))}
799
- {this.props.onUpdate ? <TableCell component="th" sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellButton)}>
800
- {!this.props.noAdd ? <Fab
801
- color="primary"
802
- size="small"
803
- disabled={this.state.editMode !== false}
804
- onClick={() => this.props.onUpdate && (this.props.onUpdate as (addNew: true) => void)(true)}
805
- >
806
- <IconAdd />
807
- </Fab> : null }
808
- </TableCell> : null}
809
- {this.props.onDelete || this.props.onUpdate ?
810
- <TableCell component="th" sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellButton)} /> : null}
811
- </TableRow>
812
- </TableHead>;
813
- }
814
-
815
- render() {
816
- const col = this.props.columns.find(_col => _col.field === this.state.orderBy);
817
- if (col) {
818
- const lookup = col.lookup;
819
- const table = stableSort(this.props.data, getComparator(this.state.order, this.state.orderBy, lookup));
820
-
821
- if (this.state.update && this.state.update.length) {
822
- this.updateTimeout && clearTimeout(this.updateTimeout);
823
- this.updateTimeout = setTimeout(() => {
824
- this.updateTimeout = null;
825
- this.setState({ update: null });
826
- }, 500);
827
- }
828
-
829
- return <div style={styles.tableContainer} className={this.props.className}>
830
- <Table style={styles.table} aria-label="simple table" size="small" stickyHeader>
831
- {this.renderHead()}
832
- <TableBody>
833
- {table.map(it => this.renderLine(it))}
834
- </TableBody>
835
- </Table>
836
- {this.renderSelectIdDialog()}
837
- {this.renderSelectColorDialog()}
838
- </div>;
839
- }
840
-
841
- return null;
842
- }
843
- }
844
- /*
845
- const columns = [
846
- {
847
- title: 'Name of field', // required, else it will be "field"
848
- field: 'fieldIdInData', // required
849
- editable: false, // or true [default - true]
850
- cellStyle: { // CSS style - // optional
851
- maxWidth: '12rem',
852
- overflow: 'hidden',
853
- wordBreak: 'break-word'
854
- },
855
- lookup: { // optional => edit will be automatically "SELECT"
856
- 'value1': 'text1',
857
- 'value2': 'text2',
858
- }
859
- },
860
- {
861
- title: 'Type', // required, else it will be "field"
862
- field: 'myType', // required
863
- editable: true, // or true [default - true]
864
- lookup: { // optional => edit will be automatically "SELECT"
865
- 'number': 'Number',
866
- 'string': 'String',
867
- 'boolean': 'Boolean',
868
- },
869
- type: 'number/string/color/oid/icon/boolean', // oid=ObjectID,icon=base64-icon
870
- editComponent: props =>
871
- <div>Prefix&#123; <br/>
872
- <textarea
873
- rows={4}
874
- style={{width: '100%', resize: 'vertical'}}
875
- value={props.value}
876
- onChange={e => props.onChange(e.target.value)}
877
- />
878
- Suffix
879
- </div>,
880
- },
881
- ];
882
- */
883
- /* const data = [
884
- {
885
- id: 'UniqueID1' // required
886
- fieldIdInData: 'Name1',
887
- myType: 'number',
888
- },
889
- {
890
- id: 'UniqueID2' // required
891
- fieldIdInData: 'Name12',
892
- myType: 'string',
893
- },
894
- ];
895
- */
896
-
897
- /*
898
- // STYLES
899
- const styles = theme => ({
900
- tableDiv: {
901
- width: '100%',
902
- overflow: 'hidden',
903
- height: 'calc(100% - 48px)',
904
- },
905
- });
906
- // renderTable
907
- renderTable() {
908
- return <div style={styles.tableDiv}>
909
- <TreeTable
910
- columns={this.columns}
911
- data={lines}
912
- onUpdate={(newData, oldData) => console.log('Update: ' + JSON.stringify(newData))}
913
- onDelete={oldData => console.log('Delete: ' + JSON.stringify(oldData))}
914
- />
915
- </div>;
916
- }
917
- */
918
-
919
- export default TreeTable;
1
+ import React, { Component } from 'react';
2
+
3
+ import { HexColorPicker as ColorPicker } from 'react-colorful';
4
+
5
+ import {
6
+ Fab,
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableRow,
12
+ TableSortLabel,
13
+ IconButton,
14
+ Select,
15
+ MenuItem,
16
+ TextField,
17
+ Checkbox,
18
+ Dialog,
19
+ } from '@mui/material';
20
+
21
+ import {
22
+ Edit as IconEdit,
23
+ Delete as IconDelete,
24
+ NavigateNext as IconExpand,
25
+ ExpandMore as IconCollapse,
26
+ Check as IconCheck,
27
+ Close as IconClose,
28
+ Add as IconAdd,
29
+ ViewHeadline as IconList,
30
+ Colorize as IconColor,
31
+ } from '@mui/icons-material';
32
+
33
+ import type Connection from '../Connection';
34
+
35
+ import DialogSelectID from '../Dialogs/SelectID';
36
+ import Utils from './Utils';
37
+ import { IobTheme } from '../types';
38
+
39
+ function getAttr(
40
+ obj: Record<string, any>,
41
+ attr: string | string[],
42
+ lookup?: Record<string, string>,
43
+ ): any {
44
+ if (typeof attr === 'string') {
45
+ attr = attr.split('.');
46
+ }
47
+
48
+ if (!obj) {
49
+ return null;
50
+ }
51
+
52
+ if (attr.length === 1) {
53
+ if (lookup && lookup[obj[attr[0]]]) {
54
+ return lookup[obj[attr[0]]];
55
+ }
56
+ return obj[attr[0]];
57
+ }
58
+
59
+ const name: string = attr.shift() as string;
60
+ return getAttr(obj[name], attr);
61
+ }
62
+
63
+ function setAttr(
64
+ obj: Record<string, any>,
65
+ attr: string | string[],
66
+ value: any,
67
+ ): void {
68
+ if (typeof attr === 'string') {
69
+ attr = attr.split('.');
70
+ }
71
+
72
+ if (attr.length === 1) {
73
+ return obj[attr[0]] = value;
74
+ }
75
+ const name: string = attr.shift() as string;
76
+ if (obj[name] === null || obj[name] === undefined) {
77
+ obj[name] = {};
78
+ }
79
+ return setAttr(obj[name], attr, value);
80
+ }
81
+
82
+ const styles: Record<string, any> = {
83
+ tableContainer: {
84
+ width: '100%',
85
+ height: '100%',
86
+ overflow: 'auto',
87
+ },
88
+ table: {
89
+ width: '100%',
90
+ minWidth: 800,
91
+ maxWidth: 1920,
92
+ },
93
+ cell: {
94
+ paddingTop: 0,
95
+ paddingBottom: 0,
96
+ paddingLeft: 4,
97
+ paddingRight: 4,
98
+ },
99
+ rowMainWithChildren: {
100
+
101
+ },
102
+ rowMainWithoutChildren: {
103
+
104
+ },
105
+ rowNoEdit: {
106
+ opacity: 0.3,
107
+ },
108
+ cellExpand: {
109
+ width: 30,
110
+ },
111
+ cellButton: {
112
+ width: 30,
113
+ },
114
+ cellHeader: {
115
+ fontWeight: 'bold',
116
+ background: (theme: IobTheme) => (theme.palette.mode === 'dark' ? '#888' : '#888'),
117
+ color: (theme: IobTheme) => (theme.palette.mode === 'dark' ? '#EEE' : '#111'),
118
+ height: 48,
119
+ wordBreak: 'break-word',
120
+ whiteSpace: 'pre',
121
+ },
122
+ width_name_nicknames: {
123
+ maxWidth: 150,
124
+ },
125
+ width_ioType: {
126
+ maxWidth: 100,
127
+ },
128
+ width_type: {
129
+ maxWidth: 100,
130
+ },
131
+ width_displayTraits: {
132
+ maxWidth: 100,
133
+ },
134
+ width_roomHint: {
135
+ maxWidth: 100,
136
+ },
137
+ rowSecondary: {
138
+ fontStyle: 'italic',
139
+ },
140
+ cellSecondary: {
141
+ fontSize: 10,
142
+ },
143
+ visuallyHidden: {
144
+ border: 0,
145
+ clip: 'rect(0 0 0 0)',
146
+ height: 1,
147
+ margin: -1,
148
+ overflow: 'hidden',
149
+ padding: 0,
150
+ position: 'absolute',
151
+ top: 20,
152
+ width: 1,
153
+ },
154
+ fieldEditWithButton: {
155
+ width: 'calc(100% - 33px)',
156
+ display: 'inline-block',
157
+ },
158
+ fieldEdit: {
159
+ width: '100%',
160
+ display: 'inline-block',
161
+ lineHeight: '50px',
162
+ verticalAlign: 'middle',
163
+ },
164
+ fieldButton: {
165
+ width: 30,
166
+ display: 'inline-block',
167
+ },
168
+ colorDialog: {
169
+ overflow: 'hidden',
170
+ padding: 15,
171
+ },
172
+ subText: {
173
+ fontSize: 10,
174
+ fontStyle: 'italic',
175
+ },
176
+ glow: {
177
+ animation: 'glow 0.2s 2 alternate',
178
+ },
179
+ };
180
+
181
+ function descendingComparator(
182
+ a: Record<string, any>,
183
+ b: Record<string, any>,
184
+ orderBy: string,
185
+ lookup?: Record<string, string>,
186
+ ): number {
187
+ const _a = getAttr(a, orderBy, lookup) || '';
188
+ const _b = getAttr(b, orderBy, lookup) || '';
189
+
190
+ if (_b < _a) {
191
+ return -1;
192
+ }
193
+ if (_b > _a) {
194
+ return 1;
195
+ }
196
+ return 0;
197
+ }
198
+
199
+ function getComparator(
200
+ order: 'desc' | 'asc',
201
+ orderBy: string,
202
+ lookup?: Record<string, string>,
203
+ ): (a: Record<string, any>, b: Record<string, any>) => number {
204
+ return order === 'desc'
205
+ ? (a, b) => descendingComparator(a, b, orderBy, lookup)
206
+ : (a, b) => -descendingComparator(a, b, orderBy, lookup);
207
+ }
208
+
209
+ function stableSort(
210
+ array: Record<string, any>[],
211
+ comparator: (a: Record<string, any>, b: Record<string, any>) => number,
212
+ ): Record<string, any>[] {
213
+ const stabilizedThis: { e: Record<string, any>; i: number }[] =
214
+ array.map((el, index) => ({ e: el, i: index }));
215
+
216
+ stabilizedThis.sort((a, b) => {
217
+ const order = comparator(a.e, b.e);
218
+ if (order) {
219
+ return order;
220
+ }
221
+ return a.i - b.i;
222
+ });
223
+
224
+ return stabilizedThis.map(item => item.e);
225
+ }
226
+
227
+ interface Column {
228
+ cellStyle?: Record<string, any>;
229
+ editComponent?: React.FC<{ value: any; rowData: Record<string, any>; onChange: (newValue: any) => void }>;
230
+ field: string;
231
+ headerStyle?: Record<string, any>;
232
+ hidden?: boolean;
233
+ lookup?: Record<string, string>;
234
+ editable?: boolean | 'never';
235
+ title?: string;
236
+ type?: 'string' | 'boolean' | 'numeric' | 'icon' | 'oid' | 'color';
237
+ subField?: string;
238
+ subLookup?: Record<string, string>;
239
+ subStyle?: Record<string, any>;
240
+ }
241
+
242
+ interface TreeTableProps {
243
+ data: Record<string, any>[];
244
+ className?: string;
245
+ /** name of table to save settings in localStorage */
246
+ name?: string;
247
+ columns: Column[];
248
+ noSort?: boolean;
249
+ onUpdate?: ((newData: Record<string, any>, oldData: Record<string, any>) => void) | ((addNew: true) => void);
250
+ onDelete?: (oldData: Record<string, any>) => void;
251
+ /** hide add button */
252
+ noAdd?: boolean;
253
+ themeType?: string;
254
+ glowOnChange?: boolean;
255
+ /** only if an oid type is used */
256
+ socket?: Connection;
257
+ /** Shift in pixels for every level */
258
+ levelShift?: number;
259
+ adapterName: string;
260
+ theme: IobTheme;
261
+ }
262
+
263
+ interface TreeTableState {
264
+ opened: string[];
265
+ editMode: number | false;
266
+ deleteMode: number | false;
267
+ editData: Record<string, any> | null;
268
+ order: 'desc' | 'asc';
269
+ update: string[] | null;
270
+ orderBy: string;
271
+ showSelectColor: boolean;
272
+ selectIdValue?: string | null;
273
+ showSelectId?: boolean;
274
+ data?: Record<string, any>[];
275
+ }
276
+
277
+ class TreeTable extends Component<TreeTableProps, TreeTableState> {
278
+ private selectCallback: ((selected: string) => void) | null = null;
279
+
280
+ private updateTimeout: ReturnType<typeof setTimeout> | null = null;
281
+
282
+ constructor(props: TreeTableProps) {
283
+ super(props);
284
+
285
+ let opened = ((window as any)._localStorage || window.localStorage).getItem(this.props.name || 'iob-table') || '[]';
286
+ try {
287
+ opened = JSON.parse(opened) || [];
288
+ } catch (e) {
289
+ opened = [];
290
+ }
291
+ if (!Array.isArray(opened)) {
292
+ opened = [];
293
+ }
294
+
295
+ this.state = {
296
+ opened,
297
+ editMode: false,
298
+ deleteMode: false,
299
+ editData: null,
300
+ order: 'asc',
301
+ update: null,
302
+ orderBy: this.props.columns[0].field,
303
+ showSelectColor: false,
304
+ };
305
+ }
306
+
307
+ static getDerivedStateFromProps(props: TreeTableProps, state: TreeTableState): Partial<TreeTableState> {
308
+ if (props.glowOnChange) {
309
+ const update: string[] = [];
310
+ let count = 0;
311
+ if (props.data && state.data) {
312
+ props.data.forEach(line => {
313
+ count++;
314
+ const oldLine = state.data?.find(it => it.id === line.id);
315
+ if (oldLine) {
316
+ if (JSON.stringify(oldLine) !== JSON.stringify(line)) {
317
+ update.push(line.id);
318
+ }
319
+ } else {
320
+ update.push(line.id);
321
+ }
322
+ });
323
+ }
324
+
325
+ if (update.length && update.length !== count) {
326
+ return { data: props.data, update };
327
+ }
328
+ return { data: props.data };
329
+ }
330
+
331
+ return { data: props.data };
332
+ }
333
+
334
+ renderCellEdit(
335
+ item: Record<string, any>,
336
+ col: Column,
337
+ ) {
338
+ let val = getAttr(item, col.field);
339
+ if (Array.isArray(val)) {
340
+ val = val[0];
341
+ }
342
+
343
+ if (col.lookup) {
344
+ return this.renderCellEditSelect(col, val);
345
+ } if (col.editComponent) {
346
+ return this.renderCellEditCustom(col, val, item);
347
+ }
348
+ if (col.type === 'boolean' || (!col.type && typeof val === 'boolean')) {
349
+ return this.renderCellEditBoolean(col, val);
350
+ }
351
+ if (col.type === 'color') {
352
+ return this.renderCellEditColor(col, val);
353
+ }
354
+ if (col.type === 'oid') {
355
+ return this.renderCellEditObjectID(col, val);
356
+ }
357
+ if (col.type === 'numeric') {
358
+ return this.renderCellEditNumber(col, val);
359
+ }
360
+
361
+ return this.renderCellEditString(col, val);
362
+ }
363
+
364
+ onChange(col: Column, oldValue: string | number | boolean, newValue: string | number | boolean) {
365
+ const editData = this.state.editData ? { ...this.state.editData } : {};
366
+ if (newValue === oldValue) {
367
+ delete editData[col.field];
368
+ } else {
369
+ editData[col.field] = newValue;
370
+ }
371
+ this.setState({ editData });
372
+ }
373
+
374
+ renderCellEditSelect(
375
+ col: Column,
376
+ val: string | number,
377
+ ) {
378
+ return <Select
379
+ variant="standard"
380
+ onChange={e => this.onChange(col, val, e.target.value)}
381
+ value={(this.state.editData && this.state.editData[col.field]) || val}
382
+ >
383
+ {col.lookup && Object.keys(col.lookup)
384
+ .map((v, i) => <MenuItem key={i} value={v}>{col.lookup?.[v]}</MenuItem>)}
385
+ </Select>;
386
+ }
387
+
388
+ renderCellEditString(
389
+ col: Column,
390
+ val: string,
391
+ ) {
392
+ return <TextField
393
+ variant="standard"
394
+ style={styles.fieldEdit}
395
+ fullWidth
396
+ value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
397
+ onChange={e => this.onChange(col, val, e.target.value)}
398
+ />;
399
+ }
400
+
401
+ renderCellEditNumber(
402
+ col: Column,
403
+ val: number,
404
+ ) {
405
+ return <TextField
406
+ variant="standard"
407
+ style={styles.fieldEdit}
408
+ type="number"
409
+ fullWidth
410
+ value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
411
+ onChange={e => this.onChange(col, val, e.target.value)}
412
+ />;
413
+ }
414
+
415
+ renderCellEditCustom(
416
+ col: Column,
417
+ val: any,
418
+ item: Record<string, any>,
419
+ ) {
420
+ const EditComponent = col.editComponent;
421
+
422
+ // use new value if exists
423
+ if (this.state.editData && this.state.editData[col.field] !== undefined) {
424
+ val = this.state.editData[col.field];
425
+ item = JSON.parse(JSON.stringify(item));
426
+ item[col.field] = val;
427
+ }
428
+
429
+ return EditComponent ? <EditComponent
430
+ value={val}
431
+ rowData={item}
432
+ onChange={(newVal: any) => this.onChange(col, val, newVal as string | number)}
433
+ /> : null;
434
+ }
435
+
436
+ renderCellEditBoolean(
437
+ col: Column,
438
+ val: boolean,
439
+ ) {
440
+ return <Checkbox
441
+ checked={this.state.editData && this.state.editData[col.field] !== undefined ? !!this.state.editData[col.field] : !!val}
442
+ onChange={e => this.onChange(col, !!val, e.target.checked)}
443
+ inputProps={{ 'aria-label': 'checkbox' }}
444
+ />;
445
+ }
446
+
447
+ renderSelectColorDialog() {
448
+ return <Dialog
449
+ sx={{
450
+ '& .MuiPaper-root': styles.root,
451
+ '& .MuiPaper-paper': styles.paper,
452
+ }}
453
+ onClose={() => {
454
+ this.selectCallback = null;
455
+ this.setState({ showSelectColor: false });
456
+ }}
457
+ open={this.state.showSelectColor}
458
+ >
459
+ <ColorPicker
460
+ color={this.state.selectIdValue as string}
461
+ onChange={color =>
462
+ this.setState({ selectIdValue: color }, () =>
463
+ this.selectCallback && this.selectCallback(color))}
464
+ />
465
+ </Dialog>;
466
+ }
467
+
468
+ renderCellEditColor(
469
+ col: Column,
470
+ val: string,
471
+ ) {
472
+ const _val = this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val;
473
+ return <div style={styles.fieldEdit}>
474
+ <TextField
475
+ variant="standard"
476
+ fullWidth
477
+ style={styles.fieldEditWithButton}
478
+ value={_val}
479
+ inputProps={{ style: { backgroundColor: _val, color: Utils.isUseBright(_val) ? '#FFF' : '#000' } }}
480
+ onChange={e => this.onChange(col, !!val, e.target.value)}
481
+ />
482
+
483
+ <IconButton
484
+ style={styles.fieldButton}
485
+ onClick={() => {
486
+ this.selectCallback = newColor => this.onChange(col, val, newColor);
487
+ this.setState({ showSelectColor: true, selectIdValue: val });
488
+ }}
489
+ size="large"
490
+ >
491
+ <IconColor />
492
+ </IconButton>
493
+ </div>;
494
+ }
495
+
496
+ renderSelectIdDialog() {
497
+ if (this.state.showSelectId && this.props.socket) {
498
+ return <DialogSelectID
499
+ key="tableSelect"
500
+ imagePrefix="../.."
501
+ dialogName={this.props.adapterName}
502
+ themeType={this.props.themeType}
503
+ theme={this.props.theme}
504
+ socket={this.props.socket as Connection}
505
+ selected={this.state.selectIdValue as string}
506
+ onClose={() => this.setState({ showSelectId: false })}
507
+ onOk={(selected /* , name */) => {
508
+ this.setState({ showSelectId: false, selectIdValue: null });
509
+ this.selectCallback && this.selectCallback(selected as string);
510
+ this.selectCallback = null;
511
+ }}
512
+ />;
513
+ }
514
+
515
+ return null;
516
+ }
517
+
518
+ renderCellEditObjectID(
519
+ col: Column,
520
+ val: string,
521
+ ) {
522
+ return <div style={styles.fieldEdit}>
523
+ <TextField
524
+ variant="standard"
525
+ fullWidth
526
+ style={styles.fieldEditWithButton}
527
+ value={this.state.editData && this.state.editData[col.field] !== undefined ? this.state.editData[col.field] : val}
528
+ onChange={e => this.onChange(col, val, e.target.value)}
529
+ />
530
+
531
+ <IconButton
532
+ style={styles.fieldButton}
533
+ onClick={() => {
534
+ this.selectCallback = selected => this.onChange(col, val, selected);
535
+ this.setState({ showSelectId: true, selectIdValue: val });
536
+ }}
537
+ size="large"
538
+ >
539
+ <IconList />
540
+ </IconButton>
541
+ </div>;
542
+ }
543
+
544
+ static renderCellNonEdit(
545
+ item: Record<string, any>,
546
+ col: Column,
547
+ ) {
548
+ let val = getAttr(item, col.field, col.lookup);
549
+ if (Array.isArray(val)) {
550
+ val = val[0];
551
+ }
552
+
553
+ if (col.type === 'boolean') {
554
+ return <Checkbox
555
+ checked={!!val}
556
+ disabled
557
+ inputProps={{ 'aria-label': 'checkbox' }}
558
+ />;
559
+ }
560
+
561
+ return val;
562
+ }
563
+
564
+ renderCell(
565
+ item: Record<string, any>,
566
+ col: Column,
567
+ level: number,
568
+ i: number,
569
+ ) {
570
+ if (this.state.editMode === i && col.editable !== 'never' && col.editable !== false) {
571
+ return <TableCell
572
+ key={col.field}
573
+ style={{ ...styles.cell, ...(level ? styles.cellSecondary : undefined), ...col.cellStyle }}
574
+ component="th"
575
+ >
576
+ {this.renderCellEdit(item, col)}
577
+ </TableCell>;
578
+ }
579
+ return <TableCell
580
+ key={col.field}
581
+ style={{ ...styles.cell, ...(level ? styles.cellSecondary : undefined), ...col.cellStyle }}
582
+ component="th"
583
+ >
584
+ {TreeTable.renderCellNonEdit(item, col)}
585
+ </TableCell>;
586
+ }
587
+
588
+ static renderCellWithSubField(
589
+ item: Record<string, any>,
590
+ col: Column,
591
+ ) {
592
+ const main = getAttr(item, col.field, col.lookup);
593
+ if (col.subField) {
594
+ const sub = getAttr(item, col.subField, col.subLookup);
595
+ return <div>
596
+ <div style={styles.mainText}>{main}</div>
597
+ <div style={{ ...styles.subText, ...(col.subStyle || undefined) }}>{sub}</div>
598
+ </div>;
599
+ }
600
+ return <div>
601
+ <div style={styles.mainText}>{main}</div>
602
+ </div>;
603
+ }
604
+
605
+ renderLine(
606
+ item: Record<string, any>,
607
+ level?: number,
608
+ ): React.JSX.Element | React.JSX.Element[] | null {
609
+ const levelShift = this.props.levelShift === undefined ? 24 : this.props.levelShift;
610
+
611
+ level = level || 0;
612
+ const i = this.props.data.indexOf(item);
613
+ if (!item) {
614
+ return null;
615
+ }
616
+ if (!level && item.parentId) {
617
+ return null;
618
+ }
619
+ if (level && !item.parentId) {
620
+ return null; // should never happen
621
+ }
622
+ // try to find children
623
+ const opened = this.state.opened.includes(item.id);
624
+ const children = this.props.data.filter(it => it.parentId === item.id);
625
+
626
+ const row = <TableRow
627
+ key={item.id}
628
+ className={`table-row-${(item.id || '').toString().replace(/[.$]/g, '_')}`}
629
+ style={{
630
+ ...((this.state.update && this.state.update.includes(item.id) && styles.glow) || undefined),
631
+ ...styles.row,
632
+ ...(level ? styles.rowSecondary : undefined),
633
+ ...(!level && children.length ? styles.rowMainWithChildren : undefined),
634
+ ...(!level && !children.length ? styles.rowMainWithoutChildren : undefined),
635
+ ...(this.state.editMode !== false && this.state.editMode !== i ? styles.rowNoEdit : undefined),
636
+ ...(this.state.deleteMode !== false && this.state.deleteMode !== i ? styles.rowNoEdit : undefined),
637
+ }}
638
+ >
639
+ <TableCell
640
+ style={{ ...styles.cell, ...styles.cellExpand, ...(level ? styles.cellSecondary : undefined) }}
641
+ >
642
+ {children.length ? <IconButton
643
+ onClick={() => {
644
+ const _opened = [...this.state.opened];
645
+ const pos = _opened.indexOf(item.id);
646
+ if (pos === -1) {
647
+ _opened.push(item.id);
648
+ _opened.sort();
649
+ } else {
650
+ _opened.splice(pos, 1);
651
+ }
652
+
653
+ ((window as any)._localStorage || window.localStorage).setItem(this.props.name || 'iob-table', JSON.stringify(_opened));
654
+
655
+ this.setState({ opened: _opened });
656
+ }}
657
+ size="small"
658
+ >
659
+ {opened ? <IconCollapse /> : <IconExpand />}
660
+ </IconButton> : null}
661
+ </TableCell>
662
+ <TableCell
663
+ scope="row"
664
+ style={{
665
+ ...styles.cell,
666
+ ...(level ? styles.cellSecondary : undefined),
667
+ ...this.props.columns[0].cellStyle,
668
+ paddingLeft: levelShift * level,
669
+ }}
670
+ >
671
+ {this.props.columns[0].subField ?
672
+ TreeTable.renderCellWithSubField(item, this.props.columns[0])
673
+ :
674
+ getAttr(item, this.props.columns[0].field, this.props.columns[0].lookup)}
675
+ </TableCell>
676
+
677
+ {this.props.columns.map((col, ii) =>
678
+ (!ii && !col.hidden ? null : this.renderCell(item, col, level, i)))}
679
+
680
+ {this.props.onUpdate ? <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
681
+ {this.state.editMode === i || this.state.deleteMode === i ?
682
+ <IconButton
683
+ disabled={this.state.editMode !== false && (!this.state.editData || !Object.keys(this.state.editData).length)}
684
+ onClick={() => {
685
+ if (this.state.editMode !== false) {
686
+ const newData = JSON.parse(JSON.stringify(item));
687
+ this.state.editData && Object.keys(this.state.editData).forEach(attr =>
688
+ setAttr(newData, attr, this.state.editData?.[attr]));
689
+ this.setState({ editMode: false }, () => this.props.onUpdate && this.props.onUpdate(newData, item));
690
+ } else {
691
+ this.setState({ deleteMode: false }, () => this.props.onDelete && this.props.onDelete(item));
692
+ }
693
+ }}
694
+ size="large"
695
+ >
696
+ <IconCheck />
697
+ </IconButton>
698
+ :
699
+ <IconButton
700
+ disabled={this.state.editMode !== false}
701
+ onClick={() => this.setState({ editMode: i, editData: null })}
702
+ size="large"
703
+ >
704
+ <IconEdit />
705
+ </IconButton>}
706
+ </TableCell> : null}
707
+
708
+ {this.props.onDelete && !this.props.onUpdate ?
709
+ <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
710
+ {this.state.deleteMode === i ?
711
+ <IconButton
712
+ disabled={this.state.editMode !== false && (!this.state.editData || !Object.keys(this.state.editData).length)}
713
+ onClick={() => this.setState({ deleteMode: false }, () => this.props.onDelete && this.props.onDelete(item))}
714
+ size="large"
715
+ >
716
+ <IconCheck />
717
+ </IconButton>
718
+ :
719
+ null}
720
+ </TableCell> : null}
721
+
722
+ {this.props.onUpdate || this.props.onDelete ? <TableCell style={{ ...styles.cell, ...styles.cellButton }}>
723
+ {this.state.editMode === i || this.state.deleteMode === i ?
724
+ <IconButton
725
+ onClick={() => this.setState({ editMode: false, deleteMode: false })}
726
+ size="large"
727
+ >
728
+ <IconClose />
729
+ </IconButton>
730
+ :
731
+ (this.props.onDelete ? <IconButton
732
+ disabled={this.state.deleteMode !== false}
733
+ onClick={() => this.setState({ deleteMode: i })}
734
+ size="large"
735
+ >
736
+ <IconDelete />
737
+ </IconButton> : null)}
738
+ </TableCell> : null}
739
+ </TableRow>;
740
+
741
+ if (!level && opened) {
742
+ const items: React.JSX.Element[] = children.map(it =>
743
+ this.renderLine(it, level + 1)) as React.JSX.Element[];
744
+ items.unshift(row);
745
+ return items;
746
+ }
747
+ return row;
748
+ }
749
+
750
+ handleRequestSort(property: string) {
751
+ const isAsc = this.state.orderBy === property && this.state.order === 'asc';
752
+ this.setState({ order: isAsc ? 'desc' : 'asc', orderBy: property });
753
+ }
754
+
755
+ renderHead() {
756
+ return <TableHead>
757
+ <TableRow key="headerRow">
758
+ <TableCell
759
+ component="th"
760
+ sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellExpand)}
761
+ />
762
+ <TableCell
763
+ component="th"
764
+ sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles[`width_${this.props.columns[0].field.replace(/\./g, '_')}`])}
765
+ style={this.props.columns[0].headerStyle || this.props.columns[0].cellStyle}
766
+ sortDirection={this.props.noSort ? false : (this.state.orderBy === this.props.columns[0].field ? this.state.order : false)}
767
+ >
768
+ {this.props.noSort ? null : <TableSortLabel
769
+ active={this.state.orderBy === this.props.columns[0].field}
770
+ direction={this.state.orderBy === this.props.columns[0].field ? this.state.order : 'asc'}
771
+ onClick={() => this.handleRequestSort(this.props.columns[0].field)}
772
+ >
773
+ {this.props.columns[0].title || this.props.columns[0].field}
774
+ {this.state.orderBy === this.props.columns[0].field ?
775
+ <span style={styles.visuallyHidden}>
776
+ {this.state.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
777
+ </span> : null}
778
+ </TableSortLabel>}
779
+ </TableCell>
780
+ {this.props.columns.map((col, i) =>
781
+ (!i && !col.hidden ? null : <TableCell
782
+ key={col.field}
783
+ sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles[`width_${col.field.replace(/\./g, '_')}`])}
784
+ style={col.headerStyle || col.cellStyle}
785
+ component="th"
786
+ >
787
+ {this.props.noSort ? null : <TableSortLabel
788
+ active={this.state.orderBy === col.field}
789
+ direction={this.state.orderBy === col.field ? this.state.order : 'asc'}
790
+ onClick={() => this.handleRequestSort(col.field)}
791
+ >
792
+ {col.title || col.field}
793
+ {this.state.orderBy === col.field ?
794
+ <span style={styles.visuallyHidden}>
795
+ {this.state.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
796
+ </span> : null}
797
+ </TableSortLabel> }
798
+ </TableCell>))}
799
+ {this.props.onUpdate ? <TableCell component="th" sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellButton)}>
800
+ {!this.props.noAdd ? <Fab
801
+ color="primary"
802
+ size="small"
803
+ disabled={this.state.editMode !== false}
804
+ onClick={() => this.props.onUpdate && (this.props.onUpdate as (addNew: true) => void)(true)}
805
+ >
806
+ <IconAdd />
807
+ </Fab> : null }
808
+ </TableCell> : null}
809
+ {this.props.onDelete || this.props.onUpdate ?
810
+ <TableCell component="th" sx={Utils.getStyle(this.props.theme, styles.cell, styles.cellHeader, styles.cellButton)} /> : null}
811
+ </TableRow>
812
+ </TableHead>;
813
+ }
814
+
815
+ render() {
816
+ const col = this.props.columns.find(_col => _col.field === this.state.orderBy);
817
+ if (col) {
818
+ const lookup = col.lookup;
819
+ const table = stableSort(this.props.data, getComparator(this.state.order, this.state.orderBy, lookup));
820
+
821
+ if (this.state.update && this.state.update.length) {
822
+ this.updateTimeout && clearTimeout(this.updateTimeout);
823
+ this.updateTimeout = setTimeout(() => {
824
+ this.updateTimeout = null;
825
+ this.setState({ update: null });
826
+ }, 500);
827
+ }
828
+
829
+ return <div style={styles.tableContainer} className={this.props.className}>
830
+ <Table style={styles.table} aria-label="simple table" size="small" stickyHeader>
831
+ {this.renderHead()}
832
+ <TableBody>
833
+ {table.map(it => this.renderLine(it))}
834
+ </TableBody>
835
+ </Table>
836
+ {this.renderSelectIdDialog()}
837
+ {this.renderSelectColorDialog()}
838
+ </div>;
839
+ }
840
+
841
+ return null;
842
+ }
843
+ }
844
+ /*
845
+ const columns = [
846
+ {
847
+ title: 'Name of field', // required, else it will be "field"
848
+ field: 'fieldIdInData', // required
849
+ editable: false, // or true [default - true]
850
+ cellStyle: { // CSS style - // optional
851
+ maxWidth: '12rem',
852
+ overflow: 'hidden',
853
+ wordBreak: 'break-word'
854
+ },
855
+ lookup: { // optional => edit will be automatically "SELECT"
856
+ 'value1': 'text1',
857
+ 'value2': 'text2',
858
+ }
859
+ },
860
+ {
861
+ title: 'Type', // required, else it will be "field"
862
+ field: 'myType', // required
863
+ editable: true, // or true [default - true]
864
+ lookup: { // optional => edit will be automatically "SELECT"
865
+ 'number': 'Number',
866
+ 'string': 'String',
867
+ 'boolean': 'Boolean',
868
+ },
869
+ type: 'number/string/color/oid/icon/boolean', // oid=ObjectID,icon=base64-icon
870
+ editComponent: props =>
871
+ <div>Prefix&#123; <br/>
872
+ <textarea
873
+ rows={4}
874
+ style={{width: '100%', resize: 'vertical'}}
875
+ value={props.value}
876
+ onChange={e => props.onChange(e.target.value)}
877
+ />
878
+ Suffix
879
+ </div>,
880
+ },
881
+ ];
882
+ */
883
+ /* const data = [
884
+ {
885
+ id: 'UniqueID1' // required
886
+ fieldIdInData: 'Name1',
887
+ myType: 'number',
888
+ },
889
+ {
890
+ id: 'UniqueID2' // required
891
+ fieldIdInData: 'Name12',
892
+ myType: 'string',
893
+ },
894
+ ];
895
+ */
896
+
897
+ /*
898
+ // STYLES
899
+ const styles = theme => ({
900
+ tableDiv: {
901
+ width: '100%',
902
+ overflow: 'hidden',
903
+ height: 'calc(100% - 48px)',
904
+ },
905
+ });
906
+ // renderTable
907
+ renderTable() {
908
+ return <div style={styles.tableDiv}>
909
+ <TreeTable
910
+ columns={this.columns}
911
+ data={lines}
912
+ onUpdate={(newData, oldData) => console.log('Update: ' + JSON.stringify(newData))}
913
+ onDelete={oldData => console.log('Delete: ' + JSON.stringify(oldData))}
914
+ />
915
+ </div>;
916
+ }
917
+ */
918
+
919
+ export default TreeTable;