From 0b91c2fb06377da19d6bd9a29d1d146d6523b5c7 Mon Sep 17 00:00:00 2001 From: GermanBluefox Date: Wed, 13 Mar 2024 09:27:14 +0800 Subject: [PATCH] Removed TS warnings --- src/src/Components/WizardHelpers.jsx | 6 +- src/src/Vis/visContextMenu.tsx | 8 +- src/src/Vis/visFormatUtils.tsx | 22 ++-- src/src/Vis/visLoadWidgets.tsx | 12 +- src/src/Vis/visNavigation.tsx | 2 +- src/src/Vis/visRxWidget.tsx | 10 +- src/src/Vis/visUtils.tsx | 33 +++--- src/src/Vis/visView.tsx | 10 +- src/src/Vis/visWidgetsCatalog.tsx | 171 ++++++++++++++++++++++----- src/src/types.d.ts | 32 +++-- 10 files changed, 211 insertions(+), 95 deletions(-) diff --git a/src/src/Components/WizardHelpers.jsx b/src/src/Components/WizardHelpers.jsx index 1a9078ed..7f1ac22c 100644 --- a/src/src/Components/WizardHelpers.jsx +++ b/src/src/Components/WizardHelpers.jsx @@ -9,7 +9,7 @@ import { Whatshot, Water, Lock, - Window, + Window as WindowIcon, Palette, PlayArrowRounded, Power, @@ -46,8 +46,8 @@ const deviceIcons = { volume: , volumeGroup: , weatherForecast: , - window: , - windowTilt: , + window: , + windowTilt: , }; const allObjects = async socket => { diff --git a/src/src/Vis/visContextMenu.tsx b/src/src/Vis/visContextMenu.tsx index 3875a337..ac89ef74 100644 --- a/src/src/Vis/visContextMenu.tsx +++ b/src/src/Vis/visContextMenu.tsx @@ -58,8 +58,8 @@ interface VisContextMenuProps { interface VisMarketplaceProps { language: ioBroker.Languages; addPage?: boolean; - widget: { name: string; date: string; widget_id: string; image_id: string; }; - installWidget: (widget: { name: string; date: string; widget_id: string; image_id: string; }) => Promise; + widget: { name: string; date: string; widget_id: string; image_id: string }; + installWidget: (widget: { name: string; date: string; widget_id: string; image_id: string }) => Promise; installedWidgets?: {id: string}[]; themeName: string; onAdded?: () => void; @@ -70,8 +70,8 @@ declare global { VisMarketplace?: { api: { apiGetWidgetRevision(widgetId: string, id: string): Promise; - }, - default: React.Component + }; + default: React.Component; }; } } diff --git a/src/src/Vis/visFormatUtils.tsx b/src/src/Vis/visFormatUtils.tsx index 7ebbfe62..1e9a0c91 100644 --- a/src/src/Vis/visFormatUtils.tsx +++ b/src/src/Vis/visFormatUtils.tsx @@ -12,13 +12,14 @@ * Licensees may copy, distribute, display, and perform the work and make derivative works based on it only for noncommercial purposes. * (Free for non-commercial use). */ -import { extractBinding } from './visUtils'; +import { type Moment } from 'moment'; import { deepClone } from '@/Utils/utils'; import { VisLegacy, AnyWidgetId, WidgetData, SingleWidget, GroupWidget, VisRxWidgetStateValues, } from '@/types'; -import { type Moment } from 'moment'; + +import { extractBinding } from './visUtils'; interface VisFormatUtilsProps { vis: VisLegacy; @@ -236,7 +237,7 @@ class VisFormatUtils { if (!dateObj) { return ''; } - let realDateObj: Date | undefined = undefined + let realDateObj: Date | undefined; const type = typeof dateObj; if (type === 'string') { realDateObj = new Date(dateObj); @@ -298,18 +299,17 @@ class VisFormatUtils { return result; } - /** * Format given binding */ formatBinding(options: { - format: string, - view: string, - wid: AnyWidgetId, - widget: SingleWidget | GroupWidget, - widgetData: WidgetData, - values?: VisRxWidgetStateValues, - moment: any, + format: string; + view: string; + wid: AnyWidgetId; + widget: SingleWidget | GroupWidget; + widgetData: WidgetData; + values?: VisRxWidgetStateValues; + moment: any; }): string { const { view, wid, widget, widgetData, moment, diff --git a/src/src/Vis/visLoadWidgets.tsx b/src/src/Vis/visLoadWidgets.tsx index 4ee57024..1c168184 100644 --- a/src/src/Vis/visLoadWidgets.tsx +++ b/src/src/Vis/visLoadWidgets.tsx @@ -122,10 +122,10 @@ function registerWidgetsLoadIndicator(cb: (process: number, max: number) => void interface VisLoadComponentContext { visWidgetsCollection: ioBroker.VisWidget; - countRef: { count: number, max: number }; + countRef: { count: number; max: number }; dynamicWidgetInstance: ioBroker.InstanceObject; - i18nPrefix: string - // List of custom react components + i18nPrefix: string; + // List of custom React components result: VisRxWidget[]; } @@ -138,7 +138,7 @@ function _loadComponentHelper(context: VisLoadComponentContext): Promise // result const promises: Promise[] = []; - for (let i: number = 0; i < context.visWidgetsCollection.components.length; i++) { + for (let i = 0; i < context.visWidgetsCollection.components.length; i++) { ((index: number, _visWidgetsCollection) => { context.countRef.max++; @@ -206,8 +206,8 @@ function getRemoteWidgets(socket: Connection, onlyWidgetSets?: false | string[]) if (visWidgetsCollection.components) { ((collection, instance) => { try { - let i18nPrefix: string = ''; - let i18nPromiseWait: Promise | undefined = undefined; + let i18nPrefix = ''; + let i18nPromiseWait: Promise | undefined; // 1. Load language file ------------------ // instance.common.visWidgets.i18n is deprecated diff --git a/src/src/Vis/visNavigation.tsx b/src/src/Vis/visNavigation.tsx index 4d749b11..f18c5aed 100644 --- a/src/src/Vis/visNavigation.tsx +++ b/src/src/Vis/visNavigation.tsx @@ -20,7 +20,7 @@ import { } from '@mui/icons-material'; import { Utils, Icon } from '@iobroker/adapter-react-v5'; -import { ViewSettings } from "@/types"; +import { ViewSettings } from '@/types'; const MENU_WIDTH_FULL = 200; const MENU_WIDTH_NARROW = 56; diff --git a/src/src/Vis/visRxWidget.tsx b/src/src/Vis/visRxWidget.tsx index 51960090..a937f529 100644 --- a/src/src/Vis/visRxWidget.tsx +++ b/src/src/Vis/visRxWidget.tsx @@ -23,7 +23,7 @@ import { Connection, I18n, Icon } from '@iobroker/adapter-react-v5'; import { Project, AnyWidgetId, RxWidgetInfo, - WidgetData, VisRxWidgetStateValues, RxWidgetInfoAttributes, + WidgetData, VisRxWidgetStateValues, RxWidgetInfoGroup, StateID, RxWidgetInfoAttributesFieldSelectSimple, } from '@/types'; import { deepClone, calculateOverflow } from '@/Utils/utils'; @@ -120,8 +120,6 @@ export interface VisRxWidgetState extends VisBaseWidgetState { class VisRxWidget, TState extends Partial = VisRxWidgetState> extends VisBaseWidget { static POSSIBLE_MUI_STYLES = POSSIBLE_MUI_STYLES; - static i18nPrefix: string | undefined; - private linkContext: { IDs: string[]; bindings: Record; @@ -162,7 +160,7 @@ class VisRxWidget, TState extends Partial = {}; // collect all attributes (only types) if (Array.isArray(options.visAttrs)) { - options.visAttrs.forEach((group: RxWidgetInfoAttributes) => + options.visAttrs.forEach((group: RxWidgetInfoGroup) => group.fields && group.fields.forEach(item => { widgetAttrInfo[item.name] = { type: (item as RxWidgetInfoAttributesFieldSelectSimple).type || '' }; })); @@ -433,10 +431,10 @@ class VisRxWidget, TState extends Partial { if (typeof data[attr] === 'string') { @@ -653,22 +652,13 @@ function getUsedObjectIDsInWidget(views: Project, view: string, wid: AnyWidgetId store.dispatch(updateWidget({ viewId: view, widgetId: wid, data: widget })); } -interface UsedObjectsResult { - IDs: [], - visibility: {}, - bindings: {}, - lastChanges: {}, - signals: {}, - byViews?: Record; -} - -function getUsedObjectIDs(views: Project, isByViews?: boolean): UsedObjectsResult | null { +function getUsedObjectIDs(views: Project, isByViews?: boolean): VisStateUsage | null { if (!views) { console.log('Check why views are not yet loaded!'); return null; } - const linkContext: UsedObjectsResult = { + const linkContext: VisStateUsage = { IDs: [], visibility: {}, bindings: {}, @@ -743,7 +733,7 @@ function getUrlParameter(attr: string): string | true { async function readFile(socket: Connection, id: string, fileName: string, withType?: boolean): Promise { const file = await socket.readFile(id, fileName); let mimeType = ''; - let data: string = ''; + let data = ''; if (typeof file === 'object') { if (withType) { if (file.mimeType) { @@ -812,7 +802,12 @@ function parseDimension(field: string | number | null | undefined): { value: num return result; } -function findWidgetUsages(views: Project, view: string, widgetId: AnyWidgetId, _result: { view: string, wid: AnyWidgetId, attr: string }[]): { view: string, wid: AnyWidgetId, attr: string }[] { +function findWidgetUsages( + views: Project, + view: string, + widgetId: AnyWidgetId, + _result: { view: string; wid: AnyWidgetId; attr: string }[], +): { view: string; wid: AnyWidgetId; attr: string }[] { if (view) { _result = _result || []; // search in specific view @@ -833,7 +828,7 @@ function findWidgetUsages(views: Project, view: string, widgetId: AnyWidgetId, _ } // search in all views - const result: { view: string, wid: AnyWidgetId, attr: string }[] = []; + const result: { view: string; wid: AnyWidgetId; attr: string }[] = []; Object.keys(views).forEach(_view => _view !== '___settings' && findWidgetUsages(views, _view, widgetId, _result)); return result; } diff --git a/src/src/Vis/visView.tsx b/src/src/Vis/visView.tsx index 6ebec272..9c52fb45 100644 --- a/src/src/Vis/visView.tsx +++ b/src/src/Vis/visView.tsx @@ -211,9 +211,9 @@ class VisView extends React.Component { // @ts-expect-error it is a trick if (this.refView.current?._originalParent) { - // @ts-ignore it is a trick + // @ts-expect-error it is a trick this.refView.current._originalParent.appendChild(this.refView.current); - // @ts-ignore it is a trick + // @ts-expect-error it is a trick this.refView.current._originalParent = null; } @@ -413,7 +413,7 @@ class VisView extends React.Component { }; }; - doubleClickOnView = (_e: React.MouseEvent) => { + doubleClickOnView = () => { if (this.props.editMode && this.props.selectedWidgets && this.props.selectedWidgets.length === 1 && @@ -878,7 +878,7 @@ class VisView extends React.Component { if (onCommand) { onCommand('include', _wid); } - }) + }); } } } @@ -1979,7 +1979,7 @@ class VisView extends React.Component { ref={this.refView} id={`visview_${this.props.view.replace(/\s/g, '_')}`} onMouseDown={!this.props.context.runtime ? e => this.props.editMode && this.mouseDownLocal && this.mouseDownLocal(e) : undefined} - onDoubleClick={this.props.context.runtime ? e => this.props.editMode && this.doubleClickOnView && this.doubleClickOnView(e) : undefined} + onDoubleClick={this.props.context.runtime ? () => this.props.editMode && this.doubleClickOnView && this.doubleClickOnView() : undefined} style={style} > diff --git a/src/src/Vis/visWidgetsCatalog.tsx b/src/src/Vis/visWidgetsCatalog.tsx index c2ea12e6..4a04e402 100644 --- a/src/src/Vis/visWidgetsCatalog.tsx +++ b/src/src/Vis/visWidgetsCatalog.tsx @@ -4,12 +4,14 @@ import { type Connection } from '@iobroker/adapter-react-v5'; import { GroupWidgetId, Project, - RxWidgetInfoAttributes, + RxWidgetInfoGroup, SingleWidgetId, CustomPaletteProperties, RxWidgetInfoAttributesField, RxWidgetAttributeType, + WidgetData, + RxWidgetInfoCustomComponentProperties, } from '@/types'; -import type VisRxWidget from '@/Vis/visRxWidget'; +import VisRxWidget from '@/Vis/visRxWidget'; import { getRemoteWidgets } from './visLoadWidgets'; // eslint-disable-next-line import/no-cycle @@ -30,13 +32,110 @@ const DEFAULT_SET_COLORS: Record = { 'spotify-premium': '#00ae03', }; -interface WidgetAttributesGroupInfoStored { - name?: string; +type RxWidgetInfoAttributesFieldAll = { + /** Field type */ + type?: RxWidgetAttributeType; + /** Field default value */ + default?: string | number | boolean; + /** if true, no edit button will be shown. Default is true. */ + readonly noButton?: boolean; + /** if true, the text will not be translated */ + readonly noTranslation?: boolean; + /** this style will be applied to the text */ + readonly style?: React.CSSProperties; + /** show multi-line editor */ + readonly multiline?: boolean; + /** Do not write 'nothing_selected' into the field by creation */ + readonly noInit?: boolean; + /** Do not subscribe on changes of the object */ + readonly noSubscribe?: boolean; + /** Filter of objects (not JSON string, it is an object), like: + - `{common: {custom: true}}` - show only objects with some custom settings + - `{common: {custom: 'sql.0'}}` - show only objects with sql.0 custom settings (only of the specific instance) + - `{common: {custom: '_dataSources'}}` - show only objects of adapters `influxdb' or 'sql' or 'history' + - `{common: {custom: 'adapterName.'}}` - show only objects of the custom settings for specific adapter (all instances) + - `{type: 'channel'}` - show only channels + - `{type: ['channel', 'device']}` - show only channels and devices + - `{common: {type: 'number'}` - show only states of type 'number + - `{common: {type: ['number', 'string']}` - show only states of type 'number and string + - `{common: {role: 'switch']}` - show only states with roles starting from switch + - `{common: {role: ['switch', 'button]}` - show only states with roles starting from `switch` and `button` + */ + filter?: { + readonly common?: { + readonly custom?: true | string | '_dataSources'; + readonly type?: ioBroker.CommonType | ioBroker.CommonType[]; + readonly role?: string | string[]; + }; + readonly type?: ioBroker.ObjectType | ioBroker.ObjectType[]; + } | string; + /** Additionally, you can provide `adapter` to filter the instances of specific adapter. With special adapter name `_dataSources` you can get all adapters with flag `common.getHistory`. */ + readonly adapter?: string; + /** In this case, only instance number (like `0`) is shown and not `history.0`. It can be set to true only with non-empty `adapter` setting. */ + readonly iShort?: boolean; + /** Options for a select type */ + options?: { readonly value: string; label: string }[] | string[]; + /** Number min value */ + min?: number; + /** Number max value */ + max?: number; + /** Number step */ + step?: number; + /** Slider marks?: array of possible marks. Like `[{value: 1, label: 'one'}, {value: 10}, {value: 100}] */ + readonly marks?: { readonly value: number; label: string }[]; + /** Controls when the value label is displayed: `auto` the value label will display when the thumb is hovered or focused. `on` will display persistently. `off` will never display. */ + readonly valueLabelDisplay?: 'on' | 'off' | 'auto'; + /** type of the widget, like `tplMaterial2Switches` */ + readonly tpl?: string; + /** if true, all widgets of all views will be shown, not only from the current view. Default is false. */ + readonly all?: boolean; + /** if true, grouped widgets will be shown too. Default is false. */ + readonly withGroups?: boolean; + /** if true, the current widget will be shown in the list too. */ + readonly withSelf?: boolean; + /** if true, it will be checked if the widget is used somewhere else and user will be asked. */ + readonly checkUsage?: boolean; + /** if true, only widgets will be shown, which are not used in some view. Default is false. */ + readonly hideUsed?: boolean; + /** if false, only one view can be selected. Default is true. */ + readonly multiple?: boolean; + /** if false, only one view can be selected. Default is true. */ + component?: ( + field: RxWidgetInfoAttributesField, + data: WidgetData, + onDataChange: (newData: WidgetData) => void, + props: RxWidgetInfoCustomComponentProperties, + ) => React.JSX.Element | React.JSX.Element[]; + + /** Name of the widget field */ + name: string; + /** Field label (i18n) */ + label?: string; + /** JS Function for conditional visibility */ + hidden?: string | ((data: any) => boolean) | ((data: any, index: number) => boolean); + /** Tooltip (i18n) */ + tooltip?: string; + /** JS Function for conditional disability */ + disabled?: string | ((data: any) => boolean) | ((data: any, index: number) => boolean); + /** JS Function for error */ + error?: string | ((data: any) => boolean) | ((data: any, index: number) => boolean); + /** Do not show binding symbol fot this field */ + readonly noBinding?: boolean; + /** Callback called if the field value changed */ + onChange?: (field: RxWidgetInfoAttributesField, data: Record, changeData: (newData: Record) => void, socket: Connection, index?: number) => Promise | string; +} + +interface WidgetAttributeInfoStored extends RxWidgetInfoAttributesFieldAll { + onChangeFunc?: string; + filterFile?: string; + filterName?: string; + filterAttrs?: string; + removeName?: string; singleName?: string; + set?: string; index?: number; - fields?: RxWidgetInfoAttributesField[]; - indexFrom?: number | string; - indexTo?: number | string; + indexFrom?: string | number; + indexTo?: string | number; iterable?: { group: string; isFirst: boolean; @@ -46,17 +145,14 @@ interface WidgetAttributesGroupInfoStored { }; } -interface WidgetAttributeInfoStored extends RxWidgetInfoAttributesField { - onChangeFunc?: string; - filterFile?: string; - filterName?: string; - filterAttrs?: string; - removeName?: string; +interface WidgetAttributesGroupInfoStored { + name?: string; + label?: string; singleName?: string; - set?: string; index?: number; - indexFrom?: string | number; - indexTo?: string | number; + fields?: RxWidgetInfoAttributesFieldAll[]; + indexFrom?: number | string; + indexTo?: number | string; iterable?: { group: string; isFirst: boolean; @@ -79,7 +175,7 @@ export interface WidgetType { resizable?: boolean; resizeLocked?: boolean; draggable?: boolean; - params: string | RxWidgetInfoAttributes[]; + params: string | readonly RxWidgetInfoGroup[]; setLabel?: string; setColor?: string; @@ -97,8 +193,16 @@ export interface WidgetType { i18nPrefix?: string; } +interface VisRxWidgetLoaded extends VisRxWidget { + readonly i18nPrefix?: string | undefined; + readonly adapter?: string; + readonly version?: string; + readonly visHidden?: boolean; + readonly url?: string; +} + class VisWidgetsCatalog { - static rxWidgets: Record> | null = null; + static rxWidgets: Record | null = null; static allWidgetsList: string[] | null = null; @@ -308,7 +412,8 @@ export const getWidgetTypes: (_usedWidgetSets?: string[]) => WidgetType[] = (use }).filter(w => w); // React widgets - const widgets = Object.values(VisWidgetsCatalog.rxWidgets) as VisRxWidget[]; + // We have here two types of widgets: native (from this repository - function) and loaded via getRemoteWidgets (objects) + const widgets = Object.values(VisWidgetsCatalog.rxWidgets) as VisRxWidgetLoaded[]; widgets.forEach(widget => { const widgetInfo = widget.getWidgetInfo(); const i18nPrefix = widget.i18nPrefix || ''; @@ -350,18 +455,19 @@ export const getWidgetTypes: (_usedWidgetSets?: string[]) => WidgetType[] = (use if (i18nPrefix && typeof widgetInfo.visAttrs === 'object') { widgetInfo.visAttrs.forEach(group => { if (group.label && !group.label.startsWith(i18nPrefix)) { - group.label = i18nPrefix + group.label; + (group as WidgetAttributesGroupInfoStored).label = i18nPrefix + group.label; } if (group.fields) { group.fields.forEach(field => { - if (field.label && !field.label.startsWith(i18nPrefix)) { - field.label = i18nPrefix + field.label; + const _field = field as unknown as WidgetAttributeInfoStored; + if (_field.label && !_field.label.startsWith(i18nPrefix)) { + _field.label = i18nPrefix + _field.label; } - if (field.tooltip && !field.tooltip.startsWith(i18nPrefix)) { - field.tooltip = i18nPrefix + field.tooltip; + if (_field.tooltip && !_field.tooltip.startsWith(i18nPrefix)) { + _field.tooltip = i18nPrefix + _field.tooltip; } - if (field.options && !field.noTranslation && Array.isArray(field.options)) { - field.options.forEach(option => { + if (_field.options && !_field.noTranslation && Array.isArray(_field.options)) { + _field.options.forEach(option => { if (typeof option === 'object') { if (option.label && !option.label.startsWith(i18nPrefix)) { option.label = i18nPrefix + option.label; @@ -423,7 +529,7 @@ interface CommonGroups { } export const parseAttributes = ( - widgetParams: string | RxWidgetInfoAttributes[], + widgetParams: string | RxWidgetInfoGroup[], widgetIndex?: number, commonGroups?: CommonGroups, commonFields?: Record, @@ -432,7 +538,7 @@ export const parseAttributes = ( ) => { if (typeof widgetParams === 'string') { let groupName = 'common'; - let indexedGroups: {[key: number]: WidgetAttributesGroupInfoStored} = {}; + let indexedGroups: { [key: number]: WidgetAttributesGroupInfoStored } = {}; let isIndexedGroup = false; commonGroups = commonGroups || { common: 1 }; commonFields = commonFields || {}; @@ -533,11 +639,14 @@ export const parseAttributes = ( } else if (field.name.match(/nav_view$/)) { field.type = 'views'; } else if (field.name === 'sound') { - field.type = 'sound'; + // field.type = 'sound'; + field.type = 'text'; } else if (field.name.includes('_effect')) { - field.type = 'effect'; + // field.type = 'effect'; + field.type = 'text'; } else if (field.name.includes('_eff_opt')) { - field.type = 'effect-options'; + // field.type = 'effect-options'; + field.type = 'text'; } if (field.type && (field.type.startsWith('id,'))) { diff --git a/src/src/types.d.ts b/src/src/types.d.ts index 20b6212d..67cd6642 100644 --- a/src/src/types.d.ts +++ b/src/src/types.d.ts @@ -802,6 +802,20 @@ export interface Window { systemLang?: ioBroker.Languages; _: (text: string, arg1?: boolean | number | string, arg2?: boolean | number | string, arg3?: boolean | number | string) => string; addWords: (words: Record>) => void; + VisMarketplace?: { + api: { + apiGetWidgetRevision(widgetId: string, id: string): Promise; + }; + default: React.Component; + }; + [promiseName: PromiseName]: Promise; + [widgetSetName: WidgetSetName]: { + __initialized: boolean; + get: (module: string) => () => void; + init?: (shareScope: any) => Promise; + }; + __widgetsLoadIndicator: (process: number, max: number) => void; + _lastAppliedStyle: string; } type ResizeHandler = 'n' | 'e' | 's' | 'w' | 'nw' | 'ne' | 'sw' | 'se'; @@ -992,7 +1006,7 @@ export interface CustomPaletteProperties { }; } -interface RxWidgetInfoAttributes { +interface RxWidgetInfoGroup { /** Name of the attributes section */ readonly name: string; /** Fields of this attribute section */ @@ -1027,25 +1041,25 @@ interface RxWidgetInfo { readonly visWidgetColor?: string; /** Groups of attributes */ - visAttrs: (readonly RxWidgetInfoAttributes[]); + visAttrs: (readonly RxWidgetInfoGroup[]); /** Default style for widget */ readonly visDefaultStyle?: React.CSSProperties; /** Position in the widget set */ readonly visOrder?: number; - /* required, that width is always equal to height (quadratic widget) */ + /** required, that width is always equal to height (quadratic widget) */ readonly visResizeLocked?: boolean; - /* if false, if widget is not resizable */ + /** if false, if widget is not resizable */ readonly visResizable?: boolean; - /* @deprecated use visResizable */ + /** @deprecated use visResizable */ readonly resizable?: boolean; - /* if false, if widget is not draggable */ + /** if false, if widget is not draggable */ readonly visDraggable?: boolean; - /* Show specific handlers */ + /** Show specific handlers */ readonly visResizeHandles?: ResizeHandler[]; - /* @deprecated use visResizeHandles */ + /** @deprecated use visResizeHandles */ readonly resizeHandles?: ResizeHandler[]; - /* Function to generate custom palette element */ + /** Function to generate custom palette element */ readonly customPalette?: (context: CustomPaletteProperties) => React.JSX.Element; }