@iobroker/gui-components 10.2.0 → 10.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +7 -1
  2. package/build/Components/ColorPicker.d.ts +94 -12
  3. package/build/Components/ColorPicker.js +567 -111
  4. package/build/Components/ColorPicker.js.map +1 -1
  5. package/build/Components/ObjectBrowser/dialogs.js +7 -3
  6. package/build/Components/ObjectBrowser/dialogs.js.map +1 -1
  7. package/build/Components/ObjectBrowser/renderLeaf.js +29 -10
  8. package/build/Components/ObjectBrowser/renderLeaf.js.map +1 -1
  9. package/build/Components/Schedule.js +176 -78
  10. package/build/Components/Schedule.js.map +1 -1
  11. package/build/Components/TreeTable.d.ts +6 -2
  12. package/build/Components/TreeTable.js +183 -103
  13. package/build/Components/TreeTable.js.map +1 -1
  14. package/build/Dialogs/Error.js +26 -5
  15. package/build/Dialogs/Error.js.map +1 -1
  16. package/build/i18n/de.json +20 -0
  17. package/build/i18n/en.json +20 -0
  18. package/build/i18n/es.json +20 -0
  19. package/build/i18n/fr.json +20 -0
  20. package/build/i18n/it.json +20 -0
  21. package/build/i18n/nl.json +20 -0
  22. package/build/i18n/pl.json +20 -0
  23. package/build/i18n/pt.json +20 -0
  24. package/build/i18n/ru.json +20 -0
  25. package/build/i18n/uk.json +20 -0
  26. package/build/i18n/zh-cn.json +20 -0
  27. package/build/index.d.ts +2 -1
  28. package/build/index.js +1 -1
  29. package/build/index.js.map +1 -1
  30. package/i18n/de.json +20 -0
  31. package/i18n/en.json +20 -0
  32. package/i18n/es.json +20 -0
  33. package/i18n/fr.json +20 -0
  34. package/i18n/it.json +20 -0
  35. package/i18n/nl.json +20 -0
  36. package/i18n/pl.json +20 -0
  37. package/i18n/pt.json +20 -0
  38. package/i18n/ru.json +20 -0
  39. package/i18n/uk.json +20 -0
  40. package/i18n/zh-cn.json +20 -0
  41. package/package.json +1 -3
@@ -2,27 +2,77 @@ import React, { Component } from 'react';
2
2
  import { Input, Radio, FormControlLabel, FormGroup, Checkbox, MenuItem, Select, TextField, Box, } from '@mui/material';
3
3
  import { I18n } from '../i18n';
4
4
  import { Utils } from './Utils';
