@iobroker/adapter-react-v5 6.1.9 → 7.0.1

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 (210) hide show
  1. package/Components/404.js +13 -13
  2. package/Components/FileBrowser.js +24 -19
  3. package/Components/FileViewer.js +14 -5
  4. package/Components/Loader.js +223 -223
  5. package/Components/Loaders/PT.css +108 -108
  6. package/Components/Loaders/PT.js +103 -103
  7. package/Components/Loaders/Vendor.css +13 -13
  8. package/Components/Loaders/Vendor.js +7 -7
  9. package/Components/ObjectBrowser.d.ts +2 -0
  10. package/Components/ObjectBrowser.js +214 -115
  11. package/Components/UploadImage.js +305 -305
  12. package/Components/loader.css +221 -221
  13. package/Components/types.d.ts +82 -82
  14. package/GenericApp.js +49 -49
  15. package/LICENSE +22 -22
  16. package/Prompt.js +7 -7
  17. package/README.md +1004 -1008
  18. package/Theme.js +8 -7
  19. package/assets/devices/Alarm Systems.svg +18 -18
  20. package/assets/devices/Amplifier.svg +21 -21
  21. package/assets/devices/Awnings.svg +4 -4
  22. package/assets/devices/Battery Status.svg +4 -4
  23. package/assets/devices/Ceiling Spotlights.svg +15 -15
  24. package/assets/devices/Chandelier.svg +6 -6
  25. package/assets/devices/Climate.svg +11 -11
  26. package/assets/devices/Coffee Makers.svg +5 -5
  27. package/assets/devices/Cold Water.svg +31 -31
  28. package/assets/devices/Computer.svg +21 -21
  29. package/assets/devices/Consumption.svg +7 -7
  30. package/assets/devices/Curtains.svg +43 -43
  31. package/assets/devices/Dishwashers.svg +11 -11
  32. package/assets/devices/Doors.svg +5 -5
  33. package/assets/devices/Doorstep.svg +35 -35
  34. package/assets/devices/Dryer.svg +13 -13
  35. package/assets/devices/Fan.svg +20 -20
  36. package/assets/devices/Floor Lamps.svg +4 -4
  37. package/assets/devices/Garage Doors.svg +9 -9
  38. package/assets/devices/Gates.svg +32 -32
  39. package/assets/devices/Hairdryer.svg +23 -23
  40. package/assets/devices/Handle.svg +6 -6
  41. package/assets/devices/Hanging Lamps.svg +8 -8
  42. package/assets/devices/Heater.svg +44 -44
  43. package/assets/devices/Hoods.svg +11 -11
  44. package/assets/devices/Hot Water.svg +9 -9
  45. package/assets/devices/Humidity.svg +41 -41
  46. package/assets/devices/Iron.svg +4 -4
  47. package/assets/devices/Irrigation.svg +22 -22
  48. package/assets/devices/Led Strip.svg +30 -30
  49. package/assets/devices/Light.svg +29 -29
  50. package/assets/devices/Lightings.svg +46 -46
  51. package/assets/devices/Lock.svg +19 -19
  52. package/assets/devices/Louvre.svg +6 -6
  53. package/assets/devices/Mowing Machine.svg +8 -8
  54. package/assets/devices/Music.svg +12 -12
  55. package/assets/devices/Outdoor Blinds.svg +6 -6
  56. package/assets/devices/People.svg +19 -19
  57. package/assets/devices/Pool.svg +7 -7
  58. package/assets/devices/Power Consumption.svg +12 -12
  59. package/assets/devices/Printer.svg +9 -9
  60. package/assets/devices/Pump.svg +9 -9
  61. package/assets/devices/Receiver.svg +18 -18
  62. package/assets/devices/Sconces.svg +9 -9
  63. package/assets/devices/Security.svg +34 -34
  64. package/assets/devices/Shading.svg +4 -4
  65. package/assets/devices/Shutters.svg +10 -10
  66. package/assets/devices/SmokeDetector.svg +12 -12
  67. package/assets/devices/Sockets.svg +13 -13
  68. package/assets/devices/Speaker.svg +35 -35
  69. package/assets/devices/Stove.svg +11 -11
  70. package/assets/devices/Table Lamps.svg +11 -11
  71. package/assets/devices/Temperature Sensors.svg +28 -28
  72. package/assets/devices/Tv.svg +7 -7
  73. package/assets/devices/Vacuum Cleaner.svg +15 -15
  74. package/assets/devices/Ventilation.svg +12 -12
  75. package/assets/devices/Washing Machines.svg +15 -15
  76. package/assets/devices/Water Consumption.svg +5 -5
  77. package/assets/devices/Water Heater.svg +8 -8
  78. package/assets/devices/Water.svg +40 -40
  79. package/assets/devices/Weather.svg +28 -28
  80. package/assets/devices/Window.svg +7 -7
  81. package/assets/lamp_ceiling.svg +8 -8
  82. package/assets/lamp_table.svg +7 -7
  83. package/assets/no_icon.svg +9 -9
  84. package/assets/rooms/Anteroom.svg +52 -52
  85. package/assets/rooms/Attic.svg +21 -21
  86. package/assets/rooms/Balcony.svg +12 -12
  87. package/assets/rooms/Barn.svg +5 -5
  88. package/assets/rooms/Basement.svg +4 -4
  89. package/assets/rooms/Bathroom.svg +38 -38
  90. package/assets/rooms/Bedroom.svg +5 -5
  91. package/assets/rooms/Boiler Room.svg +12 -12
  92. package/assets/rooms/Carport.svg +17 -17
  93. package/assets/rooms/Cellar.svg +89 -89
  94. package/assets/rooms/Chamber.svg +9 -9
  95. package/assets/rooms/Corridor.svg +52 -52
  96. package/assets/rooms/Dining Area.svg +37 -37
  97. package/assets/rooms/Dining Room.svg +37 -37
  98. package/assets/rooms/Dining.svg +37 -37
  99. package/assets/rooms/Dressing Room.svg +4 -4
  100. package/assets/rooms/Driveway.svg +14 -14
  101. package/assets/rooms/Entrance.svg +44 -44
  102. package/assets/rooms/Equipment Room.svg +14 -14
  103. package/assets/rooms/Front Yard.svg +64 -64
  104. package/assets/rooms/Gallery.svg +13 -13
  105. package/assets/rooms/Garage.svg +20 -20
  106. package/assets/rooms/Garden.svg +12 -12
  107. package/assets/rooms/Ground Floor.svg +95 -95
  108. package/assets/rooms/Guest Bathroom.svg +32 -32
  109. package/assets/rooms/Guest Room.svg +5 -5
  110. package/assets/rooms/Gym.svg +4 -4
  111. package/assets/rooms/Hall.svg +19 -19
  112. package/assets/rooms/Home Theater.svg +7 -7
  113. package/assets/rooms/Kitchen.svg +17 -17
  114. package/assets/rooms/Laundry Room.svg +11 -11
  115. package/assets/rooms/Living Area.svg +10 -10
  116. package/assets/rooms/Living Room.svg +10 -10
  117. package/assets/rooms/Locker Room.svg +16 -16
  118. package/assets/rooms/Nursery.svg +4 -4
  119. package/assets/rooms/Office.svg +8 -8
  120. package/assets/rooms/Outdoors.svg +7 -7
  121. package/assets/rooms/Playroom.svg +5 -5
  122. package/assets/rooms/Pool.svg +7 -7
  123. package/assets/rooms/Rear Wall.svg +30 -30
  124. package/assets/rooms/Second Floor.svg +95 -95
  125. package/assets/rooms/Shed.svg +16 -16
  126. package/assets/rooms/Sleeping Area.svg +22 -22
  127. package/assets/rooms/Stairway.svg +4 -4
  128. package/assets/rooms/Stairwell.svg +15 -15
  129. package/assets/rooms/Storeroom.svg +4 -4
  130. package/assets/rooms/Summer House.svg +27 -27
  131. package/assets/rooms/Swimming Pool.svg +21 -21
  132. package/assets/rooms/Terrace.svg +6 -6
  133. package/assets/rooms/Toilet.svg +10 -10
  134. package/assets/rooms/Upstairs.svg +5 -5
  135. package/assets/rooms/Wardrobe.svg +60 -60
  136. package/assets/rooms/Washroom.svg +19 -19
  137. package/assets/rooms/Wc.svg +10 -10
  138. package/assets/rooms/Windscreen.svg +60 -60
  139. package/assets/rooms/Workshop.svg +22 -22
  140. package/assets/rooms/Workspace.svg +8 -8
  141. package/craco-module-federation.js +71 -71
  142. package/icons/IconFx.js +1 -1
  143. package/icons/IconLogout.js +1 -1
  144. package/index.css +54 -54
  145. package/modulefederation.admin.config.js +31 -31
  146. package/package.json +9 -9
  147. package/src/AdminConnection.tsx +3 -3
  148. package/src/Components/404.tsx +121 -121
  149. package/src/Components/ColorPicker.tsx +315 -315
  150. package/src/Components/ComplexCron.tsx +507 -507
  151. package/src/Components/CopyToClipboard.tsx +165 -165
  152. package/src/Components/CustomModal.tsx +163 -163
  153. package/src/Components/FileBrowser.tsx +2414 -2394
  154. package/src/Components/FileViewer.tsx +393 -384
  155. package/src/Components/Icon.tsx +210 -210
  156. package/src/Components/IconPicker.tsx +149 -149
  157. package/src/Components/IconSelector.tsx +2202 -2202
  158. package/src/Components/Image.tsx +176 -176
  159. package/src/Components/Loader.tsx +304 -304
  160. package/src/Components/Logo.tsx +166 -166
  161. package/src/Components/MDUtils.tsx +100 -100
  162. package/src/Components/ObjectBrowser.tsx +8032 -7915
  163. package/src/Components/Router.tsx +90 -90
  164. package/src/Components/SaveCloseButtons.tsx +113 -113
  165. package/src/Components/Schedule.tsx +1724 -1724
  166. package/src/Components/SelectWithIcon.tsx +197 -197
  167. package/src/Components/TabContainer.tsx +55 -55
  168. package/src/Components/TabContent.tsx +37 -37
  169. package/src/Components/TabHeader.tsx +19 -19
  170. package/src/Components/TableResize.tsx +259 -259
  171. package/src/Components/TextWithIcon.tsx +148 -148
  172. package/src/Components/ToggleThemeMenu.tsx +34 -34
  173. package/src/Components/TreeTable.tsx +919 -919
  174. package/src/Components/UploadImage.tsx +599 -599
  175. package/src/Components/Utils.tsx +1794 -1794
  176. package/src/Components/loader.css +221 -221
  177. package/src/Components/withWidth.tsx +21 -21
  178. package/src/Connection.tsx +7 -7
  179. package/src/Dialogs/ComplexCron.tsx +129 -129
  180. package/src/Dialogs/Confirm.tsx +162 -162
  181. package/src/Dialogs/Cron.tsx +182 -182
  182. package/src/Dialogs/Error.tsx +72 -72
  183. package/src/Dialogs/Message.tsx +71 -71
  184. package/src/Dialogs/SelectFile.tsx +270 -270
  185. package/src/Dialogs/SelectID.tsx +298 -298
  186. package/src/Dialogs/SimpleCron.tsx +100 -100
  187. package/src/Dialogs/TextInput.tsx +107 -107
  188. package/src/GenericApp.tsx +976 -976
  189. package/src/LegacyConnection.tsx +3589 -3589
  190. package/src/Prompt.tsx +20 -20
  191. package/src/Theme.tsx +479 -479
  192. package/src/icons/IconAdapter.tsx +20 -20
  193. package/src/icons/IconAlias.tsx +20 -20
  194. package/src/icons/IconChannel.tsx +21 -21
  195. package/src/icons/IconClearFilter.tsx +22 -22
  196. package/src/icons/IconClosed.tsx +17 -17
  197. package/src/icons/IconCopy.tsx +16 -16
  198. package/src/icons/IconDevice.tsx +27 -27
  199. package/src/icons/IconDocument.tsx +17 -17
  200. package/src/icons/IconDocumentReadOnly.tsx +18 -18
  201. package/src/icons/IconExpert.tsx +18 -18
  202. package/src/icons/IconFx.tsx +36 -36
  203. package/src/icons/IconInstance.tsx +20 -20
  204. package/src/icons/IconLogout.tsx +30 -30
  205. package/src/icons/IconNoIcon.tsx +19 -19
  206. package/src/icons/IconOpen.tsx +17 -17
  207. package/src/icons/IconProps.tsx +15 -15
  208. package/src/icons/IconState.tsx +17 -17
  209. package/src/index.css +54 -54
  210. package/types.d.ts +134 -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;