underpost 2.8.881 → 2.8.882
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/.github/workflows/release.cd.yml +1 -2
- package/README.md +46 -36
- package/cli.md +86 -86
- package/conf.js +1 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +6 -6
- package/package.json +1 -1
- package/src/api/document/document.service.js +9 -1
- package/src/cli/repository.js +2 -0
- package/src/client/components/core/Auth.js +258 -89
- package/src/client/components/core/BtnIcon.js +10 -1
- package/src/client/components/core/CssCore.js +36 -27
- package/src/client/components/core/Docs.js +189 -85
- package/src/client/components/core/LoadingAnimation.js +5 -10
- package/src/client/components/core/Modal.js +255 -120
- package/src/client/components/core/ObjectLayerEngine.js +154 -158
- package/src/client/components/core/Panel.js +2 -0
- package/src/client/components/core/PanelForm.js +94 -60
- package/src/client/components/core/Router.js +15 -15
- package/src/client/components/core/ToolTip.js +83 -19
- package/src/client/components/core/Translate.js +1 -1
- package/src/client/components/core/VanillaJs.js +4 -3
- package/src/client/components/core/windowGetDimensions.js +202 -0
- package/src/client/components/default/MenuDefault.js +11 -0
- package/src/client/ssr/Render.js +1 -1
- package/src/index.js +1 -1
- package/src/server/auth.js +68 -17
- package/src/server/crypto.js +195 -76
- package/src/server/peer.js +47 -5
- package/src/server/process.js +85 -1
- package/src/server/runtime.js +13 -10
- package/test/crypto.test.js +117 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class for authentication state management and session lifecycle control.
|
|
3
|
+
* This class is designed to be used as a singleton instance (exported as 'Auth').
|
|
4
|
+
* @module src/client/components/core/Auth.js
|
|
5
|
+
* @namespace AuthClient
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import { UserMock, UserService } from '../../services/user/user.service.js';
|
|
2
9
|
import { Account } from './Account.js';
|
|
3
10
|
import { loggerFactory } from './Logger.js';
|
|
@@ -9,95 +16,202 @@ import { s } from './VanillaJs.js';
|
|
|
9
16
|
|
|
10
17
|
const logger = loggerFactory(import.meta, { trace: true });
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Manages user authentication state, tokens, and session lifecycle.
|
|
21
|
+
* @memberof AuthClient
|
|
22
|
+
*/
|
|
23
|
+
class Auth {
|
|
24
|
+
/**
|
|
25
|
+
* The current user access token (JWT).
|
|
26
|
+
* @type {string}
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
#token = '';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The token for anonymous guest sessions.
|
|
33
|
+
* @type {string}
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
#guestToken = '';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Timeout ID for the token refresh schedule.
|
|
40
|
+
* @type {number | undefined}
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
#refreshTimeout;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates an instance of Auth.
|
|
47
|
+
*/
|
|
48
|
+
constructor() {
|
|
49
|
+
// Private fields are initialized above.
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// --- Token Management ---
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sets the user's access token.
|
|
56
|
+
* @memberof AuthClient.Auth
|
|
57
|
+
* @param {string} [value=''] - The JWT token value.
|
|
58
|
+
* @returns {string} The set token value.
|
|
59
|
+
*/
|
|
60
|
+
setToken(value = '') {
|
|
61
|
+
return (this.#token = value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clears the user's access token.
|
|
66
|
+
* @memberof AuthClient.Auth
|
|
67
|
+
* @returns {string} An empty string.
|
|
68
|
+
*/
|
|
69
|
+
deleteToken() {
|
|
70
|
+
return (this.#token = '');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gets the user's access token.
|
|
75
|
+
* @memberof AuthClient.Auth
|
|
76
|
+
* @returns {string} The JWT token.
|
|
77
|
+
*/
|
|
78
|
+
getToken() {
|
|
79
|
+
return this.#token;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Sets the anonymous guest token.
|
|
84
|
+
* @memberof AuthClient.Auth
|
|
85
|
+
* @param {string} [value=''] - The guest token value.
|
|
86
|
+
* @returns {string} The set guest token value.
|
|
87
|
+
*/
|
|
88
|
+
setGuestToken(value = '') {
|
|
89
|
+
return (this.#guestToken = value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Clears the anonymous guest token.
|
|
94
|
+
* @memberof AuthClient.Auth
|
|
95
|
+
* @returns {string} An empty string.
|
|
96
|
+
*/
|
|
97
|
+
deleteGuestToken() {
|
|
98
|
+
return (this.#guestToken = '');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Gets the anonymous guest token.
|
|
103
|
+
* @memberof AuthClient.Auth
|
|
104
|
+
* @returns {string} The guest token.
|
|
105
|
+
*/
|
|
106
|
+
getGuestToken() {
|
|
107
|
+
return this.#guestToken;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Generates the JWT header string (e.g., "Bearer [token]") using the active token (user or guest).
|
|
112
|
+
* @memberof AuthClient.Auth
|
|
113
|
+
* @returns {string} The Bearer token string or an empty string.
|
|
114
|
+
*/
|
|
115
|
+
getJWT() {
|
|
116
|
+
if (this.getToken()) return `Bearer ${this.getToken()}`;
|
|
117
|
+
if (this.getGuestToken()) return `Bearer ${this.getGuestToken()}`;
|
|
42
118
|
return '';
|
|
43
|
-
}
|
|
44
|
-
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Decodes the payload section of a JWT token.
|
|
123
|
+
* @static
|
|
124
|
+
* @memberof AuthClient.Auth
|
|
125
|
+
* @param {string} token - The JWT string.
|
|
126
|
+
* @returns {object | null} The decoded JWT payload object, or null on failure.
|
|
127
|
+
*/
|
|
128
|
+
static decodeJwt(token) {
|
|
45
129
|
try {
|
|
130
|
+
// Uses atob for base64 decoding of the middle part of the JWT
|
|
46
131
|
return JSON.parse(atob(token.split('.')[1]));
|
|
47
132
|
} catch (e) {
|
|
133
|
+
logger.error('Failed to decode JWT:', e);
|
|
48
134
|
return null;
|
|
49
135
|
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// --- Session Management ---
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Schedules the access token to be refreshed shortly before it expires.
|
|
142
|
+
* Clears any existing refresh timeout before setting a new one.
|
|
143
|
+
* @memberof AuthClient.Auth
|
|
144
|
+
* @returns {void}
|
|
145
|
+
*/
|
|
146
|
+
scheduleTokenRefresh() {
|
|
147
|
+
if (this.#refreshTimeout) {
|
|
148
|
+
clearTimeout(this.#refreshTimeout);
|
|
149
|
+
this.#refreshTimeout = undefined;
|
|
54
150
|
}
|
|
55
151
|
|
|
56
|
-
const currentToken =
|
|
152
|
+
const currentToken = this.getToken();
|
|
57
153
|
if (!currentToken) return;
|
|
58
154
|
|
|
59
155
|
const payload = Auth.decodeJwt(currentToken);
|
|
60
|
-
if (!payload || !payload.refreshExpiresAt) return;
|
|
156
|
+
if (!payload || !payload.refreshExpiresAt) return; // Requires refreshExpiresAt in milliseconds
|
|
61
157
|
|
|
62
158
|
const expiresIn = payload.refreshExpiresAt - Date.now();
|
|
63
|
-
const refreshBuffer = 2 * 60 * 1000; // 2 minutes
|
|
159
|
+
const refreshBuffer = 2 * 60 * 1000; // 2 minutes buffer before expiry
|
|
64
160
|
const refreshIn = expiresIn - refreshBuffer;
|
|
65
161
|
|
|
66
|
-
logger.info(`Token refresh in ${refreshIn / (1000 * 60)} minutes`);
|
|
162
|
+
logger.info(`Token refresh scheduled in ${refreshIn / (1000 * 60)} minutes`);
|
|
67
163
|
|
|
68
|
-
if (refreshIn <= 0)
|
|
164
|
+
if (refreshIn <= 0) {
|
|
165
|
+
logger.warn('Token already expired or too close to expiry, skipping refresh schedule.');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
69
168
|
|
|
70
|
-
this
|
|
71
|
-
const { data, status } = await UserService.get({ id: 'auth' });
|
|
72
|
-
if (status === 'success') {
|
|
73
|
-
logger.info('
|
|
74
|
-
|
|
169
|
+
this.#refreshTimeout = setTimeout(async () => {
|
|
170
|
+
const { data, status } = await UserService.get({ id: 'auth' }); // API call to get a fresh token
|
|
171
|
+
if (status === 'success' && data?.token) {
|
|
172
|
+
logger.info('Successfully refreshed access token.');
|
|
173
|
+
this.setToken(data.token);
|
|
75
174
|
localStorage.setItem('jwt', data.token);
|
|
76
|
-
|
|
77
|
-
} else
|
|
175
|
+
this.scheduleTokenRefresh(); // Schedule the next refresh
|
|
176
|
+
} else {
|
|
177
|
+
logger.warn('Token refresh failed, attempting session out.');
|
|
178
|
+
this.sessionOut();
|
|
179
|
+
}
|
|
78
180
|
}, refreshIn);
|
|
79
|
-
}
|
|
80
|
-
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Establishes a user session (logged-in) or falls back to a guest session.
|
|
185
|
+
* It attempts to use the provided token or a token from localStorage ('jwt').
|
|
186
|
+
* @memberof AuthClient.Auth
|
|
187
|
+
* @param {object} [userServicePayload] - Payload from a successful login/signup call to UserService.
|
|
188
|
+
* @returns {Promise<{user: object}>} A promise resolving to the current user object.
|
|
189
|
+
*/
|
|
190
|
+
async sessionIn(userServicePayload) {
|
|
81
191
|
try {
|
|
82
|
-
|
|
192
|
+
let token = userServicePayload?.data?.token || localStorage.getItem('jwt');
|
|
193
|
+
|
|
83
194
|
if (token) {
|
|
84
|
-
|
|
195
|
+
this.setToken(token);
|
|
85
196
|
|
|
86
197
|
const result = userServicePayload
|
|
87
198
|
? userServicePayload // From login/signup
|
|
88
|
-
: await UserService.get({ id: 'auth' });
|
|
199
|
+
: await UserService.get({ id: 'auth' }); // Verify token with backend
|
|
89
200
|
|
|
90
201
|
const { status, data, message } = result;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
202
|
+
|
|
203
|
+
if (status === 'success' && data.token) {
|
|
204
|
+
// A valid user token was found/refreshed
|
|
205
|
+
this.setToken(data.token);
|
|
206
|
+
localStorage.setItem('jwt', data.token);
|
|
207
|
+
this.renderSessionUI();
|
|
95
208
|
await LogIn.Trigger({ user: data.user });
|
|
96
209
|
await Account.updateForm(data.user);
|
|
97
|
-
|
|
210
|
+
this.scheduleTokenRefresh();
|
|
98
211
|
return { user: data.user };
|
|
99
|
-
}
|
|
100
|
-
|
|
212
|
+
} else if (message && message.match('expired')) {
|
|
213
|
+
logger.warn('User session token expired.');
|
|
214
|
+
// Redirect to login modal and push notification
|
|
101
215
|
setTimeout(() => {
|
|
102
216
|
s(`.main-btn-log-in`).click();
|
|
103
217
|
NotificationManager.Push({
|
|
@@ -105,69 +219,124 @@ const Auth = {
|
|
|
105
219
|
status: 'warning',
|
|
106
220
|
});
|
|
107
221
|
});
|
|
222
|
+
}
|
|
108
223
|
}
|
|
109
|
-
|
|
110
|
-
|
|
224
|
+
|
|
225
|
+
// Cleanup failed user session attempt
|
|
226
|
+
this.deleteToken();
|
|
111
227
|
localStorage.removeItem('jwt');
|
|
112
228
|
|
|
113
|
-
// Anon guest session
|
|
229
|
+
// Anon guest session attempt
|
|
114
230
|
let guestToken = localStorage.getItem('jwt.g');
|
|
115
231
|
if (guestToken) {
|
|
116
|
-
|
|
117
|
-
let { data, status, message } = await UserService.get({ id: 'auth' });
|
|
118
|
-
if (status === 'success') {
|
|
232
|
+
this.setGuestToken(guestToken);
|
|
233
|
+
let { data, status, message } = await UserService.get({ id: 'auth' }); // Verify guest token
|
|
234
|
+
if (status === 'success' && data.token) {
|
|
235
|
+
// Guest token is valid and refreshed
|
|
236
|
+
this.setGuestToken(data.token);
|
|
237
|
+
localStorage.setItem('jwt.g', data.token);
|
|
119
238
|
await LogIn.Trigger(data);
|
|
120
239
|
await Account.updateForm(data.user);
|
|
121
240
|
return data;
|
|
122
|
-
} else
|
|
241
|
+
} else {
|
|
242
|
+
logger.error(`Guest token validation failed: ${message}`);
|
|
243
|
+
// Fall through to full sessionOut to re-create guest session
|
|
244
|
+
}
|
|
123
245
|
}
|
|
124
|
-
|
|
246
|
+
|
|
247
|
+
// If all attempts fail, create a new guest session (which calls sessionIn recursively)
|
|
248
|
+
return await this.sessionOut();
|
|
125
249
|
} catch (error) {
|
|
126
|
-
logger.error(error);
|
|
250
|
+
logger.error('Error during sessionIn process:', error);
|
|
251
|
+
// Fallback to a mock user object
|
|
127
252
|
return { user: UserMock.default };
|
|
128
253
|
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Ends the current user session (logout) and initiates a new anonymous guest session.
|
|
258
|
+
* @memberof AuthClient.Auth
|
|
259
|
+
* @returns {Promise<object>} A promise resolving to the newly created guest session data.
|
|
260
|
+
*/
|
|
261
|
+
async sessionOut() {
|
|
262
|
+
// 1. End User Session
|
|
263
|
+
try {
|
|
132
264
|
const result = await UserService.delete({ id: 'logout' });
|
|
133
265
|
localStorage.removeItem('jwt');
|
|
134
|
-
|
|
135
|
-
if (this
|
|
136
|
-
clearTimeout(this
|
|
266
|
+
this.deleteToken();
|
|
267
|
+
if (this.#refreshTimeout) {
|
|
268
|
+
clearTimeout(this.#refreshTimeout);
|
|
269
|
+
this.#refreshTimeout = undefined;
|
|
137
270
|
}
|
|
138
|
-
|
|
271
|
+
this.renderGuestUi();
|
|
272
|
+
// Reset user data in the LogIn state/model
|
|
139
273
|
LogIn.Scope.user.main.model.user = {};
|
|
140
274
|
await LogOut.Trigger(result);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
logger.error('Error during user logout:', error);
|
|
141
277
|
}
|
|
142
|
-
|
|
278
|
+
|
|
279
|
+
// 2. Start Guest Session
|
|
280
|
+
try {
|
|
143
281
|
localStorage.removeItem('jwt.g');
|
|
144
|
-
|
|
145
|
-
const result = await UserService.post({ id: 'guest' });
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
282
|
+
this.deleteGuestToken();
|
|
283
|
+
const result = await UserService.post({ id: 'guest' }); // Request a new guest token
|
|
284
|
+
|
|
285
|
+
if (result.status === 'success' && result.data.token) {
|
|
286
|
+
localStorage.setItem('jwt.g', result.data.token);
|
|
287
|
+
this.setGuestToken(result.data.token);
|
|
288
|
+
// Recursively call sessionIn to complete the guest login process (UI update, etc.)
|
|
289
|
+
return await this.sessionIn();
|
|
290
|
+
} else {
|
|
291
|
+
logger.error('Failed to get a new guest token.');
|
|
292
|
+
return { user: UserMock.default };
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
logger.error('Error during guest session creation:', error);
|
|
296
|
+
return { user: UserMock.default };
|
|
149
297
|
}
|
|
150
|
-
}
|
|
151
|
-
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// --- UI Rendering ---
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Renders the UI for a logged-in user (hides Log In/Sign Up, shows Log Out/Account).
|
|
304
|
+
* Also closes any active login/signup modals.
|
|
305
|
+
* @memberof AuthClient.Auth
|
|
306
|
+
* @returns {void}
|
|
307
|
+
*/
|
|
308
|
+
renderSessionUI() {
|
|
152
309
|
s(`.main-btn-log-in`).style.display = 'none';
|
|
153
310
|
s(`.main-btn-sign-up`).style.display = 'none';
|
|
154
311
|
s(`.main-btn-log-out`).style.display = null;
|
|
155
312
|
s(`.main-btn-account`).style.display = null;
|
|
156
313
|
setTimeout(() => {
|
|
314
|
+
// Close any open login/signup modals
|
|
157
315
|
if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
|
|
158
316
|
if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
|
|
159
317
|
});
|
|
160
|
-
}
|
|
161
|
-
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Renders the UI for a guest user (shows Log In/Sign Up, hides Log Out/Account).
|
|
322
|
+
* Also closes any active logout/account modals.
|
|
323
|
+
* @memberof AuthClient.Auth
|
|
324
|
+
* @returns {void}
|
|
325
|
+
*/
|
|
326
|
+
renderGuestUi() {
|
|
162
327
|
s(`.main-btn-log-in`).style.display = null;
|
|
163
328
|
s(`.main-btn-sign-up`).style.display = null;
|
|
164
329
|
s(`.main-btn-log-out`).style.display = 'none';
|
|
165
330
|
s(`.main-btn-account`).style.display = 'none';
|
|
166
331
|
setTimeout(() => {
|
|
332
|
+
// Close any open logout/account modals
|
|
167
333
|
if (s(`.modal-log-out`)) s(`.btn-close-modal-log-out`).click();
|
|
168
334
|
if (s(`.modal-account`)) s(`.btn-close-modal-account`).click();
|
|
169
335
|
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Export a singleton instance of the Auth class to maintain the original utility object access pattern.
|
|
340
|
+
const AuthSingleton = new Auth();
|
|
172
341
|
|
|
173
|
-
export { Auth };
|
|
342
|
+
export { AuthSingleton as Auth };
|
|
@@ -15,9 +15,12 @@ const BtnIcon = {
|
|
|
15
15
|
labelStyle: '',
|
|
16
16
|
tabHref: '',
|
|
17
17
|
tooltipHtml: '',
|
|
18
|
+
useVisibilityHover: false,
|
|
19
|
+
useMenuBtn: false,
|
|
18
20
|
},
|
|
19
21
|
) {
|
|
20
22
|
const tokenId = getId(this.Tokens, 'btn-token-');
|
|
23
|
+
if (options.useMenuBtn) options.class += ' main-menu-btn-selector';
|
|
21
24
|
this.Tokens[tokenId] = { ...options };
|
|
22
25
|
setTimeout(() => {
|
|
23
26
|
if (s(`.a-${tokenId}`)) s(`.a-${tokenId}`).onclick = (e) => e.preventDefault();
|
|
@@ -54,7 +57,13 @@ const BtnIcon = {
|
|
|
54
57
|
if (options.tooltipHtml)
|
|
55
58
|
setTimeout(() => {
|
|
56
59
|
if (s(`.${tokenId}`))
|
|
57
|
-
ToolTip.Render({
|
|
60
|
+
ToolTip.Render({
|
|
61
|
+
container: `.${tokenId}`,
|
|
62
|
+
id: tokenId,
|
|
63
|
+
htmlRender: options.tooltipHtml,
|
|
64
|
+
useVisibilityHover: !!options.useVisibilityHover,
|
|
65
|
+
useMenuBtn: !!options.useMenuBtn,
|
|
66
|
+
});
|
|
58
67
|
});
|
|
59
68
|
return render;
|
|
60
69
|
},
|
|
@@ -130,19 +130,20 @@ const CssCommonCore = async () => {
|
|
|
130
130
|
opacity: 0;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
.title-
|
|
133
|
+
.title-main-modal {
|
|
134
134
|
top: 8px;
|
|
135
|
-
font-size: 21px
|
|
136
|
-
position: absolute
|
|
135
|
+
font-size: 21px;
|
|
136
|
+
position: absolute;
|
|
137
137
|
}
|
|
138
|
-
.title-
|
|
139
|
-
font-size: 21px
|
|
138
|
+
.title-main-modal .view-title-icon {
|
|
139
|
+
font-size: 21px;
|
|
140
140
|
}
|
|
141
141
|
.down-arrow-submenu {
|
|
142
|
-
top:
|
|
143
|
-
text-align: right;
|
|
144
|
-
padding-right: 42px;
|
|
142
|
+
top: 0px;
|
|
145
143
|
color: #5f5f5f;
|
|
144
|
+
left: 115px;
|
|
145
|
+
transform-origin: center;
|
|
146
|
+
width: 0px;
|
|
146
147
|
}
|
|
147
148
|
.main-body-btn {
|
|
148
149
|
width: 50px;
|
|
@@ -153,6 +154,9 @@ const CssCommonCore = async () => {
|
|
|
153
154
|
.main-body-btn:hover {
|
|
154
155
|
font-size: 21px;
|
|
155
156
|
}
|
|
157
|
+
.main-menu-btn-selector {
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
}
|
|
156
160
|
</style>
|
|
157
161
|
<style>
|
|
158
162
|
.lds-dual-ring,
|
|
@@ -219,6 +223,16 @@ const CssCommonCore = async () => {
|
|
|
219
223
|
height: 100px;
|
|
220
224
|
color: gray;
|
|
221
225
|
}
|
|
226
|
+
.menu-label-text {
|
|
227
|
+
transition: 0.3s;
|
|
228
|
+
position: relative;
|
|
229
|
+
}
|
|
230
|
+
.menu-btn-icon {
|
|
231
|
+
font-size: 20px;
|
|
232
|
+
width: 40px;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
text-align: center;
|
|
235
|
+
}
|
|
222
236
|
</style>
|
|
223
237
|
${boxShadow({ selector: '.account-profile-image' })}
|
|
224
238
|
<div class="ag-grid-style"></div>`;
|
|
@@ -329,11 +343,12 @@ const CssCoreDark = {
|
|
|
329
343
|
}
|
|
330
344
|
.main-btn-menu {
|
|
331
345
|
text-align: left;
|
|
346
|
+
transition: none; /* sortable necessary */
|
|
332
347
|
padding: 15px;
|
|
333
|
-
transition: none;
|
|
334
348
|
margin: 0;
|
|
335
349
|
border: 0;
|
|
336
350
|
height: 52px;
|
|
351
|
+
background: #121212;
|
|
337
352
|
}
|
|
338
353
|
.main-btn-menu-active {
|
|
339
354
|
background: #212020;
|
|
@@ -435,10 +450,6 @@ const CssCoreDark = {
|
|
|
435
450
|
margin: 15px 5px 5px;
|
|
436
451
|
text-align: left;
|
|
437
452
|
}
|
|
438
|
-
.menu-btn-icon {
|
|
439
|
-
font-size: 20px;
|
|
440
|
-
margin: 12px;
|
|
441
|
-
}
|
|
442
453
|
.view-title-icon {
|
|
443
454
|
font-size: 35px;
|
|
444
455
|
margin: 20px;
|
|
@@ -535,6 +546,10 @@ const CssCoreDark = {
|
|
|
535
546
|
padding: 5px;
|
|
536
547
|
margin: 5px;
|
|
537
548
|
}
|
|
549
|
+
.submenu-btn {
|
|
550
|
+
background: rgba(255, 255, 255, 0.1);
|
|
551
|
+
border-radius: 0;
|
|
552
|
+
}
|
|
538
553
|
</style>
|
|
539
554
|
${scrollBarDarkRender()} ${borderChar(1, 'black', ['.main-body-btn-container'])}
|
|
540
555
|
`,
|
|
@@ -583,12 +598,7 @@ const CssCoreLight = {
|
|
|
583
598
|
.hover-active {
|
|
584
599
|
background: #bbbbbb;
|
|
585
600
|
}
|
|
586
|
-
|
|
587
|
-
cursor: default;
|
|
588
|
-
font-size: 20px;
|
|
589
|
-
padding: 5px;
|
|
590
|
-
margin: 5px;
|
|
591
|
-
}
|
|
601
|
+
|
|
592
602
|
.box-shadow {
|
|
593
603
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
|
594
604
|
}
|
|
@@ -649,11 +659,12 @@ const CssCoreLight = {
|
|
|
649
659
|
}
|
|
650
660
|
.main-btn-menu {
|
|
651
661
|
text-align: left;
|
|
662
|
+
transition: none; /* sortable necessary */
|
|
652
663
|
padding: 15px;
|
|
653
|
-
transition: none;
|
|
654
664
|
margin: 0;
|
|
655
665
|
border: 0;
|
|
656
666
|
height: 52px;
|
|
667
|
+
background: #fff;
|
|
657
668
|
}
|
|
658
669
|
.main-btn-menu-active {
|
|
659
670
|
background: #d8d8d8;
|
|
@@ -731,9 +742,7 @@ const CssCoreLight = {
|
|
|
731
742
|
color: #333;
|
|
732
743
|
background: 0 0;
|
|
733
744
|
}
|
|
734
|
-
|
|
735
|
-
color: #000;
|
|
736
|
-
}
|
|
745
|
+
|
|
737
746
|
input {
|
|
738
747
|
cursor: pointer;
|
|
739
748
|
color: #272727;
|
|
@@ -752,10 +761,6 @@ const CssCoreLight = {
|
|
|
752
761
|
margin: 15px 5px 5px;
|
|
753
762
|
text-align: left;
|
|
754
763
|
}
|
|
755
|
-
.menu-btn-icon {
|
|
756
|
-
font-size: 20px;
|
|
757
|
-
margin: 12px;
|
|
758
|
-
}
|
|
759
764
|
.view-title-icon {
|
|
760
765
|
font-size: 35px;
|
|
761
766
|
margin: 20px;
|
|
@@ -865,6 +870,10 @@ const CssCoreLight = {
|
|
|
865
870
|
padding: 5px;
|
|
866
871
|
margin: 5px;
|
|
867
872
|
}
|
|
873
|
+
.submenu-btn {
|
|
874
|
+
background: rgba(0, 0, 0, 0.1);
|
|
875
|
+
border-radius: 0;
|
|
876
|
+
}
|
|
868
877
|
</style>
|
|
869
878
|
${scrollBarLightRender()} ${borderChar(1, 'white', ['.main-body-btn-container'])}
|
|
870
879
|
`,
|