5
+ /**
6
+ * Indentation of a group that belongs to the radio button above it. It matches the width of the radio
7
+ * button (24px icon plus the horizontal padding of `inputRadio`), so the group starts under its label.
8
+ */
9
+ const SUB_OPTION_INDENT = 45;
10
+ /** A period row - a quiet card instead of the former full-color background */
11
+ function rowStyle(theme) {
12
+ return {
13
+ mt: '8px',
14
+ p: '6px 10px',
15
+ // the rows are `width: 100%`, so padding and border must not add to that
16
+ boxSizing: 'border-box',
17
+ borderRadius: '8px',
18
+ border: `1px solid ${theme.palette.divider}`,
19
+ // The mode ("Monthly") and its settings used to be inline-blocks, which wrapped underneath each
20
+ // other as soon as the settings got wide - with twelve months they always did. As flex items they
21
+ // stay side by side and only the settings themselves wrap.
22
+ display: 'flex',
23
+ alignItems: 'flex-start',
24
+ gap: '4px',
25
+ transition: 'background-color 0.15s ease-in-out',
26
+ '&:hover': {
27
+ backgroundColor: theme.palette.action.hover,
28
+ },
29
+ };
30
+ }
31
+ /** A sub-option inside a period row */
32
+ function subRowStyle(theme) {
33
+ return {
34
+ ml: '8px',
35
+ pl: '12px',
36
+ pb: '8px',
37
+ boxSizing: 'border-box',
38
+ borderLeft: `2px solid ${theme.palette.primary.main}`,
39
+ borderRadius: '0 8px 8px 0',
40
+ backgroundColor: theme.palette.action.hover,
41
+ // these containers are rendered even when their content is null - without this an empty
42
+ // box with an accent line would be left over next to the unselected period
43
+ '&:empty': {
44
+ display: 'none',
45
+ },
46
+ };
47
+ }
5
48
  const styles = {
6
- hr: {
49
+ hr: (theme) => ({
7
50
  border: 0,
8
- borderTop: '1px solid gray',
9
- },
51
+ borderTop: `1px solid ${theme.palette.divider}`,
52
+ margin: '16px 0',
53
+ }),
54
+ // Grows into whatever height the parent gives and only scrolls when the content really does not fit.
55
+ // It used to reserve a fixed height, which showed a scrollbar even when there was room to spare.
10
56
  scrollWindow: {
11
57
  width: '100%',
12
- overflow: 'auto',
13
- height: 'calc(100% - 22px)',
58
+ flex: '1 1 auto',
59
+ minHeight: 0,
60
+ overflowY: 'auto',
61
+ overflowX: 'hidden',
14
62
  },
15
63
  rowDiv: {
16
64
  width: '100%',
17
65
  },
18
66
  modeDiv: {
19
67
  width: 200,
68
+ flexShrink: 0,
20
69
  display: 'inline-block',
21
70
  verticalAlign: 'top',
22
71
  },
23
72
  settingsDiv: {
24
73
  display: 'inline-block',
25
74
  verticalAlign: 'top',
75
+ minWidth: 0,
26
76
  },
27
77
  inputTime: {
28
78
  width: 90,
@@ -43,70 +93,105 @@ const styles = {
43
93
  padding: '4px 12px',
44
94
  verticalAlign: 'top',
45
95
  },
96
+ // Group of checkboxes (weekdays, months, days of the month). All entries have the same width, so
97
+ // they line up in columns - a CSS grid with `auto-fill` cannot be used here, because none of the
98
+ // surrounding boxes has a definite width and the grid would collapse into a single column.
46
99
  inputGroup: {
47
- maxWidth: 400,
48
- display: 'inline-block',
100
+ // The group belongs to the radio button above it, so it is indented by that button's width -
101
+ // and it fills the rest of the row instead of wrapping into a narrow column while the place
102
+ // next to it stays empty.
103
+ marginLeft: SUB_OPTION_INDENT,
104
+ maxWidth: `calc(100% - ${SUB_OPTION_INDENT}px)`,
105
+ display: 'flex',
106
+ flexWrap: 'wrap',
107
+ columnGap: 8,
49
108
  },
50
109
  inputGroupElement: {
51
110
  width: 120,
111
+ marginRight: 0,
112
+ },
113
+ /** Ends the current line, so the entries start on a line of their own */
114
+ lineBreak: {
115
+ flexBasis: '100%',
116
+ height: 0,
52
117
  },
53
118
  inputDateDay: {
54
119
  width: 60,
120
+ marginRight: 0,
121
+ },
122
+ /** "all"/"none"/"invert" - the same width as an entry, so they line up with the columns below them */
123
+ inputGroupAction: {
124
+ width: 120,
125
+ marginRight: 0,
55
126
  },
56
127
  inputDateDayCheck: {
57
128
  padding: 4,
58
129
  },
59
130
  inputSmallCheck: {
60
131
  padding: 0,
132
+ // without this, the label sticks directly to the checkbox
133
+ marginRight: 6,
61
134
  },
62
- rowOnce: {},
63
- rowDays: (theme) => ({
64
- background: theme.palette.mode !== 'dark' ? '#ddeaff' : '#4b5057',
65
- }),
66
- rowDows: (theme) => ({
67
- background: theme.palette.mode !== 'dark' ? '#DDFFDD' : '#52646c',
68
- }),
69
- rowDates: (theme) => ({
70
- background: theme.palette.mode !== 'dark' ? '#DDDDFF' : '#747a86',
71
- }),
72
- rowWeeks: (theme) => ({
73
- background: theme.palette.mode !== 'dark' ? '#DDDDFF' : '#717680',
74
- }),
75
- rowMonths: (theme) => ({
76
- background: theme.palette.mode !== 'dark' ? '#DDFFFF' : '#1f5557',
77
- }),
78
- rowMonthsDates: (theme) => ({
79
- background: theme.palette.mode !== 'dark' ? '#EEFFFF' : '#3c5737',
135
+ // One period option (once, daily, weekly, monthly, yearly). They used to be told apart by a full color
136
+ // per row, which made the dialog look like a patchwork. Now they are quiet cards and the hierarchy
137
+ // comes from the frame and the indentation instead.
138
+ rowOnce: (theme) => rowStyle(theme),
139
+ rowDays: (theme) => rowStyle(theme),
140
+ rowDows: (theme) => rowStyle(theme),
141
+ rowDates: (theme) => rowStyle(theme),
142
+ rowWeeks: (theme) => rowStyle(theme),
143
+ rowMonths: (theme) => rowStyle(theme),
144
+ rowYears: (theme) => rowStyle(theme),
145
+ // Sub-options of a period - indented and marked with an accent line on the left
146
+ // "Dates" belongs to the same level as "Every month"/"Specific months", so it stays flush with them
147
+ // instead of sitting in an indented box below the month grid.
148
+ rowMonthsDates: {
149
+ display: 'block',
150
+ mt: '6px',
80
151
  maxWidth: 600,
152
+ },
153
+ rowDaysDows: (theme) => subRowStyle(theme),
154
+ rowDowsDows: (theme) => subRowStyle(theme),
155
+ sectionTitle: (theme) => ({
156
+ m: '16px 0 8px 0',
157
+ fontSize: '0.72rem',
158
+ fontWeight: 600,
159
+ textTransform: 'uppercase',
160
+ letterSpacing: '0.06em',
161
+ color: theme.palette.text.secondary,
81
162
  }),
82
- rowYears: (theme) => ({
83
- background: theme.palette.mode !== 'dark' ? '#fbffdd' : '#574b33',
84
- }),
85
- rowDaysDows: (theme) => ({
86
- background: theme.palette.mode !== 'dark' ? '#EEEAFF' : '#573544',
87
- pl: '10px',
88
- pb: '10px',
89
- }),
90
- rowDowsDows: (theme) => ({
91
- background: theme.palette.mode !== 'dark' ? '#EEFFEE' : '#3d4c54',
92
- pl: '10px',
93
- pb: '10px',
163
+ description: (theme) => ({
164
+ fontSize: '0.95rem',
165
+ fontWeight: 500,
166
+ color: theme.palette.primary.main,
167
+ minHeight: 22,
94
168
  }),
95
169
  };
96
- const WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
170
+ // Translation keys, not display names - they used to be the plain English words, which have no entry in the
171
+ // dictionary, so every weekday and month was shown in English no matter which language was selected.
172
+ const WEEKDAYS = [
173
+ 'ra_Sunday',
174
+ 'ra_Monday',
175
+ 'ra_Tuesday',
176
+ 'ra_Wednesday',
177
+ 'ra_Thursday',
178
+ 'ra_Friday',
179
+ 'ra_Saturday',
180
+ 'ra_Sunday',
181
+ ];
97
182
  const MONTHS = [
98
- 'January',
99
- 'February',
100
- 'March',
101
- 'April',
102
- 'May',
103
- 'June',
104
- 'July',
105
- 'August',
106
- 'September',
107
- 'October',
108
- 'November',
109
- 'December',
183
+ 'ra_January',
184
+ 'ra_February',
185
+ 'ra_March',
186
+ 'ra_April',
187
+ 'ra_May',
188
+ 'ra_June',
189
+ 'ra_July',
190
+ 'ra_August',
191
+ 'ra_September',
192
+ 'ra_October',
193
+ 'ra_November',
194
+ 'ra_December',
110
195
  ];
111
196
  const PERIODS = {
112
197
  minutes: 'minutes',
@@ -681,7 +766,7 @@ export class Schedule extends Component {
681
766
  }, margin: "normal" })))));
