underpost 3.2.4 → 3.2.8

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 (141) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +268 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +58 -2
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +304 -38
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/backup.js +1 -2
  124. package/src/server/client-build-docs.js +45 -46
  125. package/src/server/client-build.js +334 -60
  126. package/src/server/client-formatted.js +47 -16
  127. package/src/server/conf.js +29 -13
  128. package/src/server/cron.js +6 -8
  129. package/src/server/dns.js +2 -1
  130. package/src/server/ipfs-client.js +232 -91
  131. package/src/server/process.js +13 -27
  132. package/src/server/start.js +6 -3
  133. package/src/server/valkey.js +134 -235
  134. package/tsconfig.docs.json +15 -0
  135. package/typedoc.json +20 -0
  136. package/jsdoc.json +0 -52
  137. package/src/client/components/core/ColorPalette.js +0 -5267
  138. package/src/client/components/core/JoyStick.js +0 -80
  139. package/src/client/components/default/RoutesDefault.js +0 -49
  140. package/src/client/sw/default.sw.js +0 -127
  141. package/src/client/sw/template.sw.js +0 -84
@@ -4,20 +4,17 @@ import { Css, darkTheme, renderCssAttr, simpleIconsRender, ThemeEvents, Themes }
4
4
  import { buildBadgeToolTipMenuOption, Modal, renderViewTitle } from './Modal.js';
5
5
  import { listenQueryPathInstance, setQueryPath, closeModalRouteChangeEvent, getProxyPath } from './Router.js';
6
6
  import { htmls, s, sIframe } from './VanillaJs.js';
7
-
8
7
  // https://mintlify.com/docs/quickstart
