@bydata/react-supertabs 1.1.9 → 1.2.1

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 CHANGED
@@ -9,7 +9,14 @@
9
9
  - **Smooth Transitions**: Enjoy a polished user experience with fluid tab-switching animations.
10
10
  - **Keep-Alive (Experimental)**: Maintain the state of components across tab switches for seamless interaction (experimental feature).
11
11
 
12
- ### Installation and usage
12
+ ### Installation
13
+ ------
14
+
15
+ ```bash
16
+ npm install @bydata/react-supertabs
17
+ ```
18
+
19
+ ### Usage
13
20
  ------
14
21
 
15
22
  #### Basic
@@ -77,7 +84,7 @@ const SITE_PAGES = [{
77
84
  // containsSubTabs - can be added if the tab includes sub tabs.
78
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.
79
86
 
80
- import { TabProvider } from 'components/SuperTabs/TabContext';
87
+ import { TabProvider } from '@bydata/react-supertabs';
81
88
  <TabProvider
82
89
  SITE_PREFIX={SITE_PREFIX}
83
90
  SITE_PAGES={SITE_PAGES}
@@ -94,7 +101,7 @@ import { TabProvider } from 'components/SuperTabs/TabContext';
94
101
 
95
102
  >**Render the component within the App's header**
96
103
  ```
97
- import { useTabContext } from "components/SuperTabs/TabContext";
104
+ import { SuperTabs, useTabContext } from '@bydata/react-supertabs';
98
105
 
99
106
  <header>
100
107
  <SuperTabs />
@@ -139,7 +146,7 @@ For Avatar:
139
146
  }
140
147
  ```
141
148
  ```
142
- import SuperLink from "components/SuperTabs/SuperLink";
149
+ import { SuperLink } from '@bydata/react-supertabs';
143
150
  <SuperLink tab={page} isSubTab={false}>
144
151
  {page.title}
145
152
  </SuperLink>
@@ -148,7 +155,7 @@ import SuperLink from "components/SuperTabs/SuperLink";
148
155
  OR
149
156
 
150
157
  ```
151
- import { useTabContext } from "components/SuperTabs/TabContext";
158
+ import { useTabContext } from '@bydata/react-supertabs';
152
159
  const { openSuperTabOnRowClick } = useTabContext();
153
160
 
154
161
  function handleRowClick(e) {
@@ -162,8 +169,7 @@ function handleRowClick(e) {
162
169
 
163
170
  If a module requires a plus button for adding a new tab, you can include the following:
164
171
  ```
165
- import { emitter } from "components/SuperTabs/SuperTabs";
166
- import { useTabContext } from "components/SuperTabs/TabContext";
172
+ import { emitter, useTabContext } from '@bydata/react-supertabs';
167
173
 
168
174
  const { openSuperTabOnRowClick } = useTabContext();
169
175
 
@@ -198,7 +204,7 @@ useEffect(() => {
198
204
  When a tab is closed, it may be necessary to clean up some data. The `superTabClose` event can be utilized for this purpose.
199
205
 
200
206
  ```
201
- import { emitter } from 'components/SuperTabs/SuperTabs';
207
+ import { emitter } from '@bydata/react-supertabs';
202
208
 
203
209
  useEffect(() => {
204
210
  const unsub = emitter.subscribe('superTabClose', ({ appId, isSubTab, tab }) => {
@@ -215,7 +221,7 @@ useEffect(() => {
215
221
  - If a profile is deleted, the tab will remain open. To address this, we can display a "Page Not Available" message.
216
222
 
217
223
  ```
218
- import NotAvailable from 'components/SuperTabs/NotAvailable';
224
+ import { NotAvailable } from '@bydata/react-supertabs';
219
225
 
220
226
  <NotAvailable subTabName='Profile' />
221
227
  ```
@@ -223,7 +229,7 @@ import NotAvailable from 'components/SuperTabs/NotAvailable';
223
229
  >**Keep Alive (Experimental)**
224
230
 
225
231
  ```
226
- import { withKeepAlive } from "components/SuperTabs/KeepAlive";
232
+ import { withKeepAlive } from '@bydata/react-supertabs';
227
233
 
228
234
  // Same id provided for this module in navigation.js
229
235
  export default withKeepAlive(Component, Unique ID);
@@ -239,8 +245,7 @@ Keep Alive Cleanup
239
245
  ```
240
246
  // This can be incorporated into a component that is always rendered, such as the header.
241
247
 
242
- import { useAliveScope } from "components/SuperTabs/KeepAlive";
243
- import { emitter } from "components/SuperTabs/SuperTabs";
248
+ import { useAliveScope, emitter } from '@bydata/react-supertabs';
244
249
 
245
250
  const { drop } = useAliveScope();
246
251
 
package/dist/Readme.md CHANGED
@@ -1,258 +1,320 @@
1
-
2
1
  # SuperTabs
3
2
 
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
3
+ `SuperTabs` is a production-ready React navigation library for managing parent tabs and nested sub-tabs in complex applications.
47
4
 
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
- }]
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`.
76
19
 
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.
20
+ ```jsx
21
+ import { TabProvider } from "components/SuperTabs/TabContext";
22
+ import { alertService } from "super-notifier";
80
23
 
81
- import { TabProvider } from 'components/SuperTabs/TabContext';
82
24
  <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}
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}
91
36
  >
92
- <Main />
93
- </TabProvider>
37
+ <Main />
38
+ </TabProvider>;
94
39
  ```
95
40
 
96
- >**Render the component within the App's header**
97
- ```
98
- import { useTabContext } from "components/SuperTabs/TabContext";
41
+ Render tabs in header:
42
+
43
+ ```jsx
44
+ import SuperTabs from "components/SuperTabs/SuperTabs";
99
45
 
100
46
  <header>
101
- <SuperTabs />
102
- </header>
47
+ <SuperTabs />
48
+ </header>;
103
49
  ```
104
50
 
105
- >**Wrap the hyperlink as follows:**
51
+ ---
106
52
 
107
- Each tab should contain all the necessary information to be stored.
53
+ ## 2) `TabProvider` props
108
54
 
109
- Mandatory Fields:
110
- - `id`
111
- - `name`
112
- - `url`
55
+ ### Required
113
56
 
114
- For Avatar:
115
- - `showAvatar` must be set to `true`
116
- - The following fields must be provided:
117
- - `firstName`
118
- - `lastName`
119
- - `imgSrc`
57
+ - `SITE_PREFIX: string`
58
+ - `SITE_PAGES: Array<SitePage>`
120
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
+ }
121
77
  ```
122
- // Tab object Examples
123
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
124
107
  {
125
- id: 1,
126
- name: 'title',
127
- url: '/recruiter/f-1`
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
128
112
  }
113
+ ```
114
+
115
+ ### `handleOpenTab(payload[, controller])`
116
+
117
+ Expected shape:
129
118
 
119
+ ```js
130
120
  {
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,
121
+ status: 1,
122
+ tabId: 123,
123
+ children: [] // optional child tabs
140
124
  }
141
125
  ```
126
+
127
+ ### `handleCloseTab(tabId)`
128
+
129
+ Expected shape:
130
+
131
+ ```js
132
+ {
133
+ status: 1,
134
+ current_tab_id: 456 // optional
135
+ }
142
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
143
155
  import SuperLink from "components/SuperTabs/SuperLink";
156
+
144
157
  <SuperLink tab={page} isSubTab={false}>
145
- {page.title}
146
- </SuperLink>
158
+ {page.title}
159
+ </SuperLink>;
147
160
  ```
148
161
 
149
- OR
162
+ Open external links:
150
163
 
164
+ ```jsx
165
+ <SuperLink tab={{ ...page, isExternal: true }} isSubTab={false} isExternal>
166
+ {page.title}
167
+ </SuperLink>
151
168
  ```
169
+
170
+ ### Option 2: `openSuperTabOnRowClick`
171
+
172
+ ```jsx
152
173
  import { useTabContext } from "components/SuperTabs/TabContext";
174
+
153
175
  const { openSuperTabOnRowClick } = useTabContext();
154
176
 
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 });
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
+ });
159
186
  }
