From c55ea1a7bd181c52108569385627151eedc65023 Mon Sep 17 00:00:00 2001 From: GermanBluefox Date: Tue, 23 Apr 2024 09:06:23 +0800 Subject: [PATCH] Migrate ObjectBrowser to Typescript --- package.json | 2 +- .../admin/src/src/components/FileBrowser.tsx | 2 +- .../src/src/components/ObjectBrowser.tsx | 816 +++++++++--------- .../admin/src/src/dialogs/AdminUpdater.tsx | 1 - 4 files changed, 395 insertions(+), 426 deletions(-) diff --git a/package.json b/package.json index ff6aa195b..7ebfd4d00 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@fnando/sparkline": "^0.3.10", "@honkhonk/vite-plugin-svgr": "^1.1.0", - "@iobroker/adapter-react-v5": "^4.13.10", + "@iobroker/adapter-react-v5": "^4.13.11", "@iobroker/admin-component-easy-access": "^0.3.2", "@iobroker/dm-utils": "^0.1.9", "@iobroker/legacy-testing": "^1.0.11", diff --git a/packages/admin/src/src/components/FileBrowser.tsx b/packages/admin/src/src/components/FileBrowser.tsx index 92a057550..c9506f167 100644 --- a/packages/admin/src/src/components/FileBrowser.tsx +++ b/packages/admin/src/src/components/FileBrowser.tsx @@ -1225,7 +1225,7 @@ class FileBrowser extends Component { this.props.classes[`itemFolderIcon${this.state.viewType}`], isSpecialData && this.props.classes.specialFolder, )} - onClick={this.state.viewType === TABLE ? e => this.toggleFolder(item, e) : undefined} + onClick={this.state.viewType === TABLE ? (e: React.MouseEvent) => this.toggleFolder(item, e) : undefined} />
= (theme: Theme) => ({ headerCellInput: { width: 'calc(100% - 5px)', height: ROW_HEIGHT, - paddingTop: 3, + paddingTop: 0, '& .itemIcon': { verticalAlign: 'middle', width: ICON_SIZE, @@ -1532,22 +1537,8 @@ function getObjectTooltip( data: TreeItemData, lang: ioBroker.Languages, ): string { - if (!data) { - return null; - } - if (data.obj?.common?.desc) { - let tooltip = ''; - - if (typeof data.obj.common.desc === 'object') { - tooltip = data.obj.common.desc[lang] || data.obj.common.desc.en; - } else { - tooltip = data.obj.common.desc; - } - if (!tooltip) { - return null; - } - - return tooltip.toString(); + if (data?.obj?.common?.desc) { + return getName(data.obj.common.desc, lang) || null; } return null; @@ -1926,8 +1917,6 @@ function quality2text(q) { } */ -/** @typedef {{ state: ioBroker.State, obj: Record, texts: Record, dateFormat: any, isFloatComma: boolean }} FormatValueOptions */ - /** * Format a state value for visualization */ @@ -2037,6 +2026,7 @@ function formatValue( t: string; /** value */ v: string; + nbr?: boolean; }[] = [{ t: texts.value, v }]; if (state) { @@ -2114,9 +2104,9 @@ function prepareSparkData( values[i - 1].val = values[i - 1].val || 0; values[i].val = values[i].val || 0; // interpolate - const val = values[i - 1].val + - ((values[i].val - values[i - 1].val) * (time - values[i - 1].ts)) / - (values[i].ts - values[i - 1].ts); + const nm1: number = values[i - 1].val as number; + const n: number = values[i].val as number; + const val = nm1 + ((n - nm1) * (time - values[i - 1].ts)) / (values[i].ts - values[i - 1].ts); v.push(val); } @@ -2332,8 +2322,10 @@ interface ObjectBrowserProps { objectImportExport?: boolean; // optional toolbar button objectEditOfAccessControl?: boolean; // Access Control /** modal add object */ + // eslint-disable-next-line no-use-before-define modalNewObject?: (oBrowser: ObjectBrowser) => React.JSX.Element; /** modal Edit Of Access Control */ + // eslint-disable-next-line no-use-before-define modalEditOfAccessControl: (oBrowser: ObjectBrowser, data: TreeItemData) => React.JSX.Element; onObjectDelete?: (id: string, hasChildren: boolean, objectExists: boolean, childrenCount: number) => void; /** optional filter @@ -2367,7 +2359,7 @@ interface ObjectBrowserProps { /** cache of objects */ objectsWorker?: ObjectsWorker; /** function to filter out all unnecessary objects. It cannot be used together with "types" - * Example for function: `obj => obj.common && obj.common.type === 'boolean'` to show only boolean states + * Example for function: `obj => obj.common?.type === 'boolean'` to show only boolean states * */ filterFunc?: (obj: ioBroker.Object) => boolean; DragWrapper?: Component; @@ -2416,14 +2408,14 @@ interface ObjectBrowserState { showAllExportOptions: boolean; linesEnabled: boolean; showDescription: boolean; - showContextMenu: { item: TreeItem; subItem?: string; subAnchor?: HTMLLiElement } | null; + showContextMenu: { item: TreeItem; subItem?: string; subAnchor?: HTMLLIElement } | null; noStatesByExportImport: boolean; beautifyJsonExport: boolean; excludeSystemRepositoriesFromExport: boolean; excludeTranslations: boolean; updating?: boolean; modalNewObj?: null | { id: string; initialType?: ioBroker.ObjectType; initialStateType?: ioBroker.CommonType }; - error: undefined | any; + error?: undefined | any; modalEditOfAccess?: boolean; modalEditOfAccessObjData?: TreeItemData; updateOpened?: boolean; @@ -2531,6 +2523,8 @@ class ObjectBrowser extends Component { buttons?: number; }; + private changedIds: null | string[] = null; + private contextMenu: null | { item: any; ts: number }; private recordStates: string[] = []; @@ -2613,9 +2607,11 @@ class ObjectBrowser extends Component { } } - let selected = props.selected || ''; - if (typeof selected !== 'object') { - selected = [selected]; + let selected: string[]; + if (!Array.isArray(props.selected)) { + selected = [props.selected || '']; + } else { + selected = props.selected; } selected = selected.map(id => id.replace(/["']/g, '')).filter(id => id); @@ -2889,9 +2885,13 @@ class ObjectBrowser extends Component { } const columnsForAdmin = await this.getAdditionalColumns(); - this.calculateColumnsVisibility(false, null, columnsForAdmin); + this.calculateColumnsVisibility(null, null, columnsForAdmin); - const { info, root } = buildTree(this.objects, this.props); + const { info, root } = buildTree(this.objects, { + imagePrefix: this.props.imagePrefix, + root: this.props.root, + lang: this.props.lang, + }); this.root = root; this.info = info; @@ -3190,11 +3190,7 @@ class ObjectBrowser extends Component { } } - /** - * @private - * @param {boolean} isLast - */ - _renderDefinedList(isLast) { + private _renderDefinedList(isLast: boolean) { const cols = [...this.possibleCols]; cols.unshift('id'); if (this.props.columns && !this.props.columns.includes('buttons')) { @@ -3204,9 +3200,9 @@ class ObjectBrowser extends Component { } } return cols - .filter( - id => (isLast && (id === 'val' || id === 'buttons')) || (!isLast && id !== 'val' && id !== 'buttons') - ) + .filter(id => + (isLast && (id === 'val' || id === 'buttons')) || + (!isLast && id !== 'val' && id !== 'buttons')) .map(id => ( { @@ -3221,7 +3217,7 @@ class ObjectBrowser extends Component { } this.localStorage.setItem( `${this.props.dialogName || 'App'}.columns`, - JSON.stringify(columns) + JSON.stringify(columns), ); this.calculateColumnsVisibility(null, columns); this.setState({ columns }); @@ -3283,7 +3279,7 @@ class ObjectBrowser extends Component { classes={{ root: Utils.clsx( this.props.classes.dialogColumns, - this.props.classes[`transparent_${this.state.columnsDialogTransparent}`] + this.props.classes[`transparent_${this.state.columnsDialogTransparent}`], ), }} > @@ -3297,7 +3293,7 @@ class ObjectBrowser extends Component { onChange={() => { this.localStorage.setItem( `${this.props.dialogName || 'App'}.foldersFirst`, - this.state.foldersFirst ? 'false' : 'true' + this.state.foldersFirst ? 'false' : 'true', ); this.setState({ foldersFirst: !this.state.foldersFirst }); }} @@ -3313,7 +3309,7 @@ class ObjectBrowser extends Component { onChange={() => { this.localStorage.setItem( `${this.props.dialogName || 'App'}.lines`, - this.state.linesEnabled ? 'false' : 'true' + this.state.linesEnabled ? 'false' : 'true', ); this.setState({ linesEnabled: !this.state.linesEnabled }); }} @@ -3329,7 +3325,7 @@ class ObjectBrowser extends Component { onChange={() => { this.localStorage.setItem( `${this.props.dialogName || 'App'}.columnsAuto`, - this.state.columnsAuto ? 'false' : 'true' + this.state.columnsAuto ? 'false' : 'true', ); if (!this.state.columnsAuto) { this.calculateColumnsVisibility(true); @@ -3355,71 +3351,69 @@ class ObjectBrowser extends Component { {this._renderDefinedList(false)} - {this.state.columnsForAdmin && - Object.keys(this.state.columnsForAdmin) - .sort() - .map(adapter => - this.state.columnsForAdmin[adapter].map(column => { - if (!this.state.columnsAuto) { - const columns = [...(this.state.columns || [])]; - const id = `_${adapter}_${column.path}`; - const pos = columns.indexOf(id); - if (pos === -1) { - columns.push(id); - columns.sort(); - } else { - columns.splice(pos, 1); - } - this.calculateColumnsVisibility(null, columns); - this.localStorage.setItem( - `${this.props.dialogName || 'App'}.columns`, - JSON.stringify(columns), - ); - this.setState({ columns }); + {this.state.columnsForAdmin && Object.keys(this.state.columnsForAdmin) + .sort() + .map(adapter => + this.state.columnsForAdmin[adapter].map(column => { + if (!this.state.columnsAuto) { + const columns = [...(this.state.columns || [])]; + const id = `_${adapter}_${column.path}`; + const pos = columns.indexOf(id); + if (pos === -1) { + columns.push(id); + columns.sort(); + } else { + columns.splice(pos, 1); } - }} - key={`${adapter}_${column.name}`} - > - - - - - {/* - - - { - const columnsWidths = JSON.parse(JSON.stringify(this.state.columnsWidths)); - columnsWidths['_' + adapter + '_' + column.path] = e.target.value; - this.localStorage.setItem((this.props.dialogName || 'App') + '.columnsWidths', JSON.stringify(columnsWidths)); - this.calculateColumnsVisibility(null, null, null, columnsWidths); - this.setState({ columnsWidths }); + this.calculateColumnsVisibility(null, columns); + this.localStorage.setItem( + `${this.props.dialogName || 'App'}.columns`, + JSON.stringify(columns), + ); + this.setState({ columns }); + } }} - autoComplete="off" - /> - - - */} - ) - )} + key={`${adapter}_${column.name}`} + > + + + + + {/* + + + { + const columnsWidths = JSON.parse(JSON.stringify(this.state.columnsWidths)); + columnsWidths['_' + adapter + '_' + column.path] = e.target.value; + this.localStorage.setItem((this.props.dialogName || 'App') + '.columnsWidths', JSON.stringify(columnsWidths)); + this.calculateColumnsVisibility(null, null, null, columnsWidths); + this.setState({ columnsWidths }); + }} + autoComplete="off" + /> + + + */} + ))} {this._renderDefinedList(true)} @@ -3449,10 +3443,9 @@ class ObjectBrowser extends Component { return columnsForAdmin; }) - .catch(() => { - // window.alert('Cannot get adapters: ' + e); - // Object browser in Web has no additional columns - }); + // window.alert('Cannot get adapters: ' + e); + // Object browser in Web has no additional columns + .catch(() => null); } /** @@ -3581,7 +3574,7 @@ class ObjectBrowser extends Component { if (cColumns?.length) { columnsForAdmin = columnsForAdmin || {}; columnsForAdmin[obj.common.name] = cColumns.sort((a, b) => - a.path > b.path ? -1 : a.path < b.path ? 1 : 0); + (a.path > b.path ? -1 : a.path < b.path ? 1 : 0)); } } else if (obj.common && obj.common.name && columnsForAdmin && columnsForAdmin[obj.common.name]) { delete columnsForAdmin[obj.common.name]; @@ -3782,28 +3775,27 @@ class ObjectBrowser extends Component { /** * @private - * @param {string} name */ - getFilterInput(name: string) { + getFilterInput(filterName: string) { return )[name] || ''} + id={filterName} + placeholder={this.texts[`filter_${filterName}`]} + defaultValue={(this.state.filter as Record)[filterName] || ''} onChange={() => { this.filterTimer && clearTimeout(this.filterTimer); this.filterTimer = setTimeout(() => this.onFilter(), 400); }} autoComplete="off" /> - {(this.filterRefs[name]?.current?.firstChild as HTMLInputElement).value ?
{ { - (this.filterRefs[name].current.firstChild as HTMLInputElement).value = ''; - this.onFilter(name, ''); + (this.filterRefs[filterName].current.firstChild as HTMLInputElement).value = ''; + this.onFilter(filterName, ''); }} > @@ -4346,11 +4338,12 @@ class ObjectBrowser extends Component { beautify: this.state.beautifyJsonExport, excludeSystemRepositories: this.state.excludeSystemRepositoriesFromExport, excludeTranslations: this.state.excludeTranslations, - })) - } + }))} > {this.props.t('ra_All objects')} - ({Object.keys(this.objects).length}) + ( + {Object.keys(this.objects).length} + ) :
,
{t( - 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").' + 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").', )}
, ]; @@ -4528,7 +4521,7 @@ class ObjectBrowser extends Component {
,
{t( - 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").' + 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").', )}
, ]; @@ -4543,7 +4536,7 @@ class ObjectBrowser extends Component { ,
{t( - 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").' + 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").', )}
, ]; @@ -4565,7 +4558,7 @@ class ObjectBrowser extends Component { ,
{t( - 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").' + 'ra_The experts may create objects everywhere but from second level (e.g. "vis.0" or "javascript.0").', )}
, ]; @@ -4708,7 +4701,7 @@ class ObjectBrowser extends Component { onClick={() => { this.localStorage.setItem( `${this.props.dialogName || 'App'}.desc`, - this.state.showDescription ? 'false' : 'true' + this.state.showDescription ? 'false' : 'true', ); this.setState({ showDescription: !this.state.showDescription }); }} @@ -4718,63 +4711,62 @@ class ObjectBrowser extends Component { - {this.props.objectAddBoolean ? ( - -
- this.setState({ - modalNewObj: { - id: this.state.selected[0] || this.state.selectedNonObject, - }, - })} - size="large" - > - - -
-
- ) : null} - - {this.props.objectImportExport && ( - + {this.props.objectAddBoolean ? +
{ - const input = document.createElement('input'); - input.setAttribute('type', 'file'); - input.setAttribute('id', 'files'); - input.setAttribute('opacity', '0'); - input.addEventListener('change', e => this.handleJsonUpload(e), false); - input.click(); - }} + disabled={!allowObjectCreation} + onClick={() => this.setState({ + modalNewObj: { + id: this.state.selected[0] || this.state.selectedNonObject, + }, + })} size="large" > - + - - )} - {this.props.objectImportExport && - (!!this.state.selected.length || this.state.selectedNonObject) && + : null} + + {this.props.objectImportExport && - this.setState({ showExportDialog: this._getSelectedIdsForExport().length }) - } + onClick={() => { + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + input.setAttribute('id', 'files'); + input.setAttribute('opacity', '0'); + input.addEventListener('change', (e: Event) => this.handleJsonUpload(e), false); + input.click(); + }} size="large" > - + } + {this.props.objectImportExport && + (!!this.state.selected.length || this.state.selectedNonObject) && + + + this.setState({ showExportDialog: this._getSelectedIdsForExport().length })} + size="large" + > + + + }
{!!this.props.objectBrowserEditObject && (
{`${this.props.t('ra_Objects')}: ${Object.keys(this.info.objects).length}, ${this.props.t( - 'ra_States' + 'ra_States', )}: ${ Object.keys(this.info.objects).filter(el => this.info.objects[el].type === 'state').length }`} @@ -4797,8 +4789,8 @@ class ObjectBrowser extends Component { `${this.props.dialogName || 'App'}.objectSelected`, this.state.selected[0], ); - this.props.router && - this.props.router.doNavigate(null, 'custom', this.state.selected[0]); + // @ts-expect-error should work + this.props.router?.doNavigate(null, 'custom', this.state.selected[0]); } this.setState({ customDialog: ids }); } else { @@ -4828,7 +4820,7 @@ class ObjectBrowser extends Component { this.setState({ expanded }); } - private onCopy(e: Event, text: string) { + private onCopy(e: React.MouseEvent, text: string) { e.stopPropagation(); e.preventDefault(); Utils.copyToClipboard(text, null); @@ -4918,7 +4910,7 @@ class ObjectBrowser extends Component { return arrayTooltipText.length ? {arrayTooltipText.map(el => el)} - : ''; + : null; }; renderColumnButtons( @@ -4967,7 +4959,7 @@ class ObjectBrowser extends Component {
: null; } - item.data.aclTooltip = item.data.aclTooltip || this.renderTooltipAccessControl(item.data.obj.acl); + item.data.aclTooltip = item.data.aclTooltip || this.renderTooltipAccessControl(item.data.obj.acl as ioBroker.StateACL); const acl = item.data.obj.acl ? item.data.obj.type === 'state' @@ -5047,29 +5039,30 @@ class ObjectBrowser extends Component { : null, this.props.objectCustomDialog && - this.info.hasSomeCustoms && - item.data.obj.type === 'state' && - item.data.obj.common?.type !== 'file' ? { - this.localStorage.setItem(`${this.props.dialogName || 'App'}.objectSelected`, id); + this.info.hasSomeCustoms && + item.data.obj.type === 'state' && + item.data.obj.common?.type !== 'file' ? { + this.localStorage.setItem(`${this.props.dialogName || 'App'}.objectSelected`, id); - this.pauseSubscribe(true); - this.props.router && this.props.router.doNavigate(null, 'customs', id); - this.setState({ customDialog: [id] }); - }} - > - - : null, + this.pauseSubscribe(true); + // @ts-expect-error should work anyway + this.props.router?.doNavigate(null, 'customs', id); + this.setState({ customDialog: [id] }); + }} + > + + : null, ]; } @@ -5117,7 +5110,8 @@ class ObjectBrowser extends Component { aggregate: 'minmax', }) .then(values => { - const sparks = window.document.getElementsByClassName('sparkline'); + const sparks: HTMLDivElement[] = + window.document.getElementsByClassName('sparkline') as any as HTMLDivElement[]; for (let s = 0; s < sparks.length; s++) { if (sparks[s].dataset.id === id) { @@ -5132,14 +5126,11 @@ class ObjectBrowser extends Component { } } - /** - * @private - * @param {string} id - * @param {any} item - * @param {Record} classes - * @returns {JSX.Element | null} - */ - renderColumnValue(id, item, classes) { + private renderColumnValue( + id: string, + item: TreeItem, + classes: Record, + ): React.JSX.Element | null { const obj = item.data.obj; if (!obj || !this.states) { return null; @@ -5150,8 +5141,9 @@ class ObjectBrowser extends Component { } if (!this.states[id]) { if (obj.type === 'state') { + // we are waiting for state !this.recordStates.includes(id) && this.recordStates.push(id); - this.states[id] = { val: null }; + this.states[id] = { val: null } as ioBroker.State; this.subscribe(id); } return null; @@ -5159,39 +5151,38 @@ class ObjectBrowser extends Component { !this.recordStates.includes(id) && this.recordStates.push(id); const state = this.states[id]; + let info = item.data.state; if (!info) { item.data.state = formatValue({ state, - obj, + obj: obj as ioBroker.StateObject, texts: this.texts, dateFormat: this.props.dateFormat, isFloatComma: this.props.isFloatComma, }); info = item.data.state; - info.valFull = info.valFull.map(_item => { + info.valFullRx = []; + info.valFull.forEach(_item => { if (_item.t === this.texts.quality && state.q) { - return [ -
- {_item.t} - :  - {_item.v} -
, - //
{item.v}
, - !_item.nbr ?
: null, - ]; - } - return [ -
+ info.valFullRx.push(
{_item.t} :  -
, -
{_item.v} -
, - !_item.nbr ?
: null, - ]; +
); + //
{item.v}
, + !_item.nbr && info.valFullRx.push(
); + } else { + info.valFullRx.push(
+ {_item.t} + :  +
); + info.valFullRx.push(
+ {_item.v} +
); + !_item.nbr && info.valFullRx.push(
); + } }); if ( @@ -5201,76 +5192,69 @@ class ObjectBrowser extends Component { this.objects[id].common.custom && this.objects[id].common.custom[this.defaultHistory] ) { - info.valFull.push( - - ); + info.valFullRx.push(); } const copyText = info.valText.v || ''; info.val = copyText; - info.valText = [ - - {info.valText.v.toString()} - , - info.valText.u ? ( - - {info.valText.u} - - ) : null, - info.valText.s !== undefined ? ( - - ({info.valText.s}) - - ) : null, - this.onCopy(e, copyText)} - key="cc" - />, - // - ]; + info.valTextRx = []; + info.valTextRx.push( + {info.valText.v.toString()} + ); + info.valText.u && info.valTextRx.push( + {info.valText.u} + ); + info.valText.s !== undefined && info.valTextRx.push( + ( + {info.valText.s} + ) + ); + info.valTextRx.push( this.onCopy(e, copyText)} + key="cc" + />); + // } info.style = getValueStyle({ state, isExpertMode: this.state.filter.expertMode, isButton: item.data.button }); - let val = info.valText; + let val: React.JSX.Element[] = info.valTextRx; if (!this.state.filter.expertMode && item.data.button) { - val = ; + val = []; } - return ( - this.readHistory(id)} - > -
- {val} -
-
- ); + return this.readHistory(id)} + > +
+ {val} +
+
; } private _syncEnum( @@ -5296,7 +5280,8 @@ class ObjectBrowser extends Component { this.props.socket .setObject(enumId, obj) .then(() => (this.info.objects[enumId] = obj)) - .catch(e => this.showError(e))); + .catch(e => this.showError(e)), + ); } } @@ -5311,7 +5296,8 @@ class ObjectBrowser extends Component { this.props.socket .setObject(enumId, obj) .then(() => (this.info.objects[enumId] = obj)) - .catch(e => this.showError(e))); + .catch(e => this.showError(e)), + ); } } @@ -5446,7 +5432,7 @@ class ObjectBrowser extends Component { socket={this.props.socket} t={this.props.t} roles={this.info.roles} - onClose={(obj: ioBroke.Object | null) => { + onClose={(obj: ioBroker.Object | null) => { if (obj) { this.info.objects[this.state.roleDialog] = obj; } @@ -5454,15 +5440,16 @@ class ObjectBrowser extends Component { }} />; } + return null; } private onColumnsEditCustomDialogClose(isSave?: boolean) { if (isSave) { - let value = this.customColumnDialog.value; + let value: string | number | boolean = this.customColumnDialog.value; if (this.customColumnDialog.type === 'boolean') { value = value === 'true' || value === true; } else if (this.customColumnDialog.type === 'number') { - value = parseFloat(value); + value = parseFloat(value as any as string); } this.customColumnDialog = null; this.props.socket @@ -5576,7 +5563,7 @@ class ObjectBrowser extends Component { if (obj?._id?.startsWith(`${it.adapter}.`) && it.path.length > 1) { const p = it.path; let value; - let anyObj: Record = obj as Record; + const anyObj: Record = obj as Record; if (anyObj[p[0]] && typeof anyObj[p[0]] === 'object') { if (p.length === 2) { // most common case @@ -5629,7 +5616,7 @@ class ObjectBrowser extends Component { ): boolean { if (obj?._id?.startsWith(`${it.adapter}.`) && it.path.length > 1) { const p = it.path; - let anyObj: Record = obj as Record; + const anyObj: Record = obj as Record; if (anyObj[p[0]] && typeof anyObj[p[0]] === 'object') { if (p.length === 2) { // most common case @@ -5700,12 +5687,10 @@ class ObjectBrowser extends Component { this.props.classes.columnCustomEditable, this.props.classes[`columnCustom_${it.align}`], )} - onClick={() => - this.setState({ - columnsEditCustomDialog: { item, it, obj }, - customColumnDialogValueChanged: false, - }) - } + onClick={() => this.setState({ + columnsEditCustomDialog: { item, it, obj }, + customColumnDialogValueChanged: false, + })} > {text} ; @@ -5766,7 +5751,8 @@ class ObjectBrowser extends Component { } else { iconItem = ; } } else { @@ -6248,13 +6234,7 @@ class ObjectBrowser extends Component { className={classes.cellValue} style={{ width: this.columnsVisibility.val, - cursor: valueEditable - ? common?.type === 'file' - ? 'zoom-in' - : item.data.button - ? 'grab' - : 'text' - : 'default', + cursor: valueEditable ? (common?.type === 'file' ? 'zoom-in' : (item.data.button ? 'grab' : 'text')) : 'default', }} onClick={valueEditable ? () => { if (!obj || !this.states) { @@ -6291,18 +6271,13 @@ class ObjectBrowser extends Component { /** * Renders an item. - * @param {any} root - * @param {boolean} isExpanded - * @param {Record} classes - * @param {{ count: any; }} [counter] - * @returns {JSX.Element[]} */ renderItem( root: TreeItem, isExpanded: boolean, classes: Record, counter?: { count: number }, - ): (React.JSX.Element | null)[] { + ): React.JSX.Element[] { const items: (React.JSX.Element | null)[] = []; counter = counter || { count: 0 }; let leaf = this.renderLeaf(root, isExpanded, classes, counter); @@ -6320,51 +6295,47 @@ class ObjectBrowser extends Component { ; } } - root.data.id && items.push(leaf); + root.data.id && leaf && items.push(leaf); isExpanded = isExpanded === undefined ? binarySearch(this.state.expanded, root.data.id) : isExpanded; if (!root.data.id || isExpanded) { if (!this.state.foldersFirst) { - root.children && items.push( - root.children.map(item => { + root.children && items.push(root.children.map(item => { + // do not render too many items in column editor mode + if (!this.state.columnsSelectorShow || counter.count < 15) { + if (item.data.sumVisibility) { + return this.renderItem(item, undefined, classes, counter); + } + } + return null; + }) as any as React.JSX.Element); + } else { + // first only folder + root.children && items.push(root.children.map(item => { + if (item.children) { // do not render too many items in column editor mode if (!this.state.columnsSelectorShow || counter.count < 15) { if (item.data.sumVisibility) { return this.renderItem(item, undefined, classes, counter); } } - return null; - })); - } else { - // first only folder - root.children && items.push( - root.children.map(item => { - if (item.children) { - // do not render too many items in column editor mode - if (!this.state.columnsSelectorShow || counter.count < 15) { - if (item.data.sumVisibility) { - return this.renderItem(item, undefined, classes, counter); - } - } - } + } - return null; - })); + return null; + }) as any as React.JSX.Element); // then items - root.children && - items.push( - root.children.map(item => { - if (!item.children) { - // do not render too many items in column editor mode - if (!this.state.columnsSelectorShow || counter.count < 15) { - if (item.data.sumVisibility) { - return this.renderItem(item, undefined, classes, counter); - } - } + root.children && items.push(root.children.map(item => { + if (!item.children) { + // do not render too many items in column editor mode + if (!this.state.columnsSelectorShow || counter.count < 15) { + if (item.data.sumVisibility) { + return this.renderItem(item, undefined, classes, counter); } - return null; - })); + } + } + return null; + }) as any as React.JSX.Element); } } @@ -6372,7 +6343,7 @@ class ObjectBrowser extends Component { } private calculateColumnsVisibility( - aColumnsAuto?: boolean, + aColumnsAuto?: boolean | null, aColumns?: string[] | null, aColumnsForAdmin?: Record | null, aColumnsWidths?: Record, @@ -6545,7 +6516,7 @@ class ObjectBrowser extends Component { } } - resizerMouseMove = (e: React.MouseEvent) => { + resizerMouseMove = (e: MouseEvent): void => { if (this.resizerActiveDiv) { let width: number; let widthNext: number; @@ -6598,16 +6569,14 @@ class ObjectBrowser extends Component { resizerMouseDown = (e: React.MouseEvent) => { if (this.resizerActiveIndex === null || this.resizerActiveIndex === undefined) { - if (!this.storedWidths) { - this.storedWidths = JSON.parse(JSON.stringify(SCREEN_WIDTHS[this.props.width])) as ScreenWidthOne; - } + this.storedWidths = this.storedWidths || JSON.parse(JSON.stringify(SCREEN_WIDTHS[this.props.width])) as ScreenWidthOne; this.resizerCurrentWidths = this.resizerCurrentWidths || {}; - this.resizerActiveDiv = e.target.parentNode; + this.resizerActiveDiv = (e.target as HTMLDivElement).parentNode as HTMLDivElement; this.resizerActiveName = this.resizerActiveDiv.dataset.name; let i = 0; - if (e.target.dataset.left === 'true') { + if ((e.target as HTMLDivElement).dataset.left === 'true') { this.resizeLeft = true; this.resizerNextDiv = this.resizerActiveDiv.previousElementSibling as HTMLDivElement; let handle: HTMLDivElement = this.resizerNextDiv.querySelector(`.${this.props.classes.resizeHandle}`) as HTMLDivElement; @@ -6862,9 +6831,9 @@ class ObjectBrowser extends Component { className={classes.headerCell} style={{ width: this.columnsVisibility.buttons }} > - {' '} - {this.getFilterSelectCustoms()} - : null} + {' '} + {this.getFilterSelectCustoms()} + : null} ; } @@ -6898,9 +6867,9 @@ class ObjectBrowser extends Component { setTimeout(() => this.setState({ scrollBarWidth }), 100); } else if ( !this.selectedFound && - ((this.state.selected && this.state.selected[0]) || this.lastSelectedItems) + (this.state.selected?.[0] || this.lastSelectedItems?.[0]) ) { - this.scrollToItem((this.state.selected && this.state.selected[0]) || this.lastSelectedItems); + this.scrollToItem(this.state.selected?.[0] || this.lastSelectedItems?.[0]); } } } @@ -6924,8 +6893,9 @@ class ObjectBrowser extends Component { if (this.state.customDialog && this.props.objectCustomDialog) { const ObjectCustomDialog = this.props.objectCustomDialog; + // @ts-expect-error How to solve it? return (this.changedIds = [...changedIds])} + reportChangedIds={(changedIds: string[]) => (this.changedIds = [...changedIds])} objectIDs={this.state.customDialog} expertMode={this.state.filter.expertMode} isFloatComma={this.props.isFloatComma} @@ -6946,7 +6916,8 @@ class ObjectBrowser extends Component { this.forceUpdate(); } - this.props.router && this.props.router.doNavigate('tab-objects'); + // @ts-expect-error Should work + this.props.router?.doNavigate('tab-objects'); }} />; } @@ -6990,8 +6961,7 @@ class ObjectBrowser extends Component { .setObject(obj._id, obj) .then(() => this.setState({ editObjectDialog: obj._id, editObjectAlias: false }, () => this.onSelect(obj._id))) - .catch(e => this.showError(`Cannot write object: ${e}`)) - } + .catch(e => this.showError(`Cannot write object: ${e}`))} onClose={(obj: ioBroker.Object | null) => { if (obj) { let updateAlias: string; @@ -7202,7 +7172,8 @@ class ObjectBrowser extends Component { label: this.texts.customConfig, onClick: () => { this.pauseSubscribe(true); - this.props.router && this.props.router.doNavigate(null, 'customs', id); + // @ts-expect-error Should work + this.props.router?.doNavigate(null, 'customs', id); this.setState({ customDialog: [id], showContextMenu: null }); }, }, @@ -7354,7 +7325,11 @@ class ObjectBrowser extends Component { className: this.props.classes.contextMenuDelete, label: this.texts.deleteObject, onClick: () => - this.setState({ showContextMenu: null }, () => this.showDeleteDialog({ id, obj: obj || {}, item })), + this.setState({ showContextMenu: null }, () => this.showDeleteDialog({ + id, + obj: obj || {} as ioBroker.Object, + item, + })), }, }; @@ -7363,11 +7338,11 @@ class ObjectBrowser extends Component { if (ITEMS[key].subMenu) { items.push( this.setState({ + onClick={(e: React.MouseEvent) => this.setState({ showContextMenu: { item: this.state.showContextMenu.item, subItem: key, - subAnchor: e.target, + subAnchor: e.target as HTMLLIElement, }, })} className={ITEMS[key].className} @@ -7393,20 +7368,19 @@ class ObjectBrowser extends Component { this.contextMenu = null; }} > - {ITEMS[key].subMenu.map(subItem => - subItem.visibility ? (subItem.visibility ? + - - {subItem.icon} - - {subItem.label} - : null)} + {subItem.icon} + + {subItem.label} + : null))} ); } } else { @@ -7503,9 +7477,8 @@ class ObjectBrowser extends Component { /** * The rendering method of this component. - * @returns {JSX.Element} */ - render() { + render(): React.JSX.Element { this.recordStates = []; this.unsubscribeTimer && clearTimeout(this.unsubscribeTimer); @@ -7523,7 +7496,7 @@ class ObjectBrowser extends Component { null, counter, this.props.customFilter, - this.props.types + this.props.types, ); if (counter.count < 500 && !this.state.expandAllVisible) { @@ -7546,40 +7519,37 @@ class ObjectBrowser extends Component { const classes = this.props.classes; const items = this.renderItem(this.root, undefined, classes); - return ( - - {this.getToolbar()} - - {this.renderHeader()} - {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */} -
this.navigateKeyPress(event)} - tabIndex={0} - > - {items} -
-
- {this.renderContextMenu()} - {this.renderToast()} - {this.renderColumnsEditCustomDialog()} - {this.renderColumnsSelectorDialog()} - {this.renderCustomDialog()} - {this.renderEditValueDialog()} - {this.renderEditObjectDialog()} - {this.renderViewObjectFileDialog()} - {this.renderAliasEditorDialog()} - {this.renderEditRoleDialog()} - {this.renderEnumDialog()} - {this.renderErrorDialog()} - {this.renderExportDialog()} - {this.state.modalNewObj && this.props.modalNewObject && this.props.modalNewObject(this)} - {this.state.modalEditOfAccess && - this.props.modalEditOfAccessControl && - this.props.modalEditOfAccessControl(this, this.state.modalEditOfAccessObjData)} -
- ); + return + {this.getToolbar()} + + {this.renderHeader()} + {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */} +
this.navigateKeyPress(event)} + > + {items} +
+
+ {this.renderContextMenu()} + {this.renderToast()} + {this.renderColumnsEditCustomDialog()} + {this.renderColumnsSelectorDialog()} + {this.renderCustomDialog()} + {this.renderEditValueDialog()} + {this.renderEditObjectDialog()} + {this.renderViewObjectFileDialog()} + {this.renderAliasEditorDialog()} + {this.renderEditRoleDialog()} + {this.renderEnumDialog()} + {this.renderErrorDialog()} + {this.renderExportDialog()} + {this.state.modalNewObj && this.props.modalNewObject && this.props.modalNewObject(this)} + {this.state.modalEditOfAccess && + this.props.modalEditOfAccessControl && + this.props.modalEditOfAccessControl(this, this.state.modalEditOfAccessObjData)} +
; } } diff --git a/packages/admin/src/src/dialogs/AdminUpdater.tsx b/packages/admin/src/src/dialogs/AdminUpdater.tsx index 429cb5342..c4f6de4d3 100644 --- a/packages/admin/src/src/dialogs/AdminUpdater.tsx +++ b/packages/admin/src/src/dialogs/AdminUpdater.tsx @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { Button,