9
-
10
- const Docs = {
11
- RenderModal: async function (type) {
12
- const docData = this.Data.find((d) => d.type === type);
8
+ class Docs {
9
+ static async RenderModal(type) {
10
+ const docData = Docs.Data.find((d) => d.type === type);
13
11
  const ModalId = `modal-docs-${docData.type}`;
14
12
  const { barConfig } = await Themes[Css.currentTheme]();
15
13
  const parentBarMode =
16
14
  Modal.Data['modal-docs'] && Modal.Data['modal-docs'].options.barMode
17
15
  ? Modal.Data['modal-docs'].options.barMode
18
16
  : undefined;
19
-
20
- await Modal.Render({
17
+ await Modal.instance({
21
18
  barConfig,
22
19
  title: renderViewTitle(docData),
23
20
  id: ModalId,
@@ -51,7 +48,15 @@ const Docs = {
51
48
  });
52
49
  const iframeEl = s(`.iframe-${ModalId}`);
53
50
  let swaggerThemeEventKey = null;
51
+ let unbindIframeLayoutSync = null;
54
52
  if (iframeEl) {
53
+ const scheduleViewLayoutSync = () => {
54
+ const sync = () => Modal.syncViewLayout();
55
+ sync();
56
+ setTimeout(sync, 0);
57
+ setTimeout(sync, 120);
58
+ setTimeout(sync, 400);
59
+ };
55
60
  iframeEl.addEventListener('load', () => {
56
61
  try {
57
62
  const iframeWin = iframeEl.contentWindow;
@@ -62,11 +67,34 @@ const Docs = {
62
67
  } catch (e) {
63
68
  // cross-origin or security restriction — safe to ignore
64
69
  }
65
- window.scrollTo(0, 0);
70
+ // Keep the parent window scroll untouched.
71
+ // These modals are fixed-position; scrolling the parent on iframe navigation
72
+ // shifts Chrome layout calculations and leaves view modals offset by 50px.
66
73
  // Bind Shift+K inside the iframe to focus the parent SearchBox (mirrors app-wide shortcut)
67
74
  try {
75
+ const iframeWin = iframeEl.contentWindow;
68
76
  const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
69
77
  if (iframeDoc) {
78
+ if (unbindIframeLayoutSync) unbindIframeLayoutSync();
79
+ const onIframeAnchorClick = (e) => {
80
+ if (e.target && e.target.closest && e.target.closest('a')) {
81
+ scheduleViewLayoutSync();
82
+ }
83
+ };
84
+ const onIframeHashChange = () => scheduleViewLayoutSync();
85
+ const onIframePopState = () => scheduleViewLayoutSync();
86
+ iframeDoc.addEventListener('click', onIframeAnchorClick, true);
87
+ if (iframeWin) {
88
+ iframeWin.addEventListener('hashchange', onIframeHashChange);
89
+ iframeWin.addEventListener('popstate', onIframePopState);
90
+ }
91
+ unbindIframeLayoutSync = () => {
92
+ iframeDoc.removeEventListener('click', onIframeAnchorClick, true);
93
+ if (iframeWin) {
94
+ iframeWin.removeEventListener('hashchange', onIframeHashChange);
95
+ iframeWin.removeEventListener('popstate', onIframePopState);
96
+ }
97
+ };
70
98
  iframeDoc.addEventListener('keydown', (e) => {
71
99
  if (e.shiftKey && e.key.toLowerCase() === 'k') {
72
100
  e.preventDefault();
@@ -85,48 +113,30 @@ const Docs = {
85
113
  } catch (e) {
86
114
  // cross-origin or security restriction — safe to ignore
87
115
  }
116
+ scheduleViewLayoutSync();
88
117
  });
89
-
90
118
  if (type === 'src') {
91
119
  swaggerThemeEventKey = `jsdocs-iframe-${ModalId}`;
92
-
93
120
  const applyJsDocsTheme = (isDark) => {
94
121
  try {
95
- const iframeWin = iframeEl.contentWindow;
96
- if (!iframeWin) return;
97
- const theme = isDark ? 'dark' : 'light';
98
- if (typeof iframeWin.updateTheme === 'function') {
99
- // clean-jsdoc-theme exposes updateTheme() globally
100
- iframeWin.updateTheme(theme);
101
- } else {
102
- // Fallback: replicate localUpdateTheme manually
103
- const iframeDoc = iframeEl.contentDocument || iframeWin.document;
104
- if (!iframeDoc || !iframeDoc.body) return;
105
- iframeDoc.body.setAttribute('data-theme', theme);
106
- iframeDoc.body.classList.remove('dark', 'light');
107
- iframeDoc.body.classList.add(theme);
108
- const iconID = isDark ? '#light-theme-icon' : '#dark-theme-icon';
109
- const svgUses = sIframe(iframeEl, '.theme-svg-use') ? iframeDoc.querySelectorAll('.theme-svg-use') : [];
110
- svgUses.forEach((svg) => svg.setAttribute('xlink:href', iconID));
111
- iframeWin.localStorage?.setItem('theme', theme);
112
- }
122
+ const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
123
+ if (!iframeDoc || !iframeDoc.documentElement) return;
124
+ // TypeDoc built-in theme: data-theme on <html>, stored as 'tsd-theme' in localStorage
125
+ iframeDoc.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
126
+ iframeEl.contentWindow?.localStorage?.setItem('tsd-theme', isDark ? 'dark' : 'light');
113
127
  } catch (e) {
114
128
  // cross-origin or security restriction — safe to ignore
115
129
  }
116
130
  };
117
-
118
131
  // Apply current theme as soon as the iframe content is ready
119
132
  iframeEl.addEventListener('load', () => applyJsDocsTheme(darkTheme));
120
-
121
133
  // Keep in sync whenever the parent page theme changes
122
134
  ThemeEvents[swaggerThemeEventKey] = () => {
123
135
  if (s(`.iframe-${ModalId}`)) applyJsDocsTheme(darkTheme);
124
136
  };
125
137
  }
126
-
127
138
  if (type === 'api') {
128
139
  swaggerThemeEventKey = `swagger-iframe-${ModalId}`;
129
-
130
140
  const applySwaggerTheme = (isDark) => {
131
141
  try {
132
142
  const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
@@ -143,10 +153,8 @@ const Docs = {
143
153
  // cross-origin or security restriction — safe to ignore
144
154
  }
145
155
  };
146
-
147
156
  // Apply current theme as soon as the iframe content is ready
148
157
  iframeEl.addEventListener('load', () => applySwaggerTheme(darkTheme));
149
-
150
158
  // Keep in sync whenever the parent page theme changes
151
159
  ThemeEvents[swaggerThemeEventKey] = () => {
152
160
  if (s(`.iframe-${ModalId}`)) applySwaggerTheme(darkTheme);
@@ -159,7 +167,6 @@ const Docs = {
159
167
  const barHeight = barEl ? barEl.offsetHeight : Modal.headerTitleHeight;
160
168
  s(`.iframe-${ModalId}`).style.height = `${s(`.${ModalId}`).offsetHeight - barHeight}px`;
161
169
  }
162
-
163
170
  if (type.match('coverage')) {
164
171
  simpleIconsRender(`.doc-icon-coverage`);
165
172
  simpleIconsRender(`.doc-icon-coverage-link`);
@@ -167,11 +174,12 @@ const Docs = {
167
174
  };
168
175
  Modal.Data[ModalId].onObserverListener[ModalId]();
169
176
  Modal.Data[ModalId].onCloseListener[ModalId] = () => {
177
+ if (unbindIframeLayoutSync) unbindIframeLayoutSync();
170
178
  if (swaggerThemeEventKey) delete ThemeEvents[swaggerThemeEventKey];
171
179
  closeModalRouteChangeEvent({ closedId: ModalId });
172
180
  };
173
- },
174
- Data: [
181
+ }
182
+ static Data = [
175
183
  {
176
184
  type: 'repo',
177
185
  icon: html`<i class="fab fa-github"></i>`,
@@ -238,38 +246,36 @@ const Docs = {
238
246
  if (s(`.doc-icon-coverage-link`)) setTimeout(() => simpleIconsRender(`.doc-icon-coverage-link`));
239
247
  },
240
248
  },
241
- ],
242
- Tokens: {},
243
- Init: async function (options = {}) {
249
+ ];
250
+ static Tokens = {};
251
+ static async instance(options = {}) {
244
252
  const { idModal } = options;
245
- this.Tokens[idModal] = options;
253
+ Docs.Tokens[idModal] = options;
246
254
  setTimeout(() => {
247
255
  s(`.btn-docs-src`).onclick = async () => {
248
256
  setQueryPath({ path: 'docs', queryPath: 'src' });
249
- await this.RenderModal('src');
257
+ await Docs.RenderModal('src');
250
258
  };
251
259
  s(`.btn-docs-api`).onclick = async () => {
252
260
  setQueryPath({ path: 'docs', queryPath: 'api' });
253
- await this.RenderModal('api');
261
+ await Docs.RenderModal('api');
254
262
  };
255
263
  s(`.btn-docs-coverage`).onclick = async () => {
256
264
  setQueryPath({ path: 'docs', queryPath: 'coverage' });
257
- await this.RenderModal('coverage');
265
+ await Docs.RenderModal('coverage');
258
266
  };
259
-
260
267
  s(`.btn-docs-coverage-link`).onclick = () => {
261
- const docData = this.Data.find((d) => d.type === 'coverage-link');
268
+ const docData = Docs.Data.find((d) => d.type === 'coverage-link');
262
269
  location.href = docData.url();
263
270
  };
264
271
  s(`.btn-docs-repo`).onclick = () => {
265
- const docData = this.Data.find((d) => d.type === 'repo');
272
+ const docData = Docs.Data.find((d) => d.type === 'repo');
266
273
  location.href = docData.url();
267
274
  };
268
275
  s(`.btn-docs-demo`).onclick = () => {
269
- const docData = this.Data.find((d) => d.type === 'demo');
276
+ const docData = Docs.Data.find((d) => d.type === 'demo');
270
277
  location.href = docData.url();
271
278
  };
272
-
273
279
  listenQueryPathInstance({
274
280
  id: options.idModal,
275
281
  routeId: 'docs',
@@ -284,7 +290,7 @@ const Docs = {
284
290
  });
285
291
  });
286
292
  let docMenuRender = '';
287
- for (const docData of this.Data) {
293
+ for (const docData of Docs.Data) {
288
294
  if (docData.themeEvent) {
289
295
  ThemeEvents[`doc-icon-${docData.type}`] = docData.themeEvent;
290
296
  setTimeout(ThemeEvents[`doc-icon-${docData.type}`]);
@@ -296,7 +302,6 @@ const Docs = {
296
302
  style = renderCssAttr({ style: { height: '45px' } });
297
303
  labelStyle = renderCssAttr({ style: { top: '8px', left: '9px' } });
298
304
  break;
299
-
300
305
  default:
301
306
  break;
302
307
  }
@@ -306,21 +311,19 @@ const Docs = {
306
311
  ? options.subMenuIcon(docData.type)
307
312
  : docData.icon;
308
313
  docMenuRender += html`
309
- ${await BtnIcon.Render({
314
+ ${await BtnIcon.instance({
310
315
  class: `in wfa main-btn-menu submenu-btn btn-docs btn-docs-${docData.type}`,
311
316
  label: html`<span class="inl menu-btn-icon">${subMenuIcon}</span
312
317
  ><span class="menu-label-text menu-label-text-docs"> ${docData.text} </span>`,
313
318
  tabHref,
314
- tooltipHtml: await Badge.Render(buildBadgeToolTipMenuOption(docData.text, 'right')),
319
+ tooltipHtml: await Badge.instance(buildBadgeToolTipMenuOption(docData.text, 'right')),
315
320
  useMenuBtn: true,
316
321
  })}
317
322
  `;
318
323
  }
319
324
  // s(`.menu-btn-container-children`).classList.remove('hide');
320
325
  // htmls(`.nav-path-display-${'modal-menu'}`, location.pathname);
321
-
322
326
  htmls('.menu-btn-container-children-docs', docMenuRender);
323
-
324
327
  {
325
328
  const docsData = [
326
329
  {
@@ -360,7 +363,6 @@ const Docs = {
360
363
  description: 'Join our developer community',
361
364
  },
362
365
  ];
363
-
364
366
  return html`
365
367
  <style>
366
368
  .docs-landing {
@@ -499,7 +501,6 @@ const Docs = {
499
501
  </div>
500
502
  `;
501
503
  }
502
- },
503
- };
504
-
504
+ }
505
+ }
505
506
  export { Docs };
@@ -5,18 +5,16 @@ import { Input } from './Input.js';
5
5
  import { ToggleSwitch } from './ToggleSwitch.js';
6
6
  import { Translate } from './Translate.js';
7
7
  import { s, htmls } from './VanillaJs.js';
8
-
9
- const DropDown = {
10
- Tokens: {},
11
- Render: async function (options) {
12
- const id = options.id ? options.id : getId(this.Tokens, 'dropdown-');
13
- this.Tokens[id] = {
8
+ class DropDown {
9
+ static Tokens = {};
10
+ static async instance(options) {
11
+ const id = options.id ? options.id : getId(DropDown.Tokens, 'dropdown-');
12
+ DropDown.Tokens[id] = {
14
13
  onClickEvents: {},
15
14
  lastSelectValue: undefined,
16
15
  oncheckvalues: {},
17
16
  originData: options.data ? newInstance(options.data) : [],
18
17
  };
19
-
20
18
  const _renderSelectedBadges = async () => {
21
19
  if (options.type !== 'checkbox') return;
22
20
  const container = s(`.dropdown-current-${id}`);
@@ -29,7 +27,7 @@ const DropDown = {
29
27
  let badgesHtml = '';
30
28
  for (const [key, val] of selected) {
31
29
  badgesHtml += html`<span class="inl" style="display:inline-flex;align-items:center;margin:2px;">
32
- ${await Badge.Render({
30
+ ${await Badge.instance({
33
31
  text: html`<i class="fa-solid fa-tag" style="margin-right:3px;font-size:9px;"></i>${val.display}`,
34
32
  style: {
35
33
  background: darkTheme ? '#335' : '#cde',
@@ -67,12 +65,11 @@ const DropDown = {
67
65
  });
68
66
  };
69
67
  DropDown.Tokens[id]._renderSelectedBadges = _renderSelectedBadges;
70
-
71
68
  options.data.push({
72
69
  value: 'reset',
73
- display: html`<i class="fa-solid fa-broom"></i> ${Translate.Render('clear')}`,
70
+ display: html`<i class="fa-solid fa-broom"></i> ${Translate.instance('clear')}`,
74
71
  onClick: () => {
75
- console.log('DropDown onClick', this.value);
72
+ console.log('DropDown onClick', DropDown.value);
76
73
  if (options && options.resetOnClick) options.resetOnClick();
77
74
  if (options && options.type === 'checkbox') {
78
75
  DropDown.Tokens[id].oncheckvalues = {};
@@ -93,92 +90,85 @@ const DropDown = {
93
90
  }
94
91
  }
95
92
  }
96
- } else this.Tokens[id].value = undefined;
93
+ } else DropDown.Tokens[id].value = undefined;
97
94
  },
98
95
  });
99
-
100
96
  if (!(options && options.disableClose))
101
97
  options.data.push({
102
98
  value: 'close',
103
- display: html`<i class="fa-solid fa-xmark"></i> ${Translate.Render('close')}`,
99
+ display: html`<i class="fa-solid fa-xmark"></i> ${Translate.instance('close')}`,
104
100
  onClick: function () {
105
- console.log('DropDown onClick', this.value);
101
+ console.log('DropDown onClick', DropDown.value);
106
102
  },
107
103
  });
108
-
109
104
  const switchOptionsPanel = () => {
110
105
  if (Array.from(s(`.dropdown-option-${id}`).classList).includes('hide'))
111
106
  s(`.dropdown-option-${id}`).classList.remove('hide');
112
107
  else s(`.dropdown-option-${id}`).classList.add('hide');
113
108
  };
114
-
115
109
  const _render = async (data) => {
116
110
  let render = '';
117
111
  let index = -1;
118
112
  for (const optionData of data) {
119
113
  index++;
120
114
  const i = index;
121
- const valueDisplay = optionData.value.trim().replaceAll(' ', '-');
122
- setTimeout(() => {
123
- const onclick = async (e) => {
124
- if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
125
- s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass);
126
- }
127
- this.Tokens[id].lastSelectValue = valueDisplay;
128
- if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
129
- s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.add(options.lastSelectClass);
130
- }
131
-
132
- if (
133
- !(options && options.disableClose) &&
134
- (options.type !== 'checkbox' || optionData.value === 'close' || optionData.value === 'reset')
135
- )
136
- s(`.dropdown-option-${id}`).classList.add('hide');
137
-
138
- if (options.type === 'checkbox' && ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`])
139
- ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`].click();
140
- if (optionData.value !== 'close') {
141
- if (optionData.value !== 'reset') {
142
- if (options.type === 'checkbox') {
143
- _renderSelectedBadges();
144
- } else {
145
- htmls(`.dropdown-current-${id}`, optionData.display);
146
- }
147
- } else htmls(`.dropdown-current-${id}`, '');
148
-
149
- this.Tokens[id].value =
150
- options.type === 'checkbox'
151
- ? options.serviceProvider
152
- ? Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data)
153
- : data.filter((d) => d.checked).map((d) => d.data)
154
- : optionData.data;
155
-
156
- console.warn('current value dropdown id:' + id, this.Tokens[id].value);
157
-
158
- s(`.${id}`).value = this.Tokens[id].value;
159
-
160
- optionData.onClick(e);
161
- }
162
- };
163
-
164
- this.Tokens[id].onClickEvents[`dropdown-option-${id}-${i}`] = onclick;
165
- this.Tokens[id].onClickEvents[`dropdown-option-${id}-${valueDisplay}`] = onclick;
166
- this.Tokens[id].onClickEvents[`dropdown-option-${valueDisplay}`] = onclick;
167
-
168
- s(`.dropdown-option-${id}-${i}`).onclick = onclick;
169
- });
115
+ const optionValue = typeof optionData.value === 'string' ? optionData.value : `option-${i}`;
116
+ const valueDisplay = optionValue.trim().replaceAll(' ', '-');
117
+ const isGroup = optionData.kind === 'group' || optionData.nonSelectable === true;
118
+ if (!isGroup) {
119
+ setTimeout(() => {
120
+ const onclick = async (e) => {
121
+ if (options && options.lastSelectClass && s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`)) {
122
+ s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass);
123
+ }
124
+ DropDown.Tokens[id].lastSelectValue = valueDisplay;
125
+ if (options && options.lastSelectClass && s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`)) {
126
+ s(`.dropdown-option-${DropDown.Tokens[id].lastSelectValue}`).classList.add(options.lastSelectClass);
127
+ }
128
+ if (
129
+ !(options && options.disableClose) &&
130
+ (options.type !== 'checkbox' || optionData.value === 'close' || optionData.value === 'reset')
131
+ )
132
+ s(`.dropdown-option-${id}`).classList.add('hide');
133
+ if (options.type === 'checkbox' && ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`])
134
+ ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`].click();
135
+ if (optionData.value !== 'close') {
136
+ if (optionData.value !== 'reset') {
137
+ if (options.type === 'checkbox') {
138
+ _renderSelectedBadges();
139
+ } else {
140
+ htmls(`.dropdown-current-${id}`, optionData.display);
141
+ }
142
+ } else htmls(`.dropdown-current-${id}`, '');
143
+ DropDown.Tokens[id].value =
144
+ options.type === 'checkbox'
145
+ ? options.serviceProvider
146
+ ? Object.values(DropDown.Tokens[id].oncheckvalues).map((v) => v.data)
147
+ : data.filter((d) => d.checked).map((d) => d.data)
148
+ : optionData.data;
149
+ console.warn('current value dropdown id:' + id, DropDown.Tokens[id].value);
150
+ s(`.${id}`).value = DropDown.Tokens[id].value;
151
+ optionData.onClick(e);
152
+ }
153
+ };
154
+ DropDown.Tokens[id].onClickEvents[`dropdown-option-${id}-${i}`] = onclick;
155
+ DropDown.Tokens[id].onClickEvents[`dropdown-option-${id}-${valueDisplay}`] = onclick;
156
+ DropDown.Tokens[id].onClickEvents[`dropdown-option-${valueDisplay}`] = onclick;
157
+ s(`.dropdown-option-${id}-${i}`).onclick = onclick;
158
+ });
159
+ }
170
160
  render += html`
171
161
  <div
172
- class="in dropdown-option dropdown-option-${id}-${i} dropdown-option-${id}-${valueDisplay} dropdown-option-${valueDisplay} ${valueDisplay ===
173
- 'reset' &&
174
- options &&
175
- !(options.resetOption === true)
176
- ? 'hide'
162
+ class="in dropdown-option ${isGroup
163
+ ? `dropdown-option-group dropdown-option-group-${id}`
164
+ : `dropdown-option-${id}-${i} dropdown-option-${id}-${valueDisplay} dropdown-option-${valueDisplay} ${valueDisplay === 'reset' && options && !(options.resetOption === true) ? 'hide' : ''}`}"
165
+ style="${isGroup
166
+ ? 'cursor:default;opacity:.8;font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding-top:8px;padding-bottom:6px;'
177
167
  : ''}"
178
168
  >
179
- ${options.type === 'checkbox' && optionData.value !== 'close' && optionData.value !== 'reset'
169
+ ${!isGroup && options.type === 'checkbox' && optionData.value !== 'close' && optionData.value !== 'reset'
180
170
  ? html`
181
- ${await ToggleSwitch.Render({
171
+ ${await ToggleSwitch.instance({
182
172
  id: `checkbox-role-${valueDisplay}`,
183
173
  type: 'checkbox',
184
174
  disabledOnClick: true,
@@ -205,7 +195,6 @@ const DropDown = {
205
195
  }
206
196
  return { render, index };
207
197
  };
208
-
209
198
  setTimeout(() => {
210
199
  if (options.type === 'checkbox')
211
200
  options.data.map((optionData) => {
@@ -216,21 +205,16 @@ const DropDown = {
216
205
  const indexValue = options.data.findIndex((t) => t.value === options.value);
217
206
  if (indexValue > -1) setTimeout(() => s(`.dropdown-option-${id}-${indexValue}`).click());
218
207
  }
219
-
220
208
  s(`.dropdown-label-${id}`).onclick = switchOptionsPanel;
221
209
  s(`.dropdown-current-${id}`).onclick = switchOptionsPanel;
222
210
  if (options && options.open) switchOptionsPanel();
223
-
224
211
  if (options.type === 'checkbox') {
225
212
  ThemeEvents[`dropdown-badge-${id}`] = () => _renderSelectedBadges();
226
213
  }
227
-
228
214
  const dropDownSearchHandle = async () => {
229
215
  const _data = [];
230
216
  if (!s(`.search-box-${id}`)) return;
231
-
232
217
  let _value = s(`.search-box-${id}`).value.toLowerCase();
233
-
234
218
  for (const objData of options.data) {
235
219
  if (
236
220
  options.excludeSelected &&
@@ -249,7 +233,6 @@ const DropDown = {
249
233
  _data.push(objData);
250
234
  }
251
235
  }
252
-
253
236
  if (_data.length > 0) {
254
237
  const { render, index } = await _render(_data);
255
238
  htmls(`.${id}-render-container`, render);
@@ -258,12 +241,11 @@ const DropDown = {
258
241
  htmls(
259
242
  `.${id}-render-container`,
260
243
  html` <div class="inl" style="padding: 10px; color: red">
261
- <i class="fas fa-exclamation-circle"></i> ${Translate.Render('no-result-found')}
244
+ <i class="fas fa-exclamation-circle"></i> ${Translate.instance('no-result-found')}
262
245
  </div>`,
263
246
  );
264
247
  }
265
248
  };
266
-
267
249
  if (options.serviceProvider) {
268
250
  let serviceSearchTimeout = null;
269
251
  s(`.search-box-${id}`).oninput = () => {
@@ -295,7 +277,7 @@ const DropDown = {
295
277
  htmls(
296
278
  `.${id}-render-container`,
297
279
  html` <div class="inl" style="padding: 10px; color: red">
298
- <i class="fas fa-exclamation-circle"></i> ${Translate.Render('no-result-found')}
280
+ <i class="fas fa-exclamation-circle"></i> ${Translate.instance('no-result-found')}
299
281
  </div>`,
300
282
  );
301
283
  }
@@ -307,13 +289,10 @@ const DropDown = {
307
289
  } else {
308
290
  s(`.search-box-${id}`).oninput = dropDownSearchHandle;
309
291
  }
310
-
311
292
  // Not use onblur generate bug on input toggle
312
293
  // s(`.search-box-${id}`).onblur = dropDownSearchHandle;
313
294
  });
314
-
315
295
  const { render, index } = await _render(options.data);
316
-
317
296
  return html`
318
297
  <div class="inl dropdown-container ${id} ${options?.containerClass ? options.containerClass : ''}">
319
298
  <div class="in dropdown-option dropdown-label-${id} ${options && options.disableSelectLabel ? 'hide' : ''}">
@@ -326,9 +305,9 @@ const DropDown = {
326
305
  ></div>
327
306
  <div class="in dropdown-option-${id} hide">
328
307
  <div class="in dropdown-option ${options && options.disableSearchBox ? 'hide' : ''}">
329
- ${await Input.Render({
308
+ ${await Input.instance({
330
309
  id: `search-box-${id}`,
331
- label: html`<i class="fa-solid fa-magnifying-glass"></i> ${Translate.Render('search')}`,
310
+ label: html`<i class="fa-solid fa-magnifying-glass"></i> ${Translate.instance('search')}`,
332
311
  containerClass: 'in',
333
312
  placeholder: true,
334
313
  })}
@@ -337,7 +316,6 @@ const DropDown = {
337
316
  </div>
338
317
  </div>
339
318
  `;
340
- },
341
- };
342
-
319
+ }
320
+ }
343
321
  export { DropDown };
@@ -0,0 +1,92 @@
1
+ class EventBus {
2
+ constructor() {
3
+ this.target = new EventTarget();
4
+ this.listeners = new Map();
5
+ this.typeKeys = new Map();
6
+ this.bridges = new Map();
7
+ }
8
+
9
+ ensureBridge(type) {
10
+ if (this.bridges.has(type)) return;
11
+
12
+ const bridge = async (event) => {
13
+ const deferred = event._deferred;
14
+
15
+ try {
16
+ const keys = [...(this.typeKeys.get(type) ?? [])];
17
+ for (const key of keys) {
18
+ const entry = this.listeners.get(key);
19
+ if (!entry) continue;
20
+
21
+ await entry.listener(event.detail, event);
22
+ if (entry.once) this.off(key);
23
+ }
24
+
25
+ deferred?.resolve();
26
+ } catch (error) {
27
+ deferred?.reject(error);
28
+ }
29
+ };
30
+
31
+ this.bridges.set(type, bridge);
32
+ this.target.addEventListener(type, bridge);
33
+ }
34
+
35
+ on(type, listener, options = {}) {
36
+ const key = options.key ?? Symbol(type);
37
+
38
+ this.off(key);
39
+ this.ensureBridge(type);
40
+
41
+ const keys = this.typeKeys.get(type) ?? new Set();
42
+ keys.add(key);
43
+ this.typeKeys.set(type, keys);
44
+
45
+ this.listeners.set(key, {
46
+ type,
47
+ listener,
48
+ once: Boolean(options.once),
49
+ });
50
+
51
+ return () => this.off(key);
52
+ }
53
+
54
+ off(key) {
55
+ const entry = this.listeners.get(key);
56
+ if (!entry) return false;
57
+
58
+ this.listeners.delete(key);
59
+ const keys = this.typeKeys.get(entry.type);
60
+ if (keys) {
61
+ keys.delete(key);
62
+ if (keys.size === 0) this.typeKeys.delete(entry.type);
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ has(key) {
69
+ return this.listeners.has(key);
70
+ }
71
+
72
+ async emit(type, detail) {
73
+ if (!(this.typeKeys.get(type)?.size > 0)) return;
74
+
75
+ const event = new CustomEvent(type, { detail });
76
+ return await new Promise((resolve, reject) => {
77
+ event._deferred = { resolve, reject };
78
+ this.target.dispatchEvent(event);
79
+ });
80
+ }
81
+
82
+ async emitKey(key, detail) {
83
+ const entry = this.listeners.get(key);
84
+ if (!entry) return;
85
+
86
+ const event = new CustomEvent(entry.type, { detail });
87
+ await entry.listener(detail, event);
88
+ if (entry.once) this.off(key);
89
+ }
90
+ }
91
+
92
+ export { EventBus };