@bydata/react-supertabs 1.1.0

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 ADDED
@@ -0,0 +1,257 @@
1
+ # React SuperTabs
2
+
3
+ **SuperTabs** is a powerful and user-friendly tab management component. It enables seamless navigation by opening modules as tabs, with support for sub-tabs, smooth transitions. An experimental _keep-alive_ feature ensures components stay active across tab switches for enhanced usability.
4
+
5
+ ### SuperTabs Features
6
+ ------
7
+ - **Tab-based Navigation**: Open modules as tabs for an intuitive and efficient navigation experience.
8
+ - **Sub-Tabs Support**: Create nested sub-tabs within a main tab for hierarchical organization.
9
+ - **Smooth Transitions**: Enjoy a polished user experience with fluid tab-switching animations.
10
+ - **Keep-Alive (Experimental)**: Maintain the state of components across tab switches for seamless interaction (experimental feature).
11
+
12
+ ### Installation and usage
13
+ ------
14
+
15
+ #### Basic
16
+
17
+ >**Import TabProvider and wrap it around the main wrapper**
18
+
19
+ - This is needed to access useTabContext.
20
+ - Make sure to pass `SITE_PREFIX` and `SITE_PAGES` (From Navigation.js)
21
+ - `homeUrl` is optional and defaults to "/"
22
+ - `persistTabsAfterLogin` is optional and defaults to "false"
23
+ - Used to preserve tab state upon login.
24
+ - `entityIdentifierKey` is optional and defaults to 'organization_id',
25
+ - Used to identify the organization / client id
26
+ - `preventHomeRedirect` is optional and defaults to false,
27
+ - This will open a menu on click of home button instead of redirecting
28
+ - `handleOpenTab` async function to open the tab in backend
29
+ - expects the following keys in return
30
+ - { status: 1, tabId, children: undefined }
31
+ - `handleCloseTab` async function to open the tab in backend
32
+ - expects the following keys in return
33
+ - { status: 1, current_tab_id: 123 }
34
+ - `handleReorderTabs` async function to open the tab in backend
35
+ - { status: 1 }
36
+ - `getTabs` async function to open the tab in backend async () => { },
37
+ - expects the following keys in return
38
+ - { status: 1, tabs: [], current_tab: {} || null, hasNoTabs }
39
+ - hasNoTabs = user for whom there are no tabs open
40
+ - `getUserDetails` function which return the user details from user token
41
+ - `hasPrivilege` function which checks if the user has the privilege passed in function param
42
+ - `alertService` An object containing different methods for displaying toast alerts.
43
+ - can call success, warning, error, info, alert methods.
44
+ - super-notifier - NPM package can be used here
45
+ - https://www.npmjs.com/package/super-notifier
46
+
47
+ ```
48
+ Eg.
49
+ const SITE_PREFIX = 'teamLink_'
50
+ const API_BASE_URL = 'https://api.teamlink.com/data_stream'
51
+ const SITE_PAGES = [{
52
+ title: "Organization",
53
+ url: "/app/organization",
54
+ privilege: "ORGANIZATION",
55
+ id: 1,
56
+ },
57
+ {
58
+ title: "Recruiter",
59
+ url: "/app/recruiter/f-home",
60
+ privilege: "RECRUITER",
61
+ showAddButton: true,
62
+ customAddButtons: [
63
+ {
64
+ name: "Data Grid",
65
+ id: 1,
66
+ },
67
+ {
68
+ name: "Trend Master",
69
+ id: 2,
70
+ }
71
+ ],
72
+ containsSubTabs: true,
73
+ id: 2,
74
+ }]
75
+
76
+ // showAddButton - can be added if the module needs plus button.
77
+ // containsSubTabs - can be added if the tab includes sub tabs.
78
+ // customAddButtons - These buttons will be added in the sub tab list. Clicking on the button will trigger the binded callback with object as param.
79
+
80
+ import { TabProvider } from 'components/SuperTabs/TabContext';
81
+ <TabProvider
82
+ SITE_PREFIX={SITE_PREFIX}
83
+ SITE_PAGES={SITE_PAGES}
84
+ homeUrl="/app"
85
+ getTabs={getTabs}
86
+ handleOpenTab={handleOpenTab}
87
+ handleCloseTab={handleCloseTab}
88
+ handleReorderTabs={handleReorderTabs}
89
+ alertService={alertService}
90
+ >
91
+ <Main />
92
+ </TabProvider>
93
+ ```
94
+
95
+ >**Render the component within the App's header**
96
+ ```
97
+ import { useTabContext } from "components/SuperTabs/TabContext";
98
+
99
+ <header>
100
+ <SuperTabs />
101
+ </header>
102
+ ```
103
+
104
+ >**Wrap the hyperlink as follows:**
105
+
106
+ Each tab should contain all the necessary information to be stored.
107
+
108
+ Mandatory Fields:
109
+ - `id`
110
+ - `name`
111
+ - `url`
112
+
113
+ For Avatar:
114
+ - `showAvatar` must be set to `true`
115
+ - The following fields must be provided:
116
+ - `firstName`
117
+ - `lastName`
118
+ - `imgSrc`
119
+
120
+ ```
121
+ // Tab object Examples
122
+
123
+ {
124
+ id: 1,
125
+ name: 'title',
126
+ url: '/recruiter/f-1`
127
+ }
128
+
129
+ {
130
+ id: 100,
131
+ url: '/profile/100`
132
+ firstName: 'John',
133
+ lastName: 'Doe',
134
+ showAvatar: true,
135
+ imgSrc: img_url,
136
+ department,
137
+ designation,
138
+ department_id,
139
+ }
140
+ ```
141
+ ```
142
+ import SuperLink from "components/SuperTabs/SuperLink";
143
+ <SuperLink tab={page} isSubTab={false}>
144
+ {page.title}
145
+ </SuperLink>
146
+ ```
147
+
148
+ OR
149
+
150
+ ```
151
+ import { useTabContext } from "components/SuperTabs/TabContext";
152
+ const { openSuperTabOnRowClick } = useTabContext();
153
+
154
+ function handleRowClick(e) {
155
+ const tab = { id: e.id, name: e.name, url: `/recruiter/f-${e.id}` };
156
+ // isSubTab defaults to true
157
+ openSuperTabOnRowClick({ tab, isSubTab });
158
+ }
159
+ ```
160
+
161
+ >**Bind the callback for the plus button to each module during the initial render.**
162
+
163
+ If a module requires a plus button for adding a new tab, you can include the following:
164
+ ```
165
+ import { emitter } from "components/SuperTabs/SuperTabs";
166
+ import { useTabContext } from "components/SuperTabs/TabContext";
167
+
168
+ const { openSuperTabOnRowClick } = useTabContext();
169
+
170
+ const handleTabAddBtn = useCallback(() => {
171
+ openSuperTabOnRowClick({
172
+ tab: {
173
+ id: 123,
174
+ name: `New Funnel`,
175
+ url: `/recruiter/f-${123}`,
176
+ },
177
+ isSubTab: true,
178
+ });
179
+ }, [openSuperTabOnRowClick]);
180
+
181
+ useEffect(() => {
182
+ const unsub = emitter.emit("bindCallback", {
183
+ id: 2, // Same id provided for this module in navigation.js
184
+ callback: handleTabAddBtn,
185
+ });
186
+ return unsub;
187
+ }, [handleTabAddBtn]);
188
+ ```
189
+
190
+ > Note: The logo of the home tab can be updated by targeting the `brand-logo` class.
191
+
192
+ -----
193
+
194
+ #### Advanced
195
+
196
+ >**Cleanup**
197
+
198
+ When a tab is closed, it may be necessary to clean up some data. The `superTabClose` event can be utilized for this purpose.
199
+
200
+ ```
201
+ import { emitter } from 'components/SuperTabs/SuperTabs';
202
+
203
+ useEffect(() => {
204
+ const unsub = emitter.subscribe('superTabClose', ({ appId, isSubTab, tab }) => {
205
+ // Same id provided for this module in navigation.js
206
+ if (appId === 2 && isSubTab) {
207
+ // handle clean up here
208
+ }
209
+ })
210
+ return unsub;
211
+ }, []);
212
+ ```
213
+
214
+ >**Not Available page**
215
+ - If a profile is deleted, the tab will remain open. To address this, we can display a "Page Not Available" message.
216
+
217
+ ```
218
+ import NotAvailable from 'components/SuperTabs/NotAvailable';
219
+
220
+ <NotAvailable subTabName='Profile' />
221
+ ```
222
+
223
+ >**Keep Alive (Experimental)**
224
+
225
+ ```
226
+ import { withKeepAlive } from "components/SuperTabs/KeepAlive";
227
+
228
+ // Same id provided for this module in navigation.js
229
+ export default withKeepAlive(Component, Unique ID);
230
+
231
+ Eg.
232
+ const Component = () => {
233
+ return <div>Component</div>
234
+ }
235
+
236
+ export default withKeepAlive(Component, 2);
237
+ ```
238
+ Keep Alive Cleanup
239
+ ```
240
+ // This can be incorporated into a component that is always rendered, such as the header.
241
+
242
+ import { useAliveScope } from "components/SuperTabs/KeepAlive";
243
+ import { emitter } from "components/SuperTabs/SuperTabs";
244
+
245
+ const { drop } = useAliveScope();
246
+
247
+ useEffect(() => {
248
+ const unsub = emitter.subscribe('superTabClose', (e) => {
249
+ const { appId, isSubTab } = e;
250
+ if (appId && !isSubTab) {
251
+ // drop the cache of the tab
252
+ drop(appId);
253
+ }
254
+ })
255
+ return unsub;
256
+ }, []);
257
+ ```
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports["default"] = void 0;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
10
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
11
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
12
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
13
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
14
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
15
+ function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
16
+ function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
17
+ function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
18
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
19
+ function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
20
+ function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
21
+ function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
22
+ var ClickOutsideListener = exports["default"] = /*#__PURE__*/function (_React$Component) {
23
+ function ClickOutsideListener(props) {
24
+ var _this;
25
+ _classCallCheck(this, ClickOutsideListener);
26
+ _this = _callSuper(this, ClickOutsideListener, [props]);
27
+ _this.domRef = /*#__PURE__*/_react["default"].createRef();
28
+ _this.listener = _this.listener.bind(_this);
29
+ return _this;
30
+ }
31
+ _inherits(ClickOutsideListener, _React$Component);
32
+ return _createClass(ClickOutsideListener, [{
33
+ key: "componentDidMount",
34
+ value: function componentDidMount() {
35
+ document.addEventListener('click', this.listener, true);
36
+ }
37
+ }, {
38
+ key: "componentWillUnmount",
39
+ value: function componentWillUnmount() {
40
+ document.removeEventListener('click', this.listener, true);
41
+ }
42
+ }, {
43
+ key: "isClickedOutside",
44
+ value: function isClickedOutside(e) {
45
+ // e.stopPropagation();
46
+ // If the event target doesn't match bail - analysis view - save and filter panel fix
47
+ // if(e.target.matches('button') || e.target.matches('svg') || e.target.matches('rect') || e.target.matches('polyline') || e.target.matches('span')) return false;
48
+ // console.log(e.target);
49
+ // console.log(this.domRef.current);
50
+ // console.log(this.domRef.current.contains(e.target));
51
+
52
+ if (this.domRef.current && !this.domRef.current.contains(e.target)) {
53
+ return true;
54
+ }
55
+ return false;
56
+ }
57
+ }, {
58
+ key: "listener",
59
+ value: function listener(e) {
60
+ if (this.isClickedOutside(e)) {
61
+ this.props.onOutsideClick(e);
62
+ }
63
+ }
64
+ }, {
65
+ key: "render",
66
+ value: function render() {
67
+ return /*#__PURE__*/_react["default"].createElement("div", {
68
+ ref: this.domRef
69
+ }, this.props.children);
70
+ }
71
+ }]);
72
+ }(_react["default"].Component);
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.transitionDurationExtended = exports.transitionDuration = exports.fontStyle = exports.ToolbarButtonWidth = exports.SubTabWrapperPadding = exports.SubTabPadding = exports.SubTabGap = exports.StackButtonWidth = exports.MarginLeft = exports.MainTabGap = exports.HomeTabWidth = exports.CloseButtonWidth = exports.AvatarSize = void 0;
7
+ var transitionDuration = exports.transitionDuration = 300;
8
+ var transitionDurationExtended = exports.transitionDurationExtended = 800;
9
+ var HomeTabWidth = exports.HomeTabWidth = 36;
10
+ var MainTabGap = exports.MainTabGap = 2;
11
+ var SubTabGap = exports.SubTabGap = 6;
12
+ var MarginLeft = exports.MarginLeft = 5;
13
+ var SubTabPadding = exports.SubTabPadding = 20;
14
+ var SubTabWrapperPadding = exports.SubTabWrapperPadding = MarginLeft * 2;
15
+ var StackButtonWidth = exports.StackButtonWidth = 32 + MarginLeft;
16
+ var AvatarSize = exports.AvatarSize = 20 + SubTabGap;
17
+ var ToolbarButtonWidth = exports.ToolbarButtonWidth = 24;
18
+ var CloseButtonWidth = exports.CloseButtonWidth = 20; // 24-4px is for the right padding adjustment
19
+
20
+ var fontStyle = exports.fontStyle = '600 12px "Open Sans", "Poppins", sans-serif'; // "same as supertabs.scss global font style"
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports["default"] = void 0;
7
+ var _TabContext = require("./TabContext");
8
+ var _Utils = require("./Utils");
9
+ require("./NotAvailable.scss");
10
+ var NotAvailable = function NotAvailable(_ref) {
11
+ var title = _ref.title,
12
+ description = _ref.description,
13
+ _ref$subTabName = _ref.subTabName,
14
+ subTabName = _ref$subTabName === void 0 ? 'Page' : _ref$subTabName;
15
+ var _useTabContext = (0, _TabContext.useTabContext)(),
16
+ activeSubTab = _useTabContext.activeSubTab;
17
+ var heading = title !== null && title !== void 0 ? title : (0, _Utils.getTitle)(activeSubTab);
18
+ var subHeading = description !== null && description !== void 0 ? description : "".concat(subTabName, " is not available");
19
+ return /*#__PURE__*/React.createElement("div", {
20
+ className: "not-availble-wrapper"
21
+ }, /*#__PURE__*/React.createElement("div", {
22
+ className: "not-available-content"
23
+ }, /*#__PURE__*/React.createElement("h3", {
24
+ className: "title"
25
+ }, heading.replace('Loading...', 'Untitled')), /*#__PURE__*/React.createElement("p", {
26
+ className: "description"
27
+ }, subHeading)));
28
+ };
29
+ var _default = exports["default"] = NotAvailable;
@@ -0,0 +1,30 @@
1
+ .not-availble-wrapper {
2
+ display: grid;
3
+ place-items: center;
4
+ height: 100%;
5
+
6
+ .not-available-content {
7
+ display: grid;
8
+ justify-items: center;
9
+ gap: 20px;
10
+ }
11
+
12
+ .title,
13
+ .description {
14
+ font-family: 'Open sans', sans-serif;
15
+ margin: 0;
16
+ color: var(--tab-text-normal);
17
+ }
18
+
19
+ .title {
20
+ font-size: 16px;
21
+ font-weight: 600;
22
+ line-height: 1.5;
23
+ }
24
+
25
+ .description {
26
+ font-size: 14px;
27
+ font-weight: 400;
28
+ line-height: 1;
29
+ }
30
+ }
package/dist/Readme.md ADDED
@@ -0,0 +1,258 @@
1
+
2
+ # SuperTabs
3
+
4
+ **SuperTabs** is a powerful and user-friendly tab management component. It enables seamless navigation by opening modules as tabs, with support for sub-tabs, smooth transitions. An experimental _keep-alive_ feature ensures components stay active across tab switches for enhanced usability.
5
+
6
+ ### SuperTabs Features
7
+ ------
8
+ - **Tab-based Navigation**: Open modules as tabs for an intuitive and efficient navigation experience.
9
+ - **Sub-Tabs Support**: Create nested sub-tabs within a main tab for hierarchical organization.
10
+ - **Smooth Transitions**: Enjoy a polished user experience with fluid tab-switching animations.
11
+ - **Keep-Alive (Experimental)**: Maintain the state of components across tab switches for seamless interaction (experimental feature).
12
+
13
+ ### Installation and usage
14
+ ------
15
+
16
+ #### Basic
17
+
18
+ >**Import TabProvider and wrap it around the main wrapper**
19
+
20
+ - This is needed to access useTabContext.
21
+ - Make sure to pass `SITE_PREFIX` and `SITE_PAGES` (From Navigation.js)
22
+ - `homeUrl` is optional and defaults to "/"
23
+ - `persistTabsAfterLogin` is optional and defaults to "false"
24
+ - Used to preserve tab state upon login.
25
+ - `entityIdentifierKey` is optional and defaults to 'organization_id',
26
+ - Used to identify the organization / client id
27
+ - `preventHomeRedirect` is optional and defaults to false,
28
+ - This will open a menu on click of home button instead of redirecting
29
+ - `handleOpenTab` async function to open the tab in backend
30
+ - expects the following keys in return
31
+ - { status: 1, tabId, children: undefined }
32
+ - `handleCloseTab` async function to open the tab in backend
33
+ - expects the following keys in return
34
+ - { status: 1, current_tab_id: 123 }
35
+ - `handleReorderTabs` async function to open the tab in backend
36
+ - { status: 1 }
37
+ - `getTabs` async function to open the tab in backend async () => { },
38
+ - expects the following keys in return
39
+ - { status: 1, tabs: [], current_tab: {} || null, hasNoTabs }
40
+ - hasNoTabs = user for whom there are no tabs open
41
+ - `getUserDetails` function which return the user details from user token
42
+ - `hasPrivilege` function which checks if the user has the privilege passed in function param
43
+ - `alertService` An object containing different methods for displaying toast alerts.
44
+ - can call success, warning, error, info, alert methods.
45
+ - super-notifier - NPM package can be used here
46
+ - https://www.npmjs.com/package/super-notifier
47
+
48
+ ```
49
+ Eg.
50
+ const SITE_PREFIX = 'teamLink_'
51
+ const API_BASE_URL = 'https://api.teamlink.com/data_stream'
52
+ const SITE_PAGES = [{
53
+ title: "Organization",
54
+ url: "/app/organization",
55
+ privilege: "ORGANIZATION",
56
+ id: 1,
57
+ },
58
+ {
59
+ title: "Recruiter",
60
+ url: "/app/recruiter/f-home",
61
+ privilege: "RECRUITER",
62
+ showAddButton: true,
63
+ customAddButtons: [
64
+ {
65
+ name: "Data Grid",
66
+ id: 1,
67
+ },
68
+ {
69
+ name: "Trend Master",
70
+ id: 2,
71
+ }
72
+ ],
73
+ containsSubTabs: true,
74
+ id: 2,
75
+ }]
76
+
77
+ // showAddButton - can be added if the module needs plus button.
78
+ // containsSubTabs - can be added if the tab includes sub tabs.
79
+ // customAddButtons - These buttons will be added in the sub tab list. Clicking on the button will trigger the binded callback with object as param.
80
+
81
+ import { TabProvider } from 'components/SuperTabs/TabContext';
82
+ <TabProvider
83
+ SITE_PREFIX={SITE_PREFIX}
84
+ SITE_PAGES={SITE_PAGES}
85
+ homeUrl="/app"
86
+ getTabs={getTabs}
87
+ handleOpenTab={handleOpenTab}
88
+ handleCloseTab={handleCloseTab}
89
+ handleReorderTabs={handleReorderTabs}
90
+ alertService={alertService}
91
+ >
92
+ <Main />
93
+ </TabProvider>
94
+ ```
95
+
96
+ >**Render the component within the App's header**
97
+ ```
98
+ import { useTabContext } from "components/SuperTabs/TabContext";
99
+
100
+ <header>
101
+ <SuperTabs />
102
+ </header>
103
+ ```
104
+
105
+ >**Wrap the hyperlink as follows:**
106
+
107
+ Each tab should contain all the necessary information to be stored.
108
+
109
+ Mandatory Fields:
110
+ - `id`
111
+ - `name`
112
+ - `url`
113
+
114
+ For Avatar:
115
+ - `showAvatar` must be set to `true`
116
+ - The following fields must be provided:
117
+ - `firstName`
118
+ - `lastName`
119
+ - `imgSrc`
120
+
121
+ ```
122
+ // Tab object Examples
123
+
124
+ {
125
+ id: 1,
126
+ name: 'title',
127
+ url: '/recruiter/f-1`
128
+ }
129
+
130
+ {
131
+ id: 100,
132
+ url: '/profile/100`
133
+ firstName: 'John',
134
+ lastName: 'Doe',
135
+ showAvatar: true,
136
+ imgSrc: img_url,
137
+ department,
138
+ designation,
139
+ department_id,
140
+ }
141
+ ```
142
+ ```
143
+ import SuperLink from "components/SuperTabs/SuperLink";
144
+ <SuperLink tab={page} isSubTab={false}>
145
+ {page.title}
146
+ </SuperLink>
147
+ ```
148
+
149
+ OR
150
+
151
+ ```
152
+ import { useTabContext } from "components/SuperTabs/TabContext";
153
+ const { openSuperTabOnRowClick } = useTabContext();
154
+
155
+ function handleRowClick(e) {
156
+ const tab = { id: e.id, name: e.name, url: `/recruiter/f-${e.id}` };
157
+ // isSubTab defaults to true
158
+ openSuperTabOnRowClick({ tab, isSubTab });
159
+ }
160
+ ```
161
+
162
+ >**Bind the callback for the plus button to each module during the initial render.**
163
+
164
+ If a module requires a plus button for adding a new tab, you can include the following:
165
+ ```
166
+ import { emitter } from "components/SuperTabs/SuperTabs";
167
+ import { useTabContext } from "components/SuperTabs/TabContext";
168
+
169
+ const { openSuperTabOnRowClick } = useTabContext();
170
+
171
+ const handleTabAddBtn = useCallback(() => {
172
+ openSuperTabOnRowClick({
173
+ tab: {
174
+ id: 123,
175
+ name: `New Funnel`,
176
+ url: `/recruiter/f-${123}`,
177
+ },
178
+ isSubTab: true,
179
+ });
180
+ }, [openSuperTabOnRowClick]);
181
+
182
+ useEffect(() => {
183
+ const unsub = emitter.emit("bindCallback", {
184
+ id: 2, // Same id provided for this module in navigation.js
185
+ callback: handleTabAddBtn,
186
+ });
187
+ return unsub;
188
+ }, [handleTabAddBtn]);
189
+ ```
190
+
191
+ > Note: The logo of the home tab can be updated by targeting the `brand-logo` class.
192
+
193
+ -----
194
+
195
+ #### Advanced
196
+
197
+ >**Cleanup**
198
+
199
+ When a tab is closed, it may be necessary to clean up some data. The `superTabClose` event can be utilized for this purpose.
200
+
201
+ ```
202
+ import { emitter } from 'components/SuperTabs/SuperTabs';
203
+
204
+ useEffect(() => {
205
+ const unsub = emitter.subscribe('superTabClose', ({ appId, isSubTab, tab }) => {
206
+ // Same id provided for this module in navigation.js
207
+ if (appId === 2 && isSubTab) {
208
+ // handle clean up here
209
+ }
210
+ })
211
+ return unsub;
212
+ }, []);
213
+ ```
214
+
215
+ >**Not Available page**
216
+ - If a profile is deleted, the tab will remain open. To address this, we can display a "Page Not Available" message.
217
+
218
+ ```
219
+ import NotAvailable from 'components/SuperTabs/NotAvailable';
220
+
221
+ <NotAvailable subTabName='Profile' />
222
+ ```
223
+
224
+ >**Keep Alive (Experimental)**
225
+
226
+ ```
227
+ import { withKeepAlive } from "components/SuperTabs/KeepAlive";
228
+
229
+ // Same id provided for this module in navigation.js
230
+ export default withKeepAlive(Component, Unique ID);
231
+
232
+ Eg.
233
+ const Component = () => {
234
+ return <div>Component</div>
235
+ }
236
+
237
+ export default withKeepAlive(Component, 2);
238
+ ```
239
+ Keep Alive Cleanup
240
+ ```
241
+ // This can be incorporated into a component that is always rendered, such as the header.
242
+
243
+ import { useAliveScope } from "components/SuperTabs/KeepAlive";
244
+ import { emitter } from "components/SuperTabs/SuperTabs";
245
+
246
+ const { drop } = useAliveScope();
247
+
248
+ useEffect(() => {
249
+ const unsub = emitter.subscribe('superTabClose', (e) => {
250
+ const { appId, isSubTab } = e;
251
+ if (appId && !isSubTab) {
252
+ // drop the cache of the tab
253
+ drop(appId);
254
+ }
255
+ })
256
+ return unsub;
257
+ }, []);
258
+ ```