@natykufsky/react-iframe 1.0.15
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 +85 -0
- package/bridge/ReFrameBridge.js +48 -0
- package/dist/components/TabList.d.ts +6 -0
- package/dist/components/TabView.d.ts +17 -0
- package/dist/components/TabViews.d.ts +5 -0
- package/dist/context/TabProvider.d.ts +18 -0
- package/dist/context/reducer.d.ts +2 -0
- package/dist/hooks/useIFrameBridge.d.ts +1 -0
- package/dist/hooks/useTabs.d.ts +12 -0
- package/dist/index.d.ts +7 -0
- package/dist/re-frame.js +6186 -0
- package/dist/types/index.d.ts +51 -0
- package/package.json +49 -0
- package/src/styles/default.css +195 -0
- package/src/styles/index.css +1 -0
- package/styles.css +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
# react-iframe
|
|
3
|
+
|
|
4
|
+
A standalone React library to manage multi-instance IFrame workspaces with persistence, memory management, and cross-window communication.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
- **Persistent DOM**: IFrames are cached (hidden), not unmounted.
|
|
8
|
+
- **Hibernation**: Automatically unloads inactive tabs after a timeout to save memory.
|
|
9
|
+
- **LRU Management**: Automatically closes least-recently-used tabs when maximum capacity is reached.
|
|
10
|
+
- **Persistence**: Session recovery via LocalStorage.
|
|
11
|
+
- **Bridge Support**: Easy communication between parent and IFrame.
|
|
12
|
+
|
|
13
|
+
## Components
|
|
14
|
+
|
|
15
|
+
### `TabProvider`
|
|
16
|
+
Wrap your application with this provider.
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { TabProvider } from '@natykufsky/react-iframe';
|
|
20
|
+
|
|
21
|
+
function App() {
|
|
22
|
+
return (
|
|
23
|
+
<TabProvider persist={true} maxTabs={10}>
|
|
24
|
+
<Dashboard />
|
|
25
|
+
</TabProvider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### `TabList`
|
|
31
|
+
The tab navigation bar.
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { TabList } from '@natykufsky/react-iframe';
|
|
35
|
+
import '@natykufsky/react-iframe/styles.css';
|
|
36
|
+
|
|
37
|
+
// ... inside a component
|
|
38
|
+
<TabList />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `TabViews`
|
|
42
|
+
The container for all iframes.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { TabViews } from '@natykufsky/react-iframe';
|
|
46
|
+
|
|
47
|
+
// ... inside a component
|
|
48
|
+
<TabViews hibernationMinutes={30} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Hook: `useTabs`
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
const { addTab, removeTab, focusTab, tabs, activeTabId } = useTabs();
|
|
55
|
+
|
|
56
|
+
const openPage = () => {
|
|
57
|
+
addTab({
|
|
58
|
+
id: 'user-profile',
|
|
59
|
+
title: 'User Profile',
|
|
60
|
+
url: '/admin/users/1/edit',
|
|
61
|
+
icon: 'user'
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## IFrame Bridge
|
|
67
|
+
|
|
68
|
+
Include the bridge script in your child pages (e.g., in a Laravel layout):
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<script src="/path/to/ReFrameBridge.js"></script>
|
|
72
|
+
<script>
|
|
73
|
+
// Close the current tab from inside the iframe
|
|
74
|
+
reFrame.close();
|
|
75
|
+
|
|
76
|
+
// Set tab title
|
|
77
|
+
reFrame.setTitle('Editing User: Naty');
|
|
78
|
+
</script>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
In the React parent, use the `useIFrameBridge` hook:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
useIFrameBridge(); // Listens for postMessage events
|
|
85
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* ReFrameBridge.js
|
|
4
|
+
* Include this script in your IFrame pages to communicate with the parent ReFrame container.
|
|
5
|
+
*/
|
|
6
|
+
(function() {
|
|
7
|
+
const ReFrame = {
|
|
8
|
+
getTabId: function() {
|
|
9
|
+
// Usually passed via URL params or set by the parent
|
|
10
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
11
|
+
return urlParams.get('reframe_id') || window.name;
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
postAction: function(action, payload = {}) {
|
|
15
|
+
const message = {
|
|
16
|
+
type: 'REFRAME_ACTION',
|
|
17
|
+
action: action,
|
|
18
|
+
payload: {
|
|
19
|
+
id: this.getTabId(),
|
|
20
|
+
...payload
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
window.parent.postMessage(message, '*');
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
close: function() {
|
|
27
|
+
this.postAction('CLOSE_SELF');
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
setTitle: function(title) {
|
|
31
|
+
this.postAction('SET_TITLE', { title: title });
|
|
32
|
+
document.title = title;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
notify: function(message, type = 'info') {
|
|
36
|
+
this.postAction('NOTIFICATION', { message: message, type: type });
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
navigate: function(url) {
|
|
40
|
+
this.postAction('NAVIGATE', { url: url });
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
window.reFrame = ReFrame;
|
|
45
|
+
|
|
46
|
+
// Auto-discovery: If title changes, maybe notify parent?
|
|
47
|
+
// MutationObserver could be used here to auto-sync title
|
|
48
|
+
})();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface TabViewProps {
|
|
2
|
+
id: string;
|
|
3
|
+
url: string;
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
isHibernated: boolean;
|
|
6
|
+
onLoad?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function LoadingOverlay(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function HibernationPlaceholder({ onWake, title }: {
|
|
10
|
+
onWake: () => void;
|
|
11
|
+
title: string;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function TabView({ id, url, isActive, isHibernated, title, wakeTab }: TabViewProps & {
|
|
14
|
+
title: string;
|
|
15
|
+
wakeTab: (id: string) => void;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ReFrameState, ReFrameAction, Tab } from '../types';
|
|
3
|
+
interface ReFrameContextType {
|
|
4
|
+
state: ReFrameState;
|
|
5
|
+
dispatch: React.Dispatch<ReFrameAction>;
|
|
6
|
+
addTab: (tab: Omit<Tab, 'lastAccessed' | 'isHibernated'>) => void;
|
|
7
|
+
removeTab: (id: string) => void;
|
|
8
|
+
focusTab: (id: string) => void;
|
|
9
|
+
closeOthers: (id: string) => void;
|
|
10
|
+
}
|
|
11
|
+
interface ProviderProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
persist?: boolean;
|
|
14
|
+
maxTabs?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function ReFrameProvider({ children, persist, maxTabs }: ProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const useReFrame: () => ReFrameContextType;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useIFrameBridge: () => null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Tab } from '../types';
|
|
2
|
+
export declare const useTabs: () => {
|
|
3
|
+
tabs: Tab[];
|
|
4
|
+
activeTab: Tab | undefined;
|
|
5
|
+
activeTabId: string | null;
|
|
6
|
+
addTab: (tab: Omit<Tab, "lastAccessed" | "isHibernated">) => void;
|
|
7
|
+
removeTab: (id: string) => void;
|
|
8
|
+
focusTab: (id: string) => void;
|
|
9
|
+
closeOthers: (id: string) => void;
|
|
10
|
+
updateTab: (id: string, updates: Partial<Tab>) => void;
|
|
11
|
+
reorderTabs: (sourceIndex: number, destinationIndex: number) => void;
|
|
12
|
+
};
|
package/dist/index.d.ts
ADDED