underpost 3.2.5 → 3.2.9

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 (144) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +351 -1
  3. package/CLI-HELP.md +40 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +4 -4
  6. package/bin/build.js +14 -5
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +6 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
  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 +24 -15
  17. package/scripts/k3s-node-setup.sh +2 -2
  18. package/scripts/nat-iptables.sh +103 -18
  19. package/src/api/core/core.controller.js +10 -10
  20. package/src/api/core/core.service.js +10 -10
  21. package/src/api/default/default.controller.js +10 -10
  22. package/src/api/default/default.service.js +10 -10
  23. package/src/api/document/document.controller.js +12 -12
  24. package/src/api/document/document.model.js +10 -16
  25. package/src/api/file/file.controller.js +8 -8
  26. package/src/api/file/file.model.js +10 -10
  27. package/src/api/file/file.service.js +36 -36
  28. package/src/api/test/test.controller.js +8 -8
  29. package/src/api/test/test.service.js +8 -8
  30. package/src/api/user/guest.service.js +99 -0
  31. package/src/api/user/user.controller.js +6 -6
  32. package/src/api/user/user.model.js +8 -13
  33. package/src/api/user/user.service.js +3 -20
  34. package/src/cli/cluster.js +61 -14
  35. package/src/cli/db.js +47 -2
  36. package/src/cli/deploy.js +67 -35
  37. package/src/cli/fs.js +79 -8
  38. package/src/cli/image.js +43 -1
  39. package/src/cli/index.js +26 -1
  40. package/src/cli/release.js +57 -1
  41. package/src/cli/repository.js +69 -31
  42. package/src/cli/run.js +415 -36
  43. package/src/cli/ssh.js +1 -1
  44. package/src/cli/static.js +43 -115
  45. package/src/client/Default.index.js +21 -33
  46. package/src/client/components/core/404.js +4 -4
  47. package/src/client/components/core/500.js +4 -4
  48. package/src/client/components/core/Account.js +73 -60
  49. package/src/client/components/core/AgGrid.js +23 -33
  50. package/src/client/components/core/Alert.js +12 -13
  51. package/src/client/components/core/AppStore.js +1 -1
  52. package/src/client/components/core/Auth.js +35 -37
  53. package/src/client/components/core/Badge.js +7 -13
  54. package/src/client/components/core/BtnIcon.js +15 -17
  55. package/src/client/components/core/CalendarCore.js +42 -63
  56. package/src/client/components/core/Chat.js +13 -15
  57. package/src/client/components/core/ClientEvents.js +87 -0
  58. package/src/client/components/core/ColorPaletteElement.js +309 -0
  59. package/src/client/components/core/Content.js +17 -14
  60. package/src/client/components/core/Css.js +15 -71
  61. package/src/client/components/core/CssCore.js +12 -16
  62. package/src/client/components/core/D3Chart.js +4 -4
  63. package/src/client/components/core/Docs.js +64 -91
  64. package/src/client/components/core/DropDown.js +69 -91
  65. package/src/client/components/core/EventBus.js +92 -0
  66. package/src/client/components/core/EventsUI.js +14 -17
  67. package/src/client/components/core/FileExplorer.js +96 -228
  68. package/src/client/components/core/FullScreen.js +47 -75
  69. package/src/client/components/core/Input.js +24 -69
  70. package/src/client/components/core/Keyboard.js +25 -18
  71. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  72. package/src/client/components/core/LoadingAnimation.js +25 -31
  73. package/src/client/components/core/LogIn.js +41 -41
  74. package/src/client/components/core/LogOut.js +23 -14
  75. package/src/client/components/core/Modal.js +462 -178
  76. package/src/client/components/core/NotificationManager.js +14 -18
  77. package/src/client/components/core/Panel.js +54 -50
  78. package/src/client/components/core/PanelForm.js +25 -125
  79. package/src/client/components/core/Polyhedron.js +110 -214
  80. package/src/client/components/core/PublicProfile.js +39 -32
  81. package/src/client/components/core/Recover.js +48 -44
  82. package/src/client/components/core/Responsive.js +88 -32
  83. package/src/client/components/core/RichText.js +9 -18
  84. package/src/client/components/core/Router.js +24 -3
  85. package/src/client/components/core/SearchBox.js +37 -37
  86. package/src/client/components/core/SignUp.js +39 -30
  87. package/src/client/components/core/SocketIo.js +31 -2
  88. package/src/client/components/core/SocketIoHandler.js +6 -6
  89. package/src/client/components/core/ToggleSwitch.js +8 -20
  90. package/src/client/components/core/ToolTip.js +5 -17
  91. package/src/client/components/core/Translate.js +56 -59
  92. package/src/client/components/core/Validator.js +26 -16
  93. package/src/client/components/core/Wallet.js +15 -26
  94. package/src/client/components/core/Worker.js +163 -27
  95. package/src/client/components/core/windowGetDimensions.js +7 -7
  96. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  97. package/src/client/components/default/CssDefault.js +12 -12
  98. package/src/client/components/default/LogInDefault.js +6 -4
  99. package/src/client/components/default/LogOutDefault.js +6 -4
  100. package/src/client/components/default/RouterDefault.js +47 -0
  101. package/src/client/components/default/SettingsDefault.js +4 -4
  102. package/src/client/components/default/SignUpDefault.js +6 -4
  103. package/src/client/components/default/TranslateDefault.js +3 -3
  104. package/src/client/services/core/core.service.js +17 -49
  105. package/src/client/services/default/default.management.js +159 -267
  106. package/src/client/services/default/default.service.js +10 -16
  107. package/src/client/services/document/document.service.js +14 -19
  108. package/src/client/services/file/file.service.js +8 -13
  109. package/src/client/services/test/test.service.js +8 -13
  110. package/src/client/services/user/guest.service.js +86 -0
  111. package/src/client/services/user/user.management.js +5 -5
  112. package/src/client/services/user/user.service.js +14 -20
  113. package/src/client/ssr/body/404.js +3 -3
  114. package/src/client/ssr/body/500.js +3 -3
  115. package/src/client/ssr/body/CacheControl.js +5 -2
  116. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  117. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  118. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  119. package/src/client/ssr/offline/Maintenance.js +12 -11
  120. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  121. package/src/client/ssr/pages/Test.js +2 -2
  122. package/src/client/sw/core.sw.js +212 -0
  123. package/src/index.js +1 -1
  124. package/src/runtime/express/Dockerfile +4 -4
  125. package/src/runtime/lampp/Dockerfile +8 -7
  126. package/src/runtime/wp/Dockerfile +11 -17
  127. package/src/server/client-build-docs.js +45 -46
  128. package/src/server/client-build.js +334 -60
  129. package/src/server/client-formatted.js +47 -16
  130. package/src/server/conf.js +5 -4
  131. package/src/server/data-query.js +32 -20
  132. package/src/server/dns.js +22 -0
  133. package/src/server/ipfs-client.js +232 -91
  134. package/src/server/process.js +13 -27
  135. package/src/server/start.js +17 -3
  136. package/src/server/valkey.js +141 -235
  137. package/tsconfig.docs.json +15 -0
  138. package/typedoc.json +29 -0
  139. package/jsdoc.json +0 -52
  140. package/src/client/components/core/ColorPalette.js +0 -5267
  141. package/src/client/components/core/JoyStick.js +0 -80
  142. package/src/client/components/default/RoutesDefault.js +0 -49
  143. package/src/client/sw/default.sw.js +0 -127
  144. package/src/client/sw/template.sw.js +0 -84
