@iobroker/adapter-react-v5 8.0.6 → 8.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/build/Components/ObjectBrowser.d.ts +8 -399
- package/build/Components/ObjectBrowser.js +282 -1604
- package/build/Components/ObjectBrowser.js.map +1 -1
- package/build/Components/objectBrowserUtils.d.ts +170 -0
- package/build/Components/objectBrowserUtils.js +1372 -0
- package/build/Components/objectBrowserUtils.js.map +1 -0
- package/build/Dialogs/SelectID.d.ts +1 -1
- package/build/Dialogs/SelectID.js +1 -1
- package/build/Dialogs/SelectID.js.map +1 -1
- package/build/GenericApp.js.map +1 -1
- package/build/i18n/de.json +7 -5
- package/build/i18n/en.json +7 -5
- package/build/i18n/es.json +7 -5
- package/build/i18n/fr.json +7 -5
- package/build/i18n/it.json +7 -0
- package/build/i18n/nl.json +7 -0
- package/build/i18n/pl.json +7 -0
- package/build/i18n/pt.json +7 -0
- package/build/i18n/ru.json +7 -0
- package/build/i18n/uk.json +7 -0
- package/build/i18n/zh-cn.json +7 -0
- package/build/index.d.ts +2 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/types.d.ts +0 -79
- package/i18n/de.json +7 -5
- package/i18n/en.json +7 -5
- package/i18n/es.json +7 -5
- package/i18n/fr.json +7 -5
- package/i18n/it.json +7 -0
- package/i18n/nl.json +7 -0
- package/i18n/pl.json +7 -0
- package/i18n/pt.json +7 -0
- package/i18n/ru.json +7 -0
- package/i18n/uk.json +7 -0
- package/i18n/zh-cn.json +7 -0
- package/package.json +1 -1
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
import React, { Component, createRef } from 'react';
|
|
8
|
-
import
|
|
9
|
-
import { Badge, Box, Button, Checkbox, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fab, FormControl, FormControlLabel, Grid2, IconButton, Input, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, Select, Snackbar, Switch, TextField, Tooltip, } from '@mui/material';
|
|
8
|
+
import { Badge, Box, Button, Checkbox, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fab, FormControlLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Paper, Snackbar, Switch, TextField, Tooltip, } from '@mui/material';
|
|
10
9
|
// Icons
|
|
11
|
-
import { Add as AddIcon, ArrowRight as ArrowRightIcon, BedroomParent, BorderColor, Build as BuildIcon, CalendarToday as IconSchedule, Check as IconCheck, Close as IconClose, Code as IconScript, Construction, CreateNewFolder as IconFolder, Delete as IconDelete, Description as IconMeta, Edit as IconEdit, Error as IconError, FindInPage, FormatItalic as IconValueEdit,
|
|
10
|
+
import { Add as AddIcon, ArrowRight as ArrowRightIcon, BedroomParent, BorderColor, Build as BuildIcon, CalendarToday as IconSchedule, Check as IconCheck, Close as IconClose, Code as IconScript, Construction, CreateNewFolder as IconFolder, Delete as IconDelete, Description as IconMeta, Edit as IconEdit, Error as IconError, FindInPage, FormatItalic as IconValueEdit, Link as IconLink, ListAlt as IconEnum, LooksOne as LooksOneIcon, PersonOutlined as IconUser, Publish as PublishIcon, Refresh as RefreshIcon, Router as IconHost, Settings as IconConfig, ShowChart as IconChart, SupervisedUserCircle as IconGroup, TextFields as TextFieldsIcon, ViewColumn as IconColumns, Wifi as IconConnection, WifiOff as IconDisconnected, DriveFileRenameOutline, ContentPaste, UploadFile, } from '@mui/icons-material';
|
|
12
11
|
import { IconExpert } from '../icons/IconExpert';
|
|
13
12
|
import { IconAdapter } from '../icons/IconAdapter';
|
|
14
13
|
import { IconChannel } from '../icons/IconChannel';
|
|
@@ -28,15 +27,10 @@ import { Utils } from './Utils'; // @iobroker/adapter-react-v5/Components/Utils
|
|
|
28
27
|
import { TabContainer } from './TabContainer';
|
|
29
28
|
import { TabContent } from './TabContent';
|
|
30
29
|
import { TabHeader } from './TabHeader';
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
import { CustomFilterInput, CustomFilterSelect, ButtonIcon, applyFilter, binarySearch, buildTree, findEnumsForObjectAsIds, findFunctionsForObject, findNode, findRoomsForObject, formatValue, generateFile, getCustomValue, getIdFieldTooltip, getName, getObjectTooltip, getSelectIdIconFromObjects, getValueStyle, getVisibleItems, isNonExpertId, setCustomValue, prepareSparkData, COLOR_NAME_USERDATA, COLOR_NAME_ALIAS, COLOR_NAME_JAVASCRIPT, COLOR_NAME_SYSTEM, COLOR_NAME_SYSTEM_ADAPTER, ICON_SIZE, ROW_HEIGHT, styles as utilStyles, } from './objectBrowserUtils';
|
|
31
|
+
export { getSelectIdIconFromObjects };
|
|
33
32
|
const ITEM_LEVEL = 16;
|
|
34
33
|
const SMALL_BUTTON_SIZE = 20;
|
|
35
|
-
const COLOR_NAME_USERDATA = (themeType) => (themeType === 'dark' ? '#62ff25' : '#37c400');
|
|
36
|
-
const COLOR_NAME_ALIAS = (themeType) => (themeType === 'dark' ? '#ee56ff' : '#a204b4');
|
|
37
|
-
const COLOR_NAME_JAVASCRIPT = (themeType) => (themeType === 'dark' ? '#fff46e' : '#b89101');
|
|
38
|
-
const COLOR_NAME_SYSTEM = (themeType) => (themeType === 'dark' ? '#ff6d69' : '#ff6d69');
|
|
39
|
-
const COLOR_NAME_SYSTEM_ADAPTER = (themeType) => (themeType === 'dark' ? '#5773ff' : '#5773ff');
|
|
40
34
|
const COLOR_NAME_ERROR_DARK = '#ff413c';
|
|
41
35
|
const COLOR_NAME_ERROR_LIGHT = '#86211f';
|
|
42
36
|
const COLOR_NAME_CONNECTED_DARK = '#57ff45';
|
|
@@ -232,18 +226,6 @@ const styles = {
|
|
|
232
226
|
height: SMALL_BUTTON_SIZE,
|
|
233
227
|
},
|
|
234
228
|
cellIdIconOwn: {},
|
|
235
|
-
cellIdTooltip: {
|
|
236
|
-
fontSize: 14,
|
|
237
|
-
},
|
|
238
|
-
cellIdTooltipLink: {
|
|
239
|
-
color: '#7ec2fd',
|
|
240
|
-
'&:hover': {
|
|
241
|
-
color: '#7ec2fd',
|
|
242
|
-
},
|
|
243
|
-
'&:visited': {
|
|
244
|
-
color: '#7ec2fd',
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
229
|
cellCopyButton: {
|
|
248
230
|
width: SMALL_BUTTON_SIZE,
|
|
249
231
|
height: SMALL_BUTTON_SIZE,
|
|
@@ -508,9 +490,6 @@ const styles = {
|
|
|
508
490
|
height: 24,
|
|
509
491
|
marginRight: 4,
|
|
510
492
|
},
|
|
511
|
-
selectNone: {
|
|
512
|
-
opacity: 0.5,
|
|
513
|
-
},
|
|
514
493
|
itemSelected: (theme) => ({
|
|
515
494
|
background: `${theme.palette.primary.main} !important`,
|
|
516
495
|
color: `${Utils.invertColor(theme.palette.primary.main, true)} !important`,
|
|
@@ -527,25 +506,6 @@ const styles = {
|
|
|
527
506
|
// paddingLeft: 5,
|
|
528
507
|
fontSize: 16,
|
|
529
508
|
},
|
|
530
|
-
headerCellInput: {
|
|
531
|
-
width: 'calc(100% - 5px)',
|
|
532
|
-
height: ROW_HEIGHT,
|
|
533
|
-
pt: 0,
|
|
534
|
-
'& .itemIcon': {
|
|
535
|
-
verticalAlign: 'middle',
|
|
536
|
-
width: ICON_SIZE,
|
|
537
|
-
height: ICON_SIZE,
|
|
538
|
-
display: 'inline-block',
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
headerCellSelectItem: {
|
|
542
|
-
'& .itemIcon': {
|
|
543
|
-
width: ICON_SIZE,
|
|
544
|
-
height: ICON_SIZE,
|
|
545
|
-
mr: '5px',
|
|
546
|
-
display: 'inline-block',
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
509
|
visibleButtons: {
|
|
550
510
|
color: '#2196f3',
|
|
551
511
|
opacity: 0.7,
|
|
@@ -579,1343 +539,130 @@ const styles = {
|
|
|
579
539
|
},
|
|
580
540
|
buttonDiv: {
|
|
581
541
|
display: 'flex',
|
|
582
|
-
height: '100%',
|
|
583
|
-
alignItems: 'center',
|
|
584
|
-
},
|
|
585
|
-
aclText: {
|
|
586
|
-
fontSize: 13,
|
|
587
|
-
marginTop: 6,
|
|
588
|
-
},
|
|
589
|
-
rightsObject: {
|
|
590
|
-
color: '#55ff55',
|
|
591
|
-
paddingLeft: 3,
|
|
592
|
-
},
|
|
593
|
-
rightsState: {
|
|
594
|
-
color: '#86b6ff',
|
|
595
|
-
paddingLeft: 3,
|
|
596
|
-
},
|
|
597
|
-
textCenter: {
|
|
598
|
-
padding: 12,
|
|
599
|
-
textAlign: 'center',
|
|
600
|
-
},
|
|
601
|
-
tooltipAccessControl: {
|
|
602
|
-
display: 'flex',
|
|
603
|
-
flexDirection: 'column',
|
|
604
|
-
},
|
|
605
|
-
fontSizeTitle: {
|
|
606
|
-
'@media screen and (max-width: 465px)': {
|
|
607
|
-
'& *': {
|
|
608
|
-
fontSize: 12,
|
|
609
|
-
},
|
|
610
|
-
},
|
|
611
|
-
},
|
|
612
|
-
draggable: {
|
|
613
|
-
cursor: 'copy',
|
|
614
|
-
},
|
|
615
|
-
nonDraggable: {
|
|
616
|
-
cursor: 'no-drop',
|
|
617
|
-
},
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
position: 'absolute',
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
'
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}),
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
fontSize: 'smaller',
|
|
707
|
-
},
|
|
708
|
-
contextMenuWithSubMenu: {
|
|
709
|
-
display: 'flex',
|
|
710
|
-
},
|
|
711
|
-
};
|
|
712
|
-
function ButtonIcon(props) {
|
|
713
|
-
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 436 436", style: props?.style, width: "24", height: "24", className: "admin-button" },
|
|
714
|
-
React.createElement("g", { fill: "currentColor" },
|
|
715
|
-
React.createElement("path", { d: "m195.23077,24.30769c-36,3 -67,12 -96,26c-49,24 -82,61 -93,104l-3,11l-1,50c0,46 0,49 2,59l5,20c21,58 84,103 165,116c16,3 53,4 70,2c60,-6 111,-28 147,-64c21,-21 36,-49 40,-74a866,866 0 0 0 1,-104c-3,-18 -6,-28 -13,-43c-26,-52 -87,-90 -162,-101c-16,-2 -48,-3 -63,-2l1,0zm60,23c36,5 70,18 95,35c31,20 51,47 59,77c2,7 2,11 2,25c1,15 0,18 -2,26c-19,69 -104,117 -200,114c-47,-2 -90,-15 -124,-38c-31,-20 -51,-47 -59,-77c-3,-11 -4,-32 -2,-43c8,-42 41,-78 91,-101a260,260 0 0 1 140,-19l0,1zm-221,222c21,26 57,49 95,62c81,27 174,14 239,-32c14,-10 31,-27 41,-41c2,-2 2,-2 2,7c-1,23 -16,50 -38,72c-78,74 -233,74 -311,-1a121,121 0 0 1 -39,-76l0,-6l3,4l8,11z" }),
|
|
716
|
-
React.createElement("path", { d: "m201.23077,47.30769c-40,3 -79,19 -104,44c-55,55 -38,133 37,171c52,26 122,24 172,-5c30,-17 51,-42 58,-71c3,-11 3,-34 0,-45c-6,-23 -21,-44 -40,-60l-27,-16a184,184 0 0 0 -96,-18zm30,21c56,5 100,35 112,75c4,11 4,30 0,41c-8,25 -26,45 -54,59a166,166 0 0 1 -160,-8a98,98 0 0 1 -41,-53c-5,-18 -2,-39 8,-57c23,-39 79,-62 135,-57z" }))));
|
|
717
|
-
}
|
|
718
|
-
/** Converts ioB pattern into regex */
|
|
719
|
-
export function pattern2RegEx(pattern) {
|
|
720
|
-
pattern = (pattern || '').toString();
|
|
721
|
-
const startsWithWildcard = pattern[0] === '*';
|
|
722
|
-
const endsWithWildcard = pattern[pattern.length - 1] === '*';
|
|
723
|
-
pattern = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*');
|
|
724
|
-
return (startsWithWildcard ? '' : '^') + pattern + (endsWithWildcard ? '' : '$');
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Function that walks through all keys of an object or array and applies a function to each key.
|
|
728
|
-
*/
|
|
729
|
-
function walkThroughArray(object, iteratee) {
|
|
730
|
-
const copiedObject = [];
|
|
731
|
-
for (let index = 0; index < object.length; index++) {
|
|
732
|
-
iteratee(copiedObject, object[index], index);
|
|
733
|
-
}
|
|
734
|
-
return copiedObject;
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* Function that walks through all keys of an object or array and applies a function to each key.
|
|
738
|
-
*/
|
|
739
|
-
function walkThroughObject(object, iteratee) {
|
|
740
|
-
const copiedObject = {};
|
|
741
|
-
for (const key in object) {
|
|
742
|
-
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
|
743
|
-
iteratee(copiedObject, object[key], key);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return copiedObject;
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Function to reduce an object primarily by a given list of keys
|
|
750
|
-
*/
|
|
751
|
-
function filterObject(
|
|
752
|
-
/** The objects which should be filtered */
|
|
753
|
-
obj,
|
|
754
|
-
/** The keys which should be excluded */
|
|
755
|
-
filterKeys,
|
|
756
|
-
/** Whether translations should be reduced to only the english value */
|
|
757
|
-
excludeTranslations) {
|
|
758
|
-
if (Array.isArray(obj)) {
|
|
759
|
-
return walkThroughArray(obj, (result, value, key) => {
|
|
760
|
-
if (value === undefined || value === null) {
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
// if the key is an object, run it through the inner function - omitFromObject
|
|
764
|
-
const isObject = typeof value === 'object';
|
|
765
|
-
if (excludeTranslations && isObject) {
|
|
766
|
-
if (typeof value.en === 'string' && typeof value.de === 'string') {
|
|
767
|
-
result[key] = value.en;
|
|
768
|
-
return;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
result[key] = isObject ? filterObject(value, filterKeys, excludeTranslations) : value;
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
return walkThroughObject(obj, (result, value, key) => {
|
|
775
|
-
if (value === undefined || value === null) {
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
if (filterKeys.includes(key)) {
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
// if the key is an object, run it through the inner function - omitFromObject
|
|
782
|
-
const isObject = typeof value === 'object';
|
|
783
|
-
if (excludeTranslations && isObject) {
|
|
784
|
-
if (typeof value.en === 'string' && typeof value.de === 'string') {
|
|
785
|
-
result[key] = value.en;
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
result[key] = isObject ? filterObject(value, filterKeys, excludeTranslations) : value;
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
// It is an export function and used somewhere else
|
|
793
|
-
export function filterRoles(roleArray, type, defaultRoles) {
|
|
794
|
-
const bigRoleArray = [];
|
|
795
|
-
roleArray.forEach(role => (role.type === 'mixed' || role.type) === type &&
|
|
796
|
-
!bigRoleArray.includes(role.role) &&
|
|
797
|
-
bigRoleArray.push(role.role));
|
|
798
|
-
defaultRoles?.forEach(role => (role.type === 'mixed' || role.type) === type &&
|
|
799
|
-
!bigRoleArray.includes(role.role) &&
|
|
800
|
-
bigRoleArray.push(role.role));
|
|
801
|
-
bigRoleArray.sort();
|
|
802
|
-
return bigRoleArray;
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Function to generate a json-file for an object and trigger download it
|
|
806
|
-
*/
|
|
807
|
-
function generateFile(
|
|
808
|
-
/** The desired filename */
|
|
809
|
-
fileName,
|
|
810
|
-
/** The objects which should be downloaded */
|
|
811
|
-
obj,
|
|
812
|
-
/** Options to filter/reduce the output */
|
|
813
|
-
options) {
|
|
814
|
-
const el = document.createElement('a');
|
|
815
|
-
const filterKeys = [];
|
|
816
|
-
if (options.excludeSystemRepositories) {
|
|
817
|
-
filterKeys.push('system.repositories');
|
|
818
|
-
}
|
|
819
|
-
const filteredObject = filterKeys.length > 0 || options.excludeTranslations
|
|
820
|
-
? filterObject(obj, filterKeys, options.excludeTranslations)
|
|
821
|
-
: obj;
|
|
822
|
-
const data = options.beautify ? JSON.stringify(filteredObject, null, 2) : JSON.stringify(filteredObject);
|
|
823
|
-
el.setAttribute('href', `data:application/json;charset=utf-8,${encodeURIComponent(data)}`);
|
|
824
|
-
el.setAttribute('download', fileName);
|
|
825
|
-
el.style.display = 'none';
|
|
826
|
-
document.body.appendChild(el);
|
|
827
|
-
el.click();
|
|
828
|
-
document.body.removeChild(el);
|
|
829
|
-
}
|
|
830
|
-
class CustomFilterSelect extends Component {
|
|
831
|
-
hasIcons;
|
|
832
|
-
timer = null;
|
|
833
|
-
constructor(props) {
|
|
834
|
-
super(props);
|
|
835
|
-
this.state = {
|
|
836
|
-
value: props.initialValue || [],
|
|
837
|
-
};
|
|
838
|
-
this.hasIcons = !!props.values?.find(item => item.icon);
|
|
839
|
-
}
|
|
840
|
-
componentWillUnmount() {
|
|
841
|
-
if (this.timer) {
|
|
842
|
-
clearTimeout(this.timer);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
render() {
|
|
846
|
-
return (React.createElement("div", { style: { position: 'relative' } },
|
|
847
|
-
React.createElement(Select, { variant: "standard", key: this.props.name, sx: styles.headerCellInput, className: "no-underline", multiple: true, renderValue: value => {
|
|
848
|
-
if (!value?.length) {
|
|
849
|
-
return this.props.name === 'custom'
|
|
850
|
-
? this.props.texts.showAll
|
|
851
|
-
: this.props.texts[`filter_${this.props.name}`];
|
|
852
|
-
}
|
|
853
|
-
return value.map(val => {
|
|
854
|
-
const item = this.props.values.find(i => typeof i === 'object' ? i.value === val : i === val);
|
|
855
|
-
let id;
|
|
856
|
-
let _name;
|
|
857
|
-
let icon;
|
|
858
|
-
if (typeof item === 'object') {
|
|
859
|
-
id = item.value;
|
|
860
|
-
_name = item.name;
|
|
861
|
-
icon = item.icon;
|
|
862
|
-
}
|
|
863
|
-
else {
|
|
864
|
-
id = item;
|
|
865
|
-
_name = item;
|
|
866
|
-
}
|
|
867
|
-
return (React.createElement(Box, { component: "span", sx: styles.headerCellSelectItem, key: id },
|
|
868
|
-
icon || (this.hasIcons ? React.createElement("div", { className: "itemIcon" }) : null),
|
|
869
|
-
_name));
|
|
870
|
-
});
|
|
871
|
-
}, value: this.state.value, onChange: event => {
|
|
872
|
-
let selectedValues = event.target.value;
|
|
873
|
-
// '_' may be selected only alone
|
|
874
|
-
if (this.state.value[0] === '_' && selectedValues.includes('_') && selectedValues.length > 1) {
|
|
875
|
-
const pos = selectedValues.indexOf('_');
|
|
876
|
-
if (pos !== -1) {
|
|
877
|
-
selectedValues.splice(pos, 1);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
else if (this.state.value[0] !== '_' && selectedValues.includes('_')) {
|
|
881
|
-
selectedValues = ['_'];
|
|
882
|
-
}
|
|
883
|
-
// '_' may be selected only alone
|
|
884
|
-
if (selectedValues.includes('')) {
|
|
885
|
-
selectedValues = [];
|
|
886
|
-
}
|
|
887
|
-
this.setState({ value: selectedValues }, () => {
|
|
888
|
-
if (this.timer) {
|
|
889
|
-
clearTimeout(this.timer);
|
|
890
|
-
}
|
|
891
|
-
this.timer = setTimeout(() => {
|
|
892
|
-
this.timer = null;
|
|
893
|
-
this.props.onChange(this.props.name, selectedValues);
|
|
894
|
-
}, 400);
|
|
895
|
-
});
|
|
896
|
-
}, onClose: () => {
|
|
897
|
-
if (this.timer) {
|
|
898
|
-
clearTimeout(this.timer);
|
|
899
|
-
this.timer = null;
|
|
900
|
-
this.props.onChange(this.props.name, this.state.value);
|
|
901
|
-
}
|
|
902
|
-
}, inputProps: { name: this.props.name, id: this.props.name }, displayEmpty: true },
|
|
903
|
-
React.createElement(MenuItem, { key: "empty", value: "" },
|
|
904
|
-
React.createElement("span", { style: styles.selectNone }, this.props.name === 'custom'
|
|
905
|
-
? this.props.texts.showAll
|
|
906
|
-
: this.props.texts[`filter_${this.props.name}`])),
|
|
907
|
-
this.props.values?.map(item => {
|
|
908
|
-
let id;
|
|
909
|
-
let _name;
|
|
910
|
-
let icon;
|
|
911
|
-
if (typeof item === 'object') {
|
|
912
|
-
id = item.value;
|
|
913
|
-
_name = item.name;
|
|
914
|
-
icon = item.icon;
|
|
915
|
-
}
|
|
916
|
-
else {
|
|
917
|
-
id = item;
|
|
918
|
-
_name = item;
|
|
919
|
-
}
|
|
920
|
-
return (React.createElement(MenuItem, { sx: styles.headerCellSelectItem, key: id, value: id },
|
|
921
|
-
icon || (this.hasIcons ? React.createElement("div", { className: "itemIcon" }) : null),
|
|
922
|
-
_name));
|
|
923
|
-
})),
|
|
924
|
-
this.state.value.length ? (React.createElement(Box, { component: "div", sx: styles.selectClearButton },
|
|
925
|
-
React.createElement(IconButton, { size: "small", onClick: () => {
|
|
926
|
-
if (this.timer) {
|
|
927
|
-
clearTimeout(this.timer);
|
|
928
|
-
this.timer = null;
|
|
929
|
-
}
|
|
930
|
-
this.setState({ value: [] }, () => this.props.onChange(this.props.name, undefined));
|
|
931
|
-
} },
|
|
932
|
-
React.createElement(IconClose, null)))) : null));
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
class CustomFilterInput extends Component {
|
|
936
|
-
timer = null;
|
|
937
|
-
constructor(props) {
|
|
938
|
-
super(props);
|
|
939
|
-
this.state = {
|
|
940
|
-
value: props.initialValue || '',
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
componentWillUnmount() {
|
|
944
|
-
if (this.timer) {
|
|
945
|
-
clearTimeout(this.timer);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
render() {
|
|
949
|
-
return (React.createElement(FormControl, { sx: this.props.styles, key: this.props.name, title: this.props.t('ra_You can use * as wildcard'), margin: "dense" },
|
|
950
|
-
React.createElement(Input, { classes: { underline: 'no-underline' }, id: this.props.name, placeholder: this.props.texts[`filter_${this.props.name}`], value: this.state.value, onChange: event => {
|
|
951
|
-
const selectedValues = event.target.value;
|
|
952
|
-
this.setState({ value: selectedValues }, () => {
|
|
953
|
-
if (this.timer) {
|
|
954
|
-
clearTimeout(this.timer);
|
|
955
|
-
}
|
|
956
|
-
this.timer = setTimeout(() => {
|
|
957
|
-
this.timer = null;
|
|
958
|
-
this.props.onChange(this.props.name, selectedValues);
|
|
959
|
-
}, 400);
|
|
960
|
-
});
|
|
961
|
-
}, onBlur: () => {
|
|
962
|
-
if (this.timer) {
|
|
963
|
-
clearTimeout(this.timer);
|
|
964
|
-
this.timer = null;
|
|
965
|
-
this.props.onChange(this.props.name, this.state.value);
|
|
966
|
-
}
|
|
967
|
-
}, autoComplete: "off" }),
|
|
968
|
-
this.state.value ? (React.createElement("div", { style: {
|
|
969
|
-
position: 'absolute',
|
|
970
|
-
right: 0,
|
|
971
|
-
} },
|
|
972
|
-
React.createElement(IconButton, { size: "small", onClick: () => {
|
|
973
|
-
if (this.timer) {
|
|
974
|
-
clearTimeout(this.timer);
|
|
975
|
-
this.timer = null;
|
|
976
|
-
}
|
|
977
|
-
this.setState({ value: '' }, () => this.props.onChange(this.props.name, undefined));
|
|
978
|
-
} },
|
|
979
|
-
React.createElement(IconClose, null)))) : null));
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
// d=data, t=target, s=start, e=end, m=middle
|
|
983
|
-
function binarySearch(list, find, _start, _end) {
|
|
984
|
-
_start ||= 0;
|
|
985
|
-
if (_end === undefined) {
|
|
986
|
-
_end = list.length - 1;
|
|
987
|
-
if (!_end) {
|
|
988
|
-
return list[0] === find;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
const middle = Math.floor((_start + _end) / 2);
|
|
992
|
-
if (find === list[middle]) {
|
|
993
|
-
return true;
|
|
994
|
-
}
|
|
995
|
-
if (_end - 1 === _start) {
|
|
996
|
-
return list[_start] === find || list[_end] === find;
|
|
997
|
-
}
|
|
998
|
-
if (find > list[middle]) {
|
|
999
|
-
return binarySearch(list, find, middle, _end);
|
|
1000
|
-
}
|
|
1001
|
-
if (find < list[middle]) {
|
|
1002
|
-
return binarySearch(list, find, _start, middle);
|
|
1003
|
-
}
|
|
1004
|
-
return false;
|
|
1005
|
-
}
|
|
1006
|
-
function getName(name, lang) {
|
|
1007
|
-
if (typeof name === 'object') {
|
|
1008
|
-
if (!name) {
|
|
1009
|
-
return '';
|
|
1010
|
-
}
|
|
1011
|
-
return (name[lang] || name.en || '').toString();
|
|
1012
|
-
}
|
|
1013
|
-
return name ? name.toString() : '';
|
|
1014
|
-
}
|
|
1015
|
-
export function getSelectIdIconFromObjects(objects, id, lang, imagePrefix) {
|
|
1016
|
-
// `admin` has prefix '.' and `web` has '../..'
|
|
1017
|
-
imagePrefix ||= '.'; // http://localhost:8081';
|
|
1018
|
-
let src = '';
|
|
1019
|
-
const _id_ = `system.adapter.${id}`;
|
|
1020
|
-
const aIcon = id && objects[_id_]?.common?.icon;
|
|
1021
|
-
if (aIcon) {
|
|
1022
|
-
// if not BASE64
|
|
1023
|
-
if (!aIcon.startsWith('data:image/')) {
|
|
1024
|
-
if (aIcon.includes('.')) {
|
|
1025
|
-
const name = objects[_id_].common.name;
|
|
1026
|
-
if (typeof name === 'object') {
|
|
1027
|
-
src = `${imagePrefix}/adapter/${name[lang] || name.en}/${aIcon}`;
|
|
1028
|
-
}
|
|
1029
|
-
else {
|
|
1030
|
-
src = `${imagePrefix}/adapter/${name}/${aIcon}`;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
else if (aIcon && aIcon.length < 3) {
|
|
1034
|
-
return aIcon; // utf-8
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
return null; // '<i class="material-icons iob-list-icon">' + objects[_id_].common.icon + '</i>';
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
else if (aIcon.startsWith('data:image/svg')) {
|
|
1041
|
-
src = (React.createElement(SVG, { className: "iconOwn", src: aIcon, width: 28, height: 28 }));
|
|
1042
|
-
}
|
|
1043
|
-
else {
|
|
1044
|
-
src = aIcon;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
else {
|
|
1048
|
-
const common = objects[id] && objects[id].common;
|
|
1049
|
-
if (common) {
|
|
1050
|
-
const cIcon = common.icon;
|
|
1051
|
-
if (cIcon) {
|
|
1052
|
-
if (!cIcon.startsWith('data:image/')) {
|
|
1053
|
-
if (cIcon.includes('.')) {
|
|
1054
|
-
let instance;
|
|
1055
|
-
if (objects[id].type === 'instance' || objects[id].type === 'adapter') {
|
|
1056
|
-
if (typeof common.name === 'object') {
|
|
1057
|
-
src = `${imagePrefix}/adapter/${common.name[lang] || common.name.en}/${cIcon}`;
|
|
1058
|
-
}
|
|
1059
|
-
else {
|
|
1060
|
-
src = `${imagePrefix}/adapter/${common.name}/${cIcon}`;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
else if (id && id.startsWith('system.adapter.')) {
|
|
1064
|
-
instance = id.split('.', 3);
|
|
1065
|
-
if (cIcon[0] === '/') {
|
|
1066
|
-
instance[2] += cIcon;
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
instance[2] += `/${cIcon}`;
|
|
1070
|
-
}
|
|
1071
|
-
src = `${imagePrefix}/adapter/${instance[2]}`;
|
|
1072
|
-
}
|
|
1073
|
-
else {
|
|
1074
|
-
instance = id.split('.', 2);
|
|
1075
|
-
if (cIcon[0] === '/') {
|
|
1076
|
-
instance[0] += cIcon;
|
|
1077
|
-
}
|
|
1078
|
-
else {
|
|
1079
|
-
instance[0] += `/${cIcon}`;
|
|
1080
|
-
}
|
|
1081
|
-
src = `${imagePrefix}/adapter/${instance[0]}`;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
else if (aIcon && aIcon.length < 3) {
|
|
1085
|
-
return aIcon; // utf-8
|
|
1086
|
-
}
|
|
1087
|
-
else {
|
|
1088
|
-
return null;
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
else if (cIcon.startsWith('data:image/svg')) {
|
|
1092
|
-
// if base 64 image
|
|
1093
|
-
src = (React.createElement(SVG, { className: "iconOwn", src: cIcon, width: 28, height: 28 }));
|
|
1094
|
-
}
|
|
1095
|
-
else {
|
|
1096
|
-
src = cIcon;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
return src || null;
|
|
1102
|
-
}
|
|
1103
|
-
function applyFilter(item, filters, lang, objects, context, counter, customFilter, selectedTypes, _depth) {
|
|
1104
|
-
_depth ||= 0;
|
|
1105
|
-
let filteredOut = false;
|
|
1106
|
-
if (!context) {
|
|
1107
|
-
context = {};
|
|
1108
|
-
if (filters.id) {
|
|
1109
|
-
const id = filters.id.toLowerCase();
|
|
1110
|
-
if (id.includes('*')) {
|
|
1111
|
-
context.idRx = new RegExp(pattern2RegEx(filters.id), 'i');
|
|
1112
|
-
}
|
|
1113
|
-
else {
|
|
1114
|
-
context.id = id;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (filters.name) {
|
|
1118
|
-
const name = filters.name.toLowerCase();
|
|
1119
|
-
if (name.includes('*')) {
|
|
1120
|
-
context.nameRx = new RegExp(pattern2RegEx(name), 'i');
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
context.name = name;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
if (filters.type?.length) {
|
|
1127
|
-
context.type = filters.type.map(f => f.toLowerCase());
|
|
1128
|
-
}
|
|
1129
|
-
if (filters.custom?.length) {
|
|
1130
|
-
context.custom = filters.custom.map(c => c.toLowerCase());
|
|
1131
|
-
}
|
|
1132
|
-
if (filters.role?.length) {
|
|
1133
|
-
context.role = filters.role.map(r => r.toLowerCase());
|
|
1134
|
-
}
|
|
1135
|
-
if (filters.room?.length) {
|
|
1136
|
-
context.room = [];
|
|
1137
|
-
filters.room.forEach(room => {
|
|
1138
|
-
context.room = context.room.concat(objects[room]?.common?.members || []);
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
if (filters.func?.length) {
|
|
1142
|
-
context.func = [];
|
|
1143
|
-
filters.func.forEach(func => {
|
|
1144
|
-
context.func = context.func.concat(objects[func]?.common?.members || []);
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
const data = item.data;
|
|
1149
|
-
if (data?.id) {
|
|
1150
|
-
const common = data.obj?.common;
|
|
1151
|
-
if (customFilter) {
|
|
1152
|
-
if (customFilter.type) {
|
|
1153
|
-
if (typeof customFilter.type === 'string') {
|
|
1154
|
-
if (!data.obj || customFilter.type !== data.obj.type) {
|
|
1155
|
-
filteredOut = true;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
else if (Array.isArray(customFilter.type)) {
|
|
1159
|
-
if (!data.obj || !customFilter.type.includes(data.obj.type)) {
|
|
1160
|
-
filteredOut = true;
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
if (!filteredOut && customFilter.common?.type) {
|
|
1165
|
-
if (!common?.type) {
|
|
1166
|
-
filteredOut = true;
|
|
1167
|
-
}
|
|
1168
|
-
else if (typeof customFilter.common.type === 'string') {
|
|
1169
|
-
if (customFilter.common.type !== common.type) {
|
|
1170
|
-
filteredOut = true;
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
else if (Array.isArray(customFilter.common.type)) {
|
|
1174
|
-
if (!customFilter.common.type.includes(common.type)) {
|
|
1175
|
-
filteredOut = true;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
if (!filteredOut && customFilter.common?.role) {
|
|
1180
|
-
if (!common?.role) {
|
|
1181
|
-
filteredOut = true;
|
|
1182
|
-
}
|
|
1183
|
-
else if (typeof customFilter.common.role === 'string') {
|
|
1184
|
-
if (common.role.startsWith(customFilter.common.role)) {
|
|
1185
|
-
filteredOut = true;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
else if (Array.isArray(customFilter.common.role)) {
|
|
1189
|
-
if (!customFilter.common.role.find(role => common.role.startsWith(role))) {
|
|
1190
|
-
filteredOut = true;
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
if (!filteredOut && customFilter.common?.custom === '_' && common?.custom) {
|
|
1195
|
-
filteredOut = true;
|
|
1196
|
-
}
|
|
1197
|
-
else if (!filteredOut && customFilter.common?.custom && customFilter.common?.custom !== '_') {
|
|
1198
|
-
const filterOfCustom = customFilter.common.custom;
|
|
1199
|
-
if (!common?.custom) {
|
|
1200
|
-
filteredOut = true;
|
|
1201
|
-
}
|
|
1202
|
-
else if (filterOfCustom === '_dataSources') {
|
|
1203
|
-
// TODO: make it configurable
|
|
1204
|
-
if (!Object.keys(common.custom).find(id => id.startsWith('history.') || id.startsWith('sql.') || id.startsWith('influxdb.'))) {
|
|
1205
|
-
filteredOut = true;
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
else if (Array.isArray(filterOfCustom)) {
|
|
1209
|
-
// here are ['influxdb.', 'telegram.']
|
|
1210
|
-
const customs = Object.keys(common.custom); // here are ['influxdb.0', 'telegram.2']
|
|
1211
|
-
if (filterOfCustom.find(cst => customs.find(id => id.startsWith(cst)))) {
|
|
1212
|
-
filteredOut = true;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
else if (filterOfCustom !== true &&
|
|
1216
|
-
!Object.keys(common.custom).find(id => id.startsWith(filterOfCustom))) {
|
|
1217
|
-
filteredOut = true;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
if (!filteredOut && !filters.expertMode) {
|
|
1222
|
-
filteredOut =
|
|
1223
|
-
data.id === 'system' ||
|
|
1224
|
-
data.id === 'enum' ||
|
|
1225
|
-
// (data.obj && data.obj.type === 'meta') ||
|
|
1226
|
-
data.id.startsWith('system.') ||
|
|
1227
|
-
data.id.startsWith('enum.') ||
|
|
1228
|
-
data.id.startsWith('_design/') ||
|
|
1229
|
-
data.id.endsWith('.admin') ||
|
|
1230
|
-
!!common?.expert;
|
|
1231
|
-
}
|
|
1232
|
-
if (!filteredOut && context.id) {
|
|
1233
|
-
if (data.fID === undefined) {
|
|
1234
|
-
data.fID = data.id.toLowerCase();
|
|
1235
|
-
}
|
|
1236
|
-
filteredOut = !data.fID.includes(context.id);
|
|
1237
|
-
}
|
|
1238
|
-
if (!filteredOut && context.idRx) {
|
|
1239
|
-
filteredOut = !context.idRx.test(data.id);
|
|
1240
|
-
}
|
|
1241
|
-
if (!filteredOut && context.name) {
|
|
1242
|
-
if (common) {
|
|
1243
|
-
if (data.fName === undefined) {
|
|
1244
|
-
data.fName = getName(common.name, lang) || '';
|
|
1245
|
-
data.fName = data.fName.toLowerCase();
|
|
1246
|
-
}
|
|
1247
|
-
filteredOut = !data.fName.includes(context.name);
|
|
1248
|
-
}
|
|
1249
|
-
else {
|
|
1250
|
-
filteredOut = true;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
if (!filteredOut && context.nameRx) {
|
|
1254
|
-
if (common) {
|
|
1255
|
-
if (data.fName === undefined) {
|
|
1256
|
-
data.fName = getName(common.name, lang) || '';
|
|
1257
|
-
data.fName = data.fName.toLowerCase();
|
|
1258
|
-
}
|
|
1259
|
-
filteredOut = !context.nameRx.test(data.fName);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
if (!filteredOut && filters.role?.length && common) {
|
|
1263
|
-
filteredOut = !(typeof common.role === 'string' && context.role.find(role => common.role.startsWith(role)));
|
|
1264
|
-
}
|
|
1265
|
-
if (!filteredOut && context.room?.length) {
|
|
1266
|
-
filteredOut = !context.room.find(id => id === data.id || data.id.startsWith(`${id}.`));
|
|
1267
|
-
}
|
|
1268
|
-
if (!filteredOut && context.func?.length) {
|
|
1269
|
-
filteredOut = !context.func.find(id => id === data.id || data.id.startsWith(`${id}.`));
|
|
1270
|
-
}
|
|
1271
|
-
if (!filteredOut && context.type?.length) {
|
|
1272
|
-
filteredOut = !(data.obj?.type && context.type.includes(data.obj.type));
|
|
1273
|
-
}
|
|
1274
|
-
if (!filteredOut && selectedTypes) {
|
|
1275
|
-
filteredOut = !(data.obj?.type && selectedTypes.includes(data.obj.type));
|
|
1276
|
-
}
|
|
1277
|
-
if (!filteredOut && context.custom?.length) {
|
|
1278
|
-
if (common) {
|
|
1279
|
-
if (context.custom[0] === '_') {
|
|
1280
|
-
filteredOut = !!common.custom;
|
|
1281
|
-
}
|
|
1282
|
-
else if (common.custom) {
|
|
1283
|
-
filteredOut = !context.custom.find(custom => common.custom[custom]);
|
|
1284
|
-
}
|
|
1285
|
-
else {
|
|
1286
|
-
filteredOut = true;
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
else {
|
|
1290
|
-
filteredOut = context.custom[0] !== '_';
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
data.visible = !filteredOut;
|
|
1295
|
-
data.hasVisibleChildren = false;
|
|
1296
|
-
if (item.children && _depth < 20) {
|
|
1297
|
-
item.children.forEach(_item => {
|
|
1298
|
-
const visible = applyFilter(_item, filters, lang, objects, context, counter, customFilter, selectedTypes, _depth + 1);
|
|
1299
|
-
if (visible) {
|
|
1300
|
-
data.hasVisibleChildren = true;
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
// const visible = data.visible || data.hasVisibleChildren;
|
|
1305
|
-
data.sumVisibility = data.visible || data.hasVisibleChildren; // || data.hasVisibleParent;
|
|
1306
|
-
if (counter && data.sumVisibility) {
|
|
1307
|
-
counter.count++;
|
|
1308
|
-
}
|
|
1309
|
-
// show all children of visible object with opacity 0.5
|
|
1310
|
-
if (data.id && data.sumVisibility && item.children) {
|
|
1311
|
-
item.children.forEach(_item => (_item.data.hasVisibleParent = true));
|
|
1312
|
-
}
|
|
1313
|
-
return data.visible || data.hasVisibleChildren;
|
|
1314
|
-
}
|
|
1315
|
-
function getVisibleItems(item, type, objects, _result) {
|
|
1316
|
-
_result ||= [];
|
|
1317
|
-
const data = item.data;
|
|
1318
|
-
if (data.sumVisibility) {
|
|
1319
|
-
if (data.id && objects[data.id] && (!type || objects[data.id].type === type)) {
|
|
1320
|
-
_result.push(data.id);
|
|
1321
|
-
}
|
|
1322
|
-
item.children?.forEach(_item => getVisibleItems(_item, type, objects, _result));
|
|
1323
|
-
}
|
|
1324
|
-
return _result;
|
|
1325
|
-
}
|
|
1326
|
-
function getSystemIcon(objects, id, level, themeType, lang, imagePrefix) {
|
|
1327
|
-
let icon;
|
|
1328
|
-
// system or design has special icons
|
|
1329
|
-
if (id === 'alias' || id === 'alias.0') {
|
|
1330
|
-
icon = (React.createElement(IconLink, { className: "iconOwn", style: { color: COLOR_NAME_ALIAS(themeType) } }));
|
|
1331
|
-
}
|
|
1332
|
-
else if (id === '0_userdata' || id === '0_userdata.0') {
|
|
1333
|
-
icon = (React.createElement(IconData, { className: "iconOwn", style: { color: COLOR_NAME_USERDATA(themeType) } }));
|
|
1334
|
-
}
|
|
1335
|
-
else if (id.startsWith('_design/') || id === 'system') {
|
|
1336
|
-
icon = (React.createElement(IconSystem, { className: "iconOwn", style: { color: COLOR_NAME_SYSTEM(themeType) } }));
|
|
1337
|
-
}
|
|
1338
|
-
else if (id === 'system.adapter') {
|
|
1339
|
-
icon = (React.createElement(IconSystem, { className: "iconOwn", style: { color: COLOR_NAME_SYSTEM_ADAPTER(themeType) } }));
|
|
1340
|
-
}
|
|
1341
|
-
else if (id === 'system.group') {
|
|
1342
|
-
icon = React.createElement(IconGroup, { className: "iconOwn" });
|
|
1343
|
-
}
|
|
1344
|
-
else if (id === 'system.user') {
|
|
1345
|
-
icon = React.createElement(IconUser, { className: "iconOwn" });
|
|
1346
|
-
}
|
|
1347
|
-
else if (id === 'system.host') {
|
|
1348
|
-
icon = React.createElement(IconHost, { className: "iconOwn" });
|
|
1349
|
-
}
|
|
1350
|
-
else if (id.endsWith('.connection') || id.endsWith('.connected')) {
|
|
1351
|
-
icon = React.createElement(IconConnection, { className: "iconOwn" });
|
|
1352
|
-
}
|
|
1353
|
-
else if (id.endsWith('.info')) {
|
|
1354
|
-
icon = React.createElement(IconInfo, { className: "iconOwn" });
|
|
1355
|
-
}
|
|
1356
|
-
else if (objects[id] && objects[id].type === 'meta') {
|
|
1357
|
-
icon = React.createElement(IconMeta, { className: "iconOwn" });
|
|
1358
|
-
}
|
|
1359
|
-
else if (level < 2) {
|
|
1360
|
-
// detect "cloud.0"
|
|
1361
|
-
if (objects[`system.adapter.${id}`]) {
|
|
1362
|
-
icon = getSelectIdIconFromObjects(objects, `system.adapter.${id}`, lang, imagePrefix);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return icon || null;
|
|
1366
|
-
}
|
|
1367
|
-
function getObjectTooltip(data, lang) {
|
|
1368
|
-
if (data?.obj?.common?.desc) {
|
|
1369
|
-
return getName(data.obj.common.desc, lang) || null;
|
|
1370
|
-
}
|
|
1371
|
-
return null;
|
|
1372
|
-
}
|
|
1373
|
-
function getIdFieldTooltip(data, lang) {
|
|
1374
|
-
const tooltip = getObjectTooltip(data, lang);
|
|
1375
|
-
if (tooltip?.startsWith('http')) {
|
|
1376
|
-
return (React.createElement(Box, { component: "a", sx: styles.cellIdTooltipLink, href: tooltip, target: "_blank", rel: "noreferrer" }, tooltip));
|
|
1377
|
-
}
|
|
1378
|
-
return React.createElement("span", { style: styles.cellIdTooltip }, tooltip || data.id || '');
|
|
1379
|
-
}
|
|
1380
|
-
function buildTree(objects, options) {
|
|
1381
|
-
const imagePrefix = options.imagePrefix || '.';
|
|
1382
|
-
let ids = Object.keys(objects);
|
|
1383
|
-
ids.sort((a, b) => {
|
|
1384
|
-
if (a === b) {
|
|
1385
|
-
return 0;
|
|
1386
|
-
}
|
|
1387
|
-
a = a.replace(/\./g, '!!!');
|
|
1388
|
-
b = b.replace(/\./g, '!!!');
|
|
1389
|
-
if (a > b) {
|
|
1390
|
-
return 1;
|
|
1391
|
-
}
|
|
1392
|
-
return -1;
|
|
1393
|
-
});
|
|
1394
|
-
if (options.root) {
|
|
1395
|
-
ids = ids.filter(id => id === options.root || id.startsWith(`${options.root}.`));
|
|
1396
|
-
}
|
|
1397
|
-
// find empty nodes and create names for it
|
|
1398
|
-
let currentPathArr = [];
|
|
1399
|
-
let currentPath = '';
|
|
1400
|
-
let currentPathLen = 0;
|
|
1401
|
-
const root = {
|
|
1402
|
-
data: {
|
|
1403
|
-
name: '',
|
|
1404
|
-
id: '',
|
|
1405
|
-
},
|
|
1406
|
-
children: [],
|
|
1407
|
-
};
|
|
1408
|
-
const info = {
|
|
1409
|
-
funcEnums: [],
|
|
1410
|
-
roomEnums: [],
|
|
1411
|
-
roles: [],
|
|
1412
|
-
ids: [],
|
|
1413
|
-
types: [],
|
|
1414
|
-
objects,
|
|
1415
|
-
customs: ['_'],
|
|
1416
|
-
enums: [],
|
|
1417
|
-
hasSomeCustoms: false,
|
|
1418
|
-
aliasesMap: {},
|
|
1419
|
-
};
|
|
1420
|
-
let cRoot = root;
|
|
1421
|
-
for (let i = 0; i < ids.length; i++) {
|
|
1422
|
-
const id = ids[i];
|
|
1423
|
-
if (!id) {
|
|
1424
|
-
continue;
|
|
1425
|
-
}
|
|
1426
|
-
const obj = objects[id];
|
|
1427
|
-
const parts = id.split('.');
|
|
1428
|
-
if (obj.type && !info.types.includes(obj.type)) {
|
|
1429
|
-
info.types.push(obj.type);
|
|
1430
|
-
}
|
|
1431
|
-
if (obj) {
|
|
1432
|
-
const common = obj.common;
|
|
1433
|
-
const role = common?.role;
|
|
1434
|
-
if (role && !info.roles.find(it => it.role === role)) {
|
|
1435
|
-
if (typeof role !== 'string') {
|
|
1436
|
-
console.warn(`Invalid role type "${typeof role}" in "${obj._id}"`);
|
|
1437
|
-
}
|
|
1438
|
-
else {
|
|
1439
|
-
info.roles.push({ role, type: common.type });
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
else if (id.startsWith('enum.rooms.')) {
|
|
1443
|
-
info.roomEnums.push(id);
|
|
1444
|
-
info.enums.push(id);
|
|
1445
|
-
}
|
|
1446
|
-
else if (id.startsWith('enum.functions.')) {
|
|
1447
|
-
info.funcEnums.push(id);
|
|
1448
|
-
info.enums.push(id);
|
|
1449
|
-
}
|
|
1450
|
-
else if (obj.type === 'enum') {
|
|
1451
|
-
info.enums.push(id);
|
|
1452
|
-
}
|
|
1453
|
-
else if (obj.type === 'instance' && common && (common.supportCustoms || common.adminUI?.custom)) {
|
|
1454
|
-
info.hasSomeCustoms = true;
|
|
1455
|
-
info.customs.push(id.substring('system.adapter.'.length));
|
|
1456
|
-
}
|
|
1457
|
-
// Build a map of aliases
|
|
1458
|
-
if (id.startsWith('alias.') && obj.common.alias?.id) {
|
|
1459
|
-
if (typeof obj.common.alias.id === 'string') {
|
|
1460
|
-
const usedId = obj.common.alias.id;
|
|
1461
|
-
if (!info.aliasesMap[usedId]) {
|
|
1462
|
-
info.aliasesMap[usedId] = [id];
|
|
1463
|
-
}
|
|
1464
|
-
else if (!info.aliasesMap[usedId].includes(id)) {
|
|
1465
|
-
info.aliasesMap[usedId].push(id);
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
else {
|
|
1469
|
-
const readId = obj.common.alias.id.read;
|
|
1470
|
-
if (readId) {
|
|
1471
|
-
if (!info.aliasesMap[readId]) {
|
|
1472
|
-
info.aliasesMap[readId] = [id];
|
|
1473
|
-
}
|
|
1474
|
-
else if (!info.aliasesMap[readId].includes(id)) {
|
|
1475
|
-
info.aliasesMap[readId].push(id);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
const writeId = obj.common.alias.id.write;
|
|
1479
|
-
if (writeId) {
|
|
1480
|
-
if (!info.aliasesMap[writeId]) {
|
|
1481
|
-
info.aliasesMap[writeId] = [id];
|
|
1482
|
-
}
|
|
1483
|
-
else if (!info.aliasesMap[writeId].includes(id)) {
|
|
1484
|
-
info.aliasesMap[writeId].push(id);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
info.ids.push(id);
|
|
1491
|
-
let repeat;
|
|
1492
|
-
// if next level
|
|
1493
|
-
do {
|
|
1494
|
-
repeat = false;
|
|
1495
|
-
// If the current level is still OK, and we can add ID to children
|
|
1496
|
-
if (!currentPath || id.startsWith(`${currentPath}.`)) {
|
|
1497
|
-
// if more than one level added
|
|
1498
|
-
if (parts.length - currentPathLen > 1) {
|
|
1499
|
-
let curPath = currentPath;
|
|
1500
|
-
// generate missing levels
|
|
1501
|
-
for (let k = currentPathLen; k < parts.length - 1; k++) {
|
|
1502
|
-
curPath += (curPath ? '.' : '') + parts[k];
|
|
1503
|
-
// level does not exist
|
|
1504
|
-
if (!binarySearch(info.ids, curPath)) {
|
|
1505
|
-
const _cRoot = {
|
|
1506
|
-
data: {
|
|
1507
|
-
name: parts[k],
|
|
1508
|
-
parent: cRoot,
|
|
1509
|
-
id: curPath,
|
|
1510
|
-
obj: objects[curPath],
|
|
1511
|
-
level: k,
|
|
1512
|
-
icon: getSystemIcon(objects, curPath, k, options.themeType, options.lang, imagePrefix),
|
|
1513
|
-
generated: true,
|
|
1514
|
-
},
|
|
1515
|
-
};
|
|
1516
|
-
cRoot.children ||= [];
|
|
1517
|
-
cRoot.children.push(_cRoot);
|
|
1518
|
-
cRoot = _cRoot;
|
|
1519
|
-
info.ids.push(curPath); // IDs will be added by alphabet
|
|
1520
|
-
}
|
|
1521
|
-
else if (cRoot.children) {
|
|
1522
|
-
cRoot = cRoot.children.find(item => item.data.name === parts[k]);
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
const _cRoot = {
|
|
1527
|
-
data: {
|
|
1528
|
-
name: parts[parts.length - 1],
|
|
1529
|
-
title: getName(obj?.common?.name, options.lang),
|
|
1530
|
-
obj,
|
|
1531
|
-
parent: cRoot,
|
|
1532
|
-
icon: getSelectIdIconFromObjects(objects, id, options.lang, imagePrefix) ||
|
|
1533
|
-
getSystemIcon(objects, id, 0, options.themeType, options.lang, imagePrefix),
|
|
1534
|
-
id,
|
|
1535
|
-
hasCustoms: !!(obj.common?.custom && Object.keys(obj.common.custom).length),
|
|
1536
|
-
level: parts.length - 1,
|
|
1537
|
-
generated: false,
|
|
1538
|
-
button: obj.type === 'state' &&
|
|
1539
|
-
!!obj.common?.role &&
|
|
1540
|
-
typeof obj.common.role === 'string' &&
|
|
1541
|
-
obj.common.role.startsWith('button') &&
|
|
1542
|
-
obj.common?.write !== false,
|
|
1543
|
-
switch: obj.type === 'state' &&
|
|
1544
|
-
obj.common?.type === 'boolean' &&
|
|
1545
|
-
obj.common?.write !== false &&
|
|
1546
|
-
obj.common?.read !== false,
|
|
1547
|
-
url: !!obj.common?.role &&
|
|
1548
|
-
typeof obj.common.role === 'string' &&
|
|
1549
|
-
obj.common.role.startsWith('url'),
|
|
1550
|
-
},
|
|
1551
|
-
};
|
|
1552
|
-
cRoot.children ||= [];
|
|
1553
|
-
cRoot.children.push(_cRoot);
|
|
1554
|
-
cRoot = _cRoot;
|
|
1555
|
-
currentPathLen = parts.length;
|
|
1556
|
-
currentPathArr = parts;
|
|
1557
|
-
currentPath = id;
|
|
1558
|
-
}
|
|
1559
|
-
else {
|
|
1560
|
-
let u = 0;
|
|
1561
|
-
while (currentPathArr[u] === parts[u]) {
|
|
1562
|
-
u++;
|
|
1563
|
-
}
|
|
1564
|
-
if (u > 0) {
|
|
1565
|
-
let move = currentPathArr.length;
|
|
1566
|
-
currentPathArr = currentPathArr.splice(0, u);
|
|
1567
|
-
currentPathLen = u;
|
|
1568
|
-
currentPath = currentPathArr.join('.');
|
|
1569
|
-
while (move > u) {
|
|
1570
|
-
if (cRoot.data.parent) {
|
|
1571
|
-
cRoot = cRoot.data.parent;
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
console.error(`Parent is null for ${id} ${currentPath} ${currentPathArr.join('.')}`);
|
|
1575
|
-
}
|
|
1576
|
-
move--;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
else {
|
|
1580
|
-
cRoot = root;
|
|
1581
|
-
currentPathArr = [];
|
|
1582
|
-
currentPath = '';
|
|
1583
|
-
currentPathLen = 0;
|
|
1584
|
-
}
|
|
1585
|
-
repeat = true;
|
|
1586
|
-
}
|
|
1587
|
-
} while (repeat);
|
|
1588
|
-
}
|
|
1589
|
-
info.roomEnums.sort((a, b) => {
|
|
1590
|
-
const aName = getName(objects[a]?.common?.name, options.lang) || a.split('.').pop();
|
|
1591
|
-
const bName = getName(objects[b]?.common?.name, options.lang) || b.split('.').pop();
|
|
1592
|
-
if (aName > bName) {
|
|
1593
|
-
return 1;
|
|
1594
|
-
}
|
|
1595
|
-
if (aName < bName) {
|
|
1596
|
-
return -1;
|
|
1597
|
-
}
|
|
1598
|
-
return 0;
|
|
1599
|
-
});
|
|
1600
|
-
info.funcEnums.sort((a, b) => {
|
|
1601
|
-
const aName = getName(objects[a]?.common?.name, options.lang) || a.split('.').pop();
|
|
1602
|
-
const bName = getName(objects[b]?.common?.name, options.lang) || b.split('.').pop();
|
|
1603
|
-
if (aName > bName) {
|
|
1604
|
-
return 1;
|
|
1605
|
-
}
|
|
1606
|
-
if (aName < bName) {
|
|
1607
|
-
return -1;
|
|
1608
|
-
}
|
|
1609
|
-
return 0;
|
|
1610
|
-
});
|
|
1611
|
-
info.roles.sort((a, b) => a.role.localeCompare(b.role));
|
|
1612
|
-
info.types.sort();
|
|
1613
|
-
return { info, root };
|
|
1614
|
-
}
|
|
1615
|
-
function findNode(root, id, _parts, _path, _level) {
|
|
1616
|
-
if (root.data.id === id) {
|
|
1617
|
-
return root;
|
|
1618
|
-
}
|
|
1619
|
-
if (!_parts) {
|
|
1620
|
-
_parts = id.split('.');
|
|
1621
|
-
_level = 0;
|
|
1622
|
-
_path = _parts[_level];
|
|
1623
|
-
}
|
|
1624
|
-
if (!root.children && root.data.id !== id) {
|
|
1625
|
-
return null;
|
|
1626
|
-
}
|
|
1627
|
-
let found;
|
|
1628
|
-
if (root.children) {
|
|
1629
|
-
for (let i = 0; i < root.children.length; i++) {
|
|
1630
|
-
const _id = root.children[i].data.id;
|
|
1631
|
-
if (_id === _path) {
|
|
1632
|
-
found = root.children[i];
|
|
1633
|
-
break;
|
|
1634
|
-
}
|
|
1635
|
-
else if (_id > _path) {
|
|
1636
|
-
break;
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
if (found) {
|
|
1641
|
-
_level ||= 0;
|
|
1642
|
-
return findNode(found, id, _parts, `${_path}.${_parts[_level + 1]}`, _level + 1);
|
|
1643
|
-
}
|
|
1644
|
-
return null;
|
|
1645
|
-
}
|
|
1646
|
-
function findRoomsForObject(info, id, lang, rooms) {
|
|
1647
|
-
if (!id) {
|
|
1648
|
-
return { rooms: [], per: false };
|
|
1649
|
-
}
|
|
1650
|
-
rooms ||= [];
|
|
1651
|
-
for (const room of info.roomEnums) {
|
|
1652
|
-
const common = info.objects[room]?.common;
|
|
1653
|
-
if (!common) {
|
|
1654
|
-
continue;
|
|
1655
|
-
}
|
|
1656
|
-
const name = getName(common.name, lang);
|
|
1657
|
-
if (common.members?.includes(id) && !rooms.includes(name)) {
|
|
1658
|
-
rooms.push(name);
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
let ownEnums;
|
|
1662
|
-
// Check parent
|
|
1663
|
-
const parts = id.split('.');
|
|
1664
|
-
parts.pop();
|
|
1665
|
-
id = parts.join('.');
|
|
1666
|
-
if (info.objects[id]) {
|
|
1667
|
-
ownEnums = rooms.length;
|
|
1668
|
-
findRoomsForObject(info, id, lang, rooms);
|
|
1669
|
-
}
|
|
1670
|
-
return { rooms, per: !ownEnums }; // per is if the enums are from parent
|
|
1671
|
-
}
|
|
1672
|
-
function findEnumsForObjectAsIds(info, id, enumName, funcs) {
|
|
1673
|
-
if (!id) {
|
|
1674
|
-
return [];
|
|
1675
|
-
}
|
|
1676
|
-
funcs ||= [];
|
|
1677
|
-
for (let i = 0; i < info[enumName].length; i++) {
|
|
1678
|
-
const common = info.objects[info[enumName][i]]?.common;
|
|
1679
|
-
if (common?.members?.includes(id) && !funcs.includes(info[enumName][i])) {
|
|
1680
|
-
funcs.push(info[enumName][i]);
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
funcs.sort();
|
|
1684
|
-
return funcs;
|
|
1685
|
-
}
|
|
1686
|
-
function findFunctionsForObject(info, id, lang, funcs) {
|
|
1687
|
-
if (!id) {
|
|
1688
|
-
return { funcs: [], pef: false };
|
|
1689
|
-
}
|
|
1690
|
-
funcs ||= [];
|
|
1691
|
-
for (let i = 0; i < info.funcEnums.length; i++) {
|
|
1692
|
-
const common = info.objects[info.funcEnums[i]]?.common;
|
|
1693
|
-
if (!common) {
|
|
1694
|
-
continue;
|
|
1695
|
-
}
|
|
1696
|
-
const name = getName(common.name, lang);
|
|
1697
|
-
if (common.members?.includes(id) && !funcs.includes(name)) {
|
|
1698
|
-
funcs.push(name);
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
let ownEnums;
|
|
1702
|
-
// Check parent
|
|
1703
|
-
const parts = id.split('.');
|
|
1704
|
-
parts.pop();
|
|
1705
|
-
id = parts.join('.');
|
|
1706
|
-
if (info.objects[id]) {
|
|
1707
|
-
ownEnums = funcs.length;
|
|
1708
|
-
findFunctionsForObject(info, id, lang, funcs);
|
|
1709
|
-
}
|
|
1710
|
-
return { funcs, pef: !ownEnums };
|
|
1711
|
-
}
|
|
1712
|
-
/*
|
|
1713
|
-
function quality2text(q) {
|
|
1714
|
-
if (!q) {
|
|
1715
|
-
return 'ok';
|
|
1716
|
-
}
|
|
1717
|
-
const custom = q & 0xFFFF0000;
|
|
1718
|
-
let text = '';
|
|
1719
|
-
if (q & 0x40) text += 'device';
|
|
1720
|
-
if (q & 0x80) text += 'sensor';
|
|
1721
|
-
if (q & 0x01) text += ' bad';
|
|
1722
|
-
if (q & 0x02) text += ' not connected';
|
|
1723
|
-
if (q & 0x04) text += ' error';
|
|
1724
|
-
|
|
1725
|
-
return text + (custom ? '|0x' + (custom >> 16).toString(16).toUpperCase() : '') + ' [0x' + q.toString(16).toUpperCase() + ']';
|
|
1726
|
-
}
|
|
1727
|
-
*/
|
|
1728
|
-
/**
|
|
1729
|
-
* Format a state value for visualization
|
|
1730
|
-
*/
|
|
1731
|
-
function formatValue(options) {
|
|
1732
|
-
const { dateFormat, state, isFloatComma, texts, obj } = options;
|
|
1733
|
-
const states = Utils.getStates(obj);
|
|
1734
|
-
const isCommon = obj.common;
|
|
1735
|
-
let fileViewer;
|
|
1736
|
-
let v =
|
|
1737
|
-
// @ts-expect-error deprecated from js-controller 6
|
|
1738
|
-
isCommon?.type === 'file'
|
|
1739
|
-
? '[file]'
|
|
1740
|
-
: !state || state.val === null
|
|
1741
|
-
? '(null)'
|
|
1742
|
-
: state.val === undefined
|
|
1743
|
-
? '[undef]'
|
|
1744
|
-
: state.val;
|
|
1745
|
-
const type = typeof v;
|
|
1746
|
-
if (isCommon?.role && typeof isCommon.role === 'string' && isCommon.role.match(/^value\.time|^date/)) {
|
|
1747
|
-
if (v && typeof v === 'string') {
|
|
1748
|
-
if (Utils.isStringInteger(v)) {
|
|
1749
|
-
// we assume a unix ts
|
|
1750
|
-
v = new Date(parseInt(v, 10)).toString();
|
|
1751
|
-
}
|
|
1752
|
-
else {
|
|
1753
|
-
// check if parsable by new date
|
|
1754
|
-
try {
|
|
1755
|
-
const parsedDate = new Date(v);
|
|
1756
|
-
if (Utils.isValidDate(parsedDate)) {
|
|
1757
|
-
v = parsedDate.toString();
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
catch {
|
|
1761
|
-
// ignore
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
else {
|
|
1766
|
-
if (v > 946681200 && v < 946681200000) {
|
|
1767
|
-
// '2000-01-01T00:00:00' => 946681200000
|
|
1768
|
-
v *= 1_000; // maybe the time is in seconds (UNIX time)
|
|
1769
|
-
}
|
|
1770
|
-
// "null" and undefined could not be here. See `let v = (isCommon && isCommon.type === 'file') ....` above
|
|
1771
|
-
v = v ? new Date(v).toString() : v;
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
else if (isCommon?.role && typeof isCommon.role === 'string' && isCommon.role.match(/^value\.duration/)) {
|
|
1775
|
-
// Format duration values in HH:mm:ss format
|
|
1776
|
-
if (typeof v === 'number' && v >= 0) {
|
|
1777
|
-
const hours = Math.floor(v / 3600);
|
|
1778
|
-
const minutes = Math.floor((v % 3600) / 60);
|
|
1779
|
-
const seconds = Math.floor(v % 60);
|
|
1780
|
-
v = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
1781
|
-
}
|
|
1782
|
-
else if (typeof v === 'string' && Utils.isStringInteger(v)) {
|
|
1783
|
-
const numValue = parseInt(v, 10);
|
|
1784
|
-
if (numValue >= 0) {
|
|
1785
|
-
const hours = Math.floor(numValue / 3600);
|
|
1786
|
-
const minutes = Math.floor((numValue % 3600) / 60);
|
|
1787
|
-
const seconds = Math.floor(numValue % 60);
|
|
1788
|
-
v = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
else {
|
|
1793
|
-
if (type === 'number') {
|
|
1794
|
-
if (!Number.isInteger(v)) {
|
|
1795
|
-
v = Math.round(v * 100_000_000) / 100_000_000; // remove 4.00000000000000001
|
|
1796
|
-
if (isFloatComma) {
|
|
1797
|
-
v = v.toString().replace('.', ',');
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
else if (type === 'object') {
|
|
1802
|
-
v = JSON.stringify(v);
|
|
1803
|
-
}
|
|
1804
|
-
else if (type !== 'string') {
|
|
1805
|
-
v = v.toString();
|
|
1806
|
-
}
|
|
1807
|
-
else if (v.startsWith('data:image/')) {
|
|
1808
|
-
fileViewer = 'image';
|
|
1809
|
-
}
|
|
1810
|
-
if (typeof v !== 'string') {
|
|
1811
|
-
v = v.toString();
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
const valText = { v: v };
|
|
1815
|
-
// try to replace number with "common.states"
|
|
1816
|
-
if (states && states[v] !== undefined) {
|
|
1817
|
-
if (v !== states[v]) {
|
|
1818
|
-
valText.s = v;
|
|
1819
|
-
v = states[v];
|
|
1820
|
-
valText.v = v;
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
if (valText.v?.length > 40) {
|
|
1824
|
-
valText.c = valText.v;
|
|
1825
|
-
valText.v = `${valText.v.substring(0, 40)}...`;
|
|
1826
|
-
}
|
|
1827
|
-
if (isCommon?.unit) {
|
|
1828
|
-
valText.u = isCommon.unit;
|
|
1829
|
-
}
|
|
1830
|
-
let valFull;
|
|
1831
|
-
if (options.full) {
|
|
1832
|
-
if (typeof v === 'string' && v.length > 100) {
|
|
1833
|
-
valFull = [{ t: texts.value, v: `${v.substring(0, 100)}...` }];
|
|
1834
|
-
}
|
|
1835
|
-
else {
|
|
1836
|
-
valFull = [{ t: texts.value, v }];
|
|
1837
|
-
}
|
|
1838
|
-
if (state) {
|
|
1839
|
-
if (state.ack !== undefined && state.ack !== null) {
|
|
1840
|
-
valFull.push({ t: texts.ack, v: state.ack.toString() });
|
|
1841
|
-
}
|
|
1842
|
-
if (state.ts) {
|
|
1843
|
-
valFull.push({ t: texts.ts, v: state.ts ? Utils.formatDate(new Date(state.ts), dateFormat) : '' });
|
|
1844
|
-
}
|
|
1845
|
-
if (state.lc) {
|
|
1846
|
-
valFull.push({ t: texts.lc, v: state.lc ? Utils.formatDate(new Date(state.lc), dateFormat) : '' });
|
|
1847
|
-
}
|
|
1848
|
-
if (state.from) {
|
|
1849
|
-
let from = state.from.toString();
|
|
1850
|
-
if (from.startsWith('system.adapter.')) {
|
|
1851
|
-
from = from.substring(15);
|
|
1852
|
-
}
|
|
1853
|
-
valFull.push({ t: texts.from, v: from });
|
|
1854
|
-
}
|
|
1855
|
-
if (state.user) {
|
|
1856
|
-
let user = state.user.toString();
|
|
1857
|
-
if (user.startsWith('system.user.')) {
|
|
1858
|
-
user = user.substring(12);
|
|
1859
|
-
}
|
|
1860
|
-
valFull.push({ t: texts.user, v: user });
|
|
1861
|
-
}
|
|
1862
|
-
if (state.c) {
|
|
1863
|
-
valFull.push({ t: texts.c, v: state.c });
|
|
1864
|
-
}
|
|
1865
|
-
valFull.push({ t: texts.quality, v: Utils.quality2text(state.q || 0).join(', '), nbr: true });
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
return {
|
|
1869
|
-
valText,
|
|
1870
|
-
valFull,
|
|
1871
|
-
fileViewer,
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Get CSS style for given state value
|
|
1876
|
-
*/
|
|
1877
|
-
function getValueStyle(options) {
|
|
1878
|
-
const { state /* , isExpertMode, isButton */ } = options;
|
|
1879
|
-
const color = state?.ack ? (state.q ? '#ffa500' : '') : '#ff2222c9';
|
|
1880
|
-
// do not show the color of the button in non-expert mode
|
|
1881
|
-
// if (!isExpertMode && isButton) {
|
|
1882
|
-
// color = '';
|
|
1883
|
-
// }
|
|
1884
|
-
return { color };
|
|
1885
|
-
}
|
|
1886
|
-
function prepareSparkData(values, from) {
|
|
1887
|
-
// set one point every hour
|
|
1888
|
-
let time = from;
|
|
1889
|
-
let i = 1;
|
|
1890
|
-
const v = [];
|
|
1891
|
-
while (i < values.length && time < from + 25 * 3600000) {
|
|
1892
|
-
// find the interval
|
|
1893
|
-
while (values[i - 1].ts < time && time <= values[i].ts && i < values.length) {
|
|
1894
|
-
i++;
|
|
1895
|
-
}
|
|
1896
|
-
if (i === 1 && values[i - 1].ts >= time) {
|
|
1897
|
-
// assume the value was always null
|
|
1898
|
-
v.push(0);
|
|
1899
|
-
}
|
|
1900
|
-
else if (i < values.length) {
|
|
1901
|
-
if (typeof values[i].val === 'boolean' || typeof values[i - 1].val === 'boolean') {
|
|
1902
|
-
v.push(values[i].val ? 1 : 0);
|
|
1903
|
-
}
|
|
1904
|
-
else {
|
|
1905
|
-
// remove nulls
|
|
1906
|
-
values[i - 1].val ||= 0;
|
|
1907
|
-
values[i].val ||= 0;
|
|
1908
|
-
// interpolate
|
|
1909
|
-
const nm1 = values[i - 1].val;
|
|
1910
|
-
const n = values[i].val;
|
|
1911
|
-
const val = nm1 + ((n - nm1) * (time - values[i - 1].ts)) / (values[i].ts - values[i - 1].ts);
|
|
1912
|
-
v.push(val);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
time += 3600000;
|
|
1916
|
-
}
|
|
1917
|
-
return v;
|
|
1918
|
-
}
|
|
542
|
+
height: '100%',
|
|
543
|
+
alignItems: 'center',
|
|
544
|
+
},
|
|
545
|
+
aclText: {
|
|
546
|
+
fontSize: 13,
|
|
547
|
+
marginTop: 6,
|
|
548
|
+
},
|
|
549
|
+
rightsObject: {
|
|
550
|
+
color: '#55ff55',
|
|
551
|
+
paddingLeft: 3,
|
|
552
|
+
},
|
|
553
|
+
rightsState: {
|
|
554
|
+
color: '#86b6ff',
|
|
555
|
+
paddingLeft: 3,
|
|
556
|
+
},
|
|
557
|
+
textCenter: {
|
|
558
|
+
padding: 12,
|
|
559
|
+
textAlign: 'center',
|
|
560
|
+
},
|
|
561
|
+
tooltipAccessControl: {
|
|
562
|
+
display: 'flex',
|
|
563
|
+
flexDirection: 'column',
|
|
564
|
+
},
|
|
565
|
+
fontSizeTitle: {
|
|
566
|
+
'@media screen and (max-width: 465px)': {
|
|
567
|
+
'& *': {
|
|
568
|
+
fontSize: 12,
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
draggable: {
|
|
573
|
+
cursor: 'copy',
|
|
574
|
+
},
|
|
575
|
+
nonDraggable: {
|
|
576
|
+
cursor: 'no-drop',
|
|
577
|
+
},
|
|
578
|
+
iconDeviceConnected: (theme) => ({
|
|
579
|
+
color: theme.palette.mode === 'dark' ? COLOR_NAME_CONNECTED_DARK : COLOR_NAME_CONNECTED_LIGHT,
|
|
580
|
+
opacity: 0.8,
|
|
581
|
+
position: 'absolute',
|
|
582
|
+
top: 4,
|
|
583
|
+
right: 32,
|
|
584
|
+
width: 20,
|
|
585
|
+
}),
|
|
586
|
+
iconDeviceDisconnected: (theme) => ({
|
|
587
|
+
color: theme.palette.mode === 'dark' ? COLOR_NAME_DISCONNECTED_DARK : COLOR_NAME_DISCONNECTED_LIGHT,
|
|
588
|
+
opacity: 0.8,
|
|
589
|
+
position: 'absolute',
|
|
590
|
+
top: 4,
|
|
591
|
+
right: 32,
|
|
592
|
+
width: 20,
|
|
593
|
+
}),
|
|
594
|
+
iconDeviceError: (theme) => ({
|
|
595
|
+
color: theme.palette.mode === 'dark' ? COLOR_NAME_ERROR_DARK : COLOR_NAME_ERROR_LIGHT,
|
|
596
|
+
opacity: 0.8,
|
|
597
|
+
position: 'absolute',
|
|
598
|
+
top: 4,
|
|
599
|
+
right: 50,
|
|
600
|
+
width: 20,
|
|
601
|
+
}),
|
|
602
|
+
resizeHandle: {
|
|
603
|
+
display: 'block',
|
|
604
|
+
position: 'absolute',
|
|
605
|
+
cursor: 'col-resize',
|
|
606
|
+
width: 7,
|
|
607
|
+
top: 2,
|
|
608
|
+
bottom: 2,
|
|
609
|
+
zIndex: 1,
|
|
610
|
+
},
|
|
611
|
+
resizeHandleRight: {
|
|
612
|
+
right: 3,
|
|
613
|
+
borderRight: '2px dotted #888',
|
|
614
|
+
'&:hover': {
|
|
615
|
+
borderColor: '#ccc',
|
|
616
|
+
borderRightStyle: 'solid',
|
|
617
|
+
},
|
|
618
|
+
'&.active': {
|
|
619
|
+
borderColor: '#517ea5',
|
|
620
|
+
borderRightStyle: 'solid',
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
invertedBackground: (theme) => ({
|
|
624
|
+
backgroundColor: theme.palette.mode === 'dark' ? '#9a9a9a' : '#565656',
|
|
625
|
+
padding: '0 3px',
|
|
626
|
+
borderRadius: '2px 0 0 2px',
|
|
627
|
+
}),
|
|
628
|
+
invertedBackgroundFlex: (theme) => ({
|
|
629
|
+
backgroundColor: theme.palette.mode === 'dark' ? '#9a9a9a' : '#565656',
|
|
630
|
+
borderRadius: '0 2px 2px 0',
|
|
631
|
+
}),
|
|
632
|
+
contextMenuEdit: (theme) => ({
|
|
633
|
+
color: theme.palette.mode === 'dark' ? '#ffee48' : '#cbb801',
|
|
634
|
+
}),
|
|
635
|
+
contextMenuEditValue: (theme) => ({
|
|
636
|
+
color: theme.palette.mode === 'dark' ? '#5dff45' : '#1cd301',
|
|
637
|
+
}),
|
|
638
|
+
contextMenuView: (theme) => ({
|
|
639
|
+
color: theme.palette.mode === 'dark' ? '#FFF' : '#000',
|
|
640
|
+
}),
|
|
641
|
+
contextMenuCustom: (theme) => ({
|
|
642
|
+
color: theme.palette.mode === 'dark' ? '#42eaff' : '#01bbc2',
|
|
643
|
+
}),
|
|
644
|
+
contextMenuACL: (theme) => ({
|
|
645
|
+
color: theme.palette.mode === 'dark' ? '#e079ff' : '#500070',
|
|
646
|
+
}),
|
|
647
|
+
contextMenuRoom: (theme) => ({
|
|
648
|
+
color: theme.palette.mode === 'dark' ? '#ff9a33' : '#642a00',
|
|
649
|
+
}),
|
|
650
|
+
contextMenuRole: (theme) => ({
|
|
651
|
+
color: theme.palette.mode === 'dark' ? '#ffdb43' : '#562d00',
|
|
652
|
+
}),
|
|
653
|
+
contextMenuDelete: (theme) => ({
|
|
654
|
+
color: theme.palette.mode === 'dark' ? '#ff4f4f' : '#cf0000',
|
|
655
|
+
}),
|
|
656
|
+
contextMenuKeys: {
|
|
657
|
+
marginLeft: 8,
|
|
658
|
+
opacity: 0.7,
|
|
659
|
+
fontSize: 'smaller',
|
|
660
|
+
},
|
|
661
|
+
contextMenuWithSubMenu: {
|
|
662
|
+
display: 'flex',
|
|
663
|
+
},
|
|
664
|
+
...utilStyles,
|
|
665
|
+
};
|
|
1919
666
|
export const ITEM_IMAGES = {
|
|
1920
667
|
state: (React.createElement(IconState, { className: "itemIcon", style: { verticalAlign: 'middle' } })),
|
|
1921
668
|
channel: (React.createElement(IconChannel, { className: "itemIcon", style: { verticalAlign: 'middle' } })),
|
|
@@ -2077,19 +824,19 @@ export class ObjectBrowserClass extends Component {
|
|
|
2077
824
|
systemConfig;
|
|
2078
825
|
objects;
|
|
2079
826
|
defaultHistory = '';
|
|
2080
|
-
|
|
827
|
+
ctrlPressed = false;
|
|
2081
828
|
columnsVisibility = {};
|
|
2082
829
|
changedIds = null;
|
|
2083
830
|
contextMenu = null;
|
|
2084
831
|
recordStates = [];
|
|
2085
832
|
styles = {};
|
|
833
|
+
expertMode = false;
|
|
2086
834
|
customColumnDialog = null;
|
|
2087
|
-
/** Namespaces which are allowed to be edited by non-expert users */
|
|
2088
|
-
static NON_EXPERT_NAMESPACES = ['0_userdata.0.', 'alias.0.'];
|
|
2089
835
|
constructor(props) {
|
|
2090
836
|
super(props);
|
|
2091
837
|
const lastSelectedItemStr = this.localStorage.getItem(`${props.dialogName || 'App'}.objectSelected`) || '';
|
|
2092
838
|
this.selectFirst = '';
|
|
839
|
+
this.expertMode = !!this.props.expertMode;
|
|
2093
840
|
if (lastSelectedItemStr.startsWith('[')) {
|
|
2094
841
|
try {
|
|
2095
842
|
const lastSelectedItems = JSON.parse(lastSelectedItemStr);
|
|
@@ -2228,46 +975,48 @@ export class ObjectBrowserClass extends Component {
|
|
|
2228
975
|
// ignore
|
|
2229
976
|
}
|
|
2230
977
|
this.state = {
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
selected,
|
|
2234
|
-
selectedNonObject: this.localStorage.getItem(`${props.dialogName || 'App'}.selectedNonObject`) || '',
|
|
2235
|
-
filter,
|
|
2236
|
-
filterKey: 0,
|
|
2237
|
-
focused: this.localStorage.getItem(`${props.dialogName || 'App'}.focused`) || '',
|
|
2238
|
-
depth: 0,
|
|
2239
|
-
expandAllVisible: false,
|
|
2240
|
-
expanded,
|
|
2241
|
-
toast: '',
|
|
2242
|
-
scrollBarWidth: 16,
|
|
2243
|
-
customDialog,
|
|
2244
|
-
editObjectDialog: '',
|
|
2245
|
-
editObjectAlias: false, // open the edit object dialog on alias tab
|
|
2246
|
-
viewFileDialog: '',
|
|
2247
|
-
showAliasEditor: '',
|
|
2248
|
-
enumDialog: null,
|
|
2249
|
-
roleDialog: null,
|
|
2250
|
-
statesView,
|
|
978
|
+
aliasMenu: '',
|
|
979
|
+
beautifyJsonExport: true,
|
|
2251
980
|
columns,
|
|
2252
|
-
columnsForAdmin: null,
|
|
2253
|
-
columnsSelectorShow: false,
|
|
2254
981
|
columnsAuto: this.localStorage.getItem(`${props.dialogName || 'App'}.columnsAuto`) !== 'false',
|
|
2255
|
-
columnsWidths,
|
|
2256
982
|
columnsDialogTransparent: 100,
|
|
2257
983
|
columnsEditCustomDialog: null,
|
|
984
|
+
columnsForAdmin: null,
|
|
985
|
+
columnsSelectorShow: false,
|
|
986
|
+
columnsWidths,
|
|
2258
987
|
customColumnDialogValueChanged: false,
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
noStatesByExportImport: false,
|
|
2265
|
-
beautifyJsonExport: true,
|
|
988
|
+
customDialog,
|
|
989
|
+
depth: 0,
|
|
990
|
+
editObjectAlias: false, // open the edit object dialog on alias tab
|
|
991
|
+
editObjectDialog: '',
|
|
992
|
+
enumDialog: null,
|
|
2266
993
|
excludeSystemRepositoriesFromExport: true,
|
|
2267
994
|
excludeTranslations: false,
|
|
2268
|
-
|
|
2269
|
-
|
|
995
|
+
expandAllVisible: false,
|
|
996
|
+
expanded,
|
|
997
|
+
filter,
|
|
998
|
+
filterKey: 0,
|
|
999
|
+
focused: this.localStorage.getItem(`${props.dialogName || 'App'}.focused`) || '',
|
|
1000
|
+
foldersFirst,
|
|
1001
|
+
linesEnabled: this.localStorage.getItem(`${props.dialogName || 'App'}.lines`) === 'true',
|
|
1002
|
+
loaded: false,
|
|
1003
|
+
noStatesByExportImport: false,
|
|
1004
|
+
roleDialog: null,
|
|
1005
|
+
scrollBarWidth: 16,
|
|
1006
|
+
selected,
|
|
1007
|
+
selectedNonObject: this.localStorage.getItem(`${props.dialogName || 'App'}.selectedNonObject`) || '',
|
|
1008
|
+
showAliasEditor: '',
|
|
1009
|
+
showAllExportOptions: false,
|
|
1010
|
+
showContextMenu: null,
|
|
1011
|
+
showDescription: this.localStorage.getItem(`${props.dialogName || 'App'}.desc`) !== 'false',
|
|
1012
|
+
showExportDialog: false,
|
|
1013
|
+
showImportDialog: false,
|
|
1014
|
+
showImportMenu: null,
|
|
2270
1015
|
showRenameDialog: null,
|
|
1016
|
+
statesView,
|
|
1017
|
+
toast: '',
|
|
1018
|
+
tooltipInfo: null,
|
|
1019
|
+
viewFileDialog: '',
|
|
2271
1020
|
};
|
|
2272
1021
|
this.texts = {
|
|
2273
1022
|
name: props.t('ra_Name'),
|
|
@@ -2492,14 +1241,6 @@ export class ObjectBrowserClass extends Component {
|
|
|
2492
1241
|
this.showError(error);
|
|
2493
1242
|
}
|
|
2494
1243
|
}
|
|
2495
|
-
/**
|
|
2496
|
-
* Check if it is a non-expert id
|
|
2497
|
-
*/
|
|
2498
|
-
static isNonExpertId(
|
|
2499
|
-
/** id to test */
|
|
2500
|
-
id) {
|
|
2501
|
-
return !!ObjectBrowserClass.NON_EXPERT_NAMESPACES.find(saveNamespace => id.startsWith(saveNamespace));
|
|
2502
|
-
}
|
|
2503
1244
|
expandAllSelected(cb) {
|
|
2504
1245
|
const expanded = [...this.state.expanded];
|
|
2505
1246
|
let changed = false;
|
|
@@ -2588,6 +1329,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
2588
1329
|
}
|
|
2589
1330
|
}
|
|
2590
1331
|
}
|
|
1332
|
+
// This function is used
|
|
2591
1333
|
static getDerivedStateFromProps(props, state) {
|
|
2592
1334
|
const newState = {};
|
|
2593
1335
|
let changed = false;
|
|
@@ -2621,14 +1363,14 @@ export class ObjectBrowserClass extends Component {
|
|
|
2621
1363
|
}
|
|
2622
1364
|
}
|
|
2623
1365
|
onKeyPress = (event) => {
|
|
2624
|
-
if (event.type === 'keydown' && event.ctrlKey && !this.
|
|
2625
|
-
this.
|
|
1366
|
+
if (event.type === 'keydown' && event.ctrlKey && !this.ctrlPressed) {
|
|
1367
|
+
this.ctrlPressed = true;
|
|
2626
1368
|
if (this.tableRef.current) {
|
|
2627
1369
|
this.tableRef.current.className = 'highlight-link';
|
|
2628
1370
|
}
|
|
2629
1371
|
}
|
|
2630
|
-
else if (event.type === 'keyup' && !event.ctrlKey && this.
|
|
2631
|
-
this.
|
|
1372
|
+
else if (event.type === 'keyup' && !event.ctrlKey && this.ctrlPressed) {
|
|
1373
|
+
this.ctrlPressed = false;
|
|
2632
1374
|
if (this.tableRef.current) {
|
|
2633
1375
|
this.tableRef.current.className = '';
|
|
2634
1376
|
}
|
|
@@ -3579,76 +2321,78 @@ export class ObjectBrowserClass extends Component {
|
|
|
3579
2321
|
const ObjectMoveRenameDialog = this.props.objectMoveRenameDialog;
|
|
3580
2322
|
return (React.createElement(ObjectMoveRenameDialog, { expertMode: this.props.expertMode, onClose: () => this.setState({ showRenameDialog: null }), id: this.state.showRenameDialog.id, childrenIds: this.state.showRenameDialog.childrenIds, theme: this.props.theme, socket: this.props.socket, t: this.props.t, objectType: this.objects[this.state.showRenameDialog.id]?.type }));
|
|
3581
2323
|
}
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
const
|
|
3587
|
-
|
|
3588
|
-
|
|
2324
|
+
async parseJsonFile(contents) {
|
|
2325
|
+
try {
|
|
2326
|
+
const json = JSON.parse(contents);
|
|
2327
|
+
const len = Object.keys(json).length;
|
|
2328
|
+
const id = json._id;
|
|
2329
|
+
// it could be a single object or many objects
|
|
2330
|
+
if (id === undefined && len) {
|
|
2331
|
+
// many objects
|
|
2332
|
+
await this.loadObjects(json);
|
|
2333
|
+
window.alert(this.props.t('ra_%s object(s) processed', len));
|
|
2334
|
+
}
|
|
2335
|
+
else {
|
|
2336
|
+
// it is only one object in form
|
|
2337
|
+
// {
|
|
2338
|
+
// "_id": "xxx",
|
|
2339
|
+
// "common": "yyy",
|
|
2340
|
+
// "native": "zzz"
|
|
2341
|
+
// "val": JSON.stringify(value)
|
|
2342
|
+
// "ack": true
|
|
2343
|
+
// }
|
|
2344
|
+
if (!id) {
|
|
2345
|
+
return window.alert(this.props.t('ra_Invalid structure'));
|
|
2346
|
+
}
|
|
3589
2347
|
try {
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
await this.loadObjects(json);
|
|
3597
|
-
window.alert(this.props.t('ra_%s object(s) processed', len));
|
|
2348
|
+
let enums;
|
|
2349
|
+
let val;
|
|
2350
|
+
let ack;
|
|
2351
|
+
if (json.common.enums) {
|
|
2352
|
+
enums = json.common.enums;
|
|
2353
|
+
delete json.common.enums;
|
|
3598
2354
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
2355
|
+
if (json.val) {
|
|
2356
|
+
val = json.val;
|
|
2357
|
+
delete json.val;
|
|
2358
|
+
}
|
|
2359
|
+
if (json.ack !== undefined) {
|
|
2360
|
+
ack = json.ack;
|
|
2361
|
+
delete json.ack;
|
|
2362
|
+
}
|
|
2363
|
+
await this.props.socket.setObject(json._id, json);
|
|
2364
|
+
if (json.type === 'state') {
|
|
2365
|
+
if (val !== undefined && val !== null) {
|
|
2366
|
+
await this.props.socket.setState(json._id, val, ack === undefined ? true : ack);
|
|
3610
2367
|
}
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
if (json.common.enums) {
|
|
3616
|
-
enums = json.common.enums;
|
|
3617
|
-
delete json.common.enums;
|
|
3618
|
-
}
|
|
3619
|
-
if (json.val) {
|
|
3620
|
-
val = json.val;
|
|
3621
|
-
delete json.val;
|
|
3622
|
-
}
|
|
3623
|
-
if (json.ack !== undefined) {
|
|
3624
|
-
ack = json.ack;
|
|
3625
|
-
delete json.ack;
|
|
3626
|
-
}
|
|
3627
|
-
await this.props.socket.setObject(json._id, json);
|
|
3628
|
-
if (json.type === 'state') {
|
|
3629
|
-
if (val !== undefined && val !== null) {
|
|
3630
|
-
await this.props.socket.setState(json._id, val, ack === undefined ? true : ack);
|
|
3631
|
-
}
|
|
3632
|
-
else {
|
|
3633
|
-
const state = await this.props.socket.getState(json._id);
|
|
3634
|
-
if (!state || state.val === null || state.val === undefined) {
|
|
3635
|
-
await this.props.socket.setState(json._id, json.common.def === undefined ? null : json.common.def, true);
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
if (enums) {
|
|
3640
|
-
await this._createAllEnums(enums, json._id);
|
|
2368
|
+
else {
|
|
2369
|
+
const state = await this.props.socket.getState(json._id);
|
|
2370
|
+
if (!state || state.val === null || state.val === undefined) {
|
|
2371
|
+
await this.props.socket.setState(json._id, json.common.def === undefined ? null : json.common.def, true);
|
|
3641
2372
|
}
|
|
3642
|
-
window.alert(this.props.t('ra_%s was imported', json._id));
|
|
3643
|
-
}
|
|
3644
|
-
catch (err) {
|
|
3645
|
-
window.alert(err);
|
|
3646
2373
|
}
|
|
3647
2374
|
}
|
|
2375
|
+
if (enums) {
|
|
2376
|
+
await this._createAllEnums(enums, json._id);
|
|
2377
|
+
}
|
|
2378
|
+
window.alert(this.props.t('ra_%s was imported', json._id));
|
|
3648
2379
|
}
|
|
3649
2380
|
catch (err) {
|
|
3650
2381
|
window.alert(err);
|
|
3651
2382
|
}
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
catch (err) {
|
|
2386
|
+
window.alert(err);
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
handleJsonUpload(evt) {
|
|
2390
|
+
const target = evt.target;
|
|
2391
|
+
const f = target.files?.length && target.files[0];
|
|
2392
|
+
if (f) {
|
|
2393
|
+
const r = new FileReader();
|
|
2394
|
+
r.onload = (e) => {
|
|
2395
|
+
void this.parseJsonFile(e.target?.result);
|
|
3652
2396
|
return null;
|
|
3653
2397
|
};
|
|
3654
2398
|
r.readAsText(f);
|
|
@@ -3726,6 +2470,26 @@ export class ObjectBrowserClass extends Component {
|
|
|
3726
2470
|
}
|
|
3727
2471
|
return value.length ? value : t('ra_Add new child object to selected parent');
|
|
3728
2472
|
};
|
|
2473
|
+
onOpenFile() {
|
|
2474
|
+
const input = document.createElement('input');
|
|
2475
|
+
input.setAttribute('type', 'file');
|
|
2476
|
+
input.setAttribute('id', 'files');
|
|
2477
|
+
input.setAttribute('opacity', '0');
|
|
2478
|
+
input.addEventListener('change', (e) => this.handleJsonUpload(e), false);
|
|
2479
|
+
input.click();
|
|
2480
|
+
}
|
|
2481
|
+
renderInputJsonDialog() {
|
|
2482
|
+
const ObjectBrowserInsertJsonObjects = this.props.objectBrowserInsertJsonObjects;
|
|
2483
|
+
if (!this.state.showImportDialog) {
|
|
2484
|
+
return null;
|
|
2485
|
+
}
|
|
2486
|
+
return (React.createElement(ObjectBrowserInsertJsonObjects, { onClose: (text) => {
|
|
2487
|
+
this.setState({ showImportDialog: false });
|
|
2488
|
+
if (text) {
|
|
2489
|
+
void this.parseJsonFile(text);
|
|
2490
|
+
}
|
|
2491
|
+
}, t: this.props.t }));
|
|
2492
|
+
}
|
|
3729
2493
|
/**
|
|
3730
2494
|
* Renders the toolbar.
|
|
3731
2495
|
*/
|
|
@@ -3817,15 +2581,24 @@ export class ObjectBrowserClass extends Component {
|
|
|
3817
2581
|
}), size: "large" },
|
|
3818
2582
|
React.createElement(AddIcon, null))))) : null,
|
|
3819
2583
|
this.props.objectImportExport && (React.createElement(Tooltip, { title: this.props.t('ra_Add objects tree from JSON file'), slotProps: { popper: { sx: styles.tooltip } } },
|
|
3820
|
-
React.createElement(IconButton, { onClick:
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
2584
|
+
React.createElement(IconButton, { onClick: e => {
|
|
2585
|
+
if (this.props.objectBrowserInsertJsonObjects) {
|
|
2586
|
+
this.setState({ showImportMenu: e.currentTarget });
|
|
2587
|
+
}
|
|
2588
|
+
else {
|
|
2589
|
+
this.onOpenFile();
|
|
2590
|
+
}
|
|
3827
2591
|
}, size: "large" },
|
|
3828
2592
|
React.createElement(PublishIcon, null)))),
|
|
2593
|
+
this.props.objectBrowserInsertJsonObjects ? (React.createElement(Menu, { anchorEl: this.state.showImportMenu, open: !!this.state.showImportMenu, onClose: () => this.setState({ showImportMenu: null }) },
|
|
2594
|
+
React.createElement(MenuItem, { onClick: () => this.onOpenFile() },
|
|
2595
|
+
React.createElement(ListItemIcon, null,
|
|
2596
|
+
React.createElement(UploadFile, null)),
|
|
2597
|
+
React.createElement(ListItemText, null, this.props.t('ra_From file'))),
|
|
2598
|
+
React.createElement(MenuItem, { onClick: () => this.setState({ showImportDialog: true }) },
|
|
2599
|
+
React.createElement(ListItemIcon, null,
|
|
2600
|
+
React.createElement(ContentPaste, null)),
|
|
2601
|
+
React.createElement(ListItemText, null, this.props.t('ra_From text'))))) : null,
|
|
3829
2602
|
this.props.objectImportExport &&
|
|
3830
2603
|
(!!this.state.selected.length || this.state.selectedNonObject) && (React.createElement(Tooltip, { title: this.props.t('ra_Save objects tree as JSON file'), slotProps: { popper: { sx: styles.tooltip } } },
|
|
3831
2604
|
React.createElement(IconButton, { onClick: () => this.setState({ showExportDialog: this._getSelectedIdsForExport().length }), size: "large" },
|
|
@@ -3981,7 +2754,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
3981
2754
|
(item.data.obj.type === 'state'
|
|
3982
2755
|
? this.systemConfig.common.defaultNewAcl.state
|
|
3983
2756
|
: this.systemConfig.common.defaultNewAcl.object);
|
|
3984
|
-
const showEdit = this.state.filter.expertMode ||
|
|
2757
|
+
const showEdit = this.state.filter.expertMode || isNonExpertId(item.data.id);
|
|
3985
2758
|
return [
|
|
3986
2759
|
this.state.filter.expertMode && this.props.objectEditOfAccessControl ? (React.createElement(Tooltip, { key: "acl", title: item.data.aclTooltip, slotProps: { popper: { sx: styles.tooltip } } },
|
|
3987
2760
|
React.createElement(IconButton, { sx: {
|
|
@@ -4378,7 +3151,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
4378
3151
|
this.props.socket
|
|
4379
3152
|
.getObject(this.state.columnsEditCustomDialog?.obj?._id || '')
|
|
4380
3153
|
.then(obj => {
|
|
4381
|
-
if (obj &&
|
|
3154
|
+
if (obj && setCustomValue(obj, this.state.columnsEditCustomDialog?.it, value)) {
|
|
4382
3155
|
return this.props.socket.setObject(obj._id, obj);
|
|
4383
3156
|
}
|
|
4384
3157
|
throw new Error(this.props.t('ra_Cannot update attribute, because not found in the object'));
|
|
@@ -4396,7 +3169,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
4396
3169
|
return null;
|
|
4397
3170
|
}
|
|
4398
3171
|
if (!this.customColumnDialog) {
|
|
4399
|
-
const value =
|
|
3172
|
+
const value = getCustomValue(this.state.columnsEditCustomDialog.obj, this.state.columnsEditCustomDialog.it);
|
|
4400
3173
|
this.customColumnDialog = {
|
|
4401
3174
|
type: (this.state.columnsEditCustomDialog.it.type || typeof value),
|
|
4402
3175
|
initValue: (value === null || value === undefined ? '' : value).toString(),
|
|
@@ -4425,111 +3198,11 @@ export class ObjectBrowserClass extends Component {
|
|
|
4425
3198
|
React.createElement(Button, { variant: "contained", onClick: () => this.onColumnsEditCustomDialogClose(true), disabled: !this.state.customColumnDialogValueChanged, color: "primary", startIcon: React.createElement(IconCheck, null) }, this.props.t('ra_Update')),
|
|
4426
3199
|
React.createElement(Button, { color: "grey", variant: "contained", onClick: () => this.onColumnsEditCustomDialogClose(), startIcon: React.createElement(IconClose, null) }, this.props.t('ra_Cancel')))));
|
|
4427
3200
|
}
|
|
4428
|
-
static getCustomValue(obj, it) {
|
|
4429
|
-
if (obj?._id?.startsWith(`${it.adapter}.`) && it.path.length > 1) {
|
|
4430
|
-
const p = it.path;
|
|
4431
|
-
let value;
|
|
4432
|
-
const anyObj = obj;
|
|
4433
|
-
if (anyObj[p[0]] && typeof anyObj[p[0]] === 'object') {
|
|
4434
|
-
if (p.length === 2) {
|
|
4435
|
-
// most common case
|
|
4436
|
-
value = anyObj[p[0]][p[1]];
|
|
4437
|
-
}
|
|
4438
|
-
else if (p.length === 3) {
|
|
4439
|
-
value =
|
|
4440
|
-
anyObj[p[0]][p[1]] && typeof anyObj[p[0]][p[1]] === 'object' ? anyObj[p[0]][p[1]][p[2]] : null;
|
|
4441
|
-
}
|
|
4442
|
-
else if (p.length === 4) {
|
|
4443
|
-
value =
|
|
4444
|
-
anyObj[p[0]][p[1]] && typeof anyObj[p[0]][p[1]] === 'object' && anyObj[p[0]][p[1]][p[2]]
|
|
4445
|
-
? anyObj[p[0]][p[1]][p[2]][p[3]]
|
|
4446
|
-
: null;
|
|
4447
|
-
}
|
|
4448
|
-
else if (p.length === 5) {
|
|
4449
|
-
value =
|
|
4450
|
-
anyObj[p[0]][p[1]] &&
|
|
4451
|
-
typeof anyObj[p[0]][p[1]] === 'object' &&
|
|
4452
|
-
anyObj[p[0]][p[1]][p[2]] &&
|
|
4453
|
-
anyObj[p[0]][p[1]][p[2]][p[3]]
|
|
4454
|
-
? anyObj[p[0]][p[1]][p[2]][p[3]][p[4]]
|
|
4455
|
-
: null;
|
|
4456
|
-
}
|
|
4457
|
-
else if (p.length === 6) {
|
|
4458
|
-
value =
|
|
4459
|
-
anyObj[p[0]][p[1]] &&
|
|
4460
|
-
typeof anyObj[p[0]][p[1]] === 'object' &&
|
|
4461
|
-
anyObj[p[0]][p[1]][p[2]] &&
|
|
4462
|
-
anyObj[p[0]][p[1]][p[2]][p[3]] &&
|
|
4463
|
-
anyObj[p[0]][p[1]][p[2]][p[3]][p[4]]
|
|
4464
|
-
? anyObj[p[0]][p[1]][p[2]][p[3]][p[4]][p[5]]
|
|
4465
|
-
: null;
|
|
4466
|
-
}
|
|
4467
|
-
if (value === undefined || value === null) {
|
|
4468
|
-
return null;
|
|
4469
|
-
}
|
|
4470
|
-
return value;
|
|
4471
|
-
}
|
|
4472
|
-
}
|
|
4473
|
-
return null;
|
|
4474
|
-
}
|
|
4475
|
-
static setCustomValue(obj, it, value) {
|
|
4476
|
-
if (obj?._id?.startsWith(`${it.adapter}.`) && it.path.length > 1) {
|
|
4477
|
-
const p = it.path;
|
|
4478
|
-
const anyObj = obj;
|
|
4479
|
-
if (anyObj[p[0]] && typeof anyObj[p[0]] === 'object') {
|
|
4480
|
-
if (p.length === 2) {
|
|
4481
|
-
// most common case
|
|
4482
|
-
anyObj[p[0]][p[1]] = value;
|
|
4483
|
-
return true;
|
|
4484
|
-
}
|
|
4485
|
-
if (p.length === 3) {
|
|
4486
|
-
if (anyObj[p[0]][p[1]] && typeof anyObj[p[0]][p[1]] === 'object') {
|
|
4487
|
-
anyObj[p[0]][p[1]][p[2]] = value;
|
|
4488
|
-
return true;
|
|
4489
|
-
}
|
|
4490
|
-
}
|
|
4491
|
-
else if (p.length === 4) {
|
|
4492
|
-
if (anyObj[p[0]][p[1]] &&
|
|
4493
|
-
typeof anyObj[p[0]][p[1]] === 'object' &&
|
|
4494
|
-
anyObj[p[0]][p[1]][p[2]] &&
|
|
4495
|
-
typeof anyObj[p[0]][p[1]][p[2]] === 'object') {
|
|
4496
|
-
anyObj[p[0]][p[1]][p[2]][p[3]] = value;
|
|
4497
|
-
return true;
|
|
4498
|
-
}
|
|
4499
|
-
}
|
|
4500
|
-
else if (p.length === 5) {
|
|
4501
|
-
if (anyObj[p[0]][p[1]] &&
|
|
4502
|
-
typeof anyObj[p[0]][p[1]] === 'object' &&
|
|
4503
|
-
anyObj[p[0]][p[1]][p[2]] &&
|
|
4504
|
-
typeof anyObj[p[0]][p[1]][p[2]] === 'object' &&
|
|
4505
|
-
anyObj[p[0]][p[1]][p[2]][p[3]] &&
|
|
4506
|
-
typeof anyObj[p[0]][p[1]][p[2]][p[3]] === 'object') {
|
|
4507
|
-
anyObj[p[0]][p[1]][p[2]][p[3]][p[4]] = value;
|
|
4508
|
-
return true;
|
|
4509
|
-
}
|
|
4510
|
-
}
|
|
4511
|
-
else if (p.length === 6) {
|
|
4512
|
-
if (anyObj[p[0]][p[1]] &&
|
|
4513
|
-
typeof anyObj[p[0]][p[1]] === 'object' &&
|
|
4514
|
-
anyObj[p[0]][p[1]][p[2]] &&
|
|
4515
|
-
typeof anyObj[p[0]][p[1]][p[2]] === 'object' &&
|
|
4516
|
-
anyObj[p[0]][p[1]][p[2]][p[3]] &&
|
|
4517
|
-
typeof anyObj[p[0]][p[1]][p[2]][p[3]] === 'object' &&
|
|
4518
|
-
anyObj[p[0]][p[1]][p[2]][p[3]][p[4]] &&
|
|
4519
|
-
typeof anyObj[p[0]][p[1]][p[2]][p[3]][p[4]] === 'object') {
|
|
4520
|
-
anyObj[p[0]][p[1]][p[2]][p[3]][p[4]][p[5]] = value;
|
|
4521
|
-
return true;
|
|
4522
|
-
}
|
|
4523
|
-
}
|
|
4524
|
-
}
|
|
4525
|
-
}
|
|
4526
|
-
return false;
|
|
4527
|
-
}
|
|
4528
3201
|
/**
|
|
4529
3202
|
* Renders a custom value.
|
|
4530
3203
|
*/
|
|
4531
3204
|
renderCustomValue(obj, it, item) {
|
|
4532
|
-
const text =
|
|
3205
|
+
const text = getCustomValue(obj, it);
|
|
4533
3206
|
if (text !== null && text !== undefined) {
|
|
4534
3207
|
if (it.edit && !this.props.notEditable && (!it.objTypes || it.objTypes.includes(obj.type))) {
|
|
4535
3208
|
return (React.createElement(Box, { component: "div", style: {
|
|
@@ -5908,7 +4581,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
5908
4581
|
key: '0',
|
|
5909
4582
|
visibility: !!(this.props.objectBrowserEditObject &&
|
|
5910
4583
|
obj &&
|
|
5911
|
-
(this.state.filter.expertMode ||
|
|
4584
|
+
(this.state.filter.expertMode || isNonExpertId(id))),
|
|
5912
4585
|
icon: (React.createElement(IconEdit, { fontSize: "small", style: this.styles.contextMenuEdit })),
|
|
5913
4586
|
label: this.texts.editObject,
|
|
5914
4587
|
onClick: () => this.setState({ editObjectDialog: item.data.id, showContextMenu: null, editObjectAlias: false }),
|
|
@@ -5919,7 +4592,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
5919
4592
|
!this.props.notEditable &&
|
|
5920
4593
|
obj &&
|
|
5921
4594
|
obj.type === 'state' &&
|
|
5922
|
-
//
|
|
4595
|
+
// deprecated from js-controller 6
|
|
5923
4596
|
obj.common?.type !== 'file' &&
|
|
5924
4597
|
(this.state.filter.expertMode || obj.common.write !== false)),
|
|
5925
4598
|
icon: (React.createElement(IconValueEdit, { fontSize: "small", style: this.styles.contextMenuEditValue })),
|
|
@@ -5937,7 +4610,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
5937
4610
|
VIEW: {
|
|
5938
4611
|
visibility: !!this.props.objectBrowserViewFile &&
|
|
5939
4612
|
obj?.type === 'state' &&
|
|
5940
|
-
//
|
|
4613
|
+
// deprecated from js-controller 6
|
|
5941
4614
|
obj.common?.type === 'file',
|
|
5942
4615
|
icon: (React.createElement(FindInPage, { fontSize: "small", style: this.styles.contextMenuView })),
|
|
5943
4616
|
label: this.props.t('ra_View file'),
|
|
@@ -5949,7 +4622,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
5949
4622
|
this.info.hasSomeCustoms &&
|
|
5950
4623
|
obj &&
|
|
5951
4624
|
obj.type === 'state' &&
|
|
5952
|
-
//
|
|
4625
|
+
// deprecated from js-controller 6
|
|
5953
4626
|
obj.common?.type !== 'file'),
|
|
5954
4627
|
icon: (React.createElement(IconConfig, { fontSize: "small", style: item.data.hasCustoms
|
|
5955
4628
|
? this.styles.cellButtonsButtonWithCustoms
|
|
@@ -6025,7 +4698,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
6025
4698
|
this.props.objectBrowserAliasEditor &&
|
|
6026
4699
|
this.props.objectBrowserEditObject &&
|
|
6027
4700
|
obj?.type === 'state' &&
|
|
6028
|
-
//
|
|
4701
|
+
// deprecated from js-controller 6
|
|
6029
4702
|
obj.common?.type !== 'file'),
|
|
6030
4703
|
icon: (React.createElement(IconLink, { style: obj?.common?.alias
|
|
6031
4704
|
? this.styles.cellButtonsButtonWithCustoms
|
|
@@ -6276,6 +4949,10 @@ export class ObjectBrowserClass extends Component {
|
|
|
6276
4949
|
this.unsubscribeTimer = null;
|
|
6277
4950
|
this.checkUnsubscribes();
|
|
6278
4951
|
}, 200);
|
|
4952
|
+
if (this.expertMode !== !!this.state.filter.expertMode) {
|
|
4953
|
+
this.expertMode = !!this.state.filter.expertMode;
|
|
4954
|
+
this.doFilter(true);
|
|
4955
|
+
}
|
|
6279
4956
|
if (!this.state.loaded) {
|
|
6280
4957
|
return React.createElement(CircularProgress, { key: `${this.props.dialogName}_c` });
|
|
6281
4958
|
}
|
|
@@ -6334,6 +5011,7 @@ export class ObjectBrowserClass extends Component {
|
|
|
6334
5011
|
this.renderErrorDialog(),
|
|
6335
5012
|
this.renderExportDialog(),
|
|
6336
5013
|
this.renderRenameDialog(),
|
|
5014
|
+
this.renderInputJsonDialog(),
|
|
6337
5015
|
this.state.modalNewObj && this.props.modalNewObject && this.props.modalNewObject(this),
|
|
6338
5016
|
this.state.modalEditOfAccess &&
|
|
6339
5017
|
this.state.modalEditOfAccessObjData &&
|