682
767
  }
683
768
  static getDivider() {
684
- return React.createElement("hr", { style: styles.hr });
769
+ return (React.createElement(Box, { component: "hr", sx: styles.hr }));
685
770
  }
686
771
  getPeriodModes() {
687
772
  const schedule = this.state.schedule;
@@ -695,7 +780,7 @@ export class Schedule extends Component {
695
780
  schedule.period.once = Schedule.now2string(true);
696
781
  }
697
782
  return [
698
- React.createElement("div", { key: "once", style: { ...styles.rowDiv, ...styles.rowOnce } },
783
+ React.createElement(Box, { component: "div", key: "once", sx: Utils.getStyle(this.props.theme, styles.rowDiv, styles.rowOnce) },
699
784
  React.createElement("div", { style: styles.modeDiv },
700
785
  React.createElement(FormControlLabel, { control: React.createElement(Radio, { style: styles.inputRadio, checked: !!isOnce, onClick: () => {
701
786
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
@@ -792,19 +877,26 @@ export class Schedule extends Component {
792
877
  React.createElement(Box, { component: "div", sx: Utils.getStyle(this.props.theme, styles.settingsDiv, styles.rowMonthsDates) },
793
878
  React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { style: styles.inputRadio, checked: !!schedule.period.dates, onClick: () => {
794
879
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
795
- _schedule.period.months ||= 1;
796
- const dates = [];
797
- for (let i = 1; i <= 31; i++) {
798
- dates.push(i);
880
+ if (_schedule.period.dates) {
881
+ // `dates` was only ever assigned with `||=`, so the
882
+ // checkbox could be switched on but never off again
883
+ _schedule.period.dates = '';
884
+ }
885
+ else {
886
+ _schedule.period.months ||= 1;
887
+ const dates = [];
888
+ for (let i = 1; i <= 31; i++) {
889
+ dates.push(i);
890
+ }
891
+ _schedule.period.dates = JSON.stringify(dates);
892
+ _schedule.period.dows = '';
893
+ _schedule.period.years = 0;
894
+ _schedule.period.yearDate = 0;
895
+ _schedule.period.yearMonth = 0;
896
+ _schedule.period.weeks = 0;
897
+ _schedule.period.days = 0;
898
+ _schedule.period.once = '';
799
899
  }
800
- _schedule.period.dates ||= JSON.stringify(dates);
801
- _schedule.period.dows = '';
802
- _schedule.period.years = 0;
803
- _schedule.period.yearDate = 0;
804
- _schedule.period.yearMonth = 0;
805
- _schedule.period.weeks = 0;
806
- _schedule.period.days = 0;
807
- _schedule.period.once = '';
808
900
  this.onChange(_schedule);
809
901
  } }), label: I18n.t('sch_periodDates') })),
810
902
  React.createElement(Box, { component: "div", sx: Utils.getStyle(this.props.theme, styles.settingsDiv, styles.rowMonthsDates) }, this.getPeriodSettingsDates()))) : null)),
@@ -898,7 +990,7 @@ export class Schedule extends Component {
898
990
  }
899
991
  this.onChange(_schedule);
900
992
  } }), label: I18n.t('sch_periodWeekdays') }),