160
187
  ```
161
188
 
162
- >**Bind the callback for the plus button to each module during the initial render.**
189
+ ---
190
+
191
+ ## 5) Tab object notes
192
+
193
+ Common fields:
194
+
195
+ - `id`
196
+ - `url`
197
+ - one of `name` / `title`
163
198
 
164
- If a module requires a plus button for adding a new tab, you can include the following:
199
+ Optional:
200
+
201
+ - `showAvatar`, `firstName`, `lastName`, `imgSrc` or `img_url`
202
+ - `isExternal`
203
+ - any extra metadata your module uses
204
+
205
+ ```js
206
+ {
207
+ id: 100,
208
+ url: "/app/profile/100",
209
+ firstName: "John",
210
+ lastName: "Doe",
211
+ showAvatar: true,
212
+ imgSrc: "https://cdn.example.com/u100.png"
213
+ }
165
214
  ```
215
+
216
+ ---
217
+
218
+ ## 6) Add button callbacks
219
+
220
+ Bind module add callbacks using emitter:
221
+
222
+ ```jsx
223
+ import { useCallback, useEffect } from "react";
166
224
  import { emitter } from "components/SuperTabs/SuperTabs";
167
225
  import { useTabContext } from "components/SuperTabs/TabContext";
168
226
 
