@bydata/react-supertabs 1.2.5 → 1.2.7

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/Constants.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
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;
6
+ exports.transitionDurationExtended = exports.transitionDuration = exports.fontStyle = exports.ToolbarButtonWidth = exports.SubTabWrapperPadding = exports.SubTabPadding = exports.SubTabGap = exports.StackButtonWidth = exports.MarginLeft = exports.MainTabGap = exports.HomeTabWidth = exports.DEFAULT_HOME_URL = exports.CloseButtonWidth = exports.AvatarSize = void 0;
7
7
  var transitionDuration = exports.transitionDuration = 300;
8
8
  var transitionDurationExtended = exports.transitionDurationExtended = 800;
9
9
  var HomeTabWidth = exports.HomeTabWidth = 36;
@@ -17,4 +17,6 @@ var AvatarSize = exports.AvatarSize = 20 + SubTabGap;
17
17
  var ToolbarButtonWidth = exports.ToolbarButtonWidth = 24;
18
18
  var CloseButtonWidth = exports.CloseButtonWidth = 20; // 24-4px is for the right padding adjustment
19
19
 
20
- var fontStyle = exports.fontStyle = '600 12px "Open Sans", "Poppins", sans-serif'; // "same as supertabs.scss global font style"
20
+ var fontStyle = exports.fontStyle = '600 12px "Open Sans", "Poppins", sans-serif'; // "same as supertabs.scss global font style"
21
+
22
+ var DEFAULT_HOME_URL = exports.DEFAULT_HOME_URL = "/";
package/dist/Readme.md CHANGED
@@ -199,6 +199,7 @@ Common fields:
199
199
  Optional:
200
200
 
201
201
  - `showAvatar`, `firstName`, `lastName`, `imgSrc` or `img_url`
202
+ - `tooltip` for hover text; falls back to tab title/name when empty
202
203
  - `isExternal`
203
204
  - any extra metadata your module uses
204
205
 
@@ -206,6 +207,7 @@ Optional:
206
207
  {
207
208
  id: 100,
208
209
  url: "/app/profile/100",
210
+ tooltip: "Profile: John Doe",
209
211
  firstName: "John",
210
212
  lastName: "Doe",
211
213
  showAvatar: true,
package/dist/SuperLink.js CHANGED
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports["default"] = void 0;
7
7
  var _react = _interopRequireDefault(require("react"));
8
- var _reactRouterDom = require("react-router-dom");
8
+ var _reactRouter = require("react-router");
9
9
  var _TabContext = require("./TabContext");
10
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
11
11
  var SuperLink = function SuperLink(_ref) {
@@ -28,7 +28,7 @@ var SuperLink = function SuperLink(_ref) {
28
28
  isSubTab: isSubTab
29
29
  });
30
30
  };
31
- return /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, {
31
+ return /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, {
32
32
  to: tab.url,
33
33
  title: tab.title || '',
34
34
  onClick: handleSuperLinkClick
package/dist/SuperTabs.js CHANGED
@@ -5,13 +5,13 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.emitter = exports["default"] = exports.SubTab = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
- var _reactRouterDom = require("react-router-dom");
8
+ var _reactRouter = require("react-router");
9
9
  var _superDnd = require("super-dnd");
10
- var _ClickOutsideListener = _interopRequireDefault(require("./ClickOutsideListener"));
11
- var _reactHoverText = _interopRequireDefault(require("react-hover-text"));
12
10
  var _superAvatar = _interopRequireDefault(require("super-avatar"));
13
- var _SuperLink = _interopRequireDefault(require("./SuperLink"));
14
11
  var _TabStack = _interopRequireDefault(require("./TabStack"));
12
+ var _ClickOutsideListener = _interopRequireDefault(require("./ClickOutsideListener"));
13
+ var _SuperLink = _interopRequireDefault(require("./SuperLink"));
14
+ var _reactHoverText = _interopRequireDefault(require("react-hover-text"));
15
15
  var _TabContext = require("./TabContext");
16
16
  var _eventEmitter = _interopRequireDefault(require("./eventEmitter"));
17
17
  var _Utils = require("./Utils");
@@ -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 = [];
@@ -72,11 +79,10 @@ var filterSubTabs = function filterSubTabs(subTabs) {
72
79
  // return { subTabsInList, subTabsInNavbar };
73
80
  };
74
81
  var getActiveParentTabName = function getActiveParentTabName(url, homeUrl) {
75
- var homeSegments = homeUrl.split('/').filter(Boolean);
76
- if (homeSegments.length === 1) {
77
- return url.split('/')[1]; // ex. /organization/1 => organization, /recruiter/home => recruiter
78
- }
79
- return url.split('/')[2]; // e.g. /app/organization/1 => organization (has app prefix)
82
+ var urlSegments = url.split('/').filter(Boolean);
83
+ // if homeUrl is default, return the first segment (e.g. /organization/1 => organization, /recruiter/home => recruiter)
84
+ // if not default, return the second segment (e.g. /app/organization/1 => organization (has app prefix))
85
+ return homeUrl === _Constants.DEFAULT_HOME_URL ? urlSegments[0] : urlSegments[1];
80
86
  };
81
87
 
82
88
  // function calculateTransitionDuration(totalWidth, avgWidth = maxTabWidth) {
@@ -150,8 +156,8 @@ var SuperTabs = function SuperTabs(_ref) {
150
156
  }, _callee);
151
157
  }))();
