underpost 2.8.878 → 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.
Files changed (61) hide show
  1. package/.env.development +35 -3
  2. package/.env.production +40 -3
  3. package/.env.test +35 -3
  4. package/.github/workflows/release.cd.yml +3 -3
  5. package/README.md +20 -2
  6. package/bin/deploy.js +40 -0
  7. package/cli.md +3 -1
  8. package/conf.js +29 -3
  9. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  10. package/manifests/deployment/dd-test-development/deployment.yaml +6 -6
  11. package/package.json +1 -2
  12. package/src/api/document/document.controller.js +66 -0
  13. package/src/api/document/document.model.js +51 -0
  14. package/src/api/document/document.router.js +24 -0
  15. package/src/api/document/document.service.js +133 -0
  16. package/src/cli/deploy.js +1 -1
  17. package/src/cli/index.js +2 -0
  18. package/src/cli/repository.js +2 -0
  19. package/src/cli/run.js +27 -1
  20. package/src/client/Default.index.js +46 -1
  21. package/src/client/components/core/Account.js +8 -1
  22. package/src/client/components/core/AgGrid.js +18 -9
  23. package/src/client/components/core/Auth.js +258 -89
  24. package/src/client/components/core/BtnIcon.js +13 -3
  25. package/src/client/components/core/Content.js +2 -1
  26. package/src/client/components/core/CssCore.js +40 -27
  27. package/src/client/components/core/Docs.js +189 -88
  28. package/src/client/components/core/Input.js +34 -19
  29. package/src/client/components/core/LoadingAnimation.js +5 -10
  30. package/src/client/components/core/Modal.js +280 -123
  31. package/src/client/components/core/ObjectLayerEngine.js +470 -104
  32. package/src/client/components/core/ObjectLayerEngineModal.js +1 -0
  33. package/src/client/components/core/Panel.js +9 -2
  34. package/src/client/components/core/PanelForm.js +234 -76
  35. package/src/client/components/core/Router.js +15 -15
  36. package/src/client/components/core/ToolTip.js +83 -19
  37. package/src/client/components/core/Translate.js +1 -1
  38. package/src/client/components/core/VanillaJs.js +7 -3
  39. package/src/client/components/core/windowGetDimensions.js +202 -0
  40. package/src/client/components/default/MenuDefault.js +105 -41
  41. package/src/client/components/default/RoutesDefault.js +2 -0
  42. package/src/client/services/default/default.management.js +1 -0
  43. package/src/client/services/document/document.service.js +97 -0
  44. package/src/client/services/file/file.service.js +2 -0
  45. package/src/client/ssr/Render.js +1 -1
  46. package/src/client/ssr/head/DefaultScripts.js +2 -0
  47. package/src/client/ssr/head/Seo.js +1 -0
  48. package/src/index.js +1 -1
  49. package/src/mailer/EmailRender.js +1 -1
  50. package/src/server/auth.js +68 -17
  51. package/src/server/client-build.js +2 -3
  52. package/src/server/client-formatted.js +40 -12
  53. package/src/server/conf.js +5 -1
  54. package/src/server/crypto.js +195 -76
  55. package/src/server/object-layer.js +196 -0
  56. package/src/server/peer.js +47 -5
  57. package/src/server/process.js +85 -1
  58. package/src/server/runtime.js +23 -23
  59. package/src/server/ssr.js +52 -10
  60. package/src/server/valkey.js +89 -1
  61. package/test/crypto.test.js +117 -0
@@ -1,6 +1,6 @@
1
- import { getCapVariableName, newInstance, random, range, uniqueArray } from './CommonJs.js';
1
+ import { getCapVariableName, newInstance, random, range, timer, uniqueArray } from './CommonJs.js';
2
2
  import { marked } from 'marked';
3
- import { getBlobFromUint8ArrayFile, getDataFromInputFile, getRawContentFile, htmls } from './VanillaJs.js';
3
+ import { append, getBlobFromUint8ArrayFile, getDataFromInputFile, getRawContentFile, htmls, s } from './VanillaJs.js';
4
4
  import { Panel } from './Panel.js';