169
227
  const { openSuperTabOnRowClick } = useTabContext();
170
228
 
171
- const handleTabAddBtn = useCallback(() => {
229
+ const handleTabAddBtn = useCallback(
230
+ (customButtonData) => {
231
+ // customButtonData is passed when using customAddButtons
172
232
  openSuperTabOnRowClick({
173
- tab: {
174
- id: 123,
175
- name: `New Funnel`,
176
- url: `/recruiter/f-${123}`,
177
- },
178
- isSubTab: true,
233
+ tab: {
234
+ id: 123,
235
+ name: "New Funnel",
236
+ url: "/app/recruiter/f-123",
237
+ },
238
+ isSubTab: true,
179
239
  });
180
- }, [openSuperTabOnRowClick]);
240
+ },
241
+ [openSuperTabOnRowClick],
242
+ );
181
243
 
182
244
  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;
245
+ const unsub = emitter.emit("bindCallback", {
246
+ id: 2, // same parent module id from SITE_PAGES
247
+ callback: handleTabAddBtn,
248
+ });
249
+ return unsub;
188
250
  }, [handleTabAddBtn]);
189
251
  ```
190
252
 
191
- > Note: The logo of the home tab can be updated by targeting the `brand-logo` class.
192
-
193
- -----
194
-
195
- #### Advanced
253
+ ---
196
254
 
197
- >**Cleanup**
255
+ ## 7) Close cleanup event
198
256
 
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';
257
+ ```jsx
258
+ import { useEffect } from "react";
259
+ import { emitter } from "components/SuperTabs/SuperTabs";
203
260
 
204
261
  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;
262
+ const unsub = emitter.subscribe(
263
+ "superTabClose",
264
+ ({ appId, isSubTab, tab }) => {
265
+ if (appId === 2 && isSubTab) {
266
+ // cleanup here
267
+ }
268
+ },
269
+ );
270
+ return unsub;
212
271
  }, []);
213
272
  ```
214
273
 
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.
274
+ ---
217
275
 
218
- ```
219
- import NotAvailable from 'components/SuperTabs/NotAvailable';
276
+ ## 8) Not Available page
220
277
 
221
- <NotAvailable subTabName='Profile' />
278
+ ```jsx
279
+ import NotAvailable from "components/SuperTabs/NotAvailable";
280
+
281
+ <NotAvailable subTabName="Profile" />;
222
282
  ```
223
283
 
224
- >**Keep Alive (Experimental)**
284
+ ---
225
285
 
226
- ```
227
- import { withKeepAlive } from "components/SuperTabs/KeepAlive";
286
+ ## 9) Keep-alive (optional)
228
287
 
229
- // Same id provided for this module in navigation.js
230
- export default withKeepAlive(Component, Unique ID);
288
+ Use shared keep-alive utility:
231
289
 
232
- Eg.
233
- const Component = () => {
234
- return <div>Component</div>
235
- }
290
+ ```jsx
291
+ import { withKeepAlive } from "components/KeepAlive";
236
292
 
237
- export default withKeepAlive(Component, 2);
238
- ```
239
- Keep Alive Cleanup
293
+ const Component = () => <div>Component</div>;
294
+ export default withKeepAlive(Component, 2); // module id
240
295
  ```
241
- // This can be incorporated into a component that is always rendered, such as the header.
242
296
 
243
- import { useAliveScope } from "components/SuperTabs/KeepAlive";
244
- import { emitter } from "components/SuperTabs/SuperTabs";
297
+ Cleanup keep-alive cache on parent tab close:
245
298
 
