@iobroker/adapter-react-v5 7.2.3 → 7.2.4

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.
@@ -4,7 +4,7 @@
4
4
  * MIT License
5
5
  *
6
6
  */
7
- import React, { Component } from 'react';
7
+ import React, { Component, type JSX } from 'react';
8
8
  import type { Connection } from '@iobroker/socket-client';
9
9
  import type { ThemeName, ThemeType, Translate, IobTheme } from '../types';
10
10
  export interface MetaACL extends ioBroker.ObjectACL {
@@ -69,7 +69,7 @@ export interface FileBrowserProps {
69
69
  /** Padding in pixels for folder levels */
70
70
  levelPadding?: number;
71
71
  restrictToFolder?: string;
72
- modalEditOfAccessControl?: (obj: FileBrowserClass) => React.JSX.Element | null;
72
+ modalEditOfAccessControl?: (obj: FileBrowserClass) => JSX.Element | null;
73
73
  allowNonRestricted?: boolean;
74
74
  showTypeSelector?: boolean;
75
75
  }
@@ -145,38 +145,38 @@ export declare class FileBrowserClass extends Component<FileBrowserProps, FileBr
145
145
  readDirSerial(adapter: string, relPath: string): Promise<ioBroker.ReadDirResult[]>;
146
146
  processBrowseList(level?: number): void;
147
147
  browseFolder(folderId: string, _newFolders?: Folders | null, _checkEmpty?: boolean, force?: boolean): Promise<Folders>;
148
- toggleFolder(item: FolderOrFileItem, e: React.MouseEvent<Element>): void;
148
+ toggleFolder(item: FolderOrFileItem, e: React.MouseEvent): void;
149
149
  onFileChange: (id: string, fileName: string, size: number | null) => void;
150
150
  changeFolder(e: React.MouseEvent<HTMLDivElement>, folder?: string): void;
151
151
  select(id: string, e?: React.MouseEvent<HTMLDivElement> | null, cb?: () => void): void;
152
152
  getText(text?: ioBroker.StringOrTranslated | null): string | undefined;
153
- renderFolder(item: FolderOrFileItem, expanded?: boolean): React.JSX.Element | null;
154
- renderBackFolder(): React.JSX.Element;
155
- formatSize(size: number | null | undefined): React.JSX.Element;
156
- formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined): React.JSX.Element;
157
- getFileIcon(ext: string | null): React.JSX.Element;
153
+ renderFolder(item: FolderOrFileItem, expanded?: boolean): JSX.Element | null;
154
+ renderBackFolder(): JSX.Element;
155
+ formatSize(size: number | null | undefined): JSX.Element;
156
+ formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined): JSX.Element;
157
+ getFileIcon(ext: string | null): JSX.Element;
158
158
  static getEditFile(ext: string | null): boolean;
159
159
  setStateBackgroundImage: () => void;
160
160
  getStyleBackgroundImage: () => React.CSSProperties | null;
161
- renderFile(item: FolderOrFileItem): React.JSX.Element;
162
- renderItems(folderId: string): React.JSX.Element | (React.JSX.Element | null)[];
163
- renderToolbar(): React.JSX.Element;
161
+ renderFile(item: FolderOrFileItem): JSX.Element;
162
+ renderItems(folderId: string): JSX.Element | (JSX.Element | null)[];
163
+ renderToolbar(): JSX.Element;
164
164
  findItem(id: string, folders?: Folders | null): null | FolderOrFileItem;
165
- renderInputDialog(): React.JSX.Element | null;
165
+ renderInputDialog(): JSX.Element | null;
166
166
  componentDidUpdate(): void;
167
167
  findFirstFolder(id: string): string | null;
168
168
  uploadFile(fileName: string, data: string): Promise<void>;
169
- renderUpload(): React.JSX.Element[] | null;
169
+ renderUpload(): JSX.Element[] | null;
170
170
  deleteRecursive(id: string): Promise<void>;
171
171
  deleteItem(deleteItem: string): void;
172
- renderDeleteDialog(): React.JSX.Element | null;
173
- renderViewDialog(): React.JSX.Element | null;
174
- renderError(): React.JSX.Element | null;
172
+ renderDeleteDialog(): JSX.Element | null;
173
+ renderViewDialog(): JSX.Element | null;
174
+ renderError(): JSX.Element | null;
175
175
  updateItemsAcl(info: FolderOrFileItem[]): void;
176
176
  changeToPath(): void;
177
- renderBreadcrumb(): React.JSX.Element;
178
- renderPath(): React.JSX.Element;
179
- render(): React.JSX.Element;
177
+ renderBreadcrumb(): JSX.Element;
178
+ renderPath(): JSX.Element;
179
+ render(): JSX.Element;
180
180
  }
181
- declare const _default: (props: Record<string, any>) => React.JSX.Element;
181
+ declare const _default: (props: Record<string, any>) => JSX.Element;
182
182
  export default _default;
@@ -63,9 +63,6 @@ const FILE_TYPE_ICONS = {
63
63
  video: icons_material_1.Videocam,
64
64
  };
