diff --git a/MIGRATION_4_5.md b/MIGRATION_4_5.md index 3f234f4a..10f3225b 100644 --- a/MIGRATION_4_5.md +++ b/MIGRATION_4_5.md @@ -33,19 +33,17 @@ If you still have questions, try to find an answer [here](https://mui.com/guides - Look for calls of custom like ```jsx -this.props.socket._socket.emit('getObjectView', 'system', 'custom', {startKey: '', endKey:'\u9999'}, (err, objs) => { - (objs?.rows || []) - .forEach(item => console.log(item.id, item.value)); +this.props.socket._socket.emit('getObjectView', 'system', 'custom', { startKey: '', endKey: '\u9999' }, (err, objs) => { + (objs?.rows || []).forEach(item => console.log(item.id, item.value)); }); ``` to ```jsx -socket.getObjectViewCustom('custom', 'state', 'startKey', 'endKey') - .then(objects => { - Object.keys(objects).forEach(obj => console.log(obj._id)); - }); +socket.getObjectViewCustom('custom', 'state', 'startKey', 'endKey').then(objects => { + Object.keys(objects).forEach(obj => console.log(obj._id)); +}); ``` - Replace all `socket.log.error('text')` to `socket.log('text', 'error')` diff --git a/MIGRATION_5_6.md b/MIGRATION_5_6.md index c62adef9..5fa36ea5 100644 --- a/MIGRATION_5_6.md +++ b/MIGRATION_5_6.md @@ -28,15 +28,15 @@ After: ```typescript jsx const styles: Record = { - dialog: (theme: IobTheme) => ({ - height: `calc(100% - ${theme => theme.mixins.toolbar.minHeight}px)`, - p: 1, // or 8px, padding is OK too - m: '16px', // or 2, margin is OK too - gap: '5px', - borderRadius: '5px', - ml: '10px', // mt, mr, mb, but marginLeft, marginRight, marginBottom is OK too - pl: '10px', // pt, pr, pb, but paddingTop, paddingRight, paddingBottom is OK too - }), + dialog: (theme: IobTheme) => ({ + height: `calc(100% - ${theme => theme.mixins.toolbar.minHeight}px)`, + p: 1, // or 8px, padding is OK too + m: '16px', // or 2, margin is OK too + gap: '5px', + borderRadius: '5px', + ml: '10px', // mt, mr, mb, but marginLeft, marginRight, marginBottom is OK too + pl: '10px', // pt, pr, pb, but paddingTop, paddingRight, paddingBottom is OK too + }), }; ``` diff --git a/MIGRATION_6_7.md b/MIGRATION_6_7.md index e05c5d60..48f0aea6 100644 --- a/MIGRATION_6_7.md +++ b/MIGRATION_6_7.md @@ -1,16 +1,24 @@ # Migration from adapter-react-v5@6.x to adapter-react-v5@7.x + Only MUI library was updated from v5 to v6. + ## No `withStyles` at all + `withStyles` was removed completely. So you have to replace all `withStyles` with `sx` or `style` properties. ## slotProps + `inputProps` and `InputProps` are now in `slotProps` Examples: Before: + ```jsx -}}/> -``` + }} +/> +``` ```jsx -``` +``` + ## SelectID dialog + `SelectID` dialog now requires `theme` property. Without this property, the dialog will crash. diff --git a/README.md b/README.md index cdfa4822..6be88573 100644 --- a/README.md +++ b/README.md @@ -20,39 +20,34 @@ If you want to create the configuration page with ReactJS: ```json { - "name": "ADAPTERNAME-admin", - "version": "0.1.0", - "private": true, - "dependencies": { - "@iobroker/adapter-react-v5": "^7.1.0", - "@iobroker/build-tools": "^1.0.0", - "@iobroker/eslint-config": "^0.1.2", - "@mui/material": "^6.0.2", - "@mui/icons-material": "^6.0.2", - "@sentry/browser": "^8.28.0", - "babel-eslint": "^10.1.0", - "eslint": "^9.10.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-scripts": "^5.0.1", - "react-icons": "^5.3.0" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "homepage": ".", - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] + "name": "ADAPTERNAME-admin", + "version": "0.1.0", + "private": true, + "dependencies": { + "@iobroker/adapter-react-v5": "^7.1.0", + "@iobroker/build-tools": "^1.0.0", + "@iobroker/eslint-config": "^0.1.2", + "@mui/material": "^6.0.2", + "@mui/icons-material": "^6.0.2", + "@sentry/browser": "^8.28.0", + "babel-eslint": "^10.1.0", + "eslint": "^9.10.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-scripts": "^5.0.1", + "react-icons": "^5.3.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "homepage": ".", + "browserslist": [">0.2%", "not dead", "not ie <= 11", "not op_mini all"] } ``` @@ -70,6 +65,7 @@ If you want to create the configuration page with ReactJS: "build": "node tasks" } ``` + 7. Start your dummy application `npm run start` for developing or build with `npm run build` and copy files in `build` directory to `www` or to `admin`. In the admin you must rename `index.html` to `index_m.html`. 8. You can do that with `npm` tasks: `npm run build` @@ -80,7 +76,10 @@ If you want to create the configuration page with ReactJS: After ```html - + ``` insert @@ -97,8 +96,18 @@ insert const [name, val] = item.split('='); query[decodeURIComponent(name)] = val !== undefined ? decodeURIComponent(val) : true; }); - script.onload = function () { typeof window.socketLoadedHandler === 'function' && window.socketLoadedHandler(); }; - script.src = window.location.port === '3000' ? window.location.protocol + '//' + (query.host || window.location.hostname) + ':' + (query.port || 8081) + '/lib/js/socket.io.js' : '%PUBLIC_URL%/../../lib/js/socket.io.js'; + script.onload = function () { + typeof window.socketLoadedHandler === 'function' && window.socketLoadedHandler(); + }; + script.src = + window.location.port === '3000' + ? window.location.protocol + + '//' + + (query.host || window.location.hostname) + + ':' + + (query.port || 8081) + + '/lib/js/socket.io.js' + : '%PUBLIC_URL%/../../lib/js/socket.io.js'; document.head.appendChild(script); @@ -109,23 +118,23 @@ insert ```jsx class App extends GenericApp { constructor(props) { - const extendedProps = {...props}; + const extendedProps = { ...props }; extendedProps.encryptedFields = ['pass']; // this parameter will be encrypted and decrypted automatically extendedProps.translations = { - 'en': require('./i18n/en'), - 'de': require('./i18n/de'), - 'ru': require('./i18n/ru'), - 'pt': require('./i18n/pt'), - 'nl': require('./i18n/nl'), - 'fr': require('./i18n/fr'), - 'it': require('./i18n/it'), - 'es': require('./i18n/es'), - 'pl': require('./i18n/pl'), - 'uk': require('./i18n/uk'), + en: require('./i18n/en'), + de: require('./i18n/de'), + ru: require('./i18n/ru'), + pt: require('./i18n/pt'), + nl: require('./i18n/nl'), + fr: require('./i18n/fr'), + it: require('./i18n/it'), + es: require('./i18n/es'), + pl: require('./i18n/pl'), + uk: require('./i18n/uk'), 'zh-cn': require('./i18n/zh-cn'), }; // get actual admin port - extendedProps.socket = {port: parseInt(window.location.port, 10)}; + extendedProps.socket = { port: parseInt(window.location.port, 10) }; // Only if close, save buttons are not required at the bottom (e.g. if admin tab) // extendedProps.bottomButtons = false; @@ -159,7 +168,7 @@ console.log(`iobroker.scenes@${version}`); const container = document.getElementById('root'); const root = createRoot(container); -root.render(); +root.render(); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. @@ -233,24 +242,28 @@ class ExportImportDialog extends React.Component { if (!this.state.confirmDialog) { return null; } - return { - this.setState({ confirmDialog: false} ); - }} - />; + return ( + { + this.setState({ confirmDialog: false }); + }} + /> + ); } render() { - return
- - { this.renderConfirmDialog() } -
+ return ( +
+ + {this.renderConfirmDialog()} +
+ ); } } @@ -286,34 +299,36 @@ renderMessage() { import { SelectID as DialogSelectID } from '@iobroker/adapter-react-v5'; class MyComponent extends Component { - constructor(props) { - super(props); - this.state = { - showSelectId: false, - }; - } + constructor(props) { + super(props); + this.state = { + showSelectId: false, + }; + } - renderSelectIdDialog() { + renderSelectIdDialog() { if (this.state.showSelectId) { - return this.setState({showSelectId: false})} - onOk={(selected, name) => { - this.setState({showSelectId: false, selectIdValue: selected}); - }} - />; + return ( + this.setState({ showSelectId: false })} + onOk={(selected, name) => { + this.setState({ showSelectId: false, selectIdValue: selected }); + }} + /> + ); } else { return null; } } render() { - return renderSelectIdDialog(); + return renderSelectIdDialog(); } } ``` @@ -326,18 +341,20 @@ Include `"react-text-mask": "^5.4.3",` in package.json. ```jsx function renderCron() { - if (!showCron) { - return null; - } else { - return this.setState({ showCron: false })} - onOk={cronValue => { - this.setState({ cronValue }) - }} - />; - } + if (!showCron) { + return null; + } else { + return ( + this.setState({ showCron: false })} + onOk={cronValue => { + this.setState({ cronValue }); + }} + /> + ); + } } ``` @@ -417,37 +434,36 @@ It is better to use `Dialog/SelectID`, but if you want: ```jsx { - this.filters = filterConfig; - window.localStorage.setItem(this.dialogName, JSON.stringify(filterConfig)); - }} - onSelect={(selected, name, isDouble) => { - if (JSON.stringify(selected) !== JSON.stringify(this.state.selected)) { - this.setState({selected, name}, () => - isDouble && this.handleOk()); - } else if (isDouble) { - this.handleOk(); - } - }} + foldersFirst={this.props.foldersFirst} + imagePrefix={this.props.imagePrefix || this.props.prefix} // prefix is for back compatibility + defaultFilters={this.filters} + dialogName={this.dialogName} + showExpertButton={this.props.showExpertButton !== undefined ? this.props.showExpertButton : true} + style={{ width: '100%', height: '100%' }} + columns={this.props.columns || ['name', 'type', 'role', 'room', 'func', 'val']} + types={this.props.types || ['state']} + t={I18n.t} + lang={this.props.lang || I18n.getLanguage()} + socket={this.props.socket} + selected={this.state.selected} + multiSelect={this.props.multiSelect} + notEditable={this.props.notEditable === undefined ? true : this.props.notEditable} + name={this.state.name} + theme={this.props.theme} + themeName={this.props.themeName} + themeType={this.props.themeType} + customFilter={this.props.customFilter} + onFilterChanged={filterConfig => { + this.filters = filterConfig; + window.localStorage.setItem(this.dialogName, JSON.stringify(filterConfig)); + }} + onSelect={(selected, name, isDouble) => { + if (JSON.stringify(selected) !== JSON.stringify(this.state.selected)) { + this.setState({ selected, name }, () => isDouble && this.handleOk()); + } else if (isDouble) { + this.handleOk(); + } + }} /> ``` @@ -465,113 +481,120 @@ const styles = { }, }; class MyComponent extends Component { - constructor(props) { - super(props); - - this.state = { - data: [ - { - id: 'UniqueID1', // required - fieldIdInData: 'Name1', - myType: 'number', - }, - { - id: 'UniqueID2', // required - fieldIdInData: 'Name12', - myType: 'string', - }, - ], - }; - - this.columns = [ - { - title: 'Name of field', // required, else it will be "field" - field: 'fieldIdInData', // required - editable: false, // or true [default - true] - cellStyle: { // CSS style - // optional - maxWidth: '12rem', - overflow: 'hidden', - wordBreak: 'break-word', - }, - lookup: { // optional => edit will be automatically "SELECT" - 'value1': 'text1', - 'value2': 'text2', - } - }, - { - title: 'Type', // required, else it will be "field" - field: 'myType', // required - editable: true, // or true [default - true] - lookup: { // optional => edit will be automatically "SELECT" - 'number': 'Number', - 'string': 'String', - 'boolean': 'Boolean', - }, - type: 'number/string/color/oid/icon/boolean', // oid=ObjectID,icon=base64-icon - editComponent: props => -
Prefix{
-