5
5
  import { NotificationManager } from './NotificationManager.js';
6
6
  import { DocumentService } from '../../services/document/document.service.js';
@@ -10,6 +10,11 @@ import { imageShimmer, renderCssAttr } from './Css.js';
10
10
  import { Translate } from './Translate.js';
11
11
  import { Modal } from './Modal.js';
12
12
  import { closeModalRouteChangeEvents, listenQueryPathInstance, setQueryPath, getQueryParams } from './Router.js';
13
+ import { Scroll } from './Scroll.js';
14
+ import { LoadingAnimation } from './LoadingAnimation.js';
15
+ import { loggerFactory } from './Logger.js';
16
+
17
+ const logger = loggerFactory(import.meta, { trace: true });
13
18
 
14
19
  const PanelForm = {
15
20
  Data: {},
@@ -33,6 +38,11 @@ const PanelForm = {
33
38
  originData: [],
34
39
  data: [],
35
40
  filesData: [],
41
+ skip: 0,
42
+ limit: 3, // Load 5 items per page
43
+ hasMore: true,
44
+ loading: false,
45
+ lasIdAvailable: null,
36
46
  };
37
47
 
38
48
  const formData = [
@@ -132,7 +142,7 @@ const PanelForm = {
132
142
  `,
133
143
  });
134
144
  return await options.fileRender({
135
- file: PanelForm.Data[idPanel].filesData.find((f) => f._id === options.data._id).fileId.fileBlob,
145
+ file: PanelForm.Data[idPanel].filesData.find((f) => f._id === options.data._id)?.fileId?.fileBlob,
136
146
  style: {
137
147
  overflow: 'auto',
138
148
  width: '100%',
@@ -172,6 +182,15 @@ const PanelForm = {
172
182
  }
173
183
  return { status: 'error' };
174
184
  },
185
+ initAdd: async function () {
186
+ s(`.modal-${options.route}`).scrollTo({ top: 0, behavior: 'smooth' });
187
+ },
188
+ initEdit: async function ({ data }) {
189
+ s(`.modal-${options.route}`).scrollTo({ top: 0, behavior: 'smooth' });
190
+ },
191
+ noResultFound: async function () {
192
+ LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
193
+ },
175
194
  add: async function ({ data, editId }) {
176
195
  let mdFileId;
177
196
  const mdFileName = `${getCapVariableName(data.title)}.md`;
@@ -312,67 +331,104 @@ const PanelForm = {
312
331
  },
313
332
  });
314
333
 
315
- const getPanelData = async () => {
316
- const result = await DocumentService.get({
317
- id: `public/?tags=${prefixTags.join(',')}${getQueryParams().cid ? `&cid=${getQueryParams().cid}` : ''}`,
318
- });
334
+ const getPanelData = async (isLoadMore = false) => {
335
+ const panelData = PanelForm.Data[idPanel];
336
+ try {
337
+ if (panelData.loading || !panelData.hasMore) return;
338
+ panelData.loading = true;
319
339
 
320
- NotificationManager.Push({
321
- html: result.status === 'success' ? Translate.Render('success-get-posts') : result.message,
322
- status: result.status,
323
- });
324
- if (result.status === 'success') {
325
- PanelForm.Data[idPanel].originData = newInstance(result.data);
326
- PanelForm.Data[idPanel].filesData = [];
327
- PanelForm.Data[idPanel].data = [];
328
- for (const documentObject of result.data) {
329
- let mdFileId, fileId;
330
- let mdBlob, fileBlob;
331
- let mdPlain, filePlain;
332
-
333
- {
334
- const {
335
- data: [file],
336
- status,
337
- } = await FileService.get({ id: documentObject.mdFileId });
338
-
339
- // const ext = file.name.split('.')[file.name.split('.').length - 1];
340
- mdBlob = file;
341
- mdPlain = await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
342
- mdFileId = newInstance(mdPlain);
343
- }
344
- if (documentObject.fileId) {
345
- const {
346
- data: [file],
347
- status,
348
- } = await FileService.get({ id: documentObject.fileId._id });
349
-
350
- // const ext = file.name.split('.')[file.name.split('.').length - 1];
351
- fileBlob = file;
352
- filePlain = undefined;
353
- fileId = getSrcFromFileData(file);
340
+ if (!isLoadMore) {
341
+ // Reset for a fresh load
342
+ panelData.skip = 0;
343
+ panelData.hasMore = true;
344
+ }
345
+
346
+ const result = await DocumentService.get({
347
+ params: {
348
+ tags: prefixTags.join(','),
349
+ ...(getQueryParams().cid && { cid: getQueryParams().cid }),
350
+ skip: panelData.skip,
351
+ limit: panelData.limit,
352
+ },
353
+ id: 'public/',
354
+ });
355
+
356
+ if (result.status === 'success') {
357
+ if (!isLoadMore) {
358
+ panelData.originData = [];
359
+ panelData.filesData = [];
360
+ panelData.data = [];
354
361
  }
355
362
 
356
- PanelForm.Data[idPanel].filesData.push({
357
- id: documentObject._id,
358
- _id: documentObject._id,
359
- mdFileId: { mdBlob, mdPlain },
360
- fileId: { fileBlob, filePlain },
361
- });
363
+ panelData.originData.push(...newInstance(result.data.data));
364
+ panelData.lasIdAvailable = result.data.lastId;
365
+
366
+ for (const documentObject of result.data.data) {
367
+ let mdFileId, fileId;
368
+ let mdBlob, fileBlob;
369
+ let mdPlain, filePlain;
362
370
 
363
- PanelForm.Data[idPanel].data.push({
364
- id: documentObject._id,
365
- title: documentObject.title,
366
- createdAt: documentObject.createdAt,
367
- tags: documentObject.tags.filter((t) => !prefixTags.includes(t)),
368
- mdFileId: marked.parse(mdFileId),
369
- userId: documentObject.userId._id,
370
- fileId,
371
- tools: Elements.Data.user.main.model.user._id === documentObject.userId._id,
372
- _id: documentObject._id,
371
+ {
372
+ const {
373
+ data: [file],
374
+ } = await FileService.get({ id: documentObject.mdFileId });
375
+
376
+ // const ext = file.name.split('.')[file.name.split('.').length - 1];
377
+ mdBlob = file;
378
+ mdPlain = await getRawContentFile(getBlobFromUint8ArrayFile(file.data.data, file.mimetype));
379
+ mdFileId = newInstance(mdPlain);
380
+ }
381
+ if (documentObject.fileId) {
382
+ const {
383
+ data: [file],
384
+ } = await FileService.get({ id: documentObject.fileId._id });
385
+
386
+ // const ext = file.name.split('.')[file.name.split('.').length - 1];
387
+ fileBlob = file;
388
+ filePlain = undefined;
389
+ fileId = getSrcFromFileData(file);
390
+ }
391
+
392
+ panelData.filesData.push({
393
+ id: documentObject._id,
394
+ _id: documentObject._id,
395
+ mdFileId: { mdBlob, mdPlain },
396
+ fileId: { fileBlob, filePlain },
397
+ });
398
+
399
+ panelData.data.push({
400
+ id: documentObject._id,
401
+ title: documentObject.title,
402
+ createdAt: documentObject.createdAt,
403
+ tags: documentObject.tags.filter((t) => !prefixTags.includes(t)),
404
+ mdFileId: marked.parse(mdFileId),
405
+ userId: documentObject.userId._id,
406
+ fileId,
407
+ tools: Elements.Data.user.main.model.user._id === documentObject.userId._id,
408
+ _id: documentObject._id,
409
+ });
410
+ }
411
+
412
+ panelData.skip += result.data.data.length;
413
+ panelData.hasMore = result.data.data.length === panelData.limit;
414
+ if (result.data.data.length === 0 || result.data.data.pop()._id === panelData.lasIdAvailable) {
415
+ LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
416
+ panelData.hasMore = false;
417
+ }
418
+ } else {
419
+ NotificationManager.Push({
420
+ html: result.message,
421
+ status: result.status,
373
422
  });
423
+ panelData.hasMore = false;
374
424
  }
425
+ } catch (error) {
426
+ logger.error(error);
375
427
  }
428
+
429
+ await timer(250);
430
+ panelData.loading = false;
431
+ LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
376
432
  };
377
433
  const renderSrrPanelData = async () =>
378
434
  await panelRender({
@@ -420,40 +476,142 @@ const PanelForm = {
420
476
  let firsUpdateEvent = false;
421
477
  let lastCid;
422
478
  let lastUserId;
479
+ let loadingGetData = false;
423
480
  closeModalRouteChangeEvents[idPanel] = () => {
424
- this.Data[idPanel].updatePanel();
481
+ setTimeout(() => {
482
+ this.Data[idPanel].updatePanel();
483
+ });
425
484
  };
426
- this.Data[idPanel].updatePanel = async () => {
427
- const cid = getQueryParams().cid ? getQueryParams().cid : '';
428
- const forceUpdate = lastUserId !== Elements.Data.user.main.model.user._id;
429
- if (lastCid === cid && !forceUpdate) return;
430
- lastUserId = newInstance(Elements.Data.user.main.model.user._id);
431
- lastCid = cid;
432
- htmls(`.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`, await renderSrrPanelData());
433
- await getPanelData();
434
- htmls(
435
- `.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`,
436
- await panelRender({ data: this.Data[idPanel].data }),
437
- );
438
- if (!firsUpdateEvent && options.firsUpdateEvent) {
439
- firsUpdateEvent = true;
440
- await options.firsUpdateEvent();
441
- }
485
+ this.Data[idPanel].updatePanel = async (...args) => {
486
+ const _updatePanel = async (...args) => {
487
+ try {
488
+ const cid = getQueryParams().cid ? getQueryParams().cid : '';
489
+ const forceUpdate =
490
+ Elements.Data.user.main.model &&
491
+ Elements.Data.user.main.model.user &&
492
+ Elements.Data.user.main.model.user._id &&
493
+ lastUserId !== Elements.Data.user.main.model.user._id;
494
+
495
+ logger.warn(
496
+ {
497
+ idPanel,
498
+ cid,
499
+ forceUpdate,
500
+ },
501
+ JSON.stringify(Elements.Data.user.main.model.user, null, 4),
502
+ );
503
+
504
+ if (loadingGetData || (lastCid === cid && !forceUpdate)) return;
505
+ loadingGetData = true;
506
+ lastUserId = newInstance(Elements.Data.user.main.model.user._id);
507
+ lastCid = cid;
508
+
509
+ logger.warn('Init render panel data');
510
+
511
+ this.Data[idPanel] = {
512
+ ...this.Data[idPanel],
513
+ originData: [],
514
+ data: [],
515
+ filesData: [],
516
+ limit: 3, // Load 5 items per page
517
+ hasMore: true,
518
+ loading: false,
519
+ };
520
+
521
+ if (cid) this.Data[idPanel].skip = 0;
522
+
523
+ const containerSelector = `.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`;
524
+ htmls(containerSelector, await renderSrrPanelData());
525
+
526
+ await getPanelData();
527
+
528
+ htmls(
529
+ containerSelector,
530
+ html`
531
+ <div class="in">${await panelRender({ data: this.Data[idPanel].data })}</div>
532
+ <div class="in panel-placeholder-bottom panel-placeholder-bottom-${idPanel}"></div>
533
+ `,
534
+ );
535
+
536
+ if (
537
+ !this.Data[idPanel].lasIdAvailable ||
538
+ this.Data[idPanel].lasIdAvailable !==
539
+ this.Data[idPanel].originData[this.Data[idPanel].originData.length - 1]._id
540
+ )
541
+ LoadingAnimation.spinner.play(`.panel-placeholder-bottom-${idPanel}`, 'dual-ring-mini');
542
+
543
+ const scrollContainerSelector = `.modal-${options.route}`;
544
+
545
+ if (cid) {
546
+ LoadingAnimation.spinner.stop(`.panel-placeholder-bottom-${idPanel}`);
547
+ return;
548
+ }
549
+ if (this.Data[idPanel].removeScrollEvent) {
550
+ this.Data[idPanel].removeScrollEvent();
551
+ }
552
+ const { removeEvent } = Scroll.setEvent(scrollContainerSelector, async (payload) => {
553
+ const panelData = PanelForm.Data[idPanel];
554
+ if (!panelData) return;
555
+
556
+ // Infinite scroll: load more items at bottom
557
+ if (payload.atBottom && panelData.hasMore && !panelData.loading) {
558
+ const oldDataCount = panelData.data.length;
559
+ await getPanelData(true); // isLoadMore = true
560
+ const newItems = panelData.data.slice(oldDataCount);
561
+ if (newItems.length > 0) {
562
+ for (const item of newItems)
563
+ append(`.${idPanel}-render`, await Panel.Tokens[idPanel].renderPanel(item));
564
+ }
565
+ }
566
+ });
567
+ this.Data[idPanel].removeScrollEvent = removeEvent;
568
+
569
+ if (!firsUpdateEvent && options.firsUpdateEvent) {
570
+ firsUpdateEvent = true;
571
+ await options.firsUpdateEvent();
572
+ }
573
+ } catch (error) {
574
+ logger.error(error);
575
+ }
576
+ };
577
+
578
+ await _updatePanel(...args);
579
+ loadingGetData = false;
442
580
  };
443
581
  if (options.route) {
444
582
  listenQueryPathInstance({
445
583
  id: options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body',
446
584
  routeId: options.route,
447
585
  event: async (path) => {
448
- await this.Data[idPanel].updatePanel();
586
+ PanelForm.Data[idPanel] = {
587
+ ...PanelForm.Data[idPanel],
588
+ originData: [],
589
+ data: [],
590
+ filesData: [],
591
+ // skip: 0,
592
+ limit: 3, // Load 5 items per page
593
+ hasMore: true,
594
+ loading: false,
595
+ };
596
+ await PanelForm.Data[idPanel].updatePanel();
449
597
  },
450
598
  });
451
599
  if (!options.parentIdModal)
452
600
  Modal.Data['modal-menu'].onHome[idPanel] = async () => {
453
601
  lastCid = undefined;
454
602
  lastUserId = undefined;
603
+ PanelForm.Data[idPanel] = {
604
+ ...PanelForm.Data[idPanel],
605
+ originData: [],
606
+ data: [],
607
+ filesData: [],
608
+ skip: 0,
609
+ limit: 3, // Load 5 items per page
610
+ hasMore: true,
611
+ loading: false,
612
+ };
455
613
  setQueryPath({ path: options.route, queryPath: options.route === 'home' ? '?' : '' });
456
- await this.Data[idPanel].updatePanel();
614
+ await PanelForm.Data[idPanel].updatePanel();
457
615
  };
458
616
  }
459
617
 
@@ -59,7 +59,7 @@ const getProxyPath = () => {
59
59
  * @returns {void | undefined} Returns `undefined` if the new path is the same as the current path, otherwise `void` (result of `history.pushState`).
60
60
  */
61
61
  const setPath = (path = '/', options = { removeSearch: false, removeHash: false }, stateStorage = {}, title = '') => {
62
- logger.warn(`Set path input`, `${path}`);
62
+ // logger.warn(`Set path input`, `${path}`);
63
63
  if (!path) path = '/';
64
64
 
65
65
  let [inputPath, inputSearchHash] = `${path}`.split('?');
@@ -77,18 +77,18 @@ const setPath = (path = '/', options = { removeSearch: false, removeHash: false
77
77
  inputHash && !options.removeHash ? `#${inputHash}` : ''
78
78
  }`;
79
79
  const currentFullPath = `${window.location.pathname}${location.search}${location.hash}`;
80
- logger.warn(`Set path output`, {
81
- inputPath: inputPath,
82
- inputSearch: inputSearch,
83
- inputHash: inputHash,
84
- sanitizedPath: sanitizedPath,
85
- currentLocationSearch: location.search,
86
- currentLocationHash: location.hash,
87
- currentFullPath,
88
- newFullPath,
89
- });
80
+ // logger.warn(`Set path output`, {
81
+ // inputPath: inputPath,
82
+ // inputSearch: inputSearch,
83
+ // inputHash: inputHash,
84
+ // sanitizedPath: sanitizedPath,
85
+ // currentLocationSearch: location.search,
86
+ // currentLocationHash: location.hash,
87
+ // currentFullPath,
88
+ // newFullPath,
89
+ // });
90
90
  if (currentFullPath === newFullPath) {
91
- logger.warn('Prevent overwriting same path', { currentFullPath, newFullPath });
91
+ // logger.warn('Prevent overwriting same path', { currentFullPath, newFullPath });
92
92
  return;
93
93
  }
94
94
  return history.pushState.call(history, stateStorage, title, newFullPath);
@@ -149,7 +149,7 @@ const Router = function (options = { Routes: () => {}, e: new PopStateEvent() })
149
149
  const { e, Routes } = options;
150
150
  const proxyPath = getProxyPath();
151
151
  let path = window.location.pathname;
152
- logger.info(options);
152
+ // logger.info(options);
153
153
 
154
154
  for (let route of Object.keys(Routes())) {
155
155
  route = route.slice(1);
@@ -229,11 +229,11 @@ const listenQueryPathInstance = ({ id, routeId, event }, queryKey = 'cid') => {
229
229
  * @memberof PwaRouter
230
230
  */
231
231
  const closeModalRouteChangeEvent = (options = {}) => {
232
- logger.warn('closeModalRouteChangeEvent', options);
232
+ // logger.warn('closeModalRouteChangeEvent', options);
233
233
  const { closedId } = options;
234
234
  if (!closedId) return;
235
235
  if (coreUI.find((id) => closedId.startsWith(id))) {
236
- logger.warn('prevent core ui component close');
236
+ // logger.warn('prevent core ui component close');
237
237
  return;
238
238
  }
239
239
 
@@ -1,25 +1,89 @@
1
- import { append } from './VanillaJs.js';
1
+ import { renderCssAttr } from './Css.js';
2
+ import { append, s } from './VanillaJs.js';
3
+ import { Modal } from './Modal.js';
2
4
 
3
5
  const ToolTip = {
4
6
  Tokens: {},
5
- Render: async function (options = { container: '', htmlRender: '', id: '', classList: '' }) {
6
- const { container, htmlRender, id } = options;
7
- const tooltipId = 'tooltip-' + id;
8
- append(
9
- container,
10
- html`
11
- <style>
12
- ${container}:hover .${tooltipId} {
13
- visibility: visible;
14
- }
15
- .${tooltipId} {
16
- visibility: hidden;
17
- }
18
- </style>
19
- <div class="tooltip ${options?.classList ? `${options.classList} ` : ' '}${tooltipId}">${htmlRender}</div>
20
- `,
21
- );
22
- return '';
7
+ Render: async function (
8
+ options = { container: '', htmlRender: '', id: '', classList: '', useVisibilityHover: false, useMenuBtn: false },
9
+ ) {
10
+ const { container, htmlRender, id, useVisibilityHover } = options;
11
+
12
+ if (useVisibilityHover) {
13
+ const tooltipId = 'tooltip-' + id;
14
+ append(
15
+ container,
16
+ html`
17
+ <style>
18
+ ${container}:hover .${tooltipId} {
19
+ visibility: visible;
20
+ }
21
+ .${tooltipId} {
22
+ visibility: hidden;
23
+ }
24
+ </style>
25
+ <div class="tooltip ${options?.classList ? `${options.classList} ` : ' '}${tooltipId}">${htmlRender}</div>
26
+ `,
27
+ );
28
+ return;
29
+ }
30
+
31
+ const containerEl = s(container);
32
+ if (!containerEl) return;
33
+
34
+ const tooltipId = `tooltip-${id}`;
35
+ const tooltip = html`
36
+ <div
37
+ class="fix fix-tooltip ${tooltipId}"
38
+ style="${renderCssAttr({
39
+ style: {
40
+ 'z-index': 10,
41
+ position: 'absolute',
42
+ opacity: 0,
43
+ transition: 'opacity 0.2s ease-in-out',
44
+ 'pointer-events': 'none',
45
+ },
46
+ })}"
47
+ >
48
+ ${htmlRender}
49
+ </div>
50
+ `;
51
+ append('body', tooltip);
52
+
53
+ const tooltipEl = s(`.${tooltipId}`);
54
+
55
+ containerEl.addEventListener('mouseenter', () => {
56
+ if (
57
+ options.useMenuBtn &&
58
+ s(
59
+ `.btn-icon-menu-mode-${Modal.Data['modal-menu'].options.mode === 'slide-menu-right' ? 'left' : 'right'}`,
60
+ ).classList.contains('hide')
61
+ )
62
+ return;
63
+
64
+ const containerRect = containerEl.getBoundingClientRect();
65
+ const tooltipRect = tooltipEl.getBoundingClientRect();
66
+
67
+ let top = containerRect.bottom + window.scrollY + 5;
68
+ let left = containerRect.left + window.scrollX + containerRect.width / 2 - tooltipRect.width / 2;
69
+
70
+ // Adjust if it goes off-screen
71
+ if (left < 0) left = 5;
72
+ if (left + tooltipRect.width > window.innerWidth) {
73
+ left = window.innerWidth - tooltipRect.width - 5;
74
+ }
75
+ if (top + tooltipRect.height > window.innerHeight) {
76
+ top = containerRect.top + window.scrollY - tooltipRect.height - 5;
77
+ }
78
+
79
+ tooltipEl.style.top = `${top}px`;
80
+ tooltipEl.style.left = `${left}px`;
81
+ tooltipEl.style.opacity = '1';
82
+ });
83
+
84
+ containerEl.addEventListener('mouseleave', () => {
85
+ tooltipEl.style.opacity = '0';
86
+ });
23
87
  },
24
88
  };
25
89
 
@@ -34,7 +34,7 @@ const Translate = {
34
34
  Render: function (keyLang, placeholder, options = { disableTextFormat: false }) {
35
35
  if (!(keyLang in this.Data)) {
36
36
  // TODO: add translate package or library for this case
37
- logger.warn('translate key lang does not exist: ', keyLang);
37
+ // logger.warn('translate key lang does not exist: ', keyLang);
38
38
  return options.disableTextFormat ? keyLang : textFormatted(keyLang);
39
39
  }
40
40
  if (placeholder) this.Data[keyLang].placeholder = placeholder;
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { s4 } from './CommonJs.js';
8
+ import { windowGetH, windowGetW } from './windowGetDimensions.js';
8
9
 
9
10
  /*
10
11
 
@@ -232,10 +233,10 @@ const fullScreenIn = () => {
232
233
  * @memberof VanillaJS
233
234
  */
234
235
  const getResponsiveData = () => {
235
- const inner = { width: window.innerWidth, height: window.innerHeight };
236
+ const inner = { width: windowGetW(), height: windowGetH() };
236
237
  return inner.width > inner.height
237
- ? { ...inner, minValue: window.innerHeight, maxValue: window.innerWidth, minType: 'height', maxType: 'width' }
238
- : { ...inner, minValue: window.innerWidth, maxValue: window.innerHeight, minType: 'width', maxType: 'height' };
238
+ ? { ...inner, minValue: windowGetH(), maxValue: windowGetW(), minType: 'height', maxType: 'width' }
239
+ : { ...inner, minValue: windowGetW(), maxValue: windowGetH(), minType: 'width', maxType: 'height' };
239
240
  };
240
241
 
241
242
  /**
@@ -389,6 +390,8 @@ function hexToRgbA(hex) {
389
390
  throw new Error('Invalid Hex');
390
391
  }
391
392
 
393
+ const htmlStrSanitize = (str) => (str ? str.replace(/<\/?[^>]+(>|$)/g, '').trim() : '');
394
+
392
395
  export {
393
396
  s,
394
397
  htmls,
@@ -416,4 +419,5 @@ export {
416
419
  getDataFromInputFile,
417
420
  getLang,
418
421
  hexToRgbA,
422
+ htmlStrSanitize,
419
423
  };