65
65
  const styles = {
66
- dialog: (theme) => ({
67
- height: `calc(100% - ${theme.mixins.toolbar.minHeight}px)`,
68
- }),
69
66
  root: {
70
67
  width: '100%',
71
68
  overflow: 'hidden',
@@ -107,7 +104,8 @@ const styles = {
107
104
  textAlign: 'center',
108
105
  opacity: 0.1,
109
106
  transition: 'opacity 1s',
110
- margin: 4,
107
+ margin: '4px',
108
+ borderRadius: '4px',
111
109
  '&:hover': {
112
110
  background: theme.palette.secondary.light,
113
111
  color: Utils_1.default.invertColor(theme.palette.secondary.main, true),
@@ -228,8 +226,8 @@ const styles = {
228
226
  opacity: 0.4,
229
227
  },
230
228
  itemFolderIconTable: (theme) => ({
231
- marginTop: 1,
232
- marginLeft: 8,
229
+ marginTop: '1px',
230
+ marginLeft: '8px',
233
231
  display: 'inline-block',
234
232
  width: 30,
235
233
  height: 30,
@@ -362,6 +360,9 @@ const styles = {
362
360
  background: theme.palette.primary.main,
363
361
  },
364
362
  }),
363
+ pathDivBreadcrumbSelected: {
364
+ // todo: add style
365
+ },
365
366
  backgroundImageLight: {
366
367
  background: 'white',
367
368
  },
@@ -838,7 +839,19 @@ class FileBrowserClass extends react_1.Component {
838
839
  const adapter = parts.shift();
839
840
  const relPath = parts.join('/');
840
841
  // make all requests here serial
841
- const files = await this.readDirSerial(adapter || '', relPath);
842
+ let files;
843
+ try {
844
+ files = await this.readDirSerial(adapter || '', relPath);
845
+ }
846
+ catch (error) {
847
+ // work around: 0_userdata.0 is a special folder, that should exist event when other folders and itself do not exit, as the browser shows it anyway.
848
+ if (error === 'Not exists' && adapter === '0_userdata.0') {
849
+ files = [];
850
+ }
851
+ else {
852
+ throw error;
853
+ }
854
+ }
842
855
  try {
843
856
  const _folders = [];
844
857
  files.forEach(file => {
@@ -922,9 +935,7 @@ class FileBrowserClass extends react_1.Component {
922
935
  }
923
936
  }
924
937
  changeFolder(e, folder) {
925
- if (e) {
926
- e.stopPropagation();
927
- }
938
+ e === null || e === void 0 ? void 0 : e.stopPropagation();
928
939
  this.lastSelect = Date.now();
929
940
  let _folder = folder || getParentDir(this.state.currentDir);
930
941
  if (_folder === '/') {
@@ -932,8 +943,10 @@ class FileBrowserClass extends react_1.Component {
932
943
  }
933
944
  this.localStorage.setItem('files.currentDir', _folder);
934
945
  if (folder && e && (e.altKey || e.shiftKey || e.ctrlKey || e.metaKey)) {
935
- return this.setState({ selected: _folder });
946
+ this.setState({ selected: _folder });
947
+ return;
936
948
  }
949
+ // If desired folder is not yet loaded
937
950
  if (_folder && !this.state.folders[_folder]) {
938
951
  this.browseFolder(_folder)
939
952
  .then(folders => this.setState({
@@ -946,7 +959,7 @@ class FileBrowserClass extends react_1.Component {
946
959
  .catch(_e => console.error(`Cannot read folder: ${_e.message}`));
947
960
  return;
948
961
  }
949
- return this.setState({
962
+ this.setState({
950
963
  currentDir: _folder,
951
964
  selected: _folder,
952
965
  path: _folder,
@@ -997,10 +1010,9 @@ class FileBrowserClass extends react_1.Component {
997
1010
  const padding = this.state.viewType === TABLE ? item.level * this.levelPadding : 0;
998
1011
  const isUserData = item.name === USER_DATA;
999
1012
  const isSpecialData = isUserData || item.name === 'vis.0' || item.name === 'vis-2.0';
1013
+ const iconStyle = Utils_1.default.getStyle(this.props.theme, styles[`itemFolderIcon${this.state.viewType}`], isSpecialData && styles.specialFolder);
1000
1014
  return (react_1.default.createElement(material_1.Box, { component: "div", key: item.id, id: item.id, style: this.state.viewType === TABLE ? { marginLeft: padding, width: `calc(100% - ${padding}px` } : {}, onClick: e => (this.state.viewType === TABLE ? this.select(item.id, e) : this.changeFolder(e, item.id)), onDoubleClick: e => this.state.viewType === TABLE && this.toggleFolder(item, e), title: this.getText(item.title), className: "browserItem", sx: Utils_1.default.getStyle(this.props.theme, styles[`item${this.state.viewType}`], styles[`itemFolder${this.state.viewType}`], this.state.selected === item.id ? styles.itemSelected : {}, item.temp ? styles.itemFolderTemp : {}) },
1001
- react_1.default.createElement(IconEl, { style: Utils_1.default.getStyle(this.props.theme, styles[`itemFolderIcon${this.state.viewType}`], isSpecialData && styles.specialFolder), onClick: this.state.viewType === TABLE
1002
- ? (e) => this.toggleFolder(item, e)
1003
- : undefined }),
1015
+ react_1.default.createElement(IconEl, { style: iconStyle, onClick: this.state.viewType === TABLE ? (e) => this.toggleFolder(item, e) : undefined }),
1004
1016
  react_1.default.createElement(material_1.Box, { component: "div", sx: Utils_1.default.getStyle(this.props.theme, styles[`itemName${this.state.viewType}`], styles[`itemNameFolder${this.state.viewType}`]) }, isUserData ? this.props.t('ra_User files') : item.name),
1005
1017
  react_1.default.createElement(material_1.Box, { component: "div", style: styles[`itemSize${this.state.viewType}`], sx: { display: { md: 'inline-block', sm: 'none' } } }, this.state.viewType === TABLE && this.state.folders[item.id]
1006
1018
  ? this.state.folders[item.id].length
@@ -1024,7 +1036,7 @@ class FileBrowserClass extends react_1.Component {
1024
1036
  }
1025
1037
  renderBackFolder() {
1026
1038
  return (react_1.default.createElement(material_1.Box, { component: "div", key: this.state.currentDir, id: this.state.currentDir, onClick: e => this.changeFolder(e), title: this.props.t('ra_Back to %s', getParentDir(this.state.currentDir)), className: "browserItem", sx: Utils_1.default.getStyle(this.props.theme, styles[`item${this.state.viewType}`], styles[`itemFolder${this.state.viewType}`]) },
1027
- react_1.default.createElement(IconClosed_1.default, { style: styles[`itemFolderIcon${this.state.viewType}`] }),
1039
+ react_1.default.createElement(IconClosed_1.default, { style: Utils_1.default.getStyle(this.props.theme, styles[`itemFolderIcon${this.state.viewType}`]) }),
1028
1040
  react_1.default.createElement(icons_material_1.ArrowBack, { sx: styles.itemFolderIconBack }),
1029
1041
  react_1.default.createElement(material_1.Box, { component: "div", sx: Utils_1.default.getStyle(this.props.theme, styles[`itemName${this.state.viewType}`], styles[`itemNameFolder${this.state.viewType}`]) }, "..")));
1030
1042
  }
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import { type JSX } from 'react';
2
2
  export declare const EXTENSIONS: {
3
3
  images: string[];
4
4
  code: string[];
@@ -6,5 +6,5 @@ export declare const EXTENSIONS: {
6
6
  audio: string[];
7
7
  video: string[];
8
8
  };
9
- declare const _default: (props: Record<string, any>) => React.JSX.Element;
9
+ declare const _default: (props: Record<string, any>) => JSX.Element;
10
10
  export default _default;
@@ -571,6 +571,9 @@ export declare class ObjectBrowserClass extends Component<ObjectBrowserProps, Ob
571
571
  navigateKeyPress(event: React.KeyboardEvent): void;
572
572
  /**
573
573
  * Find the id from the root
574
+ *
575
+ * @param root The current root
576
+ * @param id The object id to find
574
577
  */
575
578
  private static getItemFromRoot;
576
579
  resizerReset: () => void;
@@ -871,10 +871,13 @@ function binarySearch(list, find, _start, _end) {
871
871
  return false;
872
872
  }
873
873
  function getName(name, lang) {
874
- if (name && typeof name === 'object') {
874
+ if (typeof name === 'object') {
875
+ if (!name) {
876
+ return '';
877
+ }
875
878
  return (name[lang] || name.en || '').toString();
876
879
  }
877
- return (name || '').toString();
880
+ return name ? name.toString() : '';
878
881
  }
879
882
  function getSelectIdIconFromObjects(objects, id, lang, imagePrefix) {
880
883
  // `admin` has prefix '.' and `web` has '../..'
@@ -3021,13 +3024,17 @@ class ObjectBrowserClass extends react_1.Component {
3021
3024
  }
3022
3025
  parseObjectForAdmins(columnsForAdmin, obj) {
3023
3026
  if (obj.common && obj.common.adminColumns && obj.common.name) {
3024
- let columns = obj.common.adminColumns;
3027
+ const columns = obj.common.adminColumns;
3028
+ let aColumns;
3025
3029
  if (columns && typeof columns !== 'object') {
3026
- columns = [columns];
3030
+ aColumns = [columns];
3031
+ }
3032
+ else if (columns) {
3033
+ aColumns = columns;
3027
3034
  }
3028
3035
  let cColumns;
3029
3036
  if (columns) {
3030
- cColumns = columns
3037
+ cColumns = aColumns
3031
3038
  .map((_item) => {
3032
3039
  if (typeof _item !== 'object') {
3033
3040
  return { path: _item, name: _item.split('.').pop() };
@@ -3063,7 +3070,7 @@ class ObjectBrowserClass extends react_1.Component {
3063
3070
  objTypes: item.objTypes,
3064
3071
  };
3065
3072
  })
3066
- .filter(item => item);
3073
+ .filter((item) => item);
3067
3074
  }
3068
3075
  else {
3069
3076
  cColumns = null;
@@ -5282,21 +5289,29 @@ class ObjectBrowserClass extends react_1.Component {
5282
5289
  }
5283
5290
  /**
5284
5291
  * Find the id from the root
5292
+ *
5293
+ * @param root The current root
5294
+ * @param id The object id to find
5285
5295
  */
5286
- static getItemFromRoot(
5287
- /** The current root */
5288
- root,
5289
- /** the object id to find */
5290
- id) {
5291
- var _b;
5296
+ static getItemFromRoot(root, id) {
5292
5297
  const idArr = id.split('.');
5293
5298
  let currId = '';
5294
5299
  let _root = root;
5295
- for (const idEntry of idArr) {
5300
+ for (let i = 0; i < idArr.length; i++) {
5301
+ const idEntry = idArr[i];
5296
5302
  currId = currId ? `${currId}.${idEntry}` : idEntry;
5297
- _root = (_b = _root.children) === null || _b === void 0 ? void 0 : _b.find(item => item.data.id === currId);
5298
- if (!_root) {
5299
- break;
5303
+ let found = false;
5304
+ if (_root.children) {
5305
+ for (let j = 0; j < _root.children.length; j++) {
5306
+ if (_root.children[j].data.id === currId) {
5307
+ _root = _root.children[j];
5308
+ found = true;
5309
+ break;
5310
+ }
5311
+ }
5312
+ }
5313
+ if (!found) {
5314
+ return null;
5300
5315
  }
5301
5316
  }
5302
5317
  return _root || null;
@@ -5662,7 +5677,6 @@ class ObjectBrowserClass extends react_1.Component {
5662
5677
  visibility: !!(!this.props.notEditable &&
5663
5678
  this.props.objectBrowserAliasEditor &&
5664
5679
  this.props.objectBrowserEditObject &&
5665
- this.state.filter.expertMode &&
5666
5680
  (obj === null || obj === void 0 ? void 0 : obj.type) === 'state' &&
5667
5681
  // @ts-expect-error deprecated from js-controller 6
5668
5682
  ((_e = obj.common) === null || _e === void 0 ? void 0 : _e.type) !== 'file'),
@@ -5765,7 +5779,7 @@ class ObjectBrowserClass extends react_1.Component {
5765
5779
  react_1.default.createElement(material_1.ListItemText, null,
5766
5780
  ITEMS[key].label,
5767
5781
  "..."),
5768
- react_1.default.createElement(material_1.ListItemSecondaryAction, null,
5782
+ react_1.default.createElement("div", { style: Object.assign(Object.assign({}, styles.contextMenuKeys), { opacity: 1 }) },
5769
5783
  react_1.default.createElement(icons_material_1.ArrowRight, null))));
5770
5784
  if (((_b = this.state.showContextMenu) === null || _b === void 0 ? void 0 : _b.subItem) === key) {
5771
5785
  items.push(react_1.default.createElement(material_1.Menu, { key: "subContextMenu", open: !0, anchorEl: this.state.showContextMenu.subAnchor, onClose: () => {
@@ -47,7 +47,7 @@ declare class Schedule extends Component<ScheduleProps, ScheduleState> {
47
47
  getTimeExactElements(): JSX.Element;
48
48
  static getDivider(): JSX.Element;
49
49
  getPeriodModes(): JSX.Element[];
50
- getPeriodSettingsMinutes(): JSX.Element;
50
+ getPeriodSettingsMinutes(fromTo: boolean): JSX.Element;
51
51
  getPeriodSettingsWeekdays(): JSX.Element[];
52
52
  getPeriodSettingsDaily(): JSX.Element[] | null;
53
53
  getPeriodSettingsWeekly(): JSX.Element[] | null;
@@ -526,7 +526,7 @@ class Schedule extends react_1.Component {
526
526
  desc.push(i18n_1.default.t('sch_desc_everyMinute'));
527
527
  }
528
528
  else {
529
- // every N minutes
529
+ // every N minute
530
530
  desc.push(i18n_1.default.t('sch_desc_everyNMinutes', schedule.time.interval.toString()));
531
531
  }
532
532
  }
@@ -535,7 +535,7 @@ class Schedule extends react_1.Component {
535
535
  desc.push(i18n_1.default.t('sch_desc_everyHour'));
536
536
  }
537
537
  else {
538
- // every N minutes
538
+ // every N minute
539
539
  desc.push(i18n_1.default.t('sch_desc_everyNHours', schedule.time.interval.toString()));
540
540
  }
541
541
  const start = ASTRO.indexOf(schedule.time.start) !== -1
@@ -648,7 +648,7 @@ class Schedule extends react_1.Component {
648
648
  _schedule.time.end = 'sunrise';
649
649
  this.onChange(_schedule);
650
650
  } }), label: i18n_1.default.t('sch_astroNight') })))),
651
- !schedule.time.exactTime && this.getPeriodSettingsMinutes())));
651
+ !schedule.time.exactTime && this.getPeriodSettingsMinutes(fromTo))));
652
652
  }
653
653
  getTimeExactElements() {
654
654
  const isAstro = ASTRO.includes(this.state.schedule.time.start);
@@ -868,9 +868,9 @@ class Schedule extends react_1.Component {
868
868
  MONTHS.map((month, i) => (react_1.default.createElement(material_1.MenuItem, { key: month, value: i + 1 }, i18n_1.default.t(month))))))))),
869
869
  ];
870
870
  }
871
- getPeriodSettingsMinutes() {
872
- return (react_1.default.createElement("div", { style: { display: 'inline-block' } },
873
- react_1.default.createElement("label", null, i18n_1.default.t('sch_every')),
871
+ getPeriodSettingsMinutes(fromTo) {
872
+ return (react_1.default.createElement("div", { style: { display: 'inline-block', marginTop: fromTo ? 15 : 'inherit' } },
873
+ react_1.default.createElement("label", { style: { marginLeft: 4, marginRight: 4 } }, i18n_1.default.t('sch_every')),
874
874
  react_1.default.createElement(material_1.Input, { value: this.state.schedule.time.interval, style: Object.assign(Object.assign({}, styles.inputEvery), { verticalAlign: 'bottom' }), type: "number", inputProps: { min: 1 }, onChange: e => {
875
875
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
876
876
  _schedule.time.interval = parseInt(e.target.value, 10);
@@ -681,7 +681,9 @@ class SimpleCron extends react_1.default.Component {
681
681
  return (react_1.default.createElement("div", { style: styles.mainDiv },
682
682
  react_1.default.createElement("div", { style: { paddingLeft: 8, width: 'calc(100% - 8px)' } },
683
683
  react_1.default.createElement(material_1.TextField, { variant: "standard", style: { width: '100%' }, value: this.state.cron, disabled: true, error: this.state.cron.includes('_') })),
684
- react_1.default.createElement("div", { style: { paddingLeft: 8, width: 'calc(100% - 8px)', height: 60 } }, this.state.cron.includes('_') ? i18n_1.default.t('sc_invalid_cron') : (0, cronText_1.default)(this.state.cron, this.props.language || 'en')),
684
+ react_1.default.createElement("div", { style: { paddingLeft: 8, width: 'calc(100% - 8px)', height: 60 } }, this.state.cron.includes('_')
685
+ ? i18n_1.default.t('sc_invalid_cron')
686
+ : (0, cronText_1.default)(this.state.cron, this.props.language || 'en')),
685
687
  react_1.default.createElement("div", null,
686
688
  react_1.default.createElement(material_1.FormControl, { variant: "standard", style: Object.assign(Object.assign({}, styles.formControl), { marginLeft: 8, marginTop: 8 }) },
687
689
  react_1.default.createElement(material_1.InputLabel, null, i18n_1.default.t('ra_Repeat')),
package/README.md CHANGED
@@ -13,7 +13,7 @@ If you want to create the configuration page with ReactJS:
13
13
  - Change `name` from `src` to `ADAPTERNAME-admin` (Of course replace `ADAPTERNAME` with yours)
14
14
  - Add to devDependencies:
15
15
  ```
16
- "@iobroker/adapter-react-v5": "^7.2.3",
16
+ "@iobroker/adapter-react-v5": "^7.2.4",
17
17
  ```
18
18
  Versions can be higher.
19
19
  So your `src/package.json` should look like:
@@ -24,7 +24,7 @@ If you want to create the configuration page with ReactJS:
24
24
  "version": "0.1.0",
25
25
  "private": true,
26
26
  "dependencies": {
27
- "@iobroker/adapter-react-v5": "^7.2.3",
27
+ "@iobroker/adapter-react-v5": "^7.2.4",
28
28
  "@iobroker/build-tools": "^1.0.0",
29
29
  "@iobroker/eslint-config": "^0.1.2",
30
30
  "@mui/material": "^6.0.2",
@@ -697,12 +697,12 @@ You can find the migration instructions:
697
697
  -->
698
698
 
699
699
  ## Changelog
700
- ### 7.2.3 (2024-10-05)
700
+ ### 7.2.4 (2024-10-07)
701
701
 
702
- - (@GermanBluefox) Corrected error in the simple CRON dialog
702
+ - (@GermanBluefox) Corrected file browser
703
+ - (@GermanBluefox) Changed Open/Close Folder icons
703
704
 
704
705
  ### 7.2.2 (2024-10-04)
705
-
706
706
  - (@GermanBluefox) Small layout change for Icon Picker
707
707
 
708
708
  ### 7.2.1 (2024-09-30)
package/i18n/de.json CHANGED
@@ -115,8 +115,8 @@
115
115
  "sc_to": "Bis",
116
116
  "sc_wizard": "Wizard",
117
117
  "sch_all": "alle",
118
- "sch_astroDay": "Astronomisch Tag",
119
- "sch_astroNight": "Astronomisch Nacht",
118
+ "sch_astroDay": "Astronomischer Tag",
119
+ "sch_astroNight": "Astronomische Nacht",
120
120
  "sch_astro_dawn": "Morgendämmerung",
121
121
  "sch_astro_dusk": "Abenddämmerung",
122
122
  "sch_astro_goldenHour": "Goldene Stunde",
@@ -446,4 +446,4 @@
446
446
  "ra_Friday": "Freitag",
447
447
  "ra_Saturday": "Samstag",
448
448
  "sc_invalid_cron": "Ungültiger CRON"
449
- }
449
+ }
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  // Icon copied from https://github.com/FortAwesome/Font-Awesome/blob/0d1f27efb836eb2ab994ba37221849ed64a73e5c/svgs/regular/
8
8
  const IconClosed = (props) => (react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", onClick: e => props.onClick && props.onClick(e), viewBox: "0 0 650 512", width: props.width || (props.fontSize === 'small' ? 16 : 20), height: props.height || props.width || (props.fontSize === 'small' ? 16 : 20), className: props.className, style: props.style },
9
- react_1.default.createElement("path", { fill: "currentColor", d: "M464 128H272l-64-64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V176c0-26.51-21.49-48-48-48z" })));
9
+ react_1.default.createElement("path", { fill: "currentColor", d: "m524,128l-192,0l-64,-64l-160,0c-26.51,0 -48,21.49 -48,48l0,288c0,26.51 21.49,48 48,48l416,0c26.51,0 48,-21.49 48,-48l0,-224c0,-26.51 -21.49,-48 -48,-48z" })));
10
10
  exports.default = IconClosed;
package/icons/IconOpen.js CHANGED
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  // Icon copied from https://github.com/FortAwesome/Font-Awesome/blob/0d1f27efb836eb2ab994ba37221849ed64a73e5c/svgs/regular/
8
8
  const IconOpen = (props) => (react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", onClick: e => props.onClick && props.onClick(e), viewBox: "0 0 650 512", width: props.width || (props.fontSize === 'small' ? 16 : 20), height: props.height || props.width || (props.fontSize === 'small' ? 16 : 20), className: props.className, style: props.style },
9
- react_1.default.createElement("path", { fill: "currentColor", d: "M572.694 292.093L500.27 416.248A63.997 63.997 0 0 1 444.989 448H45.025c-18.523 0-30.064-20.093-20.731-36.093l72.424-124.155A64 64 0 0 1 152 256h399.964c18.523 0 30.064 20.093 20.73 36.093zM152 224h328v-48c0-26.51-21.49-48-48-48H272l-64-64H48C21.49 64 0 85.49 0 112v278.046l69.077-118.418C86.214 242.25 117.989 224 152 224z" })));
9
+ react_1.default.createElement("path", { fill: "currentColor", d: "m631.75617,292.093l-72.424,124.155a63.997,63.997 0 0 1 -55.281,31.752l-399.964,0c-18.523,0 -30.064,-20.093 -20.731,-36.093l72.424,-124.155a64,64 0 0 1 55.282,-31.752l399.964,0c18.523,0 30.064,20.093 20.73,36.093zm-420.694,-68.093l328,0l0,-48c0,-26.51 -21.49,-48 -48,-48l-160,0l-64,-64l-160,0c-26.51,0 -48,21.49 -48,48l0,278.046l69.077,-118.418c17.137,-29.378 48.912,-47.628 82.923,-47.628z" })));
10
10
  exports.default = IconOpen;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iobroker/adapter-react-v5",
3
- "version": "7.2.3",
3
+ "version": "7.2.4",
4
4
  "description": "React classes to develop admin interfaces for ioBroker with react.",
5
5
  "author": {
6
6
  "name": "Denis Haev (bluefox)",
@@ -27,21 +27,22 @@
27
27
  "homepage": "https://github.com/ioBroker/adapter-react-v5#readme",
28
28
  "devDependencies": {},
29
29
  "dependencies": {
30
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
30
31
  "@emotion/react": "^11.13.3",
31
32
  "@emotion/styled": "^11.13.0",
32
- "@iobroker/socket-client": "^3.1.0",
33
+ "@iobroker/socket-client": "^3.1.1",
33
34
  "@iobroker/types": "^6.0.11",
34
35
  "@iobroker/js-controller-common": "^6.0.11",
35
36
  "@iobroker/js-controller-common-db": "^6.0.11",
36
- "@mui/icons-material": "^6.1.1",
37
- "@mui/material": "^6.1.1",
38
- "@mui/x-date-pickers": "^7.18.0",
39
- "@sentry/browser": "^8.32.0",
37
+ "@mui/icons-material": "^6.1.2",
38
+ "@mui/material": "^6.1.2",
39
+ "@mui/x-date-pickers": "^7.19.0",
40
+ "@sentry/browser": "^8.33.1",
40
41
  "cronstrue": "^2.50.0",
41
42
  "react-color": "^2.19.3",
42
43
  "react-colorful": "^5.6.1",
43
44
  "react-cropper": "^2.3.3",
44
- "react-dropzone": "^14.2.3",
45
+ "react-dropzone": "^14.2.9",
45
46
  "react-icons": "^5.3.0",
46
47
  "react-inlinesvg": "^4.1.3"
47
48
  }
@@ -4,7 +4,7 @@
4
4
  * MIT License
5
5
  *
6
6
  */
7
- import React, { Component } from 'react';
7
+ import React, { Component, type JSX } from 'react';
8
8
  import Dropzone from 'react-dropzone';
9
9
 
10
10
  import {
@@ -102,9 +102,6 @@ const FILE_TYPE_ICONS: Record<string, React.FC<{ fontSize?: 'small' }>> = {
102
102
  };
103
103
 
104
104
  const styles: Record<string, any> = {
105
- dialog: (theme: IobTheme) => ({
106
- height: `calc(100% - ${theme.mixins.toolbar.minHeight}px)`,
107
- }),
108
105
  root: {
109
106
  width: '100%',
110
107
  overflow: 'hidden',
@@ -147,7 +144,8 @@ const styles: Record<string, any> = {
147
144
  textAlign: 'center',
148
145
  opacity: 0.1,
149
146
  transition: 'opacity 1s',
150
- margin: 4,
147
+ margin: '4px',
148
+ borderRadius: '4px',
151
149
  '&:hover': {
152
150
  background: theme.palette.secondary.light,
153
151
  color: Utils.invertColor(theme.palette.secondary.main, true),
@@ -270,8 +268,8 @@ const styles: Record<string, any> = {
270
268
  opacity: 0.4,
271
269
  },
272
270
  itemFolderIconTable: (theme: IobTheme) => ({
273
- marginTop: 1,
274
- marginLeft: 8,
271
+ marginTop: '1px',
272
+ marginLeft: '8px',
275
273
  display: 'inline-block',
276
274
  width: 30,
277
275
  height: 30,
@@ -406,6 +404,9 @@ const styles: Record<string, any> = {
406
404
  background: theme.palette.primary.main,
407
405
  },
408
406
  }),
407
+ pathDivBreadcrumbSelected: {
408
+ // todo: add style
409
+ },
409
410
  backgroundImageLight: {
410
411
  background: 'white',
411
412
  },
@@ -500,7 +501,7 @@ export interface FileBrowserProps {
500
501
 
501
502
  restrictToFolder?: string;
502
503
 
503
- modalEditOfAccessControl?: (obj: FileBrowserClass) => React.JSX.Element | null;
504
+ modalEditOfAccessControl?: (obj: FileBrowserClass) => JSX.Element | null;
504
505
 
505
506
  allowNonRestricted?: boolean;
506
507
 
@@ -1026,7 +1027,17 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1026
1027
  const relPath = parts.join('/');
1027
1028
 
1028
1029
  // make all requests here serial
1029
- const files = await this.readDirSerial(adapter || '', relPath);
1030
+ let files: ioBroker.ReadDirResult[];
1031
+ try {
1032
+ files = await this.readDirSerial(adapter || '', relPath);
1033
+ } catch (error: unknown) {
1034
+ // work around: 0_userdata.0 is a special folder, that should exist event when other folders and itself do not exit, as the browser shows it anyway.
1035
+ if (error === 'Not exists' && adapter === '0_userdata.0') {
1036
+ files = [];
1037
+ } else {
1038
+ throw error;
1039
+ }
1040
+ }
1030
1041
  try {
1031
1042
  const _folders: FolderOrFileItem[] = [];
1032
1043
 
@@ -1090,7 +1101,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1090
1101
  return newFoldersNotNull;
1091
1102
  }
1092
1103
 
1093
- toggleFolder(item: FolderOrFileItem, e: React.MouseEvent<Element>): void {
1104
+ toggleFolder(item: FolderOrFileItem, e: React.MouseEvent): void {
1094
1105
  e?.stopPropagation();
1095
1106
  const expanded = [...this.state.expanded];
1096
1107
  const pos = expanded.indexOf(item.id);
@@ -1142,9 +1153,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1142
1153
  };
1143
1154
 
1144
1155
  changeFolder(e: React.MouseEvent<HTMLDivElement>, folder?: string): void {
1145
- if (e) {
1146
- e.stopPropagation();
1147
- }
1156
+ e?.stopPropagation();
1148
1157
 
1149
1158
  this.lastSelect = Date.now();
1150
1159
 
@@ -1157,9 +1166,11 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1157
1166
  this.localStorage.setItem('files.currentDir', _folder);
1158
1167
 
1159
1168
  if (folder && e && (e.altKey || e.shiftKey || e.ctrlKey || e.metaKey)) {
1160
- return this.setState({ selected: _folder });
1169
+ this.setState({ selected: _folder });
1170
+ return;
1161
1171
  }
1162
1172
 
1173
+ // If desired folder is not yet loaded
1163
1174
  if (_folder && !this.state.folders[_folder]) {
1164
1175
  this.browseFolder(_folder)
1165
1176
  .then(folders =>
@@ -1178,7 +1189,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1178
1189
  return;
1179
1190
  }
1180
1191
 
1181
- return this.setState(
1192
+ this.setState(
1182
1193
  {
1183
1194
  currentDir: _folder,
1184
1195
  selected: _folder,
@@ -1226,7 +1237,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1226
1237
  return undefined;
1227
1238
  }
1228
1239
 
1229
- renderFolder(item: FolderOrFileItem, expanded?: boolean): React.JSX.Element | null {
1240
+ renderFolder(item: FolderOrFileItem, expanded?: boolean): JSX.Element | null {
1230
1241
  if (
1231
1242
  this.state.viewType === TABLE &&
1232
1243
  this.state.filterEmpty &&
@@ -1241,6 +1252,11 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1241
1252
  const isUserData = item.name === USER_DATA;
1242
1253
  const isSpecialData = isUserData || item.name === 'vis.0' || item.name === 'vis-2.0';
1243
1254
 
1255
+ const iconStyle = Utils.getStyle(
1256
+ this.props.theme,
1257
+ styles[`itemFolderIcon${this.state.viewType}`],
1258
+ isSpecialData && styles.specialFolder,
1259
+ );
1244
1260
  return (
1245
1261
  <Box
1246
1262
  component="div"
@@ -1260,15 +1276,9 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1260
1276
  )}
1261
1277
  >
1262
1278
  <IconEl
1263
- style={Utils.getStyle(
1264
- this.props.theme,
1265
- styles[`itemFolderIcon${this.state.viewType}`],
1266
- isSpecialData && styles.specialFolder,
1267
- )}
1279
+ style={iconStyle}
1268
1280
  onClick={
1269
- this.state.viewType === TABLE
1270
- ? (e: React.MouseEvent<Element>) => this.toggleFolder(item, e)
1271
- : undefined
1281
+ this.state.viewType === TABLE ? (e: React.MouseEvent) => this.toggleFolder(item, e) : undefined
1272
1282
  }
1273
1283
  />
1274
1284
 
@@ -1340,7 +1350,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1340
1350
  );
1341
1351
  }
1342
1352
 
1343
- renderBackFolder(): React.JSX.Element {
1353
+ renderBackFolder(): JSX.Element {
1344
1354
  return (
1345
1355
  <Box
1346
1356
  component="div"
@@ -1355,7 +1365,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1355
1365
  styles[`itemFolder${this.state.viewType}`],
1356
1366
  )}
1357
1367
  >
1358
- <IconClosed style={styles[`itemFolderIcon${this.state.viewType}`]} />
1368
+ <IconClosed style={Utils.getStyle(this.props.theme, styles[`itemFolderIcon${this.state.viewType}`])} />
1359
1369
  <IconBack sx={styles.itemFolderIconBack} />
1360
1370
 
1361
1371
  <Box
@@ -1372,7 +1382,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1372
1382
  );
1373
1383
  }
1374
1384
 
1375
- formatSize(size: number | null | undefined): React.JSX.Element {
1385
+ formatSize(size: number | null | undefined): JSX.Element {
1376
1386
  return (
1377
1387
  <div style={styles[`itemSize${this.state.viewType}`]}>
1378
1388
  {size || size === 0 ? Utils.formatBytes(size) : ''}
@@ -1380,7 +1390,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1380
1390
  );
1381
1391
  }
1382
1392
 
1383
- formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined): React.JSX.Element {
1393
+ formatAcl(acl: ioBroker.EvaluatedFileACL | MetaACL | undefined): JSX.Element {
1384
1394
  const access: number = acl ? (acl as ioBroker.EvaluatedFileACL).permissions || (acl as MetaACL).file : 0;
1385
1395
  let accessStr: string;
1386
1396
  if (access) {
@@ -1406,7 +1416,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1406
1416
  );
1407
1417
  }
1408
1418
 
1409
- getFileIcon(ext: string | null): React.JSX.Element {
1419
+ getFileIcon(ext: string | null): JSX.Element {
1410
1420
  switch (ext) {
1411
1421
  case 'json':
1412
1422
  case 'json5':
@@ -1483,7 +1493,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1483
1493
  }
1484
1494
  };
1485
1495
 
1486
- renderFile(item: FolderOrFileItem): React.JSX.Element {
1496
+ renderFile(item: FolderOrFileItem): JSX.Element {
1487
1497
  const padding = this.state.viewType === TABLE ? item.level * this.levelPadding : 0;
1488
1498
  const ext = Utils.getFileExtension(item.name);
1489
1499
 
@@ -1639,11 +1649,11 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1639
1649
  );
1640
1650
  }
1641
1651
 
1642
- renderItems(folderId: string): React.JSX.Element | (React.JSX.Element | null)[] {
1652
+ renderItems(folderId: string): JSX.Element | (JSX.Element | null)[] {
1643
1653
  if (this.state.folders && this.state.folders[folderId]) {
1644
1654
  // tile
1645
1655
  if (this.state.viewType === TILE) {
1646
- const res: (React.JSX.Element | null)[] = [];
1656
+ const res: (JSX.Element | null)[] = [];
1647
1657
  if (folderId && folderId !== '/') {
1648
1658
  res.push(this.renderBackFolder());
1649
1659
  }
@@ -1662,7 +1672,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1662
1672
  return res;
1663
1673
  }
1664
1674
 
1665
- const totalResult: (React.JSX.Element | null)[] = [];
1675
+ const totalResult: (JSX.Element | null)[] = [];
1666
1676
  this.state.folders[folderId].forEach(item => {
1667
1677
  if (item.folder) {
1668
1678
  const expanded = this.state.expanded.includes(item.id);
@@ -1716,7 +1726,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1716
1726
  );
1717
1727
  }
1718
1728
 
1719
- renderToolbar(): React.JSX.Element {
1729
+ renderToolbar(): JSX.Element {
1720
1730
  const IconType: React.FC<{ fontSize?: 'small' }> | null = this.props.showTypeSelector
1721
1731
  ? FILE_TYPE_ICONS[this.state.filterByType || 'all'] || FILE_TYPE_ICONS.all
1722
1732
  : null;
@@ -1965,7 +1975,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
1965
1975
  return folders[parentFolder].find(item => item.id === id) || null;
1966
1976
  }
1967
1977
 
1968
- renderInputDialog(): React.JSX.Element | null {
1978
+ renderInputDialog(): JSX.Element | null {
1969
1979
  if (this.state.addFolder) {
1970
1980
  const parentFolder = this.findFirstFolder(this.state.selected);
1971
1981
 
@@ -2074,7 +2084,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2074
2084
  }
2075
2085
  }
2076
2086
 
2077
- renderUpload(): React.JSX.Element[] | null {
2087
+ renderUpload(): JSX.Element[] | null {
2078
2088
  if (this.state.uploadFile) {
2079
2089
  return [
2080
2090
  <Fab
@@ -2274,7 +2284,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2274
2284
  );
2275
2285
  }
2276
2286
 
2277
- renderDeleteDialog(): React.JSX.Element | null {
2287
+ renderDeleteDialog(): JSX.Element | null {
2278
2288
  if (this.state.deleteItem) {
2279
2289
  return (
2280
2290
  <Dialog
@@ -2322,7 +2332,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2322
2332
  return null;
2323
2333
  }
2324
2334
 
2325
- renderViewDialog(): React.JSX.Element | null {
2335
+ renderViewDialog(): JSX.Element | null {
2326
2336
  return this.state.viewer ? (
2327
2337
  <FileViewer
2328
2338
  supportSubscribes={this.supportSubscribes}
@@ -2341,7 +2351,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2341
2351
  ) : null;
2342
2352
  }
2343
2353
 
2344
- renderError(): React.JSX.Element | null {
2354
+ renderError(): JSX.Element | null {
2345
2355
  if (this.state.errorText) {
2346
2356
  return (
2347
2357
  <ErrorDialog
@@ -2415,7 +2425,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2415
2425
  }, 100);
2416
2426
  }
2417
2427
 
2418
- renderBreadcrumb(): React.JSX.Element {
2428
+ renderBreadcrumb(): JSX.Element {
2419
2429
  const parts = this.state.currentDir.startsWith('/')
2420
2430
  ? this.state.currentDir.split('/')
2421
2431
  : `/${this.state.currentDir}`.split('/');
@@ -2454,7 +2464,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2454
2464
  );
2455
2465
  }
2456
2466
 
2457
- renderPath(): React.JSX.Element {
2467
+ renderPath(): JSX.Element {
2458
2468
  return (
2459
2469
  <Box
2460
2470
  component="div"
@@ -2490,7 +2500,7 @@ export class FileBrowserClass extends Component<FileBrowserProps, FileBrowserSta
2490
2500
  );
2491
2501
  }
2492
2502
 
2493
- render(): React.JSX.Element {
2503
+ render(): JSX.Element {
2494
2504
  if (!this.props.ready) {
2495
2505
  return <LinearProgress />;
2496
2506
  }
@@ -1,6 +1,6 @@
1
1
  // File viewer in adapter-react does not support write
2
2
  // import { Buffer } from 'buffer';
3
- import React, { Component } from 'react';
3
+ import React, { Component, type JSX } from 'react';
4
4
 
5
5
  import { TextField, Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton } from '@mui/material';
6
6
 
@@ -338,7 +338,7 @@ class FileViewer extends Component<FileViewerProps, FileViewerState> {
338
338
  return null;
339
339
  }
340
340
 
341
- render(): React.JSX.Element {
341
+ render(): JSX.Element {
342
342
  return (
343
343
  <Dialog
344
344
  sx={{
@@ -28,7 +28,6 @@ import {
28
28
  ListItem,
29
29
  ListItemButton,
30
30
  ListItemIcon,
31
- ListItemSecondaryAction,
32
31
  ListItemText,
33
32
  Menu,
34
33
  MenuItem,
@@ -1111,11 +1110,14 @@ function binarySearch(list: string[], find: string, _start?: number, _end?: numb
1111
1110
  }
1112
1111
 
1113
1112
  function getName(name: ioBroker.StringOrTranslated, lang: ioBroker.Languages): string {
1114
- if (name && typeof name === 'object') {
1113
+ if (typeof name === 'object') {
1114
+ if (!name) {
1115
+ return '';
1116
+ }
1115
1117
  return (name[lang] || name.en || '').toString();
1116
1118
  }
1117
1119
 
1118
- return (name || '').toString();
1120
+ return name ? name.toString() : '';
1119
1121
  }
1120
1122
 
1121
1123
  export function getSelectIdIconFromObjects(
@@ -3824,13 +3826,16 @@ export class ObjectBrowserClass extends Component<ObjectBrowserProps, ObjectBrow
3824
3826
  obj: ioBroker.AdapterObject,
3825
3827
  ): Record<string, CustomAdminColumnStored[]> | null {
3826
3828
  if (obj.common && obj.common.adminColumns && obj.common.name) {
3827
- let columns: string | (string | ioBroker.CustomAdminColumn)[] = obj.common.adminColumns;
3829
+ const columns: string | (string | ioBroker.CustomAdminColumn)[] = obj.common.adminColumns;
3830
+ let aColumns: (string | ioBroker.CustomAdminColumn)[] | undefined;
3828
3831
  if (columns && typeof columns !== 'object') {
3829
- columns = [columns];
3832
+ aColumns = [columns];
3833
+ } else if (columns) {
3834
+ aColumns = columns as (string | ioBroker.CustomAdminColumn)[];
3830
3835
  }
3831
3836
  let cColumns: CustomAdminColumnStored[] | null;
3832
3837
  if (columns) {
3833
- cColumns = (columns as (string | ioBroker.CustomAdminColumn)[])
3838
+ cColumns = aColumns
3834
3839
  .map((_item: string | ioBroker.CustomAdminColumn) => {
3835
3840
  if (typeof _item !== 'object') {
3836
3841
  return { path: _item, name: _item.split('.').pop() };
@@ -3866,7 +3871,7 @@ export class ObjectBrowserClass extends Component<ObjectBrowserProps, ObjectBrow
3866
3871
  objTypes: item.objTypes,
3867
3872
  } as CustomAdminColumnStored;
3868
3873
  })
3869
- .filter(item => item) as CustomAdminColumnStored[];
3874
+ .filter((item: CustomAdminColumnStored) => item);
3870
3875
  } else {
3871
3876
  cColumns = null;
3872
3877
  }
@@ -7753,22 +7758,30 @@ export class ObjectBrowserClass extends Component<ObjectBrowserProps, ObjectBrow
7753
7758
 
7754
7759
  /**
7755
7760
  * Find the id from the root
7761
+ *
7762
+ * @param root The current root
7763
+ * @param id The object id to find
7756
7764
  */
7757
- private static getItemFromRoot(
7758
- /** The current root */
7759
- root: TreeItem,
7760
- /** the object id to find */
7761
- id: string,
7762
- ): TreeItem | null {
7765
+ private static getItemFromRoot(root: TreeItem, id: string): TreeItem | null {
7763
7766
  const idArr = id.split('.');
7764
7767
  let currId = '';
7765
7768
  let _root: TreeItem | null | undefined = root;
7766
7769
 
7767
- for (const idEntry of idArr) {
7770
+ for (let i = 0; i < idArr.length; i++) {
7771
+ const idEntry = idArr[i];
7768
7772
  currId = currId ? `${currId}.${idEntry}` : idEntry;
7769
- _root = _root.children?.find(item => item.data.id === currId);
7770
- if (!_root) {
7771
- break;
7773
+ let found = false;
7774
+ if (_root.children) {
7775
+ for (let j = 0; j < _root.children.length; j++) {
7776
+ if (_root.children[j].data.id === currId) {
7777
+ _root = _root.children[j];
7778
+ found = true;
7779
+ break;
7780
+ }
7781
+ }
7782
+ }
7783
+ if (!found) {
7784
+ return null;
7772
7785
  }
7773
7786
  }
7774
7787
 
@@ -8495,7 +8508,6 @@ export class ObjectBrowserClass extends Component<ObjectBrowserProps, ObjectBrow
8495
8508
  !this.props.notEditable &&
8496
8509
  this.props.objectBrowserAliasEditor &&
8497
8510
  this.props.objectBrowserEditObject &&
8498
- this.state.filter.expertMode &&
8499
8511
  obj?.type === 'state' &&
8500
8512
  // @ts-expect-error deprecated from js-controller 6
8501
8513
  obj.common?.type !== 'file'
@@ -8630,9 +8642,9 @@ export class ObjectBrowserClass extends Component<ObjectBrowserProps, ObjectBrow
8630
8642
  {ITEMS[key].label}
8631
8643
  ...
8632
8644
  </ListItemText>
8633
- <ListItemSecondaryAction>
8645
+ <div style={{ ...styles.contextMenuKeys, opacity: 1 }}>
8634
8646
  <ArrowRightIcon />
8635
- </ListItemSecondaryAction>
8647
+ </div>
8636
8648
  </MenuItem>,
8637
8649
  );
8638
8650
 
@@ -10,7 +10,7 @@ import {
10
10
  Select,
11
11
  TextField,
12
12
  Box,
13
- type Theme
13
+ type Theme,
14
14
  } from '@mui/material';
15
15
 
16
16
  import I18n from '../i18n';
@@ -558,14 +558,14 @@ class Schedule extends Component<ScheduleProps, ScheduleState> {
558
558
  // every minute
559
559
  desc.push(I18n.t('sch_desc_everyMinute'));
560
560
  } else {
561
- // every N minutes
561
+ // every N minute
562
562
  desc.push(I18n.t('sch_desc_everyNMinutes', schedule.time.interval.toString()));
563
563
  }
564
564
  } else if (schedule.time.interval === 1) {
565
565
  // every minute
566
566
  desc.push(I18n.t('sch_desc_everyHour'));
567
567
  } else {
568
- // every N minutes
568
+ // every N minute
569
569
  desc.push(I18n.t('sch_desc_everyNHours', schedule.time.interval.toString()));
570
570
  }
571
571
 
@@ -767,7 +767,7 @@ class Schedule extends Component<ScheduleProps, ScheduleState> {
767
767
  </div>
768
768
  )}
769
769
  </div>
770
- {!schedule.time.exactTime && this.getPeriodSettingsMinutes()}
770
+ {!schedule.time.exactTime && this.getPeriodSettingsMinutes(fromTo)}
771
771
  </div>
772
772
  </div>
773
773
  );
@@ -1202,13 +1202,16 @@ class Schedule extends Component<ScheduleProps, ScheduleState> {
1202
1202
  ];
1203
1203
  }
1204
1204
 
1205
- getPeriodSettingsMinutes(): JSX.Element {
1205
+ getPeriodSettingsMinutes(fromTo: boolean): JSX.Element {
1206
1206
  return (
1207
- <div style={{ display: 'inline-block' }}>
1208
- <label>{I18n.t('sch_every')}</label>
1207
+ <div style={{ display: 'inline-block', marginTop: fromTo ? 15 : 'inherit' }}>
1208
+ <label style={{ marginLeft: 4, marginRight: 4 }}>{I18n.t('sch_every')}</label>
1209
1209
  <Input
1210
1210
  value={this.state.schedule.time.interval}
1211
- style={{ ...styles.inputEvery, verticalAlign: 'bottom' }}
1211
+ style={{
1212
+ ...styles.inputEvery,
1213
+ verticalAlign: 'bottom',
1214
+ }}
1212
1215
  type="number"
1213
1216
  inputProps={{ min: 1 }}
1214
1217
  onChange={e => {
@@ -14,7 +14,7 @@ const IconClosed = (props: IconProps): React.JSX.Element => (
14
14
  >
15
15
  <path
16
16
  fill="currentColor"
17
- d="M464 128H272l-64-64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V176c0-26.51-21.49-48-48-48z"
17
+ d="m524,128l-192,0l-64,-64l-160,0c-26.51,0 -48,21.49 -48,48l0,288c0,26.51 21.49,48 48,48l416,0c26.51,0 48,-21.49 48,-48l0,-224c0,-26.51 -21.49,-48 -48,-48z"
18
18
  />
19
19
  </svg>
20
20
  );
@@ -14,7 +14,7 @@ const IconOpen = (props: IconProps): JSX.Element => (
14
14
  >
15
15
  <path
16
16
  fill="currentColor"
17
- d="M572.694 292.093L500.27 416.248A63.997 63.997 0 0 1 444.989 448H45.025c-18.523 0-30.064-20.093-20.731-36.093l72.424-124.155A64 64 0 0 1 152 256h399.964c18.523 0 30.064 20.093 20.73 36.093zM152 224h328v-48c0-26.51-21.49-48-48-48H272l-64-64H48C21.49 64 0 85.49 0 112v278.046l69.077-118.418C86.214 242.25 117.989 224 152 224z"
17
+ d="m631.75617,292.093l-72.424,124.155a63.997,63.997 0 0 1 -55.281,31.752l-399.964,0c-18.523,0 -30.064,-20.093 -20.731,-36.093l72.424,-124.155a64,64 0 0 1 55.282,-31.752l399.964,0c18.523,0 30.064,20.093 20.73,36.093zm-420.694,-68.093l328,0l0,-48c0,-26.51 -21.49,-48 -48,-48l-160,0l-64,-64l-160,0c-26.51,0 -48,21.49 -48,48l0,278.046l69.077,-118.418c17.137,-29.378 48.912,-47.628 82.923,-47.628z"
18
18
  />
19
19
  </svg>
20
20
  );