901
- isSpecific && (schedule.period.days === 1 || schedule.period.weeks) && (React.createElement(FormGroup, { row: true, style: { ...styles.inputGroup, width: 150 } }, [1, 2, 3, 4, 5, 6, 0].map(i => (React.createElement(FormControlLabel, { key: `specific_${i}`, style: styles.inputGroupElement, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: schedule.period.dows.includes(i.toString()), onChange: e => {
993
+ isSpecific && (schedule.period.days === 1 || schedule.period.weeks) && (React.createElement(FormGroup, { row: true, style: styles.inputGroup }, [1, 2, 3, 4, 5, 6, 0].map(i => (React.createElement(FormControlLabel, { key: `specific_${i}`, style: styles.inputGroupElement, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: schedule.period.dows.includes(i.toString()), onChange: e => {
902
994
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
903
995
  let daysOfWeek;
904
996
  try {
@@ -992,8 +1084,8 @@ export class Schedule extends Component {
992
1084
  dates.push(i);
993
1085
  }
994
1086
  const parsedDates = JSON.parse(schedule.period.dates);
995
- return (React.createElement(FormGroup, { row: true, style: { ...styles.inputGroup, maxWidth: 620 } },
996
- React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: parsedDates.length === 31, onChange: () => {
1087
+ return (React.createElement(FormGroup, { row: true, style: styles.inputGroup },
1088
+ React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: parsedDates.length === 31, onChange: () => {
997
1089
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
998
1090
  const _dates = [];
999
1091
  for (let i = 1; i <= 31; i++) {
@@ -1002,12 +1094,12 @@ export class Schedule extends Component {
1002
1094
  _schedule.period.dates = JSON.stringify(_dates);
1003
1095
  this.onChange(_schedule);
1004
1096
  } }), label: I18n.t('sch_all') }),
1005
- React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: !parsedDates.length, onChange: () => {
1097
+ React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: !parsedDates.length, onChange: () => {
1006
1098
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
1007
1099
  _schedule.period.dates = '[]';
1008
1100
  this.onChange(_schedule);
1009
1101
  } }), label: I18n.t('sch_no_one') }),
1010
- parsedDates.length !== 31 && !!parsedDates.length && (React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: false, onChange: () => {
1102
+ parsedDates.length !== 31 && !!parsedDates.length && (React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: false, onChange: () => {
1011
1103
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
1012
1104
  const result = [];
1013
1105
  const _parsedDates = JSON.parse(_schedule.period.dates);
@@ -1020,7 +1112,7 @@ export class Schedule extends Component {
1020
1112
  _schedule.period.dates = JSON.stringify(result);
1021
1113
  this.onChange(_schedule);
1022
1114
  } }), label: I18n.t('sch_invert') })),
1023
- React.createElement("div", null),
1115
+ React.createElement(Box, { component: "div", sx: styles.lineBreak }),
1024
1116
  dates.map(i => (React.createElement(FormControlLabel, { key: `date_${i}`, style: !i
1025
1117
  ? {
1026
1118
  ...styles.inputDateDay,
@@ -1092,7 +1184,7 @@ export class Schedule extends Component {
1092
1184
  this.onChange(_schedule);
1093
1185
  } }), label: I18n.t('sch_periodSpecificMonths') }),
1094
1186
  typeof schedule.period.months === 'string' && (React.createElement(FormGroup, { row: true, style: styles.inputGroup },
1095
- React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: parsedMonths.length === 12, onChange: () => {
1187
+ React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: parsedMonths.length === 12, onChange: () => {
1096
1188
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
1097
1189
  const months = [];
1098
1190
  for (let i = 1; i <= 12; i++) {
@@ -1101,12 +1193,12 @@ export class Schedule extends Component {
1101
1193
  _schedule.period.months = JSON.stringify(months);
1102
1194
  this.onChange(_schedule);
1103
1195
  } }), label: I18n.t('sch_all') }),
1104
- React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: !parsedMonths.length, onChange: () => {
1196
+ React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: !parsedMonths.length, onChange: () => {
1105
1197
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
1106
1198
  _schedule.period.months = '[]';
1107
1199
  this.onChange(_schedule);
1108
1200
  } }), label: I18n.t('sch_no_one') }),
1109
- parsedMonths.length !== 12 && !!parsedMonths.length && (React.createElement(FormControlLabel, { style: styles.inputDateDay, control: React.createElement(Checkbox, { style: styles.inputDateDayCheck, checked: false, onChange: () => {
1201
+ parsedMonths.length !== 12 && !!parsedMonths.length && (React.createElement(FormControlLabel, { style: styles.inputGroupAction, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: false, onChange: () => {
1110
1202
  const _schedule = JSON.parse(JSON.stringify(this.state.schedule));
1111
1203
  const result = [];
1112
1204
  const _parsedMonths = JSON.parse(_schedule.period.months);
@@ -1119,7 +1211,7 @@ export class Schedule extends Component {
1119
1211
  _schedule.period.months = JSON.stringify(result);
1120
1212
  this.onChange(_schedule);
1121
1213
  } }), label: I18n.t('sch_invert') })),
1122
- React.createElement("div", null),
1214
+ React.createElement(Box, { component: "div", sx: styles.lineBreak }),
1123
1215
  MONTHS.map((month, i) => (React.createElement(FormControlLabel, { key: `month_${i}`, style: styles.inputGroupElement, control: React.createElement(Checkbox, { style: styles.inputSmallCheck, checked: typeof schedule.period.months === 'string'
1124
1216
  ? JSON.parse(schedule.period.months).includes(i + 1)
1125
1217
  : schedule.period.months === i, onChange: e => {
@@ -1260,14 +1352,20 @@ export class Schedule extends Component {
1260
1352
  }, margin: "normal" })))));
1261
1353
  }
1262
1354
  render() {
1263
- return (React.createElement("div", { style: { height: 'calc(100% - 48px)', width: '100%', overflow: 'hidden' } },
1264
- React.createElement("div", null, this.state.desc),
1355
+ return (React.createElement("div", { style: {
1356
+ height: '100%',
1357
+ width: '100%',
1358
+ display: 'flex',
1359
+ flexDirection: 'column',
1360
+ overflow: 'hidden',
1361
+ } },
1362
+ React.createElement(Box, { component: "div", sx: styles.description }, this.state.desc),
1265
1363
  React.createElement("div", { style: styles.scrollWindow },
1266
- React.createElement("h5", null, I18n.t('sch_time')),
1364
+ React.createElement(Box, { component: "h5", sx: styles.sectionTitle }, I18n.t('sch_time')),
1267
1365
  this.getTimePeriodElements(),
1268
1366
  this.getTimeExactElements(),
1269
1367
  Schedule.getDivider(),
1270
- React.createElement("h5", null, I18n.t('sch_period')),
1368
+ React.createElement(Box, { component: "h5", sx: styles.sectionTitle }, I18n.t('sch_period')),
1271
1369
  this.getPeriodModes(),
1272
1370
  !this.state.schedule.period.once && Schedule.getDivider(),
1273
1371
  !this.state.schedule.period.once && this.getValidSettings())));