@gridsuite/commons-ui 0.205.0 → 0.206.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/announcement/AnnouncementBanner.d.ts +1 -1
- package/dist/components/announcement/AnnouncementBanner.js +3 -2
- package/dist/components/announcement/AnnouncementNotification.d.ts +1 -1
- package/dist/components/announcement/useGlobalAnnouncement.d.ts +1 -1
- package/dist/components/authentication/SignInCallbackHandler.d.ts +1 -1
- package/dist/components/authentication/SilentRenewCallbackHandler.d.ts +1 -1
- package/dist/components/authentication/authenticationType.d.ts +1 -1
- package/dist/components/authentication/utils/authService.d.ts +1 -6
- package/dist/components/authentication/utils/authService.js +18 -9
- package/dist/components/authentication/utils/userManagerMock.d.ts +7 -9
- package/dist/components/authentication/utils/userManagerMock.js +23 -27
- package/dist/components/customAGGrid/cell-renderers.js +7 -6
- package/dist/components/dnd-table/dnd-table-bottom-left-buttons.js +4 -5
- package/dist/components/dnd-table/dnd-table-bottom-right-buttons.js +6 -9
- package/dist/components/dnd-table/dnd-table.js +3 -3
- package/dist/components/dnd-table-v2/deletable-table-row.js +3 -2
- package/dist/components/dnd-table-v2/dnd-table-bottom-left-buttons.js +4 -5
- package/dist/components/dnd-table-v2/dnd-table-bottom-right-buttons.js +6 -9
- package/dist/components/dnd-table-v2/dnd-table-row.js +3 -3
- package/dist/components/dnd-table-v2/dnd-table.js +3 -2
- package/dist/components/flatParameters/FlatParameters.js +3 -3
- package/dist/components/grid/grid-item.js +3 -2
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +2 -0
- package/dist/components/inputs/ActivableChip.js +3 -2
- package/dist/components/inputs/reactHookForm/DirectoryItemsInput.js +3 -2
- package/dist/components/inputs/reactHookForm/agGridTable/BottomRightButtons.js +3 -3
- package/dist/components/inputs/reactHookForm/expandableInput/DeletableRow.js +3 -2
- package/dist/components/inputs/reactHookForm/text/DescriptionInput.js +3 -2
- package/dist/components/inputs/reactHookForm/utils/HelperPreviousValue.js +3 -4
- package/dist/components/network-modification-table/renderers/name-cell.js +3 -2
- package/dist/components/network-modifications/common/activePowerControl/ActivePowerControlForm.js +3 -2
- package/dist/components/network-modifications/common/connectivity/PositionForm.js +3 -2
- package/dist/components/network-modifications/voltageLevel/creation/tabs/substationTab/SubstationCreationSection.js +3 -2
- package/dist/components/overflowableText/OverflowableText.js +3 -2
- package/dist/components/parameters/common/limitreductions/limit-reduction-table-cell.js +3 -2
- package/dist/components/parameters/common/parameter-field.js +3 -2
- package/dist/components/parameters/common/parameter-table/parameter-table.js +3 -2
- package/dist/components/parameters/common/parameter-table/table-row.js +3 -2
- package/dist/components/parameters/common/parameter-table-field/parameter-table-field.js +3 -3
- package/dist/components/parameters/common/parameters-edition-dialog-props.d.ts +1 -1
- package/dist/components/parameters/common/voltage-level-table/custom-voltage-level-table.js +3 -2
- package/dist/components/parameters/common/widget/parameter-float.js +3 -2
- package/dist/components/parameters/dynamic-simulation/curve/common/grid-buttons.js +4 -5
- package/dist/components/parameters/dynamic-simulation/curve/dialog/curve-selector-dialog.js +4 -5
- package/dist/components/parameters/network-visualizations/network-visualizations-form.d.ts +1 -1
- package/dist/components/parameters/network-visualizations/network-visualizations-parameters-inline.d.ts +1 -1
- package/dist/components/parameters/security-analysis/security-analysis-violations-hiding.js +4 -4
- package/dist/components/parameters/short-circuit/short-circuit-icc-cluster-table-row.js +3 -2
- package/dist/components/parameters/short-circuit/short-circuit-icc-cluster-table.js +4 -3
- package/dist/components/parameters/short-circuit/short-circuit-icc-material-table.js +3 -2
- package/dist/components/parameters/voltage-init/voltage-limits-parameters.js +3 -3
- package/dist/components/tooltip/CustomTooltip.d.ts +2 -0
- package/dist/components/tooltip/CustomTooltip.js +9 -0
- package/dist/components/tooltip/index.d.ts +1 -0
- package/dist/components/tooltip/index.js +4 -0
- package/dist/components/topBar/AboutDialog.js +3 -5
- package/dist/components/topBar/TopBar.d.ts +1 -1
- package/dist/components/topBar/UserInformationDialog.d.ts +1 -1
- package/dist/hooks/use-parameters-backend.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/redux/actions/authActions.d.ts +1 -1
- package/dist/redux/commonStore.d.ts +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UUID } from 'node:crypto';
|
|
2
2
|
import { PropsWithChildren, ReactNode } from 'react';
|
|
3
3
|
import { AlertProps } from '@mui/material';
|
|
4
|
-
import { User } from 'oidc-client';
|
|
4
|
+
import { User } from 'oidc-client-ts';
|
|
5
5
|
import { AnnouncementSeverity } from '../../utils/types';
|
|
6
6
|
export type AnnouncementBannerProps = PropsWithChildren<{
|
|
7
7
|
user?: User | {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useCallback } from "react";
|
|
3
|
-
import { useTheme, Collapse, Alert, AlertTitle
|
|
3
|
+
import { useTheme, Collapse, Alert, AlertTitle } from "@mui/material";
|
|
4
4
|
import { Campaign } from "@mui/icons-material";
|
|
5
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
5
6
|
import "../../utils/types/equipmentType.js";
|
|
6
7
|
import { AnnouncementSeverity } from "../../utils/types/types.js";
|
|
7
8
|
const snackbarInfo = "#2196f3";
|
|
@@ -77,7 +78,7 @@ function AnnouncementBanner({
|
|
|
77
78
|
sx: styles.alert,
|
|
78
79
|
children: [
|
|
79
80
|
title && /* @__PURE__ */ jsx(AlertTitle, { children: title }),
|
|
80
|
-
/* @__PURE__ */ jsx(
|
|
81
|
+
/* @__PURE__ */ jsx(CustomTooltip, { title: children, children: /* @__PURE__ */ jsx("span", { children }) })
|
|
81
82
|
]
|
|
82
83
|
}
|
|
83
84
|
) });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Dispatch } from 'react';
|
|
2
2
|
import { Location, NavigateFunction } from 'react-router';
|
|
3
|
-
import {
|
|
3
|
+
import { UserManager } from 'oidc-client-ts';
|
|
4
4
|
import { AuthenticationActions } from '../../../redux/actions/authActions';
|
|
5
5
|
type IdpSettingsGetter = () => Promise<IdpSettings>;
|
|
6
6
|
export type IdpSettings = {
|
|
@@ -17,11 +17,6 @@ type CustomUserManager = UserManager & {
|
|
|
17
17
|
maxExpiresIn?: number;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
|
-
declare global {
|
|
21
|
-
interface Window {
|
|
22
|
-
OIDCLog?: Log;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
20
|
export declare function login(location: Location, userManagerInstance: UserManager | null): Promise<void> | undefined;
|
|
26
21
|
export declare function logout(dispatch: Dispatch<AuthenticationActions>, userManagerInstance: UserManager | null): Promise<void> | undefined;
|
|
27
22
|
export declare function dispatchUser(dispatch: Dispatch<AuthenticationActions>, userManagerInstance: CustomUserManager): Promise<void>;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { jwtDecode } from "jwt-decode";
|
|
2
|
-
import {
|
|
2
|
+
import { UserManager } from "oidc-client-ts";
|
|
3
3
|
import { UserManagerMock } from "./userManagerMock.js";
|
|
4
4
|
import { setLoggedUser, setLogoutError, setSignInCallbackError, setShowAuthenticationRouterLogin, resetAuthenticationRouterError } from "../../../redux/actions/authActions.js";
|
|
5
|
-
window.OIDCLog = Log;
|
|
6
5
|
const hackAuthorityKey = "oidc.hack.authority";
|
|
7
6
|
const oidcHackReloadedKey = "gridsuite-oidc-hack-reloaded";
|
|
8
7
|
const pathKey = "powsybl-gridsuite-current-path";
|
|
9
|
-
const
|
|
8
|
+
const accessTokenExpiringNotificationTimeInSeconds = 60;
|
|
10
9
|
function isIssuerError(error) {
|
|
11
10
|
return error.message.includes("Invalid issuer in token");
|
|
12
11
|
}
|
|
@@ -30,6 +29,9 @@ function reloadTimerOnExpiresIn(user, userManager, expiresIn) {
|
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
31
|
function getIdTokenExpiresIn(user) {
|
|
32
|
+
if (!user.id_token) {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
33
35
|
const now = Date.now() / 1e3;
|
|
34
36
|
const { exp } = jwtDecode(user.id_token);
|
|
35
37
|
if (exp === void 0) {
|
|
@@ -52,6 +54,9 @@ function handleSigninSilent(dispatch, userManager) {
|
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
function computeMinExpiresIn(expiresIn, idToken, maxExpiresIn) {
|
|
57
|
+
if (!idToken) {
|
|
58
|
+
return expiresIn;
|
|
59
|
+
}
|
|
55
60
|
const now = Date.now() / 1e3;
|
|
56
61
|
const { exp } = jwtDecode(idToken);
|
|
57
62
|
if (exp === void 0) {
|
|
@@ -89,7 +94,7 @@ function logout(dispatch, userManagerInstance) {
|
|
|
89
94
|
if (user) {
|
|
90
95
|
return userManagerInstance.signoutRedirect({
|
|
91
96
|
extraQueryParams: {
|
|
92
|
-
TargetResource: userManagerInstance.settings.post_logout_redirect_uri
|
|
97
|
+
TargetResource: userManagerInstance.settings.post_logout_redirect_uri ?? ""
|
|
93
98
|
}
|
|
94
99
|
}).then(() => {
|
|
95
100
|
console.debug("logged out, window is closing...");
|
|
@@ -114,7 +119,7 @@ function dispatchUser(dispatch, userManagerInstance) {
|
|
|
114
119
|
reloadTimerOnExpiresIn(
|
|
115
120
|
user,
|
|
116
121
|
userManagerInstance,
|
|
117
|
-
computeMinExpiresIn(user.expires_in, user.id_token, userManagerInstance.idpSettings?.maxExpiresIn)
|
|
122
|
+
computeMinExpiresIn(user.expires_in ?? 0, user.id_token, userManagerInstance.idpSettings?.maxExpiresIn)
|
|
118
123
|
);
|
|
119
124
|
return dispatch(setLoggedUser(user));
|
|
120
125
|
}
|
|
@@ -180,7 +185,7 @@ function handleUser(dispatch, userManager) {
|
|
|
180
185
|
console.log(
|
|
181
186
|
`Error in silent renew, but idtoken ALMOST expiring (expiring in${idTokenExpiresIn}) => last chance, next error will logout`,
|
|
182
187
|
`maxExpiresIn = ${userManager.idpSettings.maxExpiresIn}`,
|
|
183
|
-
`last renew attempt in ${idTokenExpiresIn -
|
|
188
|
+
`last renew attempt in ${idTokenExpiresIn - accessTokenExpiringNotificationTimeInSeconds}seconds`,
|
|
184
189
|
error
|
|
185
190
|
);
|
|
186
191
|
reloadTimerOnExpiresIn(user, userManager, idTokenExpiresIn);
|
|
@@ -198,13 +203,17 @@ function handleUser(dispatch, userManager) {
|
|
|
198
203
|
);
|
|
199
204
|
}
|
|
200
205
|
});
|
|
201
|
-
},
|
|
206
|
+
}, accessTokenExpiringNotificationTimeInSeconds * 1e3);
|
|
202
207
|
});
|
|
203
208
|
console.debug("dispatch user");
|
|
204
209
|
dispatchUser(dispatch, userManager);
|
|
205
210
|
}
|
|
206
211
|
async function initializeAuthenticationDev(dispatch, isSilentRenew, isSigninCallback) {
|
|
207
|
-
const userManager = new UserManagerMock({
|
|
212
|
+
const userManager = new UserManagerMock({
|
|
213
|
+
authority: "mock",
|
|
214
|
+
client_id: "mock",
|
|
215
|
+
redirect_uri: "mock"
|
|
216
|
+
});
|
|
208
217
|
if (!isSilentRenew) {
|
|
209
218
|
handleUser(dispatch, userManager);
|
|
210
219
|
if (!isSigninCallback) {
|
|
@@ -224,7 +233,7 @@ async function initializeAuthenticationProd(dispatch, isSilentRenew, idpSettings
|
|
|
224
233
|
silent_redirect_uri: idpSettings.silent_redirect_uri,
|
|
225
234
|
scope: idpSettings.scope,
|
|
226
235
|
automaticSilentRenew: !isSilentRenew,
|
|
227
|
-
|
|
236
|
+
accessTokenExpiringNotificationTimeInSeconds,
|
|
228
237
|
response_type: "code"
|
|
229
238
|
};
|
|
230
239
|
const userManager = new UserManager(settings);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
declare class Events
|
|
3
|
-
userLoadedCallbacks:
|
|
4
|
-
load(): void;
|
|
1
|
+
import { OidcClientSettings, SigninRequest, SigninResponse, SignoutRequest, SignoutResponse, User, UserLoadedCallback, UserManagerSettings } from 'oidc-client-ts';
|
|
2
|
+
declare class Events {
|
|
3
|
+
userLoadedCallbacks: UserLoadedCallback[];
|
|
4
|
+
load(_user: User): void;
|
|
5
5
|
unload(): void;
|
|
6
6
|
addAccessTokenExpiring(): void;
|
|
7
7
|
removeAccessTokenExpiring(): void;
|
|
8
8
|
addAccessTokenExpired(): void;
|
|
9
9
|
removeAccessTokenExpired(): void;
|
|
10
|
-
addUserLoaded(callback:
|
|
10
|
+
addUserLoaded(callback: UserLoadedCallback): void;
|
|
11
11
|
removeUserLoaded(): void;
|
|
12
12
|
addUserUnloaded(): void;
|
|
13
13
|
removeUserUnloaded(): void;
|
|
@@ -20,11 +20,10 @@ declare class Events implements UserManagerEvents {
|
|
|
20
20
|
addUserSessionChanged(): void;
|
|
21
21
|
removeUserSessionChanged(): void;
|
|
22
22
|
}
|
|
23
|
-
export declare class UserManagerMock
|
|
23
|
+
export declare class UserManagerMock {
|
|
24
24
|
events: Events;
|
|
25
25
|
readonly settings: OidcClientSettings;
|
|
26
26
|
private static readonly user;
|
|
27
|
-
readonly metadataService: MetadataService;
|
|
28
27
|
private static readonly STORAGE_KEY;
|
|
29
28
|
constructor(settings: UserManagerSettings);
|
|
30
29
|
getUser(): Promise<User | null>;
|
|
@@ -46,9 +45,8 @@ export declare class UserManagerMock implements UserManager {
|
|
|
46
45
|
querySessionStatus(): Promise<{
|
|
47
46
|
session_state: string;
|
|
48
47
|
sub: string;
|
|
49
|
-
sid: undefined;
|
|
50
48
|
}>;
|
|
51
|
-
|
|
49
|
+
revokeTokens(): Promise<void>;
|
|
52
50
|
startSilentRenew(): void;
|
|
53
51
|
stopSilentRenew(): void;
|
|
54
52
|
createSigninRequest(): Promise<SigninRequest>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { User } from "oidc-client-ts";
|
|
1
2
|
class Events {
|
|
2
3
|
constructor() {
|
|
3
4
|
this.userLoadedCallbacks = [];
|
|
4
5
|
}
|
|
5
|
-
load() {
|
|
6
|
+
load(_user) {
|
|
6
7
|
}
|
|
7
8
|
unload() {
|
|
8
9
|
}
|
|
@@ -42,7 +43,6 @@ class Events {
|
|
|
42
43
|
}
|
|
43
44
|
const _UserManagerMock = class _UserManagerMock {
|
|
44
45
|
constructor(settings) {
|
|
45
|
-
this.metadataService = null;
|
|
46
46
|
this.settings = settings;
|
|
47
47
|
this.events = new Events();
|
|
48
48
|
}
|
|
@@ -105,11 +105,10 @@ const _UserManagerMock = class _UserManagerMock {
|
|
|
105
105
|
async querySessionStatus() {
|
|
106
106
|
return {
|
|
107
107
|
session_state: "",
|
|
108
|
-
sub: ""
|
|
109
|
-
sid: void 0
|
|
108
|
+
sub: ""
|
|
110
109
|
};
|
|
111
110
|
}
|
|
112
|
-
async
|
|
111
|
+
async revokeTokens() {
|
|
113
112
|
}
|
|
114
113
|
startSilentRenew() {
|
|
115
114
|
}
|
|
@@ -128,28 +127,25 @@ const _UserManagerMock = class _UserManagerMock {
|
|
|
128
127
|
return {};
|
|
129
128
|
}
|
|
130
129
|
};
|
|
131
|
-
_UserManagerMock.user = Object.freeze(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
state: null,
|
|
151
|
-
toStorageString: () => "Mock of UserManager"
|
|
152
|
-
});
|
|
130
|
+
_UserManagerMock.user = Object.freeze(
|
|
131
|
+
new User({
|
|
132
|
+
profile: {
|
|
133
|
+
name: "John Doe",
|
|
134
|
+
email: "Jhon.Doe@rte-france.com",
|
|
135
|
+
iss: "",
|
|
136
|
+
sub: "",
|
|
137
|
+
aud: "",
|
|
138
|
+
exp: Number.MAX_SAFE_INTEGER,
|
|
139
|
+
iat: 0
|
|
140
|
+
},
|
|
141
|
+
id_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IllNRUxIVDBndmIwbXhvU0RvWWZvbWpxZmpZVSJ9.eyJhdWQiOiI5YzQwMjQ2MS1iMmFiLTQ3NjctOWRiMy02Njg1OWJiMGZjZDAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vNzUwMmRhZDUtZDY0Yy00NmM3LTlkNDctYjE2ZjU4MGZjZmE5L3YyLjAiLCJpYXQiOjE1ODUzMzEyNDksIm5iZiI6MTU4NTMzMTI0OSwiZXhwIjoyNTg1MzM1MTQ5LCJhaW8iOiJBV1FBbS84UEFBQUF3Q0xyTDRIUEUvTnVjOU9OdHN0SUV4cVpyMUlqa1FGbXJvUW5EUzJBaksyWnpneUhQTldPdkE3bitveHkvRzgxWElsb1A0TitsQjZINFJteElwakhNYVArTjIyTzVnMUFaR04yc1d6VHA5T3JWMDIvOXhndXJBMjZrdUNXbGg2RSIsImF0X2hhc2giOiJJaWRYdGRHdzVkbjlOZDFQblVvbDh3IiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkLyIsIm5vbmNlIjoiMjkzZTcxNzhmOWE5NGZlNjg1ZWY3MjdlZTg5MTYxYjEiLCJzdWIiOiJyTnZjWXJMSXJSN25iSDJPQlhoOFkzU05wZEtPc3dfTUNkX3F3NF9vNDRJIiwidGlkIjoiNzUwMmRhZDUtZDY0Yy00NmM3LTlkNDctYjE2ZjU4MGZjZmE5IiwidXRpIjoiUFBYdkw1UWxDMG1oMGp2N3NaNGJBQSIsInZlciI6IjIuMCJ9.dPAh24KTfsqmDaRoBtMLcayAWnDqVtydQ97P1a99dg93JsDu4Jhxju9vlzvjd6Ro5a1RZdrKFKB_pgC2DkQ3wSeYjpdSNyBAlW1_ryq65JkTJVMp33OsM_7SdjaRIiJfPiJ3U9jRBSyj7ofoHCLUjD_Uu-XreKxpMGhFHOQIO72UfXg8TBpsapjkEv9Dyz2UqMa2BQvO5mxKw93LNg5BI6j2a5LhbMEmmRWqfxWGITJ9TWfHjYdFkrXKcmvWZ9D2b4tsw_5NorDxkuzVFhA89M_0ASzOXoj1Yb6LgdkzWXDimssvyyz5Oe4V3gdkAe8Jj7Uwz-9AR-MO2kNkH7ytHA",
|
|
142
|
+
session_state: "session state",
|
|
143
|
+
access_token: "eyJ0eXAiOiJKV1QiLCJub25jZSI6InhKWHlQeXVrU1paQ3BOeEcxZUQway1lVDF0YzZtQ01ZVkZKcnBDOTJxc28iLCJhbGciOiJSUzI1NiIsIng1dCI6IllNRUxIVDBndmIwbXhvU0RvWWZvbWpxZmpZVSIsImtpZCI6IllNRUxIVDBndmIwbXhvU0RvWWZvbWpxZmpZVSJ9.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83NTAyZGFkNS1kNjRjLTQ2YzctOWQ0Ny1iMTZmNTgwZmNmYTkvIiwiaWF0IjoxNTg1MzMxMjQ5LCJuYmYiOjE1ODUzMzEyNDksImV4cCI6MTU4NTMzNTE0OSwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IkFVUUF1LzhQQUFBQXdwc3RYMlVkY2VDQWx4dU9tVHpIY0R3RlhTWUtYanIvZUNTSi9PdTRqbTJyUVBCUml0U1dWMThmNldCVEdNdnQ5ZGx0Ry9lTXB1VXZqaTN2NCtzanh3PT0iLCJhbHRzZWNpZCI6IjE6bGl2ZS5jb206MDAwMzQwMDExOUZEOTIxMiIsImFtciI6WyJwd2QiXSwiYXBwX2Rpc3BsYXluYW1lIjoic3BhIiwiYXBwaWQiOiI5YzQwMjQ2MS1iMmFiLTQ3NjctOWRiMy02Njg1OWJiMGZjZDAiLCJhcHBpZGFjciI6IjAiLCJlbWFpbCI6ImNoYW1zZWRkaW5lLmJlbmhhbWVkQGVuc2ktdW1hLnRuIiwiZmFtaWx5X25hbWUiOiJCRU5IQU1FRCIsImdpdmVuX25hbWUiOiJDaGFtc2VkZGluZSIsImlkcCI6ImxpdmUuY29tIiwiaXBhZGRyIjoiNzcuMjA0LjE0Ni4xNTkiLCJuYW1lIjoiQ2hhbXNlZGRpbmUgQkVOSEFNRUQiLCJvaWQiOiIzNTIzYmQ3OC0yZjIxLTQ3ZjYtODhlOC1hYWIzYjZmMjdmNjAiLCJwbGF0ZiI6IjE0IiwicHVpZCI6IjEwMDMyMDAwOURFMDg1NkEiLCJzY3AiOiJVc2VyLlJlYWQgcHJvZmlsZSBvcGVuaWQgZW1haWwiLCJzdWIiOiJjVEd5LVlfV3FLR2x1cmRUVDdSUVlfY3FjSDJoVHpEdllZTmotQ3hONXA0IiwidGlkIjoiNzUwMmRhZDUtZDY0Yy00NmM3LTlkNDctYjE2ZjU4MGZjZmE5IiwidW5pcXVlX25hbWUiOiJsaXZlLmNvbSNjaGFtc2VkZGluZS5iZW5oYW1lZEBlbnNpLXVtYS50biIsInV0aSI6IlBQWHZMNVFsQzBtaDBqdjdzWjRiQUEiLCJ2ZXIiOiIxLjAiLCJ4bXNfc3QiOnsic3ViIjoick52Y1lyTElyUjduYkgyT0JYaDhZM1NOcGRLT3N3X01DZF9xdzRfbzQ0SSJ9LCJ4bXNfdGNkdCI6MTU4MjgyMDM1Mn0.W_ccOGW_AGdg37KSMi7LWHtvm3Mw5p1dHjgDIrUaXduKF2iLS4dCaPw7yeo4VjAcOyV6C0h6ABLDCtkwVt8BSDTIIU7DaT8k2bRbMCCq69BmeiYPsbp-yX6ywGCx5DHsnOLqI2oHbBQktA2Nmv9Va651Pbm3OpSPuGPdVimkFCcnisiGlUOej1ZMNwyVT6386O2pERPtxmFUt_D1dKLxBXxBNxLVUG5BG3bI7wMpBOHEUA5CbaBzYXmGrLMXVVbrj9OsF-WQ6aNoqsm9cicX6pJB60lFz1dxLeSgcFO7Zh2K3PFe4FnXCqAvNPadQMz_kJEO9_phlDV85c2MPqeXbA",
|
|
144
|
+
token_type: "Bearer",
|
|
145
|
+
scope: "scopes",
|
|
146
|
+
expires_at: Number.MAX_SAFE_INTEGER
|
|
147
|
+
})
|
|
148
|
+
);
|
|
153
149
|
_UserManagerMock.STORAGE_KEY = "powsybl-gridsuite-mock-user";
|
|
154
150
|
let UserManagerMock = _UserManagerMock;
|
|
155
151
|
export {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Checkbox, Box
|
|
2
|
+
import { Checkbox, Box } from "@mui/material";
|
|
3
3
|
import { useRef, useState } from "react";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
5
6
|
import { isBlankOrEmpty } from "../../utils/conversionUtils.js";
|
|
6
7
|
import "../../utils/types/equipmentType.js";
|
|
7
8
|
import "@mui/icons-material";
|
|
@@ -68,7 +69,7 @@ function NumericCellRenderer({ value, fractionDigits }) {
|
|
|
68
69
|
const numericalValue = typeof value === "number" ? value : Number.parseFloat(value);
|
|
69
70
|
const cellValue = formatNumericCell(numericalValue, fractionDigits);
|
|
70
71
|
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(
|
|
71
|
-
|
|
72
|
+
CustomTooltip,
|
|
72
73
|
{
|
|
73
74
|
disableFocusListener: true,
|
|
74
75
|
disableTouchListener: true,
|
|
@@ -78,7 +79,7 @@ function NumericCellRenderer({ value, fractionDigits }) {
|
|
|
78
79
|
) });
|
|
79
80
|
}
|
|
80
81
|
function BaseCellRenderer({ value, tooltip }) {
|
|
81
|
-
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(
|
|
82
|
+
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(CustomTooltip, { disableFocusListener: true, disableTouchListener: true, title: tooltip || value || "", children: /* @__PURE__ */ jsx(Box, { sx: styles.overflow, children: value }) }) });
|
|
82
83
|
}
|
|
83
84
|
function ErrorCellRenderer({ value }) {
|
|
84
85
|
const intl = useIntl();
|
|
@@ -92,7 +93,7 @@ function DefaultCellRenderer(props) {
|
|
|
92
93
|
}
|
|
93
94
|
function NetworkModificationNameCellRenderer({ value }) {
|
|
94
95
|
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(
|
|
95
|
-
|
|
96
|
+
CustomTooltip,
|
|
96
97
|
{
|
|
97
98
|
disableFocusListener: true,
|
|
98
99
|
disableTouchListener: true,
|
|
@@ -157,7 +158,7 @@ function MessageLogCellRenderer({
|
|
|
157
158
|
}) });
|
|
158
159
|
};
|
|
159
160
|
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(
|
|
160
|
-
|
|
161
|
+
CustomTooltip,
|
|
161
162
|
{
|
|
162
163
|
disableFocusListener: true,
|
|
163
164
|
disableTouchListener: true,
|
|
@@ -187,7 +188,7 @@ function ContingencyCellRenderer({
|
|
|
187
188
|
if (cellValue == null || tooltipValue == null) {
|
|
188
189
|
return null;
|
|
189
190
|
}
|
|
190
|
-
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(
|
|
191
|
+
return /* @__PURE__ */ jsx(Box, { sx: mergeSx(styles.tableCell), children: /* @__PURE__ */ jsx(CustomTooltip, { title: /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-line" }, children: tooltipValue }), children: /* @__PURE__ */ jsx(Box, { sx: styles.overflow, children: cellValue }) }) });
|
|
191
192
|
}
|
|
192
193
|
export {
|
|
193
194
|
BooleanCellRenderer,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Grid,
|
|
2
|
+
import { Grid, IconButton } from "@mui/material";
|
|
3
3
|
import { Upload, Replay } from "@mui/icons-material";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
5
6
|
function DndTableBottomLeftButtons({
|
|
6
7
|
handleUploadButton,
|
|
7
8
|
uploadButtonMessageId,
|
|
@@ -14,12 +15,11 @@ function DndTableBottomLeftButtons({
|
|
|
14
15
|
const intl = useIntl();
|
|
15
16
|
return /* @__PURE__ */ jsxs(Grid, { container: true, item: true, xs: true, spacing: 1, children: [
|
|
16
17
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
17
|
-
|
|
18
|
+
CustomTooltip,
|
|
18
19
|
{
|
|
19
20
|
title: intl.formatMessage({
|
|
20
21
|
id: uploadButtonMessageId
|
|
21
22
|
}),
|
|
22
|
-
placement: "top",
|
|
23
23
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
24
24
|
IconButton,
|
|
25
25
|
{
|
|
@@ -32,12 +32,11 @@ function DndTableBottomLeftButtons({
|
|
|
32
32
|
}
|
|
33
33
|
) }),
|
|
34
34
|
withResetButton && /* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
35
|
-
|
|
35
|
+
CustomTooltip,
|
|
36
36
|
{
|
|
37
37
|
title: intl.formatMessage({
|
|
38
38
|
id: resetButtonMessageId
|
|
39
39
|
}),
|
|
40
|
-
placement: "top",
|
|
41
40
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { color: "primary", onClick: () => handleResetButton(), disabled, children: /* @__PURE__ */ jsx(Replay, {}) }) })
|
|
42
41
|
}
|
|
43
42
|
) })
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Grid,
|
|
2
|
+
import { Grid, IconButton } from "@mui/material";
|
|
3
3
|
import { AddCircle, Delete, ArrowUpward, ArrowDownward } from "@mui/icons-material";
|
|
4
4
|
import { useWatch } from "react-hook-form";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
6
7
|
import { SELECTED } from "./dnd-table.type.js";
|
|
7
8
|
function DndTableBottomRightButtons({
|
|
8
9
|
arrayFormName,
|
|
@@ -23,12 +24,11 @@ function DndTableBottomRightButtons({
|
|
|
23
24
|
const lastRowSelected = noRowsSelected ? void 0 : currentRows[currentRows.length - 1]?.[SELECTED];
|
|
24
25
|
return /* @__PURE__ */ jsxs(Grid, { container: true, item: true, xs: true, spacing: 1, sx: { justifyContent: "flex-end" }, children: [
|
|
25
26
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
26
|
-
|
|
27
|
+
CustomTooltip,
|
|
27
28
|
{
|
|
28
29
|
title: intl.formatMessage({
|
|
29
30
|
id: "DndAddRows"
|
|
30
31
|
}),
|
|
31
|
-
placement: "top",
|
|
32
32
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
33
33
|
IconButton,
|
|
34
34
|
{
|
|
@@ -41,12 +41,11 @@ function DndTableBottomRightButtons({
|
|
|
41
41
|
}
|
|
42
42
|
) }),
|
|
43
43
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
44
|
-
|
|
44
|
+
CustomTooltip,
|
|
45
45
|
{
|
|
46
46
|
title: intl.formatMessage({
|
|
47
47
|
id: "DndDeleteRows"
|
|
48
48
|
}),
|
|
49
|
-
placement: "top",
|
|
50
49
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
51
50
|
IconButton,
|
|
52
51
|
{
|
|
@@ -60,12 +59,11 @@ function DndTableBottomRightButtons({
|
|
|
60
59
|
) }),
|
|
61
60
|
showMoveArrow && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
61
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
63
|
-
|
|
62
|
+
CustomTooltip,
|
|
64
63
|
{
|
|
65
64
|
title: intl.formatMessage({
|
|
66
65
|
id: "MoveUpRows"
|
|
67
66
|
}),
|
|
68
|
-
placement: "top",
|
|
69
67
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
70
68
|
IconButton,
|
|
71
69
|
{
|
|
@@ -78,12 +76,11 @@ function DndTableBottomRightButtons({
|
|
|
78
76
|
}
|
|
79
77
|
) }),
|
|
80
78
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
81
|
-
|
|
79
|
+
CustomTooltip,
|
|
82
80
|
{
|
|
83
81
|
title: intl.formatMessage({
|
|
84
82
|
id: "MoveDownRows"
|
|
85
83
|
}),
|
|
86
|
-
placement: "top",
|
|
87
84
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
88
85
|
IconButton,
|
|
89
86
|
{
|
|
@@ -2,10 +2,11 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import { useState, useRef, useCallback, useMemo } from "react";
|
|
4
4
|
import { useFormContext, useWatch } from "react-hook-form";
|
|
5
|
-
import { Grid, TableContainer, Table, TableHead, TableRow, TableCell, Box, TableBody,
|
|
5
|
+
import { Grid, TableContainer, Table, TableHead, TableRow, TableCell, Box, TableBody, Checkbox } from "@mui/material";
|
|
6
6
|
import { DragIndicator } from "@mui/icons-material";
|
|
7
7
|
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
|
8
8
|
import { useIntl } from "react-intl";
|
|
9
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
9
10
|
import { MAX_ROWS_NUMBER, SELECTED, DndColumnType } from "./dnd-table.type.js";
|
|
10
11
|
import { DndTableBottomLeftButtons } from "./dnd-table-bottom-left-buttons.js";
|
|
11
12
|
import { DndTableBottomRightButtons } from "./dnd-table-bottom-right-buttons.js";
|
|
@@ -337,12 +338,11 @@ function DndTable(props) {
|
|
|
337
338
|
cellIdxRef.current = 0;
|
|
338
339
|
const tableRow = /* @__PURE__ */ jsxs(TableRow, { ref: provided.innerRef, ...provided.draggableProps, children: [
|
|
339
340
|
!disableDragAndDrop && /* @__PURE__ */ jsx(
|
|
340
|
-
|
|
341
|
+
CustomTooltip,
|
|
341
342
|
{
|
|
342
343
|
title: intl.formatMessage({
|
|
343
344
|
id: "DragAndDrop"
|
|
344
345
|
}),
|
|
345
|
-
placement: "right",
|
|
346
346
|
children: /* @__PURE__ */ jsx(
|
|
347
347
|
TableCell,
|
|
348
348
|
{
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
|
-
import { TableRow, TableCell,
|
|
4
|
+
import { TableRow, TableCell, IconButton } from "@mui/material";
|
|
5
5
|
import { Delete } from "@mui/icons-material";
|
|
6
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
6
7
|
function DeletableTableRow({
|
|
7
8
|
onClick,
|
|
8
9
|
disabledDeletion,
|
|
@@ -20,7 +21,7 @@ function DeletableTableRow({
|
|
|
20
21
|
children: [
|
|
21
22
|
children,
|
|
22
23
|
!disabledDeletion && /* @__PURE__ */ jsx(TableCell, { sx: { width: "5rem", textAlign: "center" }, children: isMouseHover && /* @__PURE__ */ jsx(
|
|
23
|
-
|
|
24
|
+
CustomTooltip,
|
|
24
25
|
{
|
|
25
26
|
title: intl.formatMessage({
|
|
26
27
|
id: "DeleteRows"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Grid,
|
|
2
|
+
import { Grid, IconButton } from "@mui/material";
|
|
3
3
|
import { Upload, Replay } from "@mui/icons-material";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
5
6
|
function DndTableBottomLeftButtons({
|
|
6
7
|
handleUploadButton,
|
|
7
8
|
uploadButtonMessageId,
|
|
@@ -14,12 +15,11 @@ function DndTableBottomLeftButtons({
|
|
|
14
15
|
const intl = useIntl();
|
|
15
16
|
return /* @__PURE__ */ jsxs(Grid, { container: true, item: true, xs: true, spacing: 1, children: [
|
|
16
17
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
17
|
-
|
|
18
|
+
CustomTooltip,
|
|
18
19
|
{
|
|
19
20
|
title: intl.formatMessage({
|
|
20
21
|
id: uploadButtonMessageId
|
|
21
22
|
}),
|
|
22
|
-
placement: "top",
|
|
23
23
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
24
24
|
IconButton,
|
|
25
25
|
{
|
|
@@ -32,12 +32,11 @@ function DndTableBottomLeftButtons({
|
|
|
32
32
|
}
|
|
33
33
|
) }),
|
|
34
34
|
withResetButton && /* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
35
|
-
|
|
35
|
+
CustomTooltip,
|
|
36
36
|
{
|
|
37
37
|
title: intl.formatMessage({
|
|
38
38
|
id: resetButtonMessageId
|
|
39
39
|
}),
|
|
40
|
-
placement: "top",
|
|
41
40
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { color: "primary", onClick: () => handleResetButton(), disabled, children: /* @__PURE__ */ jsx(Replay, {}) }) })
|
|
42
41
|
}
|
|
43
42
|
) })
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Grid,
|
|
2
|
+
import { Grid, IconButton } from "@mui/material";
|
|
3
3
|
import { AddCircle, Delete, ArrowUpward, ArrowDownward } from "@mui/icons-material";
|
|
4
4
|
import { useWatch } from "react-hook-form";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
6
7
|
import { SELECTED } from "./dnd-table.type.js";
|
|
7
8
|
function DndTableBottomRightButtons({
|
|
8
9
|
arrayFormName,
|
|
@@ -23,12 +24,11 @@ function DndTableBottomRightButtons({
|
|
|
23
24
|
const lastRowSelected = noRowsSelected ? void 0 : currentRows[currentRows.length - 1]?.[SELECTED];
|
|
24
25
|
return /* @__PURE__ */ jsxs(Grid, { container: true, item: true, xs: true, spacing: 1, sx: { justifyContent: "flex-end" }, children: [
|
|
25
26
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
26
|
-
|
|
27
|
+
CustomTooltip,
|
|
27
28
|
{
|
|
28
29
|
title: intl.formatMessage({
|
|
29
30
|
id: "DndAddRows"
|
|
30
31
|
}),
|
|
31
|
-
placement: "top",
|
|
32
32
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
33
33
|
IconButton,
|
|
34
34
|
{
|
|
@@ -41,12 +41,11 @@ function DndTableBottomRightButtons({
|
|
|
41
41
|
}
|
|
42
42
|
) }),
|
|
43
43
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
44
|
-
|
|
44
|
+
CustomTooltip,
|
|
45
45
|
{
|
|
46
46
|
title: intl.formatMessage({
|
|
47
47
|
id: "DndDeleteRows"
|
|
48
48
|
}),
|
|
49
|
-
placement: "top",
|
|
50
49
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
51
50
|
IconButton,
|
|
52
51
|
{
|
|
@@ -60,12 +59,11 @@ function DndTableBottomRightButtons({
|
|
|
60
59
|
) }),
|
|
61
60
|
showMoveArrow && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
61
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
63
|
-
|
|
62
|
+
CustomTooltip,
|
|
64
63
|
{
|
|
65
64
|
title: intl.formatMessage({
|
|
66
65
|
id: "MoveUpRows"
|
|
67
66
|
}),
|
|
68
|
-
placement: "top",
|
|
69
67
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
70
68
|
IconButton,
|
|
71
69
|
{
|
|
@@ -78,12 +76,11 @@ function DndTableBottomRightButtons({
|
|
|
78
76
|
}
|
|
79
77
|
) }),
|
|
80
78
|
/* @__PURE__ */ jsx(Grid, { item: true, children: /* @__PURE__ */ jsx(
|
|
81
|
-
|
|
79
|
+
CustomTooltip,
|
|
82
80
|
{
|
|
83
81
|
title: intl.formatMessage({
|
|
84
82
|
id: "MoveDownRows"
|
|
85
83
|
}),
|
|
86
|
-
placement: "top",
|
|
87
84
|
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
88
85
|
IconButton,
|
|
89
86
|
{
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { TableCell } from "@mui/material";
|
|
3
3
|
import { DragIndicator } from "@mui/icons-material";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
|
+
import { CustomTooltip } from "../tooltip/CustomTooltip.js";
|
|
5
6
|
import { CheckboxInput } from "../inputs/reactHookForm/booleans/CheckboxInput.js";
|
|
6
7
|
import { SELECTED, DndColumnType } from "./dnd-table.type.js";
|
|
7
8
|
import "../overflowableText/OverflowableText.js";
|
|
@@ -140,12 +141,11 @@ function DndTableRow({
|
|
|
140
141
|
disabledDeletion: disabledDeletion || multiselect,
|
|
141
142
|
children: [
|
|
142
143
|
!disableDragAndDrop && /* @__PURE__ */ jsx(
|
|
143
|
-
|
|
144
|
+
CustomTooltip,
|
|
144
145
|
{
|
|
145
146
|
title: intl.formatMessage({
|
|
146
147
|
id: "DragAndDrop"
|
|
147
148
|
}),
|
|
148
|
-
placement: "right",
|
|
149
149
|
children: /* @__PURE__ */ jsx(
|
|
150
150
|
TableCell,
|
|
151
151
|
{
|