@iobroker/adapter-react-v5 8.2.7 → 8.2.8
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.
- package/README.md +3 -0
- package/build/Components/FileBrowser.d.ts +38 -1
- package/build/Components/FileBrowser.js +88 -2
- package/build/Components/FileBrowser.js.map +1 -1
- package/build/Components/ObjectBrowser.d.ts +14 -1
- package/build/Components/ObjectBrowser.js +113 -3
- package/build/Components/ObjectBrowser.js.map +1 -1
- package/build/Components/objectBrowser.types.d.ts +23 -0
- package/build/index.d.ts +2 -2
- package/build/index.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -14,6 +14,17 @@ export interface MetaACL extends ioBroker.ObjectACL {
|
|
|
14
14
|
export interface MetaObject extends ioBroker.MetaObject {
|
|
15
15
|
acl: MetaACL;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A navigation target the parent can use to drive the browser (and that the browser reports back).
|
|
19
|
+
* Maps cleanly to a route like `#tab-files/<mode>/<encoded-id>` (the parent encodes the file path,
|
|
20
|
+
* whose `/` separators would otherwise clash with the hash). The browser never reads the URL.
|
|
21
|
+
*/
|
|
22
|
+
export interface FileBrowserNavigation {
|
|
23
|
+
/** `select` = just highlight the file; `view` = open the file viewer. */
|
|
24
|
+
mode: 'select' | 'view';
|
|
25
|
+
/** The file path/ID (e.g. `email.admin/custom/assets/Components.js`). */
|
|
26
|
+
id: string;
|
|
27
|
+
}
|
|
17
28
|
export interface FileBrowserProps {
|
|
18
29
|
/** The key to identify this component. */
|
|
19
30
|
key?: string;
|
|
@@ -61,6 +72,17 @@ export interface FileBrowserProps {
|
|
|
61
72
|
filterByType?: 'images' | 'code' | 'txt';
|
|
62
73
|
/** Callback for file selection. */
|
|
63
74
|
onSelect?: (id: string | string[], isDoubleClick?: boolean, isFolder?: boolean) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Drive selection and the file viewer from the parent (e.g. from the URL). When this prop
|
|
77
|
+
* changes the browser selects the file and opens the requested viewer. The browser does NOT
|
|
78
|
+
* read the URL itself — all URL parsing/writing lives in the parent component.
|
|
79
|
+
*/
|
|
80
|
+
navigateTo?: FileBrowserNavigation | null;
|
|
81
|
+
/**
|
|
82
|
+
* Called when the user navigates inside the browser (selects a file or opens/closes the viewer)
|
|
83
|
+
* so the parent can reflect it in the URL. The browser never touches the URL itself.
|
|
84
|
+
*/
|
|
85
|
+
onNavigateTo?: (navigation: FileBrowserNavigation | null) => void;
|
|
64
86
|
/** Theme name */
|
|
65
87
|
themeName?: ThemeName;
|
|
66
88
|
/** Theme type. */
|
|
@@ -132,6 +154,10 @@ export declare class FileBrowserClass extends Component<FileBrowserProps, FileBr
|
|
|
132
154
|
private readonly limitToObjectID;
|
|
133
155
|
private readonly limitToPath;
|
|
134
156
|
private lastSelect;
|
|
157
|
+
/** Last navigation applied from `navigateTo` or reported via `onNavigateTo` (loop guard). */
|
|
158
|
+
private lastNav;
|
|
159
|
+
/** True while applying `navigateTo`, so the derived-state watcher does not echo it back. */
|
|
160
|
+
private applyingNav;
|
|
135
161
|
private setOpacityTimer;
|
|
136
162
|
private cacheFoldersTimeout;
|
|
137
163
|
private foldersLoading;
|
|
@@ -168,7 +194,18 @@ export declare class FileBrowserClass extends Component<FileBrowserProps, FileBr
|
|
|
168
194
|
renderToolbar(): JSX.Element;
|
|
169
195
|
findItem(id: string, folders?: Folders | null): null | FolderOrFileItem;
|
|
170
196
|
renderInputDialog(): JSX.Element | null;
|
|
171
|
-
|
|
197
|
+
/** Strip the image prefix from a viewer href to get back the raw file path. */
|
|
198
|
+
private viewerToId;
|
|
199
|
+
/** Derive the current navigation target from the viewer/selection state. */
|
|
200
|
+
private getStateNav;
|
|
201
|
+
private static navEqual;
|
|
202
|
+
/** Apply a navigation target coming from the parent (`navigateTo`): select + open the viewer. */
|
|
203
|
+
private applyNavigateTo;
|
|
204
|
+
/** Apply the initial `navigateTo` once the browser is ready (called from componentDidMount). */
|
|
205
|
+
applyInitialNavigateTo(): void;
|
|
206
|
+
/** Reconcile `navigateTo` (parent/URL) with the browser's selection/viewer state. */
|
|
207
|
+
private reconcileNavigation;
|
|
208
|
+
componentDidUpdate(prevProps: FileBrowserProps): void;
|
|
172
209
|
findFirstFolder(id: string): string | null;
|
|
173
210
|
uploadFile(fileName: string, data: string): Promise<void>;
|
|
174
211
|
renderUpload(): JSX.Element[] | null;
|
|
@@ -394,6 +394,10 @@ export class FileBrowserClass extends Component {
|
|
|
394
394
|
limitToObjectID = null;
|
|
395
395
|
limitToPath = null;
|
|
396
396
|
lastSelect = null;
|
|
397
|
+
/** Last navigation applied from `navigateTo` or reported via `onNavigateTo` (loop guard). */
|
|
398
|
+
lastNav = null;
|
|
399
|
+
/** True while applying `navigateTo`, so the derived-state watcher does not echo it back. */
|
|
400
|
+
applyingNav = false;
|
|
397
401
|
setOpacityTimer = null;
|
|
398
402
|
cacheFoldersTimeout = null;
|
|
399
403
|
foldersLoading = null;
|
|
@@ -543,7 +547,9 @@ export class FileBrowserClass extends Component {
|
|
|
543
547
|
}
|
|
544
548
|
async componentDidMount() {
|
|
545
549
|
this.mounted = true;
|
|
546
|
-
this.loadFolders()
|
|
550
|
+
this.loadFolders()
|
|
551
|
+
.then(() => this.applyInitialNavigateTo())
|
|
552
|
+
.catch(error => console.error(`Cannot load folders: ${error}`));
|
|
547
553
|
this.browseList = [];
|
|
548
554
|
this.browseListRunning = false;
|
|
549
555
|
this.supportSubscribes = await this.props.socket.checkFeatureSupported('BINARY_STATE_EVENT');
|
|
@@ -1372,7 +1378,86 @@ export class FileBrowserClass extends Component {
|
|
|
1372
1378
|
}
|
|
1373
1379
|
return null;
|
|
1374
1380
|
}
|
|
1375
|
-
|
|
1381
|
+
// --- Routing (navigateTo / onNavigateTo) ---
|
|
1382
|
+
// The browser never reads the URL; the parent drives it via `navigateTo` and is informed of user
|
|
1383
|
+
// navigation via `onNavigateTo`. All URL parsing/encoding lives in the parent component.
|
|
1384
|
+
/** Strip the image prefix from a viewer href to get back the raw file path. */
|
|
1385
|
+
viewerToId(viewer) {
|
|
1386
|
+
return viewer.startsWith(this.imagePrefix) ? viewer.substring(this.imagePrefix.length) : viewer;
|
|
1387
|
+
}
|
|
1388
|
+
/** Derive the current navigation target from the viewer/selection state. */
|
|
1389
|
+
getStateNav() {
|
|
1390
|
+
if (this.state.viewer) {
|
|
1391
|
+
return { mode: 'view', id: this.viewerToId(this.state.viewer) };
|
|
1392
|
+
}
|
|
1393
|
+
if (this.state.selected) {
|
|
1394
|
+
return { mode: 'select', id: this.state.selected };
|
|
1395
|
+
}
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
static navEqual(a, b) {
|
|
1399
|
+
if (!a || !b) {
|
|
1400
|
+
return !a && !b;
|
|
1401
|
+
}
|
|
1402
|
+
return a.mode === b.mode && a.id === b.id;
|
|
1403
|
+
}
|
|
1404
|
+
/** Apply a navigation target coming from the parent (`navigateTo`): select + open the viewer. */
|
|
1405
|
+
applyNavigateTo(nav) {
|
|
1406
|
+
this.applyingNav = true;
|
|
1407
|
+
const done = () => {
|
|
1408
|
+
this.applyingNav = false;
|
|
1409
|
+
};
|
|
1410
|
+
if (!nav?.id) {
|
|
1411
|
+
this.setState({ viewer: '', formatEditFile: '' }, done);
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const { id } = nav;
|
|
1415
|
+
this.select(id, null, () => {
|
|
1416
|
+
this.scrollToSelected();
|
|
1417
|
+
if (nav.mode === 'view') {
|
|
1418
|
+
this.setState({ viewer: this.imagePrefix + id, formatEditFile: Utils.getFileExtension(id) }, done);
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
this.setState({ viewer: '', formatEditFile: '' }, done);
|
|
1422
|
+
}
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
/** Apply the initial `navigateTo` once the browser is ready (called from componentDidMount). */
|
|
1426
|
+
applyInitialNavigateTo() {
|
|
1427
|
+
const nav = this.props.navigateTo ?? null;
|
|
1428
|
+
if (nav?.id) {
|
|
1429
|
+
this.lastNav = nav;
|
|
1430
|
+
this.applyNavigateTo(nav);
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
// Don't push the restored-from-localStorage selection into the URL on load.
|
|
1434
|
+
this.lastNav = this.getStateNav();
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
/** Reconcile `navigateTo` (parent/URL) with the browser's selection/viewer state. */
|
|
1438
|
+
reconcileNavigation(prevProps) {
|
|
1439
|
+
if (this.props.navigateTo === undefined && !this.props.onNavigateTo) {
|
|
1440
|
+
return; // routing not used by this consumer
|
|
1441
|
+
}
|
|
1442
|
+
if (this.applyingNav) {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const propNav = this.props.navigateTo ?? null;
|
|
1446
|
+
const stateNav = this.getStateNav();
|
|
1447
|
+
if (FileBrowserClass.navEqual(propNav, stateNav)) {
|
|
1448
|
+
this.lastNav = stateNav;
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
if (!FileBrowserClass.navEqual(propNav, prevProps.navigateTo ?? null)) {
|
|
1452
|
+
this.lastNav = propNav;
|
|
1453
|
+
this.applyNavigateTo(propNav);
|
|
1454
|
+
}
|
|
1455
|
+
else if (!FileBrowserClass.navEqual(stateNav, this.lastNav)) {
|
|
1456
|
+
this.lastNav = stateNav;
|
|
1457
|
+
this.props.onNavigateTo?.(stateNav);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
componentDidUpdate(prevProps /* , prevState, snapshot */) {
|
|
1376
1461
|
if (this.setOpacityTimer) {
|
|
1377
1462
|
clearTimeout(this.setOpacityTimer);
|
|
1378
1463
|
}
|
|
@@ -1383,6 +1468,7 @@ export class FileBrowserClass extends Component {
|
|
|
1383
1468
|
items[i].style.opacity = '1';
|
|
1384
1469
|
}
|
|
1385
1470
|
}, 100);
|
|
1471
|
+
this.reconcileNavigation(prevProps);
|
|
1386
1472
|
}
|
|
1387
1473
|
findFirstFolder(id) {
|
|
1388
1474
|
let parentFolder = id;
|