@bydata/react-supertabs 1.2.4 → 1.2.6

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,322 @@
1
+ # SuperTabs
2
+
3
+ `SuperTabs` is a production-ready React navigation library for managing parent tabs and nested sub-tabs in complex applications.
4
+
5
+ It provides a robust tab lifecycle with route integration, privilege-aware module access, and extensible UI controls for module-level workflows.
6
+
7
+ ### Highlights
8
+
9
+ - Parent tab and sub-tab navigation with URL synchronization
10
+ - Drag-and-drop tab reordering
11
+ - Configurable module actions (`showAddButton` and `customAddButtons`)
12
+ - Pluggable handlers for open, close, reorder, and fetch operations
13
+
14
+ ---
15
+
16
+ ## 1) Setup
17
+
18
+ Wrap your app with `TabProvider`.
19
+
20
+ ```jsx
21
+ import { TabProvider } from "components/SuperTabs/TabContext";
22
+ import { alertService } from "super-notifier";
23
+
24
+ <TabProvider
25
+ SITE_PREFIX={SITE_PREFIX}
26
+ SITE_PAGES={SITE_PAGES}
27
+ homeUrl="/app"
28
+ getUserDetails={getUser}
29
+ hasPrivilege={(privilege) => checkPrivilege(getUser().privileges, privilege)}
30
+ getTabs={getTabs}
31
+ handleOpenTab={handleOpenTab}
32
+ handleCloseTab={handleCloseTab}
33
+ handleReorderTabs={handleReorderTabs}
34
+ isDefaultExpanded={true}
35
+ alertService={alertService}
36
+ >
37
+ <Main />
38
+ </TabProvider>;
39
+ ```
40
+
41
+ Render tabs in header:
42
+
43
+ ```jsx
44
+ import SuperTabs from "components/SuperTabs/SuperTabs";
45
+
46
+ <header>
47
+ <SuperTabs />
48
+ </header>;
49
+ ```
50
+
51
+ ---
52
+
53
+ ## 2) `TabProvider` props
54
+
55
+ ### Required
56
+
57
+ - `SITE_PREFIX: string`
58
+ - `SITE_PAGES: Array<SitePage>`
59
+
60
+ `SitePage` is typically:
61
+
62
+ ```js
63
+ {
64
+ id: 2,
65
+ title: "Recruiter",
66
+ url: "/app/recruiter/f-home",
67
+ privilege: "RECRUITER",
68
+ containsSubTabs: true,
69
+ showAddButton: true, // boolean or privilege array
70
+ customAddButtons: [
71
+ { id: 1, name: "Data Grid", privilege: "RECRUITER" },
72
+ { id: 2, name: "Trend Master", privilege: "RECRUITER" }
73
+ ],
74
+ showInMobile: true, // optional
75
+ isExternal: false // optional
76
+ }
77
+ ```
78
+
79
+ ### Optional
80
+
81
+ - `homeUrl` (default: `/`)
82
+ - `entityIdentifierKey` (default: `organization_id`)
83
+ - `persistTabsAfterLogin` (default: `false`)
84
+ - `preventHomeRedirect` (default: `false`)
85
+ - When `true`, clicking home opens module menu instead of redirecting.
86
+ - `retainTabsAfterEntityChange` (default: `false`)
87
+ - `shouldRedirectToSubTab` (default: `true`)
88
+ - `isMobileView` (default: `false`)
89
+ - `isDefaultExpanded` (default: `false`)
90
+ - `getUserDetails` (function)
91
+ - `hasPrivilege` (function)
92
+ - `getTabs` (async function)
93
+ - `handleOpenTab` (async function)
94
+ - `handleCloseTab` (async function)
95
+ - `handleReorderTabs` (async function)
96
+ - `alertService` object with optional methods: `success`, `warning`, `error`, `info`, `alert`
97
+
98
+ ---
99
+
100
+ ## 3) API callback contracts
101
+
102
+ ### `getTabs({ payload, controller })`
103
+
104
+ Expected shape:
105
+
106
+ ```js
107
+ {
108
+ status: 1,
109
+ tabs: [], // flat backend tabs with tab_info
110
+ current_tab: {} | null, // current tab from backend
111
+ hasNoTabs: false // true when backend has no open tabs
112
+ }
113
+ ```
114
+
115
+ ### `handleOpenTab(payload[, controller])`
116
+
117
+ Expected shape:
118
+
119
+ ```js
120
+ {
121
+ status: 1,
122
+ tabId: 123,
123
+ children: [] // optional child tabs
124
+ }
125
+ ```
126
+
127
+ ### `handleCloseTab(tabId)`
128
+
129
+ Expected shape:
130
+
131
+ ```js
132
+ {
133
+ status: 1,
134
+ current_tab_id: 456 // optional
135
+ }
136
+ ```
137
+
138
+ ### `handleReorderTabs(payload)`
139
+
140
+ Expected shape:
141
+
142
+ ```js
143
+ {
144
+ status: 1;
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 4) Opening tabs
151
+
152
+ ### Option 1: `SuperLink`
153
+
154
+ ```jsx
155
+ import SuperLink from "components/SuperTabs/SuperLink";
156
+
157
+ <SuperLink tab={page} isSubTab={false}>
158
+ {page.title}
159
+ </SuperLink>;
160
+ ```
161
+
162
+ Open external links:
163
+
164
+ ```jsx
165
+ <SuperLink tab={{ ...page, isExternal: true }} isSubTab={false} isExternal>
166
+ {page.title}
167
+ </SuperLink>
168
+ ```
169
+
170
+ ### Option 2: `openSuperTabOnRowClick`
171
+
172
+ ```jsx
173
+ import { useTabContext } from "components/SuperTabs/TabContext";
174
+
175
+ const { openSuperTabOnRowClick } = useTabContext();
176
+
177
+ function handleRowClick(row) {
178
+ openSuperTabOnRowClick({
179
+ tab: {
180
+ id: row.id,
181
+ name: row.name,
182
+ url: `/app/recruiter/f-${row.id}`,
183
+ },
184
+ isSubTab: true, // defaults to true
185
+ });
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## 5) Tab object notes
192
+
193
+ Common fields:
194
+
195
+ - `id`
196
+ - `url`
197
+ - one of `name` / `title`
198
+
199
+ Optional:
200
+
201
+ - `showAvatar`, `firstName`, `lastName`, `imgSrc` or `img_url`
202
+ - `tooltip` for hover text; falls back to tab title/name when empty
203
+ - `isExternal`
204
+ - any extra metadata your module uses
205
+
206
+ ```js
207
+ {
208
+ id: 100,
209
+ url: "/app/profile/100",
210
+ tooltip: "Profile: John Doe",
211
+ firstName: "John",
212
+ lastName: "Doe",
213
+ showAvatar: true,
214
+ imgSrc: "https://cdn.example.com/u100.png"
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ## 6) Add button callbacks
221
+
222
+ Bind module add callbacks using emitter:
223
+
224
+ ```jsx
225
+ import { useCallback, useEffect } from "react";
226
+ import { emitter } from "components/SuperTabs/SuperTabs";
227
+ import { useTabContext } from "components/SuperTabs/TabContext";
228
+
229
+ const { openSuperTabOnRowClick } = useTabContext();
230
+
231
+ const handleTabAddBtn = useCallback(
232
+ (customButtonData) => {
233
+ // customButtonData is passed when using customAddButtons
234
+ openSuperTabOnRowClick({
235
+ tab: {
236
+ id: 123,
237
+ name: "New Funnel",
238
+ url: "/app/recruiter/f-123",
239
+ },
240
+ isSubTab: true,
241
+ });
242
+ },
243
+ [openSuperTabOnRowClick],
244
+ );
245
+
246
+ useEffect(() => {
247
+ const unsub = emitter.emit("bindCallback", {
248
+ id: 2, // same parent module id from SITE_PAGES
249
+ callback: handleTabAddBtn,
250
+ });
251
+ return unsub;
252
+ }, [handleTabAddBtn]);
253
+ ```
254
+
255
+ ---
256
+
257
+ ## 7) Close cleanup event
258
+
259
+ ```jsx
260
+ import { useEffect } from "react";
261
+ import { emitter } from "components/SuperTabs/SuperTabs";
262
+
263
+ useEffect(() => {
264
+ const unsub = emitter.subscribe(
265
+ "superTabClose",
266
+ ({ appId, isSubTab, tab }) => {
267
+ if (appId === 2 && isSubTab) {
268
+ // cleanup here
269
+ }
270
+ },
271
+ );
272
+ return unsub;
273
+ }, []);
274
+ ```
275
+
276
+ ---
277
+
278
+ ## 8) Not Available page
279
+
280
+ ```jsx
281
+ import NotAvailable from "components/SuperTabs/NotAvailable";
282
+
283
+ <NotAvailable subTabName="Profile" />;
284
+ ```
285
+
286
+ ---
287
+
288
+ ## 9) Keep-alive (optional)
289
+
290
+ Use shared keep-alive utility:
291
+
292
+ ```jsx
293
+ import { withKeepAlive } from "components/KeepAlive";
294
+
295
+ const Component = () => <div>Component</div>;
296
+ export default withKeepAlive(Component, 2); // module id
297
+ ```
298
+
299
+ Cleanup keep-alive cache on parent tab close:
300
+
301
+ ```jsx
302
+ import { useEffect } from "react";
303
+ import { useAliveScope } from "components/KeepAlive";
304
+ import { emitter } from "components/SuperTabs/SuperTabs";
305
+
306
+ const { drop } = useAliveScope();
307
+
308
+ useEffect(() => {
309
+ const unsub = emitter.subscribe("superTabClose", ({ appId, isSubTab }) => {
310
+ if (appId && !isSubTab) {
311
+ drop(appId);
312
+ }
313
+ });
314
+ return unsub;
315
+ }, [drop]);
316
+ ```
317
+
318
+ ---
319
+
320
+ ## 10) Styling note
321
+
322
+ Home tab logo can be styled with `.brand-logo`.
package/dist/SuperTabs.js CHANGED
@@ -56,6 +56,13 @@ var tooltipStyles = {
56
56
  backgroundColor: 'var(--tab-background-overlay, #272B32)',
57
57
  color: 'var(--tab-text-normal, #BDC3CC)'
58
58
  };
59
+ var getTooltipContent = function getTooltipContent(item, fallbackTitle) {
60
+ var customTooltip = item === null || item === void 0 ? void 0 : item.tooltip;
61
+ if (typeof customTooltip === 'string' && customTooltip.trim()) {
62
+ return customTooltip.trim();
63
+ }
64
+ return fallbackTitle;
65
+ };
59
66
  var filterSubTabs = function filterSubTabs(subTabs) {
60
67
  // const subTabsInList = [];
61
68
  var subTabsInNavbar = [];
@@ -884,7 +891,7 @@ var SuperTabs = function SuperTabs(_ref) {
884
891
  className: 'child-tabs-wrapper tab-' + tab.id + (showSubTabs ? ' expanded' : '')
885
892
  // (isActive ? ' active' : '')
886
893
  }, /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
887
- content: tab.title,
894
+ content: getTooltipContent(tab, tab.title),
888
895
  delay: 500,
889
896
  followCursor: true,
890
897
  placement: "bottom",
@@ -1002,7 +1009,7 @@ var SuperTabs = function SuperTabs(_ref) {
1002
1009
  isDragDisabled: isMobileScreen
1003
1010
  }, function (provided, snapshot) {
1004
1011
  return /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
1005
- content: tab.title,
1012
+ content: getTooltipContent(tab, tab.title),
1006
1013
  delay: 500,
1007
1014
  followCursor: true,
1008
1015
  placement: "bottom",
@@ -1186,7 +1193,7 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1186
1193
  index: subIndex
1187
1194
  }, function (subprovided, snapshot) {
1188
1195
  return /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
1189
- content: title,
1196
+ content: getTooltipContent(subTab, title),
1190
1197
  delay: 500,
1191
1198
  followCursor: true,
1192
1199
  show: showTooltip,
@@ -293,6 +293,8 @@ function TabProvider(_ref9) {
293
293
  setTabModePreference = _useState26[1];
294
294
  var previousEntityId = (0, _usePrevious["default"])(currentEntityId);
295
295
  var closeTabFailedRequestsRef = (0, _react.useRef)({});
296
+ /** AbortController per client tab id for in-flight open_tab API */
297
+ var openTabAbortByClientIdRef = (0, _react.useRef)({});
296
298
  (0, _react.useEffect)(function () {
297
299
  localStorage.setItem(SITE_PREFIX + "tab_mode_preference", JSON.stringify(tabModePreference));
298
300
  }, [tabModePreference]);
@@ -570,6 +572,7 @@ function TabProvider(_ref9) {
570
572
  isSubTab,
571
573
  _ref16$keepInactive,
572
574
  keepInactive,
575
+ openTabAbortController,
573
576
  _payload$tab_info,
574
577
  isEditMode,
575
578
  unique_identifier,
@@ -586,6 +589,8 @@ function TabProvider(_ref9) {
586
589
  payload,
587
590
  storedExpandedIds,
588
591
  isExpanded,
592
+ clientTabId,
593
+ previousOpenAbort,
589
594
  response,
590
595
  _tabId,
591
596
  newTab,
@@ -595,6 +600,8 @@ function TabProvider(_ref9) {
595
600
  while (1) switch (_context5.p = _context5.n) {
596
601
  case 0:
597
602
  _ref16 = _args5.length > 0 && _args5[0] !== undefined ? _args5[0] : {}, tab = _ref16.tab, _ref16$isHomeTab = _ref16.isHomeTab, isHomeTab = _ref16$isHomeTab === void 0 ? false : _ref16$isHomeTab, _ref16$isSubTab = _ref16.isSubTab, isSubTab = _ref16$isSubTab === void 0 ? false : _ref16$isSubTab, _ref16$keepInactive = _ref16.keepInactive, keepInactive = _ref16$keepInactive === void 0 ? false : _ref16$keepInactive;
603
+ // return;
604
+ openTabAbortController = null;
598
605
  _context5.p = 1;
599
606
  if (!isSubTab) {
600
607
  updateActiveTab(tab);
@@ -643,10 +650,16 @@ function TabProvider(_ref9) {
643
650
  }
644
651
  }
645
652
  (_payload$tab_info = payload.tab_info) === null || _payload$tab_info === void 0 || delete _payload$tab_info.isClientChanged;
646
- // return;
653
+ clientTabId = tab.id;
654
+ previousOpenAbort = openTabAbortByClientIdRef.current[clientTabId];
655
+ if (previousOpenAbort) {
656
+ previousOpenAbort.abort();
657
+ }
658
+ openTabAbortController = new AbortController();
659
+ openTabAbortByClientIdRef.current[clientTabId] = openTabAbortController;
647
660
  currentOpenTabCallerRef.current = payload.tab_info;
648
661
  _context5.n = 3;
649
- return handleOpenTab(payload);
662
+ return handleOpenTab(payload, openTabAbortController);
650
663
  case 3:
651
664
  response = _context5.v;
652
665
  if (!(response.status === 1)) {
@@ -749,12 +762,19 @@ function TabProvider(_ref9) {
749
762
  case 9:
750
763
  _context5.p = 9;
751
764
  _t = _context5.v;
752
- if (window.isDebugOn) {
753
- alertService === null || alertService === void 0 || alertService.error(_t.message ? _t.message : 'Error in opening tab!');
765
+ if (_t instanceof DOMException && _t.name === 'AbortError') {
766
+ // Tab closed or superseded while open was in progress
767
+ } else {
768
+ if (window.isDebugOn) {
769
+ alertService === null || alertService === void 0 || alertService.error(_t.message ? _t.message : 'Error in opening tab!');
770
+ }
771
+ console.log("openTab - error", _t);
754
772
  }
755
- console.log("openTab - error", _t);
756
773
  case 10:
757
774
  _context5.p = 10;
775
+ if (openTabAbortController != null && openTabAbortByClientIdRef.current[tab.id] === openTabAbortController) {
776
+ delete openTabAbortByClientIdRef.current[tab.id];
777
+ }
758
778
  currentOpenTabCallerRef.current = null;
759
779
  return _context5.f(10);
760
780
  case 11:
@@ -1094,8 +1114,10 @@ function TabProvider(_ref9) {
1094
1114
  tab,
1095
1115
  _ref25$isSubTab,
1096
1116
  isSubTab,
1097
- response,
1117
+ pendingOpenAbort,
1098
1118
  _activeSubTabRef$curr5,
1119
+ response,
1120
+ _activeSubTabRef$curr6,
1099
1121
  tabId,
1100
1122
  updatedTabModePreference,
1101
1123
  _args8 = arguments,
@@ -1231,7 +1253,21 @@ function TabProvider(_ref9) {
1231
1253
  break;
1232
1254
  }
1233
1255
  console.log('Tab id not found', tab);
1234
- history.replace("/");
1256
+ pendingOpenAbort = openTabAbortByClientIdRef.current[tab.id];
1257
+ if (pendingOpenAbort) {
1258
+ pendingOpenAbort.abort();
1259
+ delete openTabAbortByClientIdRef.current[tab.id];
1260
+ }
1261
+ if (isSubTab && activeSubTabRef.current) {
1262
+ if (isSubTab && ((_activeSubTabRef$curr5 = activeSubTabRef.current) === null || _activeSubTabRef$curr5 === void 0 ? void 0 : _activeSubTabRef$curr5.open_order) > 2) {
1263
+ updateCurrentTab({
1264
+ tab: activeSubTabRef.current,
1265
+ isSubTab: true,
1266
+ parentId: activeTabRef.current.id
1267
+ });
1268
+ }
1269
+ activeSubTabRef.current.open_order = 1;
1270
+ }
1235
1271
  return _context8.a(2);
1236
1272
  case 2:
1237
1273
  _context8.n = 3;
@@ -1245,7 +1281,7 @@ function TabProvider(_ref9) {
1245
1281
  console.log('closeTab - response.current_tab_id', response.current_tab_id, activeSubTabRef.current);
1246
1282
  // if (isSubTab && response.current_tab_id !== activeSubTabRef.current?.tabId ) {
1247
1283
  if (isSubTab && activeSubTabRef.current) {
1248
- if (isSubTab && ((_activeSubTabRef$curr5 = activeSubTabRef.current) === null || _activeSubTabRef$curr5 === void 0 ? void 0 : _activeSubTabRef$curr5.open_order) > 2) {
1284
+ if (isSubTab && ((_activeSubTabRef$curr6 = activeSubTabRef.current) === null || _activeSubTabRef$curr6 === void 0 ? void 0 : _activeSubTabRef$curr6.open_order) > 2) {
1249
1285
  updateCurrentTab({
1250
1286
  tab: activeSubTabRef.current,
1251
1287
  isSubTab: true,
@@ -1486,7 +1522,7 @@ function TabProvider(_ref9) {
1486
1522
  if (currentEntityId != null && currentEntityId != undefined) {
1487
1523
  var fetchTabDetails = /*#__PURE__*/function () {
1488
1524
  var _ref29 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
1489
- var href, orgId, payload, response, _response$current_tab, _response$current_tab2, _response$current_tab4, pathName, isHomeTab, tabsOutputWithoutDuplicates, tabsOutput, _activeTab, _activeSubTab, currentActiveTab, _activeTab2, _response$current_tab3, tab_info, rest, flattenedObj, isCurrentTabOpen, openedParentTab, updatedParentTab, flattenedParentObj, parentTab, _activeSubTabRef$curr6, parentTabFromData, _flattenedParentObj, _tab_info, _rest, _flattenedObj, isSubTab, parent, parentTabInfo, parentRest, _flattenedParentObj2, tabsMap, restTabs, id, _tabsMap$id, _activeSubTab$tabId, _activeSubTab2, _activeTab3, tabId, isEditMode, tempTabModePreference, updated, _t8;
1525
+ var href, orgId, payload, response, _response$current_tab, _response$current_tab2, _response$current_tab4, pathName, isHomeTab, tabsOutputWithoutDuplicates, tabsOutput, _activeTab, _activeSubTab, currentActiveTab, _activeTab2, _response$current_tab3, tab_info, rest, flattenedObj, isCurrentTabOpen, openedParentTab, updatedParentTab, flattenedParentObj, parentTab, _activeSubTabRef$curr7, parentTabFromData, _flattenedParentObj, _tab_info, _rest, _flattenedObj, isSubTab, parent, parentTabInfo, parentRest, _flattenedParentObj2, tabsMap, restTabs, id, _tabsMap$id, _activeSubTab$tabId, _activeSubTab2, _activeTab3, tabId, isEditMode, tempTabModePreference, updated, _t8;
1490
1526
  return _regenerator().w(function (_context10) {
1491
1527
  while (1) switch (_context10.p = _context10.n) {
1492
1528
  case 0:
@@ -1617,7 +1653,7 @@ function TabProvider(_ref9) {
1617
1653
  name: 'Loading...',
1618
1654
  title: ''
1619
1655
  });
1620
- if ((_activeSubTabRef$curr6 = activeSubTabRef.current) !== null && _activeSubTabRef$curr6 !== void 0 && _activeSubTabRef$curr6.tabId) _activeSubTab.tabId = activeSubTabRef.current.tabId;
1656
+ if ((_activeSubTabRef$curr7 = activeSubTabRef.current) !== null && _activeSubTabRef$curr7 !== void 0 && _activeSubTabRef$curr7.tabId) _activeSubTab.tabId = activeSubTabRef.current.tabId;
1621
1657
  case 10:
1622
1658
  _context10.n = 12;
1623
1659
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bydata/react-supertabs",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "A customizable React super tabs component.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
package/README.md DELETED
@@ -1,262 +0,0 @@
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
13
- ------
14
-
15
- ```bash
16
- npm install @bydata/react-supertabs
17
- ```
18
-
19
- ### Usage
20
- ------
21
-
22
- #### Basic
23
-
24
- >**Import TabProvider and wrap it around the main wrapper**
25
-
26
- - This is needed to access useTabContext.
27
- - Make sure to pass `SITE_PREFIX` and `SITE_PAGES` (From Navigation.js)
28
- - `homeUrl` is optional and defaults to "/"
29
- - `persistTabsAfterLogin` is optional and defaults to "false"
30
- - Used to preserve tab state upon login.
31
- - `entityIdentifierKey` is optional and defaults to 'organization_id',
32
- - Used to identify the organization / client id
33
- - `preventHomeRedirect` is optional and defaults to false,
34
- - This will open a menu on click of home button instead of redirecting
35
- - `handleOpenTab` async function to open the tab in backend
36
- - expects the following keys in return
37
- - { status: 1, tabId, children: undefined }
38
- - `handleCloseTab` async function to open the tab in backend
39
- - expects the following keys in return
40
- - { status: 1, current_tab_id: 123 }
41
- - `handleReorderTabs` async function to open the tab in backend
42
- - { status: 1 }
43
- - `getTabs` async function to open the tab in backend async () => { },
44
- - expects the following keys in return
45
- - { status: 1, tabs: [], current_tab: {} || null, hasNoTabs }
46
- - hasNoTabs = user for whom there are no tabs open
47
- - `getUserDetails` function which return the user details from user token
48
- - `hasPrivilege` function which checks if the user has the privilege passed in function param
49
- - `alertService` An object containing different methods for displaying toast alerts.
50
- - can call success, warning, error, info, alert methods.
51
- - super-notifier - NPM package can be used here
52
- - https://www.npmjs.com/package/super-notifier
53
-
54
- ```
55
- Eg.
56
- const SITE_PREFIX = 'teamLink_'
57
- const API_BASE_URL = 'https://api.teamlink.com/data_stream'
58
- const SITE_PAGES = [{
59
- title: "Organization",
60
- url: "/app/organization",
61
- privilege: "ORGANIZATION",
62
- id: 1,
63
- },
64
- {
65
- title: "Recruiter",
66
- url: "/app/recruiter/f-home",
67
- privilege: "RECRUITER",
68
- showAddButton: true,
69
- customAddButtons: [
70
- {
71
- name: "Data Grid",
72
- id: 1,
73
- },
74
- {
75
- name: "Trend Master",
76
- id: 2,
77
- }
78
- ],
79
- containsSubTabs: true,
80
- id: 2,
81
- }]
82
-
83
- // showAddButton - can be added if the module needs plus button.
84
- // containsSubTabs - can be added if the tab includes sub tabs.
85
- // customAddButtons - These buttons will be added in the sub tab list. Clicking on the button will trigger the binded callback with object as param.
86
-
87
- import { TabProvider } from '@bydata/react-supertabs';
88
- <TabProvider
89
- SITE_PREFIX={SITE_PREFIX}
90
- SITE_PAGES={SITE_PAGES}
91
- homeUrl="/app"
92
- getTabs={getTabs}
93
- handleOpenTab={handleOpenTab}
94
- handleCloseTab={handleCloseTab}
95
- handleReorderTabs={handleReorderTabs}
96
- alertService={alertService}
97
- >
98
- <Main />
99
- </TabProvider>
100
- ```
101
-
102
- >**Render the component within the App's header**
103
- ```
104
- import { SuperTabs, useTabContext } from '@bydata/react-supertabs';
105
-
106
- <header>
107
- <SuperTabs />
108
- </header>
109
- ```
110
-
111
- >**Wrap the hyperlink as follows:**
112
-
113
- Each tab should contain all the necessary information to be stored.
114
-
115
- Mandatory Fields:
116
- - `id`
117
- - `name`
118
- - `url`
119
-
120
- For Avatar:
121
- - `showAvatar` must be set to `true`
122
- - The following fields must be provided:
123
- - `firstName`
124
- - `lastName`
125
- - `imgSrc`
126
-
127
- ```
128
- // Tab object Examples
129
-
130
- {
131
- id: 1,
132
- name: 'title',
133
- url: '/recruiter/f-1`
134
- }
135
-
136
- {
137
- id: 100,
138
- url: '/profile/100`
139
- firstName: 'John',
140
- lastName: 'Doe',
141
- showAvatar: true,
142
- imgSrc: img_url,
143
- department,
144
- designation,
145
- department_id,
146
- }
147
- ```
148
- ```
149
- import { SuperLink } from '@bydata/react-supertabs';
150
- <SuperLink tab={page} isSubTab={false}>
151
- {page.title}
152
- </SuperLink>
153
- ```
154
-
155
- OR
156
-
157
- ```
158
- import { useTabContext } from '@bydata/react-supertabs';
159
- const { openSuperTabOnRowClick } = useTabContext();
160
-
161
- function handleRowClick(e) {
162
- const tab = { id: e.id, name: e.name, url: `/recruiter/f-${e.id}` };
163
- // isSubTab defaults to true
164
- openSuperTabOnRowClick({ tab, isSubTab });
165
- }
166
- ```
167
-
168
- >**Bind the callback for the plus button to each module during the initial render.**
169
-
170
- If a module requires a plus button for adding a new tab, you can include the following:
171
- ```
172
- import { emitter, useTabContext } from '@bydata/react-supertabs';
173
-
174
- const { openSuperTabOnRowClick } = useTabContext();
175
-
176
- const handleTabAddBtn = useCallback(() => {
177
- openSuperTabOnRowClick({
178
- tab: {
179
- id: 123,
180
- name: `New Funnel`,
181
- url: `/recruiter/f-${123}`,
182
- },
183
- isSubTab: true,
184
- });
185
- }, [openSuperTabOnRowClick]);
186
-
187
- useEffect(() => {
188
- const unsub = emitter.emit("bindCallback", {
189
- id: 2, // Same id provided for this module in navigation.js
190
- callback: handleTabAddBtn,
191
- });
192
- return unsub;
193
- }, [handleTabAddBtn]);
194
- ```
195
-
196
- > Note: The logo of the home tab can be updated by targeting the `brand-logo` class.
197
-
198
- -----
199
-
200
- #### Advanced
201
-
202
- >**Cleanup**
203
-
204
- When a tab is closed, it may be necessary to clean up some data. The `superTabClose` event can be utilized for this purpose.
205
-
206
- ```
207
- import { emitter } from '@bydata/react-supertabs';
208
-
209
- useEffect(() => {
210
- const unsub = emitter.subscribe('superTabClose', ({ appId, isSubTab, tab }) => {
211
- // Same id provided for this module in navigation.js
212
- if (appId === 2 && isSubTab) {
213
- // handle clean up here
214
- }
215
- })
216
- return unsub;
217
- }, []);
218
- ```
219
-
220
- >**Not Available page**
221
- - If a profile is deleted, the tab will remain open. To address this, we can display a "Page Not Available" message.
222
-
223
- ```
224
- import { NotAvailable } from '@bydata/react-supertabs';
225
-
226
- <NotAvailable subTabName='Profile' />
227
- ```
228
-
229
- >**Keep Alive (Experimental)**
230
-
231
- ```
232
- import { withKeepAlive } from '@bydata/react-supertabs';
233
-
234
- // Same id provided for this module in navigation.js
235
- export default withKeepAlive(Component, Unique ID);
236
-
237
- Eg.
238
- const Component = () => {
239
- return <div>Component</div>
240
- }
241
-
242
- export default withKeepAlive(Component, 2);
243
- ```
244
- Keep Alive Cleanup
245
- ```
246
- // This can be incorporated into a component that is always rendered, such as the header.
247
-
248
- import { useAliveScope, emitter } from '@bydata/react-supertabs';
249
-
250
- const { drop } = useAliveScope();
251
-
252
- useEffect(() => {
253
- const unsub = emitter.subscribe('superTabClose', (e) => {
254
- const { appId, isSubTab } = e;
255
- if (appId && !isSubTab) {
256
- // drop the cache of the tab
257
- drop(appId);
258
- }
259
- })
260
- return unsub;
261
- }, []);
262
- ```