246
- const { drop } = useAliveScope();
299
+ ```jsx
300
+ import { useEffect } from "react";
301
+ import { useAliveScope } from "components/KeepAlive";
302
+ import { emitter } from "components/SuperTabs/SuperTabs";
303
+
304
+ const { drop } = useAliveScope();
247
305
 
248
306
  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
- ```
307
+ const unsub = emitter.subscribe("superTabClose", ({ appId, isSubTab }) => {
308
+ if (appId && !isSubTab) {
309
+ drop(appId);
310
+ }
311
+ });
312
+ return unsub;
313
+ }, [drop]);
314
+ ```
315
+
316
+ ---
317
+
318
+ ## 10) Styling note
319
+
320
+ Home tab logo can be styled with `.brand-logo`.
package/dist/SuperTabs.js CHANGED
@@ -91,6 +91,7 @@ var SuperTabs = function SuperTabs(_ref) {
91
91
  var addButtonCallBackRefs = (0, _react.useRef)({});
92
92
  var transitionTimeOutRef = (0, _react.useRef)(null);
93
93
  var expandingTabIdRef = (0, _react.useRef)(null);
94
+ var homeTabRef = (0, _react.useRef)(null);
94
95
  var _useTabContext = (0, _TabContext.useTabContext)(),
95
96
  lastOpenedUrl = _useTabContext.lastOpenedUrl,
96
97
  SITE_PAGES = _useTabContext.SITE_PAGES,
@@ -169,6 +170,10 @@ var SuperTabs = function SuperTabs(_ref) {
169
170
  _useState8 = _slicedToArray(_useState7, 2),
170
171
  isHoveringBrand = _useState8[0],
171
172
  setIsHoveringBrand = _useState8[1];
173
+ var _useState9 = (0, _react.useState)(5),
174
+ _useState0 = _slicedToArray(_useState9, 2),
175
+ appPagesLeft = _useState0[0],
176
+ setAppPagesLeft = _useState0[1];
172
177
 
173
178
  // Refs for hover timeout management
174
179
  var hoverTimeoutRef = (0, _react.useRef)(null);
@@ -326,6 +331,15 @@ var SuperTabs = function SuperTabs(_ref) {
326
331
  clearTimeout(hoverOffTimeoutRef.current);
327
332
  };
328
333
  }, []);
334
+ var updateAppPagesPosition = (0, _react.useCallback)(function () {
335
+ var _homeTabRef$current;
336
+ var homeTabRect = (_homeTabRef$current = homeTabRef.current) === null || _homeTabRef$current === void 0 ? void 0 : _homeTabRef$current.getBoundingClientRect();
337
+ if (!homeTabRect) return;
338
+ setAppPagesLeft(Math.max(0, homeTabRect.x));
339
+ }, []);
340
+ (0, _react.useLayoutEffect)(function () {
341
+ updateAppPagesPosition();
342
+ }, [updateAppPagesPosition, showMainMenu]);
329
343
 
330
344
  // Hover handlers for brand wrapper
331
345
  var handleBrandHoverEnter = function handleBrandHoverEnter() {
@@ -495,8 +509,8 @@ var SuperTabs = function SuperTabs(_ref) {
495
509
  setIsSubTabCollapsing(false);
496
510
  }, 1000);
497
511
  setTimeout(function () {
498
- resetMaxWidth(null);
499
- }, 200);
512
+ updateMaxWidth();
513
+ }, 210);
500
514
  } else {
501
515
  if (superTabsWrapperElemRef.current) superTabsWrapperElemRef.current.style.setProperty('--transition-duration-extended', "".concat(_Constants.transitionDurationExtended, "ms"));
502
516
  // expandedTabIDRef.current = id;
@@ -692,7 +706,8 @@ var SuperTabs = function SuperTabs(_ref) {
692
706
  ref: superTabsWrapperElemRef
693
707
  }, /*#__PURE__*/_react["default"].createElement(_reactRouterDom.NavLink, {
694
708
  to: homeUrl,
695
- className: "home-tab"
709
+ className: "home-tab",
710
+ ref: homeTabRef
696
711
  // activeClassName="active"
697
712
  // title="Home"
698
713
  ,
@@ -716,6 +731,7 @@ var SuperTabs = function SuperTabs(_ref) {
716
731
  tab: (0, _Utils.getHomeTab)(homeUrl),
717
732
  isHomeTab: true
718
733
  });
734
+ resetMaxWidth();
719
735
  },
720
736
  exact: true
721
737
  }, /*#__PURE__*/_react["default"].createElement("div", {
@@ -731,6 +747,9 @@ var SuperTabs = function SuperTabs(_ref) {
731
747
  }
732
748
  }, /*#__PURE__*/_react["default"].createElement("div", {
733
749
  className: "app-pages-wrapper",
750
+ style: {
751
+ left: "".concat(appPagesLeft, "px")
752
+ },
734
753
  onMouseEnter: handleMenuHoverEnter,
735
754
  onMouseLeave: handleMenuHoverLeave
736
755
  }, SITE_PAGES.map(function (page, index) {
@@ -817,7 +836,7 @@ var SuperTabs = function SuperTabs(_ref) {
817
836
  }, 0) : subTabWithOpenOrderOneWidth || 0;
818
837
  var getItemStyle = function getItemStyle(draggableStyle) {
819
838
  return _objectSpread({
820
- maxWidth: expandedTabID ? "".concat(tab.width + additionalWidth || width, "px") : "".concat(width, "px")
839
+ maxWidth: maxWidth ? "".concat(maxWidth, "px") : "".concat(width, "px")
821
840
  }, draggableStyle);
822
841
  };
823
842
  var _filterSubTabs = filterSubTabs(tab.subTabs),
@@ -873,9 +892,10 @@ var SuperTabs = function SuperTabs(_ref) {
873
892
  to: tab.url,
874
893
  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' : '')
875
894
  // (showSubTabs ? ' expanded' : '')
895
+ // style={{ maxWidth: expandedTabID ? `${(tab.width + additionalWidth || width) - 10}px` : `${width}px` }}
876
896
  ,
877
897
  style: {
878
- maxWidth: expandedTabID ? "".concat((tab.width + additionalWidth || width) - 10, "px") : "".concat(width, "px")
898
+ maxWidth: maxWidth ? "".concat(maxWidth, "px") : "".concat(width, "px")
879
899
  }
880
900
  // title={tab.title}
881
901
  ,
@@ -1052,7 +1072,7 @@ var TabContentWrapper = function TabContentWrapper(_ref9) {
1052
1072
  }, /*#__PURE__*/_react["default"].createElement("div", null, title))), isActive && /*#__PURE__*/_react["default"].createElement("div", {
1053
1073
  className: "tab-close-btn-wrapper"
1054
1074
  }, /*#__PURE__*/_react["default"].createElement("button", {
1055
- className: "tab-close-btn",
1075
+ className: "super-tab-close-btn",
1056
1076
  title: "Close tab",
1057
1077
  onClick: function onClick(e) {
1058
1078
  e.preventDefault();
@@ -1082,7 +1102,7 @@ var TabContentWrapper = function TabContentWrapper(_ref9) {
1082
1102
  })))), Suffix && isActive && window.innerWidth < 640 && /*#__PURE__*/_react["default"].createElement(Suffix, null), !isActive && /*#__PURE__*/_react["default"].createElement("div", {
1083
1103
  className: "tab-close-btn-wrapper"
1084
1104
  }, /*#__PURE__*/_react["default"].createElement("button", {
1085
- className: "tab-close-btn",
1105
+ className: "super-tab-close-btn",
1086
1106
  title: "Close tab",
1087
1107
  onClick: function onClick(e) {
1088
1108
  e.preventDefault();
@@ -1113,10 +1133,10 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1113
1133
  index = _ref0.index,
1114
1134
  hasSingleSubTab = _ref0.hasSingleSubTab;
1115
1135
  // const { closingUnexpandedSubTab, openingUnexpandedSubTab, subTabChange } = useTabContext();
1116
- var _useState9 = (0, _react.useState)(true),
1117
- _useState0 = _slicedToArray(_useState9, 2),
1118
- showTooltip = _useState0[0],
1119
- setShowTooltip = _useState0[1];
1136
+ var _useState1 = (0, _react.useState)(true),
1137
+ _useState10 = _slicedToArray(_useState1, 2),
1138
+ showTooltip = _useState10[0],
1139
+ setShowTooltip = _useState10[1];
1120
1140
  var _useTabContext3 = (0, _TabContext.useTabContext)(),
1121
1141
  hiddenExternalToolbars = _useTabContext3.hiddenExternalToolbars,
1122
1142
  tabModePreference = _useTabContext3.tabModePreference;
@@ -1151,8 +1171,9 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1151
1171
  return _objectSpread({
1152
1172
  // calculateWidth({ text: title, hasAvatar: subTab.showAvatar, });
1153
1173
  marginLeft: isVisible ? "".concat(_Constants.MarginLeft, "px") : '0px',
1154
- // maxWidth: maxWidth ? `${maxWidth}px` : `${width}px`,
1174
+ // maxWidth: maxWidth && !isSubTabActive ? `${maxWidth}px` : `${width}px`,
1155
1175
  width: isVisible ? "".concat(expandedTabID === tab.id || closingTabID === tab.id ? subTab.width + additionalWidth || width : width, "px") : '0px',
1176
+ // width: isVisible ? `${!isSubTabActive && (expandedTabID === tab.id || closingTabID === tab.id) ? subTab.width + additionalWidth || width : width}px` : '0px',
1156
1177
  opacity: isVisible ? 1 : 0
1157
1178
  }, draggableStyle);
1158
1179
  };
@@ -1176,6 +1197,7 @@ var SubTab = exports.SubTab = function SubTab(_ref0) {
1176
1197
  className: "tab sub-tab" + (show ? ' show' : '') + (
1177
1198
  // (isTabClosing ? ' slide-out' : '') +
1178
1199
  // (isTabOpening ? ' fade-in' : '') +
1200
+ // (expandedTabID && subTab.width && !isSubTabActive && compareDecimals(subTab.width, width) === -1 ? ' fade-at-end' : '') +
1179
1201
  expandedTabID && subTab.width && (0, _Utils.compareDecimals)(subTab.width, width) === -1 ? ' fade-at-end' : '') + (
1180
1202
  // (closingUnexpandedSubTab || openingUnexpandedSubTab ? ' no-transition' : '') +
1181
1203
  snapshot.isDragging ? ' dragging' : '') + (subTab.isFixed ? ' fixed' : ''),
@@ -72,7 +72,7 @@ body[data-theme="light"] {
72
72
  .app-pages-wrapper {
73
73
  // Adjust positioning for mobile if needed
74
74
  top: calc(100% + 10px);
75
- left: 0;
75
+ // left: 0;
76
76
  width: calc(100vw - 20px);
77
77
  max-width: 280px;
78
78
  }
@@ -169,25 +169,6 @@ body[data-theme="light"] {
169
169
  }
170
170
  }
171
171
 
172
- .tab-close-btn {
173
- height: 16px;
174
- width: 16px;
175
- padding: 0;
176
- position: relative;
177
- top: 0;
178
-
179
- cursor: pointer;
180
- border: none;
181
- background-color: var(--tab-text-active);
182
- mask: url(./images/icon-close-lite.svg) no-repeat center center;
183
- mask-size: 8px;
184
- transition: background-color ease-out (--transition-duration);
185
-
186
- &:hover {
187
- background-color: var(--tab-text-active);
188
- }
189
- }
190
-
191
172
  .tab {
192
173
  position: relative;
193
174
  color: var(--tab-text-active);
@@ -398,7 +379,7 @@ body[data-theme="light"] {
398
379
  animation-timing-function: cubic-bezier(0.07, -0.24, 0.6, 0.34);
399
380
  animation-duration: var(--transition-duration);
400
381
 
401
- .tab-close-btn {
382
+ .super-tab-close-btn {
402
383
  background-color: var(--tab-color-white);
403
384
  }
404
385
  }
@@ -891,4 +872,23 @@ body[data-theme="light"] {
891
872
  transform: translateY(0) scale(1);
892
873
  }
893
874
  }
875
+ }
876
+
877
+ .super-tab-close-btn {
878
+ height: 16px;
879
+ width: 16px;
880
+ padding: 0;
881
+ position: relative;
882
+ top: 0;
883
+
884
+ cursor: pointer;
885
+ border: none;
886
+ background-color: var(--tab-text-active);
887
+ mask: url(./images/icon-close-lite.svg) no-repeat center center;
888
+ mask-size: 8px;
889
+ transition: background-color ease-out var(--transition-duration);
890
+
891
+ &:hover {
892
+ background-color: var(--tab-text-active);
893
+ }
894
894
  }
@@ -252,8 +252,8 @@ function TabProvider(_ref9) {
252
252
  subTabWrapperWidth = _useState12[0],
253
253
  setSubTabWrapperWidth = _useState12[1];
254
254
  var _useState13 = (0, _react.useState)(function () {
255
- var _getUserDetails;
256
- return ((_getUserDetails = getUserDetails()) === null || _getUserDetails === void 0 ? void 0 : _getUserDetails[entityIdentifierKey]) || null;
255
+ var _getUserDetails$entit, _getUserDetails;
256
+ return (_getUserDetails$entit = (_getUserDetails = getUserDetails()) === null || _getUserDetails === void 0 ? void 0 : _getUserDetails[entityIdentifierKey]) !== null && _getUserDetails$entit !== void 0 ? _getUserDetails$entit : null;
257
257
  }),
258
258
  _useState14 = _slicedToArray(_useState13, 2),
259
259
  currentEntityId = _useState14[0],
@@ -390,16 +390,29 @@ function TabProvider(_ref9) {
390
390
  var openedSubTabsLength = subTabs.filter(function (t) {
391
391
  return t.order !== 0;
392
392
  }).length;
393
- var totalTabs = openParentTabsLength + (expandedTabIDRef.current ? openedSubTabsLength : subTabWithOpenOrderOne ? 1 : 0);
394
- var containerWidth = ((_superTabsWrapperElem = superTabsWrapperElemRef.current) === null || _superTabsWrapperElem === void 0 ? void 0 : _superTabsWrapperElem.offsetWidth) - (_Constants.HomeTabWidth + _Constants.MainTabGap * openParentTabsLength + openedSubTabsLength * _Constants.MarginLeft + _Constants.StackButtonWidth + _Constants.SubTabWrapperPadding);
395
- var maxWidth = Math.ceil(containerWidth / totalTabs * 100) / 100;
393
+ var totalVisibleTabs = openParentTabsLength + (expandedTabIDRef.current ? openedSubTabsLength : subTabWithOpenOrderOne ? 1 : 0);
394
+ var visibleSubTabsForMargin = expandedTabIDRef.current ? openedSubTabsLength : 0;
395
+ var containerWidth = ((_superTabsWrapperElem = superTabsWrapperElemRef.current) === null || _superTabsWrapperElem === void 0 ? void 0 : _superTabsWrapperElem.offsetWidth) - (_Constants.HomeTabWidth + _Constants.MainTabGap * openParentTabsLength + visibleSubTabsForMargin * _Constants.MarginLeft + _Constants.StackButtonWidth + _Constants.SubTabWrapperPadding);
396
+ // console.log('offsetWidth', superTabsWrapperElemRef.current?.offsetWidth, 'HomeTabWidth', HomeTabWidth, 'MainTabGap', MainTabGap, 'openedSubTabsLength', openedSubTabsLength, 'MarginLeft', MarginLeft, 'StackButtonWidth', StackButtonWidth, 'SubTabWrapperPadding', SubTabWrapperPadding);
397
+ // console.log('offsetWidth', superTabsWrapperElemRef.current?.offsetWidth, 'HomeTabWidth', HomeTabWidth, 'MainTabGap', MainTabGap * openParentTabsLength, 'openedSubTabsLength', visibleSubTabsForMargin * MarginLeft, 'StackButtonWidth', StackButtonWidth, 'SubTabWrapperPadding', SubTabWrapperPadding);
398
+ var maxWidth = Math.ceil(containerWidth / totalVisibleTabs * 100) / 100;
396
399
  var totalMinWidth = 0;
397
400
  var numElementsNeedingSpace = 0;
398
401
 
399
402
  // Calculate the total width and minimum width of all elements
400
403
  var activeTabWidth = activeTabRef.current ? (0, _Utils.getTextWidth)((0, _Utils.getTitle)(activeTabRef.current), _Constants.fontStyle) + 42 : 0;
401
- var tabLengthWithoutActiveTab = totalTabs - 1;
402
- maxWidth = maxWidth - (activeTabWidth - maxWidth) / tabLengthWithoutActiveTab;
404
+ var tabLengthWithoutActiveTab = totalVisibleTabs - 1;
405
+ var subTabWithOpenOrderOneWidth = subTabWithOpenOrderOne ? Math.ceil((0, _Utils.calculateWidth)({
406
+ text: (0, _Utils.getTitle)(subTabWithOpenOrderOne),
407
+ hasAvatar: subTabWithOpenOrderOne.showAvatar,
408
+ addMargin: false
409
+ }) * 100) / 100 : 0;
410
+ var shouldReserveOpenSubTabWidth = !expandedTabIDRef.current ? !!subTabWithOpenOrderOne : !!openedSubTabsLength;
411
+ var tabLengthWithoutReservedTabs = tabLengthWithoutActiveTab - (shouldReserveOpenSubTabWidth ? 1 : 0);
412
+ var widthToRedistribute = activeTabWidth - maxWidth + (shouldReserveOpenSubTabWidth ? subTabWithOpenOrderOneWidth - maxWidth : 0);
413
+ if (tabLengthWithoutReservedTabs > 0) {
414
+ maxWidth = maxWidth - widthToRedistribute / tabLengthWithoutReservedTabs;
415
+ }
403
416
  setMaxWidth(maxWidth);
404
417
  var newData = data.map(function (tab) {
405
418
  var _activeTabRef$current2;
@@ -1064,13 +1077,18 @@ function TabProvider(_ref9) {
1064
1077
  }
1065
1078
  return tabsCopy;
1066
1079
  });
1080
+ var oldTitle = (0, _Utils.getTitle)(oldTab);
1081
+ var newTitle = (0, _Utils.getTitle)(newTab);
1082
+ if (oldTitle !== newTitle) {
1083
+ updateMaxWidth();
1084
+ }
1067
1085
  // openTab({ tab, isSubTab });
1068
1086
  updateCurrentTab({
1069
1087
  tab: tab,
1070
1088
  isSubTab: isSubTab,
1071
1089
  parentId: activeTabRef.current.id
1072
1090
  });
1073
- }, [updateCurrentTab, updateActiveSubTab, updateActiveTab, updateTabs, currentEntityId]);
1091
+ }, [updateTabs, updateCurrentTab, currentEntityId, updateActiveSubTab, updateActiveTab, updateMaxWidth]);
1074
1092
  var closeTab = (0, _react.useCallback)(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
1075
1093
  var _ref25,
1076
1094
  tab,
package/dist/TabList.js CHANGED
@@ -178,7 +178,7 @@ var TabList = function TabList(_ref) {
178
178
  value: tabListSearch,
179
179
  onChange: handleSearch
180
180
  }), tabListSearch.trim() && /*#__PURE__*/_react["default"].createElement("button", {
181
- className: "tab-list-close tab-close-btn",
181
+ className: "tab-list-close super-tab-close-btn",
182
182
  onClick: function onClick() {
183
183
  return setTabListSearch('');
184
184
  }
@@ -220,7 +220,7 @@ var TabList = function TabList(_ref) {
220
220
  }), /*#__PURE__*/_react["default"].createElement("div", {
221
221
  className: "title-wrapper"
222
222
  }, title, !tab.isFixed && /*#__PURE__*/_react["default"].createElement("span", {
223
- className: "tab-list-close-btn tab-close-btn",
223
+ className: "tab-list-close-btn super-tab-close-btn",
224
224
  onClick: function onClick(e) {
225
225
  e.preventDefault();
226
226
  e.stopPropagation();
package/dist/TabList.scss CHANGED
@@ -115,7 +115,7 @@
115
115
  width: 100%;
116
116
  }
117
117
 
118
- .tab-close-btn {
118
+ .super-tab-close-btn {
119
119
  opacity: 0;
120
120
  visibility: hidden;
121
121
  transition: all 0.3s;
@@ -125,7 +125,7 @@
125
125
  &:hover {
126
126
  background-color: var(--tab-background-button-secondary);
127
127
 
128
- .tab-close-btn {
128
+ .super-tab-close-btn {
129
129
  opacity: 1;
130
130
  visibility: visible;
131
131
  }
@@ -225,7 +225,7 @@
225
225
  transition: all 0.3s;
226
226
  }
227
227
 
228
- .tab-close-btn {
228
+ .super-tab-close-btn {
229
229
  opacity: 1;
230
230
  visibility: visible;
231
231
  }
@@ -259,7 +259,7 @@
259
259
  background-color: var(--tab-background-button-secondary);
260
260
  }
261
261
 
262
- .tab-close-btn {
262
+ .super-tab-close-btn {
263
263
  opacity: 0;
264
264
  visibility: hidden;
265
265
  transition: background-color 0.3s;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bydata/react-supertabs",
3
- "version": "1.1.9",
3
+ "version": "1.2.1",
4
4
  "description": "A customizable React super tabs component.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",