@jetbrains/ring-ui-built 7.0.94 → 7.0.96
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/components/auth/auth-core.d.ts +19 -2
- package/components/auth/auth-core.js +88 -48
- package/components/auth/auth.js +1 -1
- package/components/auth/request-builder.d.ts +12 -0
- package/components/auth/request-builder.js +27 -0
- package/components/list/list.d.ts +1 -1
- package/components/list/list.js +12 -1
- package/components/old-browsers-message/white-list.js +2 -2
- package/components/style.css +1 -1
- package/components/tabs/dumb-tabs.js +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import TokenValidator, { type TokenValidationError, type TokenValidatorConfig }
|
|
|
8
8
|
import type AuthDialogService from '../auth-dialog-service/auth-dialog-service';
|
|
9
9
|
export declare const DEFAULT_EXPIRES_TIMEOUT: number;
|
|
10
10
|
export declare const DEFAULT_BACKGROUND_TIMEOUT: number;
|
|
11
|
+
export declare const TOKEN_REFRESH_RETRY_DELAYS: number[];
|
|
11
12
|
export declare const USER_CHANGED_EVENT = "userChange";
|
|
12
13
|
export declare const DOMAIN_USER_CHANGED_EVENT = "domainUser";
|
|
13
14
|
export declare const LOGOUT_EVENT = "logout";
|
|
@@ -80,6 +81,8 @@ export interface AuthConfig extends TokenValidatorConfig {
|
|
|
80
81
|
translations?: AuthTranslations | null | undefined;
|
|
81
82
|
userParams?: RequestParams | undefined;
|
|
82
83
|
waitForRedirectTimeout: number;
|
|
84
|
+
tokenRefreshRetryDelays: readonly number[];
|
|
85
|
+
rpInitiatedLogout: boolean;
|
|
83
86
|
}
|
|
84
87
|
type AuthPayloadMap = {
|
|
85
88
|
userChange: [AuthUser | undefined | void, void];
|
|
@@ -120,6 +123,7 @@ declare class Auth implements HTTPAuth {
|
|
|
120
123
|
static DEFAULT_CONFIG: Omit<AuthConfig, "serverUri">;
|
|
121
124
|
static API_PATH: string;
|
|
122
125
|
static API_AUTH_PATH: string;
|
|
126
|
+
static API_LOGOUT_PATH: string;
|
|
123
127
|
static API_PROFILE_PATH: string;
|
|
124
128
|
static CLOSE_BACKEND_DOWN_MESSAGE: string;
|
|
125
129
|
static CLOSE_WINDOW_MESSAGE: string;
|
|
@@ -137,6 +141,7 @@ declare class Auth implements HTTPAuth {
|
|
|
137
141
|
_tokenValidator: TokenValidator | null;
|
|
138
142
|
private _postponed;
|
|
139
143
|
private _backendCheckPromise;
|
|
144
|
+
private _forceTokenUpdatePromise;
|
|
140
145
|
private _authDialogService;
|
|
141
146
|
_domainStorage: AuthStorage<UserChange>;
|
|
142
147
|
user: AuthUser | null;
|
|
@@ -166,9 +171,18 @@ declare class Auth implements HTTPAuth {
|
|
|
166
171
|
requestToken(): Promise<string | null>;
|
|
167
172
|
/**
|
|
168
173
|
* Get new token in the background or redirect to the login page.
|
|
169
|
-
*
|
|
174
|
+
*
|
|
175
|
+
* Retries background token refresh with delays from {@link AuthConfig.tokenRefreshRetryDelays}
|
|
176
|
+
* with increasing delays before showing the auth dialog.
|
|
177
|
+
* This handles transient failures that commonly occur after network
|
|
178
|
+
* recovery (e.g. waking from sleep, switching networks) where the first
|
|
179
|
+
* iframe-based auth attempt fails but a subsequent one succeeds once
|
|
180
|
+
* the Hub session is re-established.
|
|
181
|
+
*
|
|
182
|
+
* @return {Promise.<string | null>}
|
|
170
183
|
*/
|
|
171
184
|
forceTokenUpdate(): Promise<string | null>;
|
|
185
|
+
private _doForceTokenUpdate;
|
|
172
186
|
loadCurrentService(): Promise<void>;
|
|
173
187
|
getAPIPath(): string;
|
|
174
188
|
/**
|
|
@@ -187,7 +201,10 @@ declare class Auth implements HTTPAuth {
|
|
|
187
201
|
private _extractErrorMessage;
|
|
188
202
|
private _showBackendDownDialog;
|
|
189
203
|
/**
|
|
190
|
-
* Wipe accessToken and redirect to
|
|
204
|
+
* Wipe accessToken and redirect to logout endpoint.
|
|
205
|
+
* Uses RP-initiated logout flow (oauth2/logout) when rpInitiatedLogout config is enabled,
|
|
206
|
+
* falls back to oauth2/auth redirect otherwise.
|
|
207
|
+
* See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
|
|
191
208
|
*/
|
|
192
209
|
logout(extraParams?: Record<string, unknown>): Promise<void>;
|
|
193
210
|
private _runEmbeddedLogin;
|
|
@@ -47,6 +47,7 @@ const DEFAULT_BACKGROUND_TIMEOUT = 10 * 1000;
|
|
|
47
47
|
const DEFAULT_BACKEND_CHECK_TIMEOUT = 10 * 1000;
|
|
48
48
|
const BACKGROUND_REDIRECT_TIMEOUT = 20 * 1000;
|
|
49
49
|
const DEFAULT_WAIT_FOR_REDIRECT_TIMEOUT = 5 * 1000;
|
|
50
|
+
const TOKEN_REFRESH_RETRY_DELAYS = [0, 2000, 5000];
|
|
50
51
|
const USER_CHANGED_EVENT = 'userChange';
|
|
51
52
|
const DOMAIN_USER_CHANGED_EVENT = 'domainUser';
|
|
52
53
|
const LOGOUT_EVENT = 'logout';
|
|
@@ -75,12 +76,15 @@ const DEFAULT_CONFIG = {
|
|
|
75
76
|
onBackendDown: () => () => {},
|
|
76
77
|
defaultExpiresIn: DEFAULT_EXPIRES_TIMEOUT,
|
|
77
78
|
waitForRedirectTimeout: DEFAULT_WAIT_FOR_REDIRECT_TIMEOUT,
|
|
79
|
+
tokenRefreshRetryDelays: TOKEN_REFRESH_RETRY_DELAYS,
|
|
80
|
+
rpInitiatedLogout: true,
|
|
78
81
|
translations: null
|
|
79
82
|
};
|
|
80
83
|
class Auth {
|
|
81
84
|
static DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
82
85
|
static API_PATH = 'api/rest/';
|
|
83
86
|
static API_AUTH_PATH = 'oauth2/auth';
|
|
87
|
+
static API_LOGOUT_PATH = 'oauth2/logout';
|
|
84
88
|
static API_PROFILE_PATH = 'users/me';
|
|
85
89
|
static CLOSE_BACKEND_DOWN_MESSAGE = 'backend-check-succeeded';
|
|
86
90
|
static CLOSE_WINDOW_MESSAGE = 'close-login-window';
|
|
@@ -98,6 +102,7 @@ class Auth {
|
|
|
98
102
|
_tokenValidator = null;
|
|
99
103
|
_postponed = false;
|
|
100
104
|
_backendCheckPromise = null;
|
|
105
|
+
_forceTokenUpdatePromise = null;
|
|
101
106
|
_authDialogService = undefined;
|
|
102
107
|
_domainStorage;
|
|
103
108
|
user = null;
|
|
@@ -149,6 +154,7 @@ class Auth {
|
|
|
149
154
|
});
|
|
150
155
|
this._requestBuilder = new AuthRequestBuilder({
|
|
151
156
|
authorization: this.config.serverUri + Auth.API_PATH + Auth.API_AUTH_PATH,
|
|
157
|
+
logout: this.config.serverUri + Auth.API_PATH + Auth.API_LOGOUT_PATH,
|
|
152
158
|
clientId,
|
|
153
159
|
redirect,
|
|
154
160
|
redirectUri,
|
|
@@ -251,6 +257,8 @@ class Auth {
|
|
|
251
257
|
throw error;
|
|
252
258
|
}
|
|
253
259
|
if (this._canShowDialogs()) {
|
|
260
|
+
// eslint-disable-next-line no-console
|
|
261
|
+
console.error('RingUI Auth: Init failure', error);
|
|
254
262
|
this._showAuthDialog({
|
|
255
263
|
nonInteractive: true,
|
|
256
264
|
error
|
|
@@ -270,7 +278,7 @@ class Auth {
|
|
|
270
278
|
if (this.user && userID === this.user.id) {
|
|
271
279
|
return;
|
|
272
280
|
}
|
|
273
|
-
this.forceTokenUpdate();
|
|
281
|
+
this.forceTokenUpdate().catch(noop);
|
|
274
282
|
});
|
|
275
283
|
let state;
|
|
276
284
|
try {
|
|
@@ -294,7 +302,7 @@ class Auth {
|
|
|
294
302
|
serviceID
|
|
295
303
|
} = message;
|
|
296
304
|
if (serviceID !== this.config.clientId && (!userID || this.user?.id !== userID)) {
|
|
297
|
-
this.forceTokenUpdate();
|
|
305
|
+
this.forceTokenUpdate().catch(noop);
|
|
298
306
|
}
|
|
299
307
|
}
|
|
300
308
|
// Access token appears to be valid.
|
|
@@ -389,9 +397,27 @@ class Auth {
|
|
|
389
397
|
}
|
|
390
398
|
/**
|
|
391
399
|
* Get new token in the background or redirect to the login page.
|
|
392
|
-
*
|
|
400
|
+
*
|
|
401
|
+
* Retries background token refresh with delays from {@link AuthConfig.tokenRefreshRetryDelays}
|
|
402
|
+
* with increasing delays before showing the auth dialog.
|
|
403
|
+
* This handles transient failures that commonly occur after network
|
|
404
|
+
* recovery (e.g. waking from sleep, switching networks) where the first
|
|
405
|
+
* iframe-based auth attempt fails but a subsequent one succeeds once
|
|
406
|
+
* the Hub session is re-established.
|
|
407
|
+
*
|
|
408
|
+
* @return {Promise.<string | null>}
|
|
393
409
|
*/
|
|
394
|
-
|
|
410
|
+
forceTokenUpdate() {
|
|
411
|
+
if (this._forceTokenUpdatePromise) {
|
|
412
|
+
return this._forceTokenUpdatePromise;
|
|
413
|
+
}
|
|
414
|
+
this._forceTokenUpdatePromise = this._doForceTokenUpdate().finally(() => {
|
|
415
|
+
this._forceTokenUpdatePromise = null;
|
|
416
|
+
});
|
|
417
|
+
return this._forceTokenUpdatePromise;
|
|
418
|
+
}
|
|
419
|
+
async _doForceTokenUpdate() {
|
|
420
|
+
var _lastError$message;
|
|
395
421
|
try {
|
|
396
422
|
if (!this._backendCheckPromise) {
|
|
397
423
|
this._backendCheckPromise = this._checkBackendsStatusesIfEnabled();
|
|
@@ -402,43 +428,47 @@ class Auth {
|
|
|
402
428
|
} finally {
|
|
403
429
|
this._backendCheckPromise = null;
|
|
404
430
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (!(error instanceof Error)) {
|
|
410
|
-
return null;
|
|
431
|
+
let lastError = null;
|
|
432
|
+
for (const delay of this.config.tokenRefreshRetryDelays) {
|
|
433
|
+
if (delay > 0) {
|
|
434
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
411
435
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
436
|
+
try {
|
|
437
|
+
var _await$this$_backgrou;
|
|
438
|
+
return (_await$this$_backgrou = await this._backgroundFlow?.authorize()) !== null && _await$this$_backgrou !== void 0 ? _await$this$_backgrou : null;
|
|
439
|
+
} catch (error) {
|
|
440
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (this._canShowDialogs()) {
|
|
444
|
+
return new Promise(resolve => {
|
|
445
|
+
const onTryAgain = async () => {
|
|
446
|
+
try {
|
|
447
|
+
const result = await this._backgroundFlow?.authorize();
|
|
448
|
+
resolve(result !== null && result !== void 0 ? result : null);
|
|
449
|
+
} catch (retryError) {
|
|
450
|
+
if (retryError instanceof Error) {
|
|
451
|
+
this._showAuthDialog({
|
|
452
|
+
nonInteractive: true,
|
|
453
|
+
error: retryError,
|
|
454
|
+
onTryAgain
|
|
455
|
+
});
|
|
427
456
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
457
|
+
throw retryError;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
this._showAuthDialog({
|
|
461
|
+
nonInteractive: true,
|
|
462
|
+
error: lastError,
|
|
463
|
+
onTryAgain
|
|
434
464
|
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
throw new TokenValidator.TokenValidationError(error.message);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const authRequest = await this._requestBuilder?.prepareAuthRequest();
|
|
468
|
+
if (authRequest) {
|
|
469
|
+
this._redirectCurrentPage(authRequest.url);
|
|
441
470
|
}
|
|
471
|
+
throw new TokenValidator.TokenValidationError((_lastError$message = lastError?.message) !== null && _lastError$message !== void 0 ? _lastError$message : 'Failed to refresh token');
|
|
442
472
|
}
|
|
443
473
|
async loadCurrentService() {
|
|
444
474
|
if (this._service.serviceName) {
|
|
@@ -523,7 +553,13 @@ class Auth {
|
|
|
523
553
|
}
|
|
524
554
|
_beforeLogout(params) {
|
|
525
555
|
if (this._canShowDialogs()) {
|
|
526
|
-
|
|
556
|
+
const onTryAgain = async () => {
|
|
557
|
+
await this.forceTokenUpdate();
|
|
558
|
+
};
|
|
559
|
+
this._showAuthDialog({
|
|
560
|
+
onTryAgain,
|
|
561
|
+
...params
|
|
562
|
+
});
|
|
527
563
|
return;
|
|
528
564
|
}
|
|
529
565
|
this.logout();
|
|
@@ -568,7 +604,7 @@ class Auth {
|
|
|
568
604
|
return;
|
|
569
605
|
}
|
|
570
606
|
if (this.user?.guest && nonInteractive) {
|
|
571
|
-
this.forceTokenUpdate();
|
|
607
|
+
this.forceTokenUpdate().catch(noop);
|
|
572
608
|
} else {
|
|
573
609
|
this._initDeferred?.resolve?.();
|
|
574
610
|
}
|
|
@@ -698,20 +734,22 @@ class Auth {
|
|
|
698
734
|
});
|
|
699
735
|
}
|
|
700
736
|
/**
|
|
701
|
-
* Wipe accessToken and redirect to
|
|
737
|
+
* Wipe accessToken and redirect to logout endpoint.
|
|
738
|
+
* Uses RP-initiated logout flow (oauth2/logout) when rpInitiatedLogout config is enabled,
|
|
739
|
+
* falls back to oauth2/auth redirect otherwise.
|
|
740
|
+
* See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
|
|
702
741
|
*/
|
|
703
742
|
async logout(extraParams) {
|
|
704
|
-
const requestParams = {
|
|
705
|
-
request_credentials: 'required',
|
|
706
|
-
...extraParams
|
|
707
|
-
};
|
|
708
743
|
await this._checkBackendsStatusesIfEnabled();
|
|
709
744
|
await this.listeners.trigger('logout');
|
|
710
745
|
this._updateDomainUser(null);
|
|
711
746
|
await this._storage?.wipeToken();
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
747
|
+
const request = this.config.rpInitiatedLogout ? await this._requestBuilder?.prepareLogoutRequest(extraParams) : await this._requestBuilder?.prepareAuthRequest({
|
|
748
|
+
request_credentials: 'required',
|
|
749
|
+
...extraParams
|
|
750
|
+
});
|
|
751
|
+
if (request) {
|
|
752
|
+
this._redirectCurrentPage(request.url);
|
|
715
753
|
}
|
|
716
754
|
}
|
|
717
755
|
async _runEmbeddedLogin() {
|
|
@@ -743,6 +781,8 @@ class Auth {
|
|
|
743
781
|
this.listeners.trigger(USER_CHANGED_EVENT, user);
|
|
744
782
|
}
|
|
745
783
|
} catch (e) {
|
|
784
|
+
// eslint-disable-next-line no-console
|
|
785
|
+
console.error('RingUI Auth: login failure', e);
|
|
746
786
|
this._beforeLogout();
|
|
747
787
|
}
|
|
748
788
|
}
|
|
@@ -916,4 +956,4 @@ class Auth {
|
|
|
916
956
|
}
|
|
917
957
|
}
|
|
918
958
|
|
|
919
|
-
export { Auth, DEFAULT_BACKGROUND_TIMEOUT, DEFAULT_EXPIRES_TIMEOUT, DOMAIN_USER_CHANGED_EVENT, LOGOUT_EVENT, LOGOUT_POSTPONED_EVENT, USER_CHANGED_EVENT, USER_CHANGE_POSTPONED_EVENT, Auth as default };
|
|
959
|
+
export { Auth, DEFAULT_BACKGROUND_TIMEOUT, DEFAULT_EXPIRES_TIMEOUT, DOMAIN_USER_CHANGED_EVENT, LOGOUT_EVENT, LOGOUT_POSTPONED_EVENT, TOKEN_REFRESH_RETRY_DELAYS, USER_CHANGED_EVENT, USER_CHANGE_POSTPONED_EVENT, Auth as default };
|
package/components/auth/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import WindowFlow from './window-flow.js';
|
|
2
2
|
import onBackendDown from './down-notification.js';
|
|
3
3
|
import { Auth } from './auth-core.js';
|
|
4
|
-
export { DEFAULT_BACKGROUND_TIMEOUT, DEFAULT_EXPIRES_TIMEOUT, DOMAIN_USER_CHANGED_EVENT, LOGOUT_EVENT, LOGOUT_POSTPONED_EVENT, USER_CHANGED_EVENT, USER_CHANGE_POSTPONED_EVENT } from './auth-core.js';
|
|
4
|
+
export { DEFAULT_BACKGROUND_TIMEOUT, DEFAULT_EXPIRES_TIMEOUT, DOMAIN_USER_CHANGED_EVENT, LOGOUT_EVENT, LOGOUT_POSTPONED_EVENT, TOKEN_REFRESH_RETRY_DELAYS, USER_CHANGED_EVENT, USER_CHANGE_POSTPONED_EVENT } from './auth-core.js';
|
|
5
5
|
import './response-parser.js';
|
|
6
6
|
import '../global/url.js';
|
|
7
7
|
import 'react-compiler-runtime';
|
|
@@ -3,6 +3,7 @@ import type { AuthState } from './storage';
|
|
|
3
3
|
import type AuthStorage from './storage';
|
|
4
4
|
export interface AuthRequestBuilderConfig {
|
|
5
5
|
authorization: string;
|
|
6
|
+
logout?: string | null | undefined;
|
|
6
7
|
redirectUri?: string | null | undefined;
|
|
7
8
|
requestCredentials?: string | null | undefined;
|
|
8
9
|
clientId?: string | null | undefined;
|
|
@@ -38,6 +39,17 @@ export default class AuthRequestBuilder {
|
|
|
38
39
|
url: string;
|
|
39
40
|
stateId: string;
|
|
40
41
|
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Build a logout URL for RP-initiated logout flow.
|
|
44
|
+
* See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
|
|
45
|
+
*
|
|
46
|
+
* @param {object=} extraParams additional query parameters for logout request
|
|
47
|
+
* @return {Promise.<{url: string, stateId: string}>} logout URL with required parameters
|
|
48
|
+
*/
|
|
49
|
+
prepareLogoutRequest(extraParams?: Record<string, unknown> | null | undefined): Promise<{
|
|
50
|
+
url: string;
|
|
51
|
+
stateId: string;
|
|
52
|
+
}>;
|
|
41
53
|
/**
|
|
42
54
|
* @param {string} id
|
|
43
55
|
* @param {StoredState} storedState
|
|
@@ -52,6 +52,33 @@ class AuthRequestBuilder {
|
|
|
52
52
|
stateId
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a logout URL for RP-initiated logout flow.
|
|
57
|
+
* See: https://youtrack.jetbrains.com/projects/HUB/articles/HUB-A-43#rp-initiated-logout
|
|
58
|
+
*
|
|
59
|
+
* @param {object=} extraParams additional query parameters for logout request
|
|
60
|
+
* @return {Promise.<{url: string, stateId: string}>} logout URL with required parameters
|
|
61
|
+
*/
|
|
62
|
+
async prepareLogoutRequest(extraParams) {
|
|
63
|
+
if (!this.config.logout) {
|
|
64
|
+
throw new Error('Logout URL is not configured');
|
|
65
|
+
}
|
|
66
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
67
|
+
const stateId = AuthRequestBuilder._uuid();
|
|
68
|
+
const logoutParams = {
|
|
69
|
+
client_id: this.config.clientId,
|
|
70
|
+
state: stateId,
|
|
71
|
+
...extraParams
|
|
72
|
+
};
|
|
73
|
+
await this._saveState(stateId, {
|
|
74
|
+
restoreLocation: window.location.href,
|
|
75
|
+
scopes: [...this.config.scopes]
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
url: encodeURL(this.config.logout, logoutParams),
|
|
79
|
+
stateId
|
|
80
|
+
};
|
|
81
|
+
}
|
|
55
82
|
/**
|
|
56
83
|
* @param {string} id
|
|
57
84
|
* @param {StoredState} storedState
|
|
@@ -109,7 +109,7 @@ export default class List<T = unknown> extends Component<ListProps<T>, ListState
|
|
|
109
109
|
};
|
|
110
110
|
componentDidMount(): void;
|
|
111
111
|
shouldComponentUpdate(nextProps: ListProps<T>, nextState: ListState<T>): boolean;
|
|
112
|
-
componentDidUpdate(prevProps: ListProps<T>): void;
|
|
112
|
+
componentDidUpdate(prevProps: ListProps<T>, prevState: ListState<T>): void;
|
|
113
113
|
componentWillUnmount(): void;
|
|
114
114
|
scheduleScrollListener: (cb: () => void) => void;
|
|
115
115
|
static isItemType: typeof isItemType;
|
package/components/list/list.js
CHANGED
|
@@ -155,13 +155,24 @@ class List extends Component {
|
|
|
155
155
|
shouldComponentUpdate(nextProps, nextState) {
|
|
156
156
|
return Object.keys(nextProps).some(key => !Object.is(nextProps[key], this.props[key])) || Object.keys(nextState).some(key => nextState[key] !== this.state[key]);
|
|
157
157
|
}
|
|
158
|
-
componentDidUpdate(prevProps) {
|
|
158
|
+
componentDidUpdate(prevProps, prevState) {
|
|
159
159
|
if (this.virtualizedList && prevProps.data !== this.props.data) {
|
|
160
160
|
this.virtualizedList.recomputeRowHeights();
|
|
161
161
|
}
|
|
162
162
|
const {
|
|
163
163
|
activeIndex
|
|
164
164
|
} = this.state;
|
|
165
|
+
if (!this.virtualizedList && !this.props.disableScrollToActive && this.state.needScrollToActive && activeIndex != null && activeIndex !== prevState.activeIndex) {
|
|
166
|
+
const itemId = this.getId(this.props.data[activeIndex]);
|
|
167
|
+
if (itemId) {
|
|
168
|
+
document.getElementById(itemId)?.scrollIntoView?.({
|
|
169
|
+
block: 'center'
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
this.setState({
|
|
173
|
+
needScrollToActive: false
|
|
174
|
+
});
|
|
175
|
+
}
|
|
165
176
|
const isActiveItemRetainedPosition = activeIndex ? prevProps.data[activeIndex]?.key === this.props.data[activeIndex]?.key : false;
|
|
166
177
|
if ((this.props.activeIndex === null || this.props.activeIndex === undefined) && getDataHash(this.props.data) !== getDataHash(prevProps.data) && shouldActivateFirstItem(this.props) && !isActiveItemRetainedPosition) {
|
|
167
178
|
this.activateFirst();
|
|
@@ -5,11 +5,11 @@ const MAJOR_VERSION_INDEX = 0;
|
|
|
5
5
|
/**
|
|
6
6
|
* SUPPORTED_BROWSERS are defined by Babel plugin, see babel config
|
|
7
7
|
*/
|
|
8
|
-
if (!["and_chr 144", "and_ff 147", "and_qq 14.9", "and_uc 15.5", "android 144", "chrome 144", "chrome 143", "chrome 142", "chrome 141", "chrome 140", "chrome 139", "chrome 138", "chrome 137", "chrome 136", "chrome 135", "chrome 134", "chrome 133", "chrome 132", "chrome 131", "chrome 130", "chrome 129", "chrome 128", "chrome 127", "chrome 126", "chrome 125", "chrome 124", "chrome 123", "chrome 122", "chrome 121", "chrome 120", "chrome 119", "chrome 118", "chrome 117", "chrome 116", "chrome 115", "chrome 112", "chrome 109", "edge 144", "edge 143", "edge 142", "edge 141", "edge 140", "edge 139", "edge 138", "edge 137", "edge 136", "edge 135", "edge 134", "edge 133", "edge 132", "edge 131", "edge 130", "edge 129", "edge 128", "edge 127", "edge 126", "edge 125", "edge 124", "edge 123", "edge 122", "edge 121", "edge 120", "edge 119", "edge 118", "edge 117", "edge 116", "edge 115", "firefox 147", "firefox 146", "firefox 145", "firefox 144", "firefox 143", "firefox 142", "firefox 141", "firefox 140", "firefox 139", "firefox 138", "firefox 137", "firefox 136", "firefox 135", "firefox 134", "firefox 133", "firefox 132", "firefox 131", "firefox 130", "firefox 129", "firefox 128", "firefox 127", "firefox 126", "firefox 125", "firefox 124", "firefox 123", "firefox 122", "firefox 121", "firefox 120", "firefox 119", "firefox 118", "firefox 117", "firefox 116", "ios_saf 26.2", "ios_saf 26.1", "ios_saf 26.0", "ios_saf 18.5-18.7", "ios_saf 18.4", "ios_saf 18.3", "ios_saf 18.2", "ios_saf 18.1", "ios_saf 18.0", "ios_saf 17.6-17.7", "ios_saf 17.5", "ios_saf 17.4", "ios_saf 17.3", "ios_saf 17.2", "ios_saf 17.1", "ios_saf 17.0", "ios_saf 16.6-16.7", "ios_saf 16.5", "ios_saf 16.4", "kaios 3.0-3.1", "kaios 2.5", "op_mini all", "op_mob 80", "opera 125", "opera 124", "safari 26.2", "safari 26.1", "safari 26.0", "safari 18.5-18.6", "safari 18.4", "safari 18.3", "safari 18.2", "safari 18.1", "safari 18.0", "safari 17.6", "safari 17.5", "safari 17.4", "safari 17.3", "safari 17.2", "safari 17.1", "safari 17.0", "safari 16.6", "safari 16.5", "safari 16.4", "samsung 29", "samsung 28"]) {
|
|
8
|
+
if (!["and_chr 144", "and_ff 147", "and_qq 14.9", "and_uc 15.5", "android 144", "chrome 145", "chrome 144", "chrome 143", "chrome 142", "chrome 141", "chrome 140", "chrome 139", "chrome 138", "chrome 137", "chrome 136", "chrome 135", "chrome 134", "chrome 133", "chrome 132", "chrome 131", "chrome 130", "chrome 129", "chrome 128", "chrome 127", "chrome 126", "chrome 125", "chrome 124", "chrome 123", "chrome 122", "chrome 121", "chrome 120", "chrome 119", "chrome 118", "chrome 117", "chrome 116", "chrome 115", "chrome 112", "chrome 109", "edge 144", "edge 143", "edge 142", "edge 141", "edge 140", "edge 139", "edge 138", "edge 137", "edge 136", "edge 135", "edge 134", "edge 133", "edge 132", "edge 131", "edge 130", "edge 129", "edge 128", "edge 127", "edge 126", "edge 125", "edge 124", "edge 123", "edge 122", "edge 121", "edge 120", "edge 119", "edge 118", "edge 117", "edge 116", "edge 115", "firefox 147", "firefox 146", "firefox 145", "firefox 144", "firefox 143", "firefox 142", "firefox 141", "firefox 140", "firefox 139", "firefox 138", "firefox 137", "firefox 136", "firefox 135", "firefox 134", "firefox 133", "firefox 132", "firefox 131", "firefox 130", "firefox 129", "firefox 128", "firefox 127", "firefox 126", "firefox 125", "firefox 124", "firefox 123", "firefox 122", "firefox 121", "firefox 120", "firefox 119", "firefox 118", "firefox 117", "firefox 116", "ios_saf 26.3", "ios_saf 26.2", "ios_saf 26.1", "ios_saf 26.0", "ios_saf 18.5-18.7", "ios_saf 18.4", "ios_saf 18.3", "ios_saf 18.2", "ios_saf 18.1", "ios_saf 18.0", "ios_saf 17.6-17.7", "ios_saf 17.5", "ios_saf 17.4", "ios_saf 17.3", "ios_saf 17.2", "ios_saf 17.1", "ios_saf 17.0", "ios_saf 16.6-16.7", "ios_saf 16.5", "ios_saf 16.4", "kaios 3.0-3.1", "kaios 2.5", "op_mini all", "op_mob 80", "opera 125", "opera 124", "safari 26.3", "safari 26.2", "safari 26.1", "safari 26.0", "safari 18.5-18.6", "safari 18.4", "safari 18.3", "safari 18.2", "safari 18.1", "safari 18.0", "safari 17.6", "safari 17.5", "safari 17.4", "safari 17.3", "safari 17.2", "safari 17.1", "safari 17.0", "safari 16.6", "safari 16.5", "safari 16.4", "samsung 29", "samsung 28"]) {
|
|
9
9
|
// eslint-disable-next-line no-console
|
|
10
10
|
console.warn('Ring UI: no SUPPORTED_BROWSERS passed. Please check babel config.');
|
|
11
11
|
}
|
|
12
|
-
const SUPPORTED = ["and_chr 144", "and_ff 147", "and_qq 14.9", "and_uc 15.5", "android 144", "chrome 144", "chrome 143", "chrome 142", "chrome 141", "chrome 140", "chrome 139", "chrome 138", "chrome 137", "chrome 136", "chrome 135", "chrome 134", "chrome 133", "chrome 132", "chrome 131", "chrome 130", "chrome 129", "chrome 128", "chrome 127", "chrome 126", "chrome 125", "chrome 124", "chrome 123", "chrome 122", "chrome 121", "chrome 120", "chrome 119", "chrome 118", "chrome 117", "chrome 116", "chrome 115", "chrome 112", "chrome 109", "edge 144", "edge 143", "edge 142", "edge 141", "edge 140", "edge 139", "edge 138", "edge 137", "edge 136", "edge 135", "edge 134", "edge 133", "edge 132", "edge 131", "edge 130", "edge 129", "edge 128", "edge 127", "edge 126", "edge 125", "edge 124", "edge 123", "edge 122", "edge 121", "edge 120", "edge 119", "edge 118", "edge 117", "edge 116", "edge 115", "firefox 147", "firefox 146", "firefox 145", "firefox 144", "firefox 143", "firefox 142", "firefox 141", "firefox 140", "firefox 139", "firefox 138", "firefox 137", "firefox 136", "firefox 135", "firefox 134", "firefox 133", "firefox 132", "firefox 131", "firefox 130", "firefox 129", "firefox 128", "firefox 127", "firefox 126", "firefox 125", "firefox 124", "firefox 123", "firefox 122", "firefox 121", "firefox 120", "firefox 119", "firefox 118", "firefox 117", "firefox 116", "ios_saf 26.2", "ios_saf 26.1", "ios_saf 26.0", "ios_saf 18.5-18.7", "ios_saf 18.4", "ios_saf 18.3", "ios_saf 18.2", "ios_saf 18.1", "ios_saf 18.0", "ios_saf 17.6-17.7", "ios_saf 17.5", "ios_saf 17.4", "ios_saf 17.3", "ios_saf 17.2", "ios_saf 17.1", "ios_saf 17.0", "ios_saf 16.6-16.7", "ios_saf 16.5", "ios_saf 16.4", "kaios 3.0-3.1", "kaios 2.5", "op_mini all", "op_mob 80", "opera 125", "opera 124", "safari 26.2", "safari 26.1", "safari 26.0", "safari 18.5-18.6", "safari 18.4", "safari 18.3", "safari 18.2", "safari 18.1", "safari 18.0", "safari 17.6", "safari 17.5", "safari 17.4", "safari 17.3", "safari 17.2", "safari 17.1", "safari 17.0", "safari 16.6", "safari 16.5", "safari 16.4", "samsung 29", "samsung 28"] || [];
|
|
12
|
+
const SUPPORTED = ["and_chr 144", "and_ff 147", "and_qq 14.9", "and_uc 15.5", "android 144", "chrome 145", "chrome 144", "chrome 143", "chrome 142", "chrome 141", "chrome 140", "chrome 139", "chrome 138", "chrome 137", "chrome 136", "chrome 135", "chrome 134", "chrome 133", "chrome 132", "chrome 131", "chrome 130", "chrome 129", "chrome 128", "chrome 127", "chrome 126", "chrome 125", "chrome 124", "chrome 123", "chrome 122", "chrome 121", "chrome 120", "chrome 119", "chrome 118", "chrome 117", "chrome 116", "chrome 115", "chrome 112", "chrome 109", "edge 144", "edge 143", "edge 142", "edge 141", "edge 140", "edge 139", "edge 138", "edge 137", "edge 136", "edge 135", "edge 134", "edge 133", "edge 132", "edge 131", "edge 130", "edge 129", "edge 128", "edge 127", "edge 126", "edge 125", "edge 124", "edge 123", "edge 122", "edge 121", "edge 120", "edge 119", "edge 118", "edge 117", "edge 116", "edge 115", "firefox 147", "firefox 146", "firefox 145", "firefox 144", "firefox 143", "firefox 142", "firefox 141", "firefox 140", "firefox 139", "firefox 138", "firefox 137", "firefox 136", "firefox 135", "firefox 134", "firefox 133", "firefox 132", "firefox 131", "firefox 130", "firefox 129", "firefox 128", "firefox 127", "firefox 126", "firefox 125", "firefox 124", "firefox 123", "firefox 122", "firefox 121", "firefox 120", "firefox 119", "firefox 118", "firefox 117", "firefox 116", "ios_saf 26.3", "ios_saf 26.2", "ios_saf 26.1", "ios_saf 26.0", "ios_saf 18.5-18.7", "ios_saf 18.4", "ios_saf 18.3", "ios_saf 18.2", "ios_saf 18.1", "ios_saf 18.0", "ios_saf 17.6-17.7", "ios_saf 17.5", "ios_saf 17.4", "ios_saf 17.3", "ios_saf 17.2", "ios_saf 17.1", "ios_saf 17.0", "ios_saf 16.6-16.7", "ios_saf 16.5", "ios_saf 16.4", "kaios 3.0-3.1", "kaios 2.5", "op_mini all", "op_mob 80", "opera 125", "opera 124", "safari 26.3", "safari 26.2", "safari 26.1", "safari 26.0", "safari 18.5-18.6", "safari 18.4", "safari 18.3", "safari 18.2", "safari 18.1", "safari 18.0", "safari 17.6", "safari 17.5", "safari 17.4", "safari 17.3", "safari 17.2", "safari 17.1", "safari 17.0", "safari 16.6", "safari 16.5", "safari 16.4", "samsung 29", "samsung 28"] || [];
|
|
13
13
|
const WHITE_LISTED_BROWSERS = ['chrome', 'firefox', 'safari', 'edge'];
|
|
14
14
|
const WHITE_LIST = SUPPORTED.reduce((acc, item) => {
|
|
15
15
|
var _item$match;
|