152
158
  }, [updateMaxWidth]);
153
- var history = (0, _reactRouterDom.useHistory)();
154
- var location = (0, _reactRouterDom.useLocation)();
159
+ var navigate = (0, _reactRouter.useNavigate)();
160
+ var location = (0, _reactRouter.useLocation)();
155
161
  var _useState = (0, _react.useState)(function (isDefaultExpanded) {
156
162
  if (isDefaultExpanded) updateMaxWidth();
157
163
  return isDefaultExpanded;
@@ -705,8 +711,8 @@ var SuperTabs = function SuperTabs(_ref) {
705
711
  return /*#__PURE__*/_react["default"].createElement("div", {
706
712
  className: "super-tabs-wrapper",
707
713
  ref: superTabsWrapperElemRef
708
- }, /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, {
709
- to: "/",
714
+ }, /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, {
715
+ to: homeUrl,
710
716
  className: "home-tab",
711
717
  ref: homeTabRef
712
718
  // activeClassName="active"
@@ -734,7 +740,7 @@ var SuperTabs = function SuperTabs(_ref) {
734
740
  });
735
741
  resetMaxWidth();
736
742
  },
737
- exact: true
743
+ end: true
738
744
  }, /*#__PURE__*/_react["default"].createElement("div", {
739
745
  className: "brand-wrapper"
740
746
  }, /*#__PURE__*/_react["default"].createElement("span", {
@@ -884,12 +890,12 @@ var SuperTabs = function SuperTabs(_ref) {
884
890
  className: 'child-tabs-wrapper tab-' + tab.id + (showSubTabs ? ' expanded' : '')
885
891
  // (isActive ? ' active' : '')
886
892
  }, /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
887
- content: tab.title,
893
+ content: getTooltipContent(tab, tab.title),
888
894
  delay: 500,
889
895
  followCursor: true,
890
896
  placement: "bottom",
891
897
  stylingOptions: tooltipStyles
892
- }, /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, {
898
+ }, /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, {
893
899
  to: tab.url,
894
900
  className: "tab" + " tab-" + tab.id + ((expandedTabID ? tab.width : maxWidth) && (0, _Utils.compareDecimals)(expandedTabID ? tab.width || width : maxWidth, width) === -1 ? ' fade-at-end' : '') + (!hasAccessToTab ? ' disabled-tab' : '') + (containsSubTabs ? ' has-child' : '') + (shrinkedTab ? ' shrinked-tab' : '')
895
901
  // (showSubTabs ? ' expanded' : '')
@@ -922,7 +928,7 @@ var SuperTabs = function SuperTabs(_ref) {
922
928
  // return updated;
923
929
  // });
924
930
  }
925
- history.push(lastOpenedTabUrl || tab.url);
931
+ navigate(lastOpenedTabUrl || tab.url);
926
932
  // } else {
927
933
  // setTimeout(() => {
928
934
  // toggleSubTabs(tab);
@@ -935,7 +941,7 @@ var SuperTabs = function SuperTabs(_ref) {
935
941
  ignoreActiveTabUpdate: ignoreActiveTabUpdate
936
942
  });
937
943
  },
938
- exact: tab.fixed
944
+ end: tab.fixed
939
945
  }, /*#__PURE__*/_react["default"].createElement(TabContentWrapper, {
940
946
  tab: tab,
941
947
  index: index,
@@ -1002,12 +1008,12 @@ var SuperTabs = function SuperTabs(_ref) {
1002
1008
  isDragDisabled: isMobileScreen
1003
1009
  }, function (provided, snapshot) {
1004
1010
  return /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
1005
- content: tab.title,
1011
+ content: getTooltipContent(tab, tab.title),
1006
1012
  delay: 500,
1007
1013
  followCursor: true,
1008
1014
  placement: "bottom",
1009
1015
  stylingOptions: tooltipStyles
1010
- }, /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, _extends({}, provided.draggableProps, provided.dragHandleProps, {
1016
+ }, /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, _extends({}, provided.draggableProps, provided.dragHandleProps, {
1011
1017
  ref: provided.innerRef,
1012
1018
  to: url,
1013
1019
  onClick: function onClick() {
@@ -1017,7 +1023,7 @@ var SuperTabs = function SuperTabs(_ref) {
1017
1023
  },
1018
1024
  className: "tab parent-super-tab" + (snapshot.isDragging ? " dragging" : "") + (maxWidth && (0, _Utils.compareDecimals)(maxWidth, width) === -1 ? ' fade-at-end' : '') + (expandedTabID ? ' shrinked-tab' : '') + (!hasAccessToTab ? " disabled-tab" : ""),
1019
1025
  style: getItemStyle(provided.draggableProps.style),
1020
- exact: true
1026
+ end: true
1021
1027
  // exact={tab.exact}
1022
1028
  // title={tab.title}
1023
1029
  }), /*#__PURE__*/_react["default"].createElement(TabContentWrapper, {
@@ -1141,7 +1147,7 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1141
1147
  var _useTabContext3 = (0, _TabContext.useTabContext)(),
1142
1148
  hiddenExternalToolbars = _useTabContext3.hiddenExternalToolbars,
1143
1149
  tabModePreference = _useTabContext3.tabModePreference;
1144
- var location = (0, _reactRouterDom.useLocation)();
1150
+ var location = (0, _reactRouter.useLocation)();
1145
1151
  var subTabUrl = (_subTab$url = subTab.url) !== null && _subTab$url !== void 0 ? _subTab$url : "/".concat(tabUrl, "/f-").concat(subTab.id);
1146
1152
  var subTabUrlWithoutSearchParam = subTabUrl.split('?')[0];
1147
1153
  var pathname = location.pathname;
@@ -1186,13 +1192,13 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1186
1192
  index: subIndex
1187
1193
  }, function (subprovided, snapshot) {
1188
1194
  return /*#__PURE__*/_react["default"].createElement(_reactHoverText["default"], {
1189
- content: title,
1195
+ content: getTooltipContent(subTab, title),
1190
1196
  delay: 500,
1191
1197
  followCursor: true,
1192
1198
  show: showTooltip,
1193
1199
  placement: "bottom",
1194
1200
  stylingOptions: tooltipStyles
1195
- }, /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, _extends({}, subprovided.draggableProps, subprovided.dragHandleProps, {
1201
+ }, /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, _extends({}, subprovided.draggableProps, subprovided.dragHandleProps, {
1196
1202
  ref: subprovided.innerRef,
1197
1203
  to: subTabUrl,
1198
1204
  id: "tab-".concat(tab.id, "-") + subTab.id.toString().replace('/', '-'),
@@ -1206,7 +1212,7 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1206
1212
  style: getItemStyle(showSubTabs || show, subprovided.draggableProps.style, width)
1207
1213
  // title={subTab.name}
1208
1214
  ,
1209
- exact: false,
1215
+ end: false,
1210
1216
  onClick: function onClick() {
1211
1217
  return handleTabClick({
1212
1218
  tab: subTab,
@@ -752,7 +752,7 @@ body[data-theme="light"] {
752
752
  width: 5px;
753
753
  height: 10px;
754
754
  background-color: var(--tab-text-active);
755
- mask: url(./images/icon-chevron-right.svg) no-repeat center center;
755
+ mask: url(../../images/icon-chevron-right.svg) no-repeat center center;
756
756
  mask-size: 5px;
757
757
  transition: background-color ease-out var(--transition-duration),
758
758
  var(--transition-duration) var(--transition-duration-extended);
@@ -9,7 +9,7 @@ exports.TabProvider = TabProvider;
9
9
  exports.getUniqueIdentifier = void 0;
10
10
  exports.useTabContext = useTabContext;
11
11
  var _react = _interopRequireWildcard(require("react"));
12
- var _reactRouterDom = require("react-router-dom");
12
+ var _reactRouter = require("react-router");
13
13
  var _usePrevious = _interopRequireDefault(require("./usePrevious"));
14
14
  var _Utils = require("./Utils");
15
15
  var _Constants = require("./Constants");
@@ -154,7 +154,7 @@ function TabProvider(_ref9) {
154
154
  SITE_PREFIX = _ref9.SITE_PREFIX,
155
155
  SITE_PAGES = _ref9.SITE_PAGES,
156
156
  _ref9$homeUrl = _ref9.homeUrl,
157
- homeUrl = _ref9$homeUrl === void 0 ? '/' : _ref9$homeUrl,
157
+ homeUrl = _ref9$homeUrl === void 0 ? _Constants.DEFAULT_HOME_URL : _ref9$homeUrl,
158
158
  _ref9$entityIdentifie = _ref9.entityIdentifierKey,
159
159
  entityIdentifierKey = _ref9$entityIdentifie === void 0 ? 'organization_id' : _ref9$entityIdentifie,
160
160
  _ref9$persistTabsAfte = _ref9.persistTabsAfterLogin,
@@ -211,8 +211,8 @@ function TabProvider(_ref9) {
211
211
  isDefaultExpanded = _ref9$isDefaultExpand === void 0 ? false : _ref9$isDefaultExpand,
212
212
  _ref9$alertService = _ref9.alertService,
213
213
  alertService = _ref9$alertService === void 0 ? {} : _ref9$alertService;
214
- var history = (0, _reactRouterDom.useHistory)();
215
- var location = (0, _reactRouterDom.useLocation)();
214
+ var navigate = (0, _reactRouter.useNavigate)();
215
+ var location = (0, _reactRouter.useLocation)();
216
216
  var homeTabIdRef = (0, _react.useRef)(null);
217
217
  var superTabsWrapperElemRef = (0, _react.useRef)(null);
218
218
  var _useState = (0, _react.useState)(false),
@@ -536,9 +536,11 @@ function TabProvider(_ref9) {
536
536
  }
537
537
  }
538
538
  setTabModePreference(updatedTabModePreference);
539
- history.replace({
539
+ navigate({
540
540
  pathname: location.pathname,
541
541
  search: "?".concat(searchParams.toString())
542
+ }, {
543
+ replace: true
542
544
  });
543
545
  };
544
546
  var openSuperTabOnRowClick = function openSuperTabOnRowClick() {
@@ -553,7 +555,7 @@ function TabProvider(_ref9) {
553
555
  var isTabOpen = !isSubTab && tabsRef.current.some(function (t) {
554
556
  return t.url === tab.url;
555
557
  });
556
- history.push(tab.url);
558
+ navigate(tab.url);
557
559
  // if (!isSubTab && tab.skipAPICall) return;
558
560
  isSubTab ? openSubTab({
559
561
  tab: tab
@@ -727,7 +729,7 @@ function TabProvider(_ref9) {
727
729
  subTabs: _subTabs.sort(_Utils.sortByOrder)
728
730
  }) : t;
729
731
  });
730
- var pathName = history.location.pathname;
732
+ var pathName = location.pathname;
731
733
  var isSubTabActive = _subTabs.find(function (t) {
732
734
  return t.url === pathName;
733
735
  });
@@ -1189,7 +1191,9 @@ function TabProvider(_ref9) {
1189
1191
  if (tabModePreference.includes(newActiveSubTab.tabId)) {
1190
1192
  url += "?edit=true";
1191
1193
  }
1192
- history.replace(url);
1194
+ navigate(url, {
1195
+ replace: true
1196
+ });
1193
1197
  } else {
1194
1198
  updateActiveSubTab({});
1195
1199
  lastOpenedUrl.current[currentEntityId + '_' + activeTabRef.current.id] = null;
@@ -1197,7 +1201,9 @@ function TabProvider(_ref9) {
1197
1201
  if (tabModePreference.includes(activeTabRef.current.tabId)) {
1198
1202
  _url += "?edit=true";
1199
1203
  }
1200
- history.replace(_url);
1204
+ navigate(_url, {
1205
+ replace: true
1206
+ });
1201
1207
  }
1202
1208
  } else {
1203
1209
  newTabs[parentTabIndex].subTabs = newTabs[parentTabIndex].subTabs.map(function (subTab) {
@@ -1230,11 +1236,15 @@ function TabProvider(_ref9) {
1230
1236
  var newActiveTab = nextTab !== null && nextTab !== void 0 ? nextTab : newTabs[tabIndex - 1];
1231
1237
  updateActiveTab(newActiveTab);
1232
1238
  lastOpenedUrl.current[currentEntityId + '_' + activeTabRef.current.id] = newActiveTab;
1233
- history.replace(newActiveTab.url);
1239
+ navigate(newActiveTab.url, {
1240
+ replace: true
1241
+ });
1234
1242
  } else {
1235
1243
  lastOpenedUrl.current[currentEntityId + '_' + activeTabRef.current.id] = null;
1236
1244
  updateActiveTab({});
1237
- history.replace("/");
1245
+ navigate("/", {
1246
+ replace: true
1247
+ });
1238
1248
  }
1239
1249
  }
1240
1250
  newTabs = newTabs.filter(function (t) {
@@ -1549,7 +1559,7 @@ function TabProvider(_ref9) {
1549
1559
  _context10.n = 13;
1550
1560
  break;
1551
1561
  }
1552
- pathName = history.location.pathname;
1562
+ pathName = location.pathname;
1553
1563
  isHomeTab = pathName === homeUrl || pathName === '/';
1554
1564
  _context10.n = 3;
1555
1565
  return findDuplicateEntriesAndCloseTab(response.tabs);
@@ -1578,7 +1588,9 @@ function TabProvider(_ref9) {
1578
1588
  // lastOpenedUrl.current[activeTab.id] = activeSubTab.url;
1579
1589
  }
1580
1590
  isHomeTab = false;
1581
- history.replace(pathName);
1591
+ navigate(pathName, {
1592
+ replace: true
1593
+ });
1582
1594
  }
1583
1595
  isCurrentTabOpen = !isHomeTab && tabsOutput.find(function (tab) {
1584
1596
  var _tab$tab_info$url;
@@ -1613,7 +1625,7 @@ function TabProvider(_ref9) {
1613
1625
  _context10.n = 6;
1614
1626
  break;
1615
1627
  case 5:
1616
- history.push(homeUrl);
1628
+ navigate(homeUrl);
1617
1629
  case 6:
1618
1630
  _context10.n = 10;
1619
1631
  break;
@@ -1806,7 +1818,7 @@ function TabProvider(_ref9) {
1806
1818
  };
1807
1819
  }());
1808
1820
  if (activeTab.url) {
1809
- history.push(activeTab.url);
1821
+ navigate(activeTab.url);
1810
1822
  }
1811
1823
  }
1812
1824
  }
package/dist/TabList.js CHANGED
@@ -8,7 +8,7 @@ exports["default"] = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactDom = require("react-dom");
10
10
  var _TabContext = require("./TabContext");
11
- var _reactRouterDom = require("react-router-dom");
11
+ var _reactRouter = require("react-router");
12
12
  var _ClickOutsideListener = _interopRequireDefault(require("./ClickOutsideListener"));
13
13
  require("./TabList.scss");
14
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
@@ -188,7 +188,7 @@ var TabList = function TabList(_ref) {
188
188
  className: "tab-list-tabs-wrapper"
189
189
  }, filteredTabs.length > 0 ? filteredTabs.map(function (tab, index) {
190
190
  var title = tab.title || tab.name || (tab !== null && tab !== void 0 && tab.firstName ? "".concat(tab.firstName, " ").concat(tab.lastName || '') : 'Untitled');
191
- return /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, {
191
+ return /*#__PURE__*/_react["default"].createElement(_reactRouter.NavLink, {
192
192
  className: "tab-list-open-tab ".concat(id === activeTab.id && tab.id === activeSubTab.id ? 'active' : ''),
193
193
  key: tab.id || index,
194
194
  onClick: function onClick(e) {
package/dist/Utils.js CHANGED
@@ -13,7 +13,9 @@ exports.getTextWidth = getTextWidth;
13
13
  exports.getUserDetailsFromToken = exports.getUniqueId = exports.getTitle = void 0;
14
14
  exports.isChildPath = isChildPath;
15
15
  exports.isDev = isDev;
16
- exports.normalizeUrl = exports.isEmpty = void 0;
16
+ exports.isEmpty = void 0;
17
+ exports.isStage = isStage;
18
+ exports.normalizeUrl = void 0;
17
19
  exports.removeSpaces = removeSpaces;
18
20
  exports.reorder = reorder;
19
21
  exports.reorderSubTabs = reorderSubTabs;
@@ -163,6 +165,9 @@ function reorderSubTabs(array, sourceIndex, destinationIndex) {
163
165
  function isDev() {
164
166
  return process.env.REACT_APP_CUSTOM_NODE_ENV === 'development';
165
167
  }
168
+ function isStage() {
169
+ return process.env.REACT_APP_CUSTOM_NODE_ENV === 'stage';
170
+ }
166
171
  function removeSpaces(str) {
167
172
  return str.replace(/\s/g, '');
168
173
  }
@@ -0,0 +1,6 @@
1
+ <svg width="36" height="30" viewBox="0 0 36 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M8 4.00098C8 2.89641 8.89543 2.00098 10 2.00098H32C33.1046 2.00098 34 2.89641 34 4.00098V26.001C34 27.1055 33.1046 28.001 32 28.001H10C8.89543 28.001 8 27.1055 8 26.001V4.00098Z" fill="#1D2024"/>
3
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10 0.000976562H32C34.2091 0.000976562 36 1.79184 36 4.00098V26.001C36 28.2101 34.2091 30.001 32 30.001H10C7.79086 30.001 6 28.2101 6 26.001V4.00098C6 1.79184 7.79086 0.000976562 10 0.000976562ZM10 2.00098C8.89543 2.00098 8 2.89641 8 4.00098V26.001C8 27.1055 8.89543 28.001 10 28.001H32C33.1046 28.001 34 27.1055 34 26.001V4.00098C34 2.89641 33.1046 2.00098 32 2.00098H10Z" fill="black"/>
4
+ <path d="M2 4.00098C2 2.89641 2.89543 2.00098 4 2.00098H26C27.1046 2.00098 28 2.89641 28 4.00098V26.001C28 27.1055 27.1046 28.001 26 28.001H4C2.89543 28.001 2 27.1055 2 26.001V4.00098Z" fill="#1D2024"/>
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M4 0.000976562H26C28.2091 0.000976562 30 1.79184 30 4.00098V26.001C30 28.2101 28.2091 30.001 26 30.001H4C1.79086 30.001 0 28.2101 0 26.001V4.00098C0 1.79184 1.79086 0.000976562 4 0.000976562ZM4 2.00098C2.89543 2.00098 2 2.89641 2 4.00098V26.001C2 27.1055 2.89543 28.001 4 28.001H26C27.1046 28.001 28 27.1055 28 26.001V4.00098C28 2.89641 27.1046 2.00098 26 2.00098H4Z" fill="black"/>
6
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bydata/react-supertabs",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
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
- ```