@@ -1,23 +1,18 @@
1
- import { Badge } from './Badge.js';
2
- import { BtnIcon } from './BtnIcon.js';
3
- import { Css, darkTheme, renderCssAttr, simpleIconsRender, ThemeEvents, Themes } from './Css.js';
4
- import { buildBadgeToolTipMenuOption, Modal, renderViewTitle } from './Modal.js';
1
+ import { Css, darkTheme, simpleIconsRender, ThemeEvents, Themes } from './Css.js';
2
+ import { Modal, renderViewTitle } from './Modal.js';
5
3
  import { listenQueryPathInstance, setQueryPath, closeModalRouteChangeEvent, getProxyPath } from './Router.js';
6
- import { htmls, s, sIframe } from './VanillaJs.js';
7
-
4
+ import { s, sIframe } from './VanillaJs.js';
8
5
  // https://mintlify.com/docs/quickstart
9
-
10
- const Docs = {
11
- RenderModal: async function (type) {
12
- const docData = this.Data.find((d) => d.type === type);
6
+ class Docs {
7
+ static async RenderModal(type) {
8
+ const docData = Docs.Data.find((d) => d.type === type);
13
9
  const ModalId = `modal-docs-${docData.type}`;
14
10
  const { barConfig } = await Themes[Css.currentTheme]();
15
11
  const parentBarMode =
16
12
  Modal.Data['modal-docs'] && Modal.Data['modal-docs'].options.barMode
17
13
  ? Modal.Data['modal-docs'].options.barMode
18
14
  : undefined;
19
-
20
- await Modal.Render({
15
+ await Modal.instance({
21
16
  barConfig,
22
17
  title: renderViewTitle(docData),
23
18
  id: ModalId,
@@ -51,7 +46,15 @@ const Docs = {
51
46
  });
52
47
  const iframeEl = s(`.iframe-${ModalId}`);
53
48
  let swaggerThemeEventKey = null;
49
+ let unbindIframeLayoutSync = null;
54
50
  if (iframeEl) {
51
+ const scheduleViewLayoutSync = () => {
52
+ const sync = () => Modal.syncViewLayout();
53
+ sync();
54
+ setTimeout(sync, 0);
55
+ setTimeout(sync, 120);
56
+ setTimeout(sync, 400);
57
+ };
55
58
  iframeEl.addEventListener('load', () => {
56
59
  try {
57
60
  const iframeWin = iframeEl.contentWindow;
@@ -62,11 +65,34 @@ const Docs = {
62
65
  } catch (e) {
63
66
  // cross-origin or security restriction — safe to ignore
64
67
  }
65
- window.scrollTo(0, 0);
68
+ // Keep the parent window scroll untouched.
69
+ // These modals are fixed-position; scrolling the parent on iframe navigation
70
+ // shifts Chrome layout calculations and leaves view modals offset by 50px.
66
71
  // Bind Shift+K inside the iframe to focus the parent SearchBox (mirrors app-wide shortcut)
67
72
  try {
73
+ const iframeWin = iframeEl.contentWindow;
68
74
  const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
69
75
  if (iframeDoc) {
76
+ if (unbindIframeLayoutSync) unbindIframeLayoutSync();
77
+ const onIframeAnchorClick = (e) => {
78
+ if (e.target && e.target.closest && e.target.closest('a')) {
79
+ scheduleViewLayoutSync();
80
+ }
81
+ };
82
+ const onIframeHashChange = () => scheduleViewLayoutSync();
83
+ const onIframePopState = () => scheduleViewLayoutSync();
84
+ iframeDoc.addEventListener('click', onIframeAnchorClick, true);
85
+ if (iframeWin) {
86
+ iframeWin.addEventListener('hashchange', onIframeHashChange);
87
+ iframeWin.addEventListener('popstate', onIframePopState);
88
+ }
89
+ unbindIframeLayoutSync = () => {
90
+ iframeDoc.removeEventListener('click', onIframeAnchorClick, true);
91
+ if (iframeWin) {
92
+ iframeWin.removeEventListener('hashchange', onIframeHashChange);
93
+ iframeWin.removeEventListener('popstate', onIframePopState);
94
+ }
95
+ };
70
96
  iframeDoc.addEventListener('keydown', (e) => {
71
97
  if (e.shiftKey && e.key.toLowerCase() === 'k') {
72
98
  e.preventDefault();
@@ -85,48 +111,30 @@ const Docs = {
85
111
  } catch (e) {
86
112
  // cross-origin or security restriction — safe to ignore
87
113
  }
114
+ scheduleViewLayoutSync();
88
115
  });
89
-
90
116
  if (type === 'src') {
91
117
  swaggerThemeEventKey = `jsdocs-iframe-${ModalId}`;
92
-
93
118
  const applyJsDocsTheme = (isDark) => {
94
119
  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
- }
120
+ const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
121
+ if (!iframeDoc || !iframeDoc.documentElement) return;
122
+ // TypeDoc built-in theme: data-theme on <html>, stored as 'tsd-theme' in localStorage
123
+ iframeDoc.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
124
+ iframeEl.contentWindow?.localStorage?.setItem('tsd-theme', isDark ? 'dark' : 'light');
113
125
  } catch (e) {
114
126
  // cross-origin or security restriction — safe to ignore
115
127
  }
116
128
  };
117
-
118
129
  // Apply current theme as soon as the iframe content is ready
119
130
  iframeEl.addEventListener('load', () => applyJsDocsTheme(darkTheme));
120
-
121
131
  // Keep in sync whenever the parent page theme changes
122
132
  ThemeEvents[swaggerThemeEventKey] = () => {
123
133
  if (s(`.iframe-${ModalId}`)) applyJsDocsTheme(darkTheme);
124
134
  };
125
135
  }
126
-
127
136
  if (type === 'api') {
128
137
  swaggerThemeEventKey = `swagger-iframe-${ModalId}`;
129
-
130
138
  const applySwaggerTheme = (isDark) => {
131
139
  try {
132
140
  const iframeDoc = iframeEl.contentDocument || iframeEl.contentWindow?.document;
@@ -143,10 +151,8 @@ const Docs = {
143
151
  // cross-origin or security restriction — safe to ignore
144
152
  }
145
153
  };
146
-
147
154
  // Apply current theme as soon as the iframe content is ready
148
155
  iframeEl.addEventListener('load', () => applySwaggerTheme(darkTheme));
149
-
150
156
  // Keep in sync whenever the parent page theme changes
151
157
  ThemeEvents[swaggerThemeEventKey] = () => {
152
158
  if (s(`.iframe-${ModalId}`)) applySwaggerTheme(darkTheme);
@@ -159,7 +165,6 @@ const Docs = {
159
165
  const barHeight = barEl ? barEl.offsetHeight : Modal.headerTitleHeight;
160
166
  s(`.iframe-${ModalId}`).style.height = `${s(`.${ModalId}`).offsetHeight - barHeight}px`;
161
167
  }
162
-
163
168
  if (type.match('coverage')) {
164
169
  simpleIconsRender(`.doc-icon-coverage`);
165
170
  simpleIconsRender(`.doc-icon-coverage-link`);
@@ -167,11 +172,12 @@ const Docs = {
167
172
  };
168
173
  Modal.Data[ModalId].onObserverListener[ModalId]();
169
174
  Modal.Data[ModalId].onCloseListener[ModalId] = () => {
175
+ if (unbindIframeLayoutSync) unbindIframeLayoutSync();
170
176
  if (swaggerThemeEventKey) delete ThemeEvents[swaggerThemeEventKey];
171
177
  closeModalRouteChangeEvent({ closedId: ModalId });
172
178
  };
173
- },
174
- Data: [
179
+ }
180
+ static Data = [
175
181
  {
176
182
  type: 'repo',
177
183
  icon: html`<i class="fab fa-github"></i>`,
@@ -238,38 +244,36 @@ const Docs = {
238
244
  if (s(`.doc-icon-coverage-link`)) setTimeout(() => simpleIconsRender(`.doc-icon-coverage-link`));
239
245
  },
240
246
  },
241
- ],
242
- Tokens: {},
243
- Init: async function (options = {}) {
247
+ ];
248
+ static Tokens = {};
249
+ static async instance(options = {}) {
244
250
  const { idModal } = options;
245
- this.Tokens[idModal] = options;
251
+ Docs.Tokens[idModal] = options;
246
252
  setTimeout(() => {
247
253
  s(`.btn-docs-src`).onclick = async () => {
248
254
  setQueryPath({ path: 'docs', queryPath: 'src' });
249
- await this.RenderModal('src');
255
+ await Docs.RenderModal('src');
250
256
  };
251
257
  s(`.btn-docs-api`).onclick = async () => {
252
258
  setQueryPath({ path: 'docs', queryPath: 'api' });
253
- await this.RenderModal('api');
259
+ await Docs.RenderModal('api');
254
260
  };
255
261
  s(`.btn-docs-coverage`).onclick = async () => {
256
262
  setQueryPath({ path: 'docs', queryPath: 'coverage' });
257
- await this.RenderModal('coverage');
263
+ await Docs.RenderModal('coverage');
258
264
  };
259
-
260
265
  s(`.btn-docs-coverage-link`).onclick = () => {
261
- const docData = this.Data.find((d) => d.type === 'coverage-link');
266
+ const docData = Docs.Data.find((d) => d.type === 'coverage-link');
262
267
  location.href = docData.url();
263
268
  };
264
269
  s(`.btn-docs-repo`).onclick = () => {
265
- const docData = this.Data.find((d) => d.type === 'repo');
270
+ const docData = Docs.Data.find((d) => d.type === 'repo');
266
271
  location.href = docData.url();
267
272
  };
268
273
  s(`.btn-docs-demo`).onclick = () => {
269
- const docData = this.Data.find((d) => d.type === 'demo');
274
+ const docData = Docs.Data.find((d) => d.type === 'demo');
270
275
  location.href = docData.url();
271
276
  };
272
-
273
277
  listenQueryPathInstance({
274
278
  id: options.idModal,
275
279
  routeId: 'docs',
@@ -283,44 +287,15 @@ const Docs = {
283
287
  },
284
288
  });
285
289
  });
286
- let docMenuRender = '';
287
- for (const docData of this.Data) {
290
+ // Register theme events for items that have them (Docs-specific concern)
291
+ for (const docData of Docs.Data) {
288
292
  if (docData.themeEvent) {
289
293
  ThemeEvents[`doc-icon-${docData.type}`] = docData.themeEvent;
290
294
  setTimeout(ThemeEvents[`doc-icon-${docData.type}`]);
291
295
  }
292
- let tabHref, style, labelStyle;
293
- switch (docData.type) {
294
- case 'repo':
295
- case 'coverage-link':
296
- style = renderCssAttr({ style: { height: '45px' } });
297
- labelStyle = renderCssAttr({ style: { top: '8px', left: '9px' } });
298
- break;
299
-
300
- default:
301
- break;
302
- }
303
- tabHref = docData.url();
304
- const subMenuIcon =
305
- options.subMenuIcon && typeof options.subMenuIcon === 'function'
306
- ? options.subMenuIcon(docData.type)
307
- : docData.icon;
308
- docMenuRender += html`
309
- ${await BtnIcon.Render({
310
- class: `in wfa main-btn-menu submenu-btn btn-docs btn-docs-${docData.type}`,
311
- label: html`<span class="inl menu-btn-icon">${subMenuIcon}</span
312
- ><span class="menu-label-text menu-label-text-docs"> ${docData.text} </span>`,
313
- tabHref,
314
- tooltipHtml: await Badge.Render(buildBadgeToolTipMenuOption(docData.text, 'right')),
315
- useMenuBtn: true,
316
- })}
317
- `;
318
296
  }
319
- // s(`.menu-btn-container-children`).classList.remove('hide');
320
- // htmls(`.nav-path-display-${'modal-menu'}`, location.pathname);
321
-
322
- htmls('.menu-btn-container-children-docs', docMenuRender);
323
-
297
+ // Build submenu items and populate — submenu system is owned by Modal
298
+ Modal.subMenuPopulate('docs', await Modal.buildSubMenuItemsHtml('docs', Docs.Data, options));
324
299
  {
325
300
  const docsData = [
326
301
  {
@@ -360,7 +335,6 @@ const Docs = {
360
335
  description: 'Join our developer community',
361
336
  },
362
337
  ];
363
-
364
338
  return html`
365
339
  <style>
366
340
  .docs-landing {
@@ -499,7 +473,6 @@ const Docs = {
499
473
  </div>
500
474
  `;
501
475
  }
502
- },
503
- };
504
-
476
+ }
477
+ }
505
478
  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 };