@nutin/cli 1.2.3 → 1.3.1

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 (80) hide show
  1. package/CHANGELOG.md +70 -1
  2. package/README.md +25 -18
  3. package/lib/index.mjs +23 -20
  4. package/lib/src/context-builder.mjs +40 -0
  5. package/lib/src/file-generator.mjs +26 -126
  6. package/lib/src/git-manager.mjs +1 -1
  7. package/lib/src/json-generator.mjs +31 -0
  8. package/lib/src/package-manager.mjs +21 -18
  9. package/lib/src/print.mjs +1 -0
  10. package/lib/src/project-generator.mjs +53 -0
  11. package/lib/src/prompts.mjs +16 -75
  12. package/lib/src/template-compiler.mjs +1 -1
  13. package/lib/src/utils.mjs +9 -9
  14. package/lib/src/version.mjs +1 -0
  15. package/package.json +1 -1
  16. package/templates/base/.gitignore.hbs +1 -0
  17. package/templates/base/builder.config.js.hbs +8 -0
  18. package/templates/base/src/app/globals.d.ts.hbs +12 -8
  19. package/templates/base/src/app/main.ts.hbs +2 -1
  20. package/templates/base/src/app/views/home/home.view.test.js.hbs +26 -0
  21. package/templates/base/src/app/views/home/home.view.ts.hbs +2 -2
  22. package/templates/base/src/app/views/not-found/not-found.view.ts.hbs +1 -1
  23. package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +46 -26
  24. package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +36 -22
  25. package/templates/base/src/core/base-classes/base-component/helpers/children.helper.ts.hbs +4 -4
  26. package/templates/base/src/core/base-classes/base-component/helpers/dom.helper.ts.hbs +60 -13
  27. package/templates/base/src/core/base-classes/base-component/helpers/event.helper.ts.hbs +16 -17
  28. package/templates/base/src/core/base-classes/base-component/helpers/security.helper.ts.hbs +40 -8
  29. package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +16 -16
  30. package/templates/base/src/core/base-classes/component/component.ts.hbs +20 -24
  31. package/templates/base/src/core/base-classes/component/helpers/config.helper.ts.hbs +19 -2
  32. package/templates/base/src/core/base-classes/component/helpers/data-binding.helper.ts.hbs +2 -2
  33. package/templates/base/src/core/base-classes/service/service.ts.hbs +35 -12
  34. package/templates/base/src/core/base-classes/view/view.ts.hbs +6 -6
  35. package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +13 -13
  36. package/templates/base/src/core/services/http-client/helpers/http-builder.helper.ts.hbs +18 -18
  37. package/templates/base/src/core/services/http-client/helpers/http-manager.helper.ts.hbs +24 -24
  38. package/templates/base/src/core/services/http-client/http-client.ts.hbs +19 -19
  39. package/templates/base/src/core/services/pipe-registry/pipe-registry.ts.hbs +6 -6
  40. package/templates/base/src/core/services/router/helpers/navigation-manager.helper.ts.hbs +15 -14
  41. package/templates/base/src/core/services/router/helpers/route-guard-manager.helper.ts.hbs +22 -22
  42. package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +4 -36
  43. package/templates/base/src/core/services/router/router.ts.hbs +45 -46
  44. package/templates/base/src/core/services/store/store.ts.hbs +6 -6
  45. package/templates/base/src/core/utils/anchor-manager/anchor-manager.utils.ts.hbs +41 -15
  46. package/templates/base/src/core/utils/button-manager/button-manager.utils.ts.hbs +40 -19
  47. package/templates/base/src/core/utils/helpers/attributes.helper.ts.hbs +11 -5
  48. package/templates/base/src/libs/pipes/pipes.ts.hbs +1 -1
  49. package/templates/base/src/libs/popover/helpers/focus-trap.helper.ts.hbs +197 -0
  50. package/templates/base/src/libs/popover/helpers/popover-dom.helper.ts.hbs +75 -0
  51. package/templates/base/src/libs/popover/popover.ts.hbs +89 -96
  52. package/templates/base/src/libs/snackbar/snackbar.ts.hbs +7 -4
  53. package/templates/base/src/styles/base/_variables.scss.hbs +1 -0
  54. package/templates/base/src/styles/core/_anchors.scss.hbs +10 -0
  55. package/templates/base/src/styles/core/_index.scss.hbs +6 -0
  56. package/templates/base/src/styles/core/_popover.scss.hbs +96 -0
  57. package/templates/base/src/styles/core/_snackbar.scss.hbs +50 -0
  58. package/templates/base/src/styles/main.scss.hbs +1 -1
  59. package/templates/base/tools/builder/builder.js.hbs +9 -3
  60. package/templates/base/tools/builder/core/add-tags.js.hbs +46 -0
  61. package/templates/base/tools/builder/core/copy-static.js.hbs +35 -35
  62. package/templates/base/tools/builder/core/finalize-build.js.hbs +6 -6
  63. package/templates/base/tools/builder/core/hash-files.js.hbs +52 -0
  64. package/templates/base/tools/builder/core/validate-html.js.hbs +12 -32
  65. package/templates/base/tools/utils/get-files-recursive.js.hbs +17 -8
  66. package/templates/features/deployHelper/tools/builder/core/compress-files.js.hbs +56 -0
  67. package/templates/{base → features/deployHelper}/tools/deployment/Dockerfile.hbs +12 -4
  68. package/templates/{base → features/deployHelper}/tools/deployment/nginx.conf.hbs +30 -2
  69. package/templates/features/i18n/src/core/base-classes/base-component/helpers/i18n.helper.ts.hbs +4 -6
  70. package/templates/features/i18n/src/core/services/i18n/i18n.ts.hbs +76 -52
  71. package/templates/features/stylinNutin/src/styles/core/_utilities.scss.hbs +31 -0
  72. package/templates/features/template/src/app/views/home/home.view.html.hbs +1 -1
  73. package/templates/features/template/tools/builder/core/merge-templates.js.hbs +2 -2
  74. package/templates/features/testinNutin/testin-nutin/core/src/utils/test-queue.js.hbs +2 -2
  75. package/templates/features/testinNutin/testin-nutin/runner.js.hbs +1 -0
  76. package/templates/features/testinNutin/testin-nutin.config.js.hbs +2 -2
  77. package/templates/base/src/styles/core/_libs.scss.hbs +0 -132
  78. package/templates/base/tools/deployment/gzip.conf.hbs +0 -19
  79. package/templates/features/i18n/src/app/views/home/locales/fr.json.hbs +0 -9
  80. /package/templates/{base → features/deployHelper}/.dockerignore.hbs +0 -0
@@ -4,20 +4,13 @@ import { {{#if i18n}}I18nService, {{/if}}View } from "../../../index.js";
4
4
  * Navigation - handles path normalization and history management
5
5
  */
6
6
  export class NavigationManager {
7
- static normalizePath(path: string): string {
8
- const url = new URL(path, window.location.origin);
7
+ public static normalizePath(path: string): string {
8
+ const collapsed = path.replace(/\/\/+/g, '/');
9
+ const url = new URL(collapsed, window.location.origin);
9
10
  return (url.pathname || '/').replace(/\/+$/, '') || '/';
10
11
  }
11
12
 
12
- static shouldPushState(
13
- pushState: boolean,
14
- currentPath: string,
15
- normalizedPath: string
16
- ): boolean {
17
- return pushState && currentPath !== normalizedPath;
18
- }
19
-
20
- static updateHistory(
13
+ public static updateHistory(
21
14
  normalizedPath: string,
22
15
  currentPath: string,
23
16
  pushState: boolean
@@ -27,7 +20,7 @@ export class NavigationManager {
27
20
  }
28
21
  }
29
22
 
30
- static updateMetaContent(currentView: View): void {
23
+ public static updateMetaContent(currentView: View): void {
31
24
  if (!currentView.shouldUpdateMetaContent()) return;
32
25
 
33
26
  const strippedName = currentView.viewName.replace('-view', '');
@@ -44,11 +37,11 @@ export class NavigationManager {
44
37
  {{/if}}
45
38
  }
46
39
 
47
- static getCurrentPath(): string {
40
+ public static getCurrentPath(): string {
48
41
  return this.normalizePath(window.location.pathname);
49
42
  }
50
43
 
51
- static matchPattern(pattern: string, path: string): Record<string, string> | null {
44
+ public static matchPattern(pattern: string, path: string): Record<string, string> | null {
52
45
  const paramNames: string[] = [];
53
46
 
54
47
  const regexPattern = pattern
@@ -75,4 +68,12 @@ export class NavigationManager {
75
68
  });
76
69
  return params;
77
70
  }
71
+
72
+ private static shouldPushState(
73
+ pushState: boolean,
74
+ currentPath: string,
75
+ normalizedPath: string
76
+ ): boolean {
77
+ return pushState && currentPath !== normalizedPath;
78
+ }
78
79
  }
@@ -25,31 +25,11 @@ export type RouteConfig = (() => View) | {
25
25
  export type Routes = Record<string, RouteConfig>;
26
26
 
27
27
  export class RouteGuardsManager {
28
- static getViewConstructor(routeConfig: RouteConfig): () => View {
28
+ public static getViewConstructor(routeConfig: RouteConfig): () => View {
29
29
  return typeof routeConfig === 'function' ? routeConfig : routeConfig.view;
30
30
  }
31
31
 
32
- static getRouteGuards(routeConfig: RouteConfig): RouteGuard[] {
33
- return typeof routeConfig === 'function' ? [] : (routeConfig.guards || []);
34
- }
35
-
36
- /**
37
- * Runs all guards in sequence with route parameters and returns the result
38
- */
39
- static async runGuards(
40
- guards: RouteGuard[],
41
- params: Record<string, string>
42
- ): Promise<boolean | string> {
43
- for (const guard of guards) {
44
- const result = await guard(params);
45
- if (result !== true) {
46
- return result; // false or redirect path
47
- }
48
- }
49
- return true;
50
- }
51
-
52
- static async processRouteGuards(
32
+ public static async processRouteGuards(
53
33
  routeConfig: RouteConfig,
54
34
  targetPath: string,
55
35
  params: Record<string, string> = {}
@@ -71,4 +51,24 @@ export class RouteGuardsManager {
71
51
  const viewConstructor = this.getViewConstructor(routeConfig);
72
52
  return { allowed: true, viewConstructor };
73
53
  }
54
+
55
+ private static getRouteGuards(routeConfig: RouteConfig): RouteGuard[] {
56
+ return typeof routeConfig === 'function' ? [] : (routeConfig.guards || []);
57
+ }
58
+
59
+ /**
60
+ * Runs all guards in sequence with route parameters and returns the result
61
+ */
62
+ private static async runGuards(
63
+ guards: RouteGuard[],
64
+ params: Record<string, string>
65
+ ): Promise<boolean | string> {
66
+ for (const guard of guards) {
67
+ const result = await guard(params);
68
+ if (result !== true) {
69
+ return result; // false or redirect path
70
+ }
71
+ }
72
+ return true;
73
+ }
74
74
  }
@@ -5,7 +5,7 @@ import { View, AppEventBus } from '../../../index.js';
5
5
  */
6
6
  export class ViewRenderManager {
7
7
  {{#if transition}}
8
- static renderAnimatedView(view: View): void {
8
+ public static renderAnimatedView(view: View): void {
9
9
  const container = document.getElementById('app');
10
10
  const el = view.render();
11
11
  el.classList.add('app-view-transition');
@@ -17,7 +17,7 @@ export class ViewRenderManager {
17
17
  }
18
18
 
19
19
  {{/if}}
20
- static async transitionOutCurrentView(currentView: View | null): Promise<null> {
20
+ public static async transitionOutCurrentView(currentView: View | null): Promise<null> {
21
21
  if (!currentView) return null;
22
22
  {{#if transition}}
23
23
  const container = document.getElementById('app');
@@ -33,7 +33,7 @@ export class ViewRenderManager {
33
33
  return null;
34
34
  }
35
35
 
36
- static renderNewView(
36
+ public static renderNewView(
37
37
  viewConstructor: () => View,
38
38
  params: Record<string, string> = {}
39
39
  ): View {
@@ -49,43 +49,11 @@ export class ViewRenderManager {
49
49
  view.render();
50
50
 
51
51
  {{/if}}
52
- this.cleanupOptionalContent();
53
-
54
52
  this.emitEvent('view-mount', view.viewName);
55
53
  return view;
56
54
  }
57
55
 
58
- static cleanupOptionalContent() {
59
- const isEmpty = (el: HTMLElement): boolean => {
60
- const attrName = el.dataset.optional?.trim();
61
-
62
- if (attrName && attrName !== "") {
63
- const attrValue = el.getAttribute(attrName);
64
- return !attrValue || attrValue.trim() === "" || attrValue === "undefined";
65
- }
66
-
67
- if (el instanceof HTMLImageElement) {
68
- return !el.src || el.src.trim() === "";
69
- }
70
-
71
- if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
72
- return !el.value?.trim();
73
- }
74
-
75
- if (el instanceof HTMLMediaElement || el instanceof HTMLSourceElement) {
76
- return !el.getAttribute("src");
77
- }
78
-
79
- const content = el.textContent?.trim();
80
- return !content || content === "undefined";
81
- };
82
-
83
- document.querySelectorAll<HTMLElement>("[data-optional]").forEach(el => {
84
- if (isEmpty(el)) el.remove();
85
- });
86
- }
87
-
88
- static emitEvent(event: EventKey, viewName: string): void {
56
+ private static emitEvent(event: EventKey, viewName: string): void {
89
57
  AppEventBus.emit(event, viewName);
90
58
  }
91
59
  {{#if transition}}
@@ -5,7 +5,6 @@ import { NavigationManager } from './helpers/navigation-manager.helper.js';
5
5
 
6
6
  // centralized export
7
7
  export { Routes, RouteGuard } from './helpers/route-guard-manager.helper.js';
8
- export { ViewRenderManager } from './helpers/view-render-manager.helper.js';
9
8
 
10
9
  /**
11
10
  * IRouter is a type alias for the instance of Router, not a true interface
@@ -26,6 +25,51 @@ class Router {
26
25
  this.navigate(NavigationManager.getCurrentPath());
27
26
  }
28
27
 
28
+ public async reload(): Promise<void> {
29
+ const currentRoute = NavigationManager.getCurrentPath();
30
+ await this.navigate(currentRoute, false);
31
+ }
32
+
33
+ public async navigate(path: string | '', pushState: boolean = true): Promise<void> {
34
+ const normalizedPath = NavigationManager.normalizePath(path);
35
+ const currentPath = NavigationManager.getCurrentPath();
36
+
37
+ // Try to match the route with parameters
38
+ const routeMatch = this.matchRoute(normalizedPath);
39
+
40
+ if (!routeMatch) {
41
+ await this.handleNotFound(normalizedPath, currentPath, pushState);
42
+ return;
43
+ }
44
+
45
+ const guardResult = await this.handleGuards(
46
+ normalizedPath,
47
+ routeMatch.route,
48
+ routeMatch.params,
49
+ pushState
50
+ );
51
+
52
+ if (!guardResult) return;
53
+
54
+ this._currentView = await ViewRenderManager.transitionOutCurrentView(this._currentView);
55
+ this._currentParams = routeMatch.params;
56
+ this._currentView = ViewRenderManager.renderNewView(
57
+ guardResult.viewConstructor!,
58
+ routeMatch.params
59
+ );
60
+
61
+ NavigationManager.updateMetaContent(this._currentView);
62
+ NavigationManager.updateHistory(normalizedPath, currentPath, pushState);
63
+ }
64
+
65
+ public getCurrentParams(): Record<string, string> {
66
+ return { ...this._currentParams };
67
+ }
68
+
69
+ public getParam(key: string): string | undefined {
70
+ return this._currentParams[key];
71
+ }
72
+
29
73
  private initializeEventListeners(): void {
30
74
  window.addEventListener('popstate', () => this.handlePopState());
31
75
  AppEventBus.subscribe('navigate', (path: string) => this.navigate(path));
@@ -95,51 +139,6 @@ class Router {
95
139
 
96
140
  return guardResult;
97
141
  }
98
-
99
- public async reload(): Promise<void> {
100
- const currentRoute = NavigationManager.getCurrentPath();
101
- await this.navigate(currentRoute, false);
102
- }
103
-
104
- public async navigate(path: string | '', pushState: boolean = true): Promise<void> {
105
- const normalizedPath = NavigationManager.normalizePath(path);
106
- const currentPath = NavigationManager.getCurrentPath();
107
-
108
- // Try to match the route with parameters
109
- const routeMatch = this.matchRoute(normalizedPath);
110
-
111
- if (!routeMatch) {
112
- await this.handleNotFound(normalizedPath, currentPath, pushState);
113
- return;
114
- }
115
-
116
- const guardResult = await this.handleGuards(
117
- normalizedPath,
118
- routeMatch.route,
119
- routeMatch.params,
120
- pushState
121
- );
122
-
123
- if (!guardResult) return;
124
-
125
- this._currentView = await ViewRenderManager.transitionOutCurrentView(this._currentView);
126
- this._currentParams = routeMatch.params;
127
- this._currentView = ViewRenderManager.renderNewView(
128
- guardResult.viewConstructor!,
129
- routeMatch.params
130
- );
131
-
132
- NavigationManager.updateMetaContent(this._currentView);
133
- NavigationManager.updateHistory(normalizedPath, currentPath, pushState);
134
- }
135
-
136
- public getCurrentParams(): Record<string, string> {
137
- return { ...this._currentParams };
138
- }
139
-
140
- public getParam(key: string): string | undefined {
141
- return this._currentParams[key];
142
- }
143
142
  }
144
143
 
145
144
  export const AppRouter = (routes: Routes) => new Router(routes);
@@ -10,10 +10,6 @@ export class Store extends Service<Store> {
10
10
  this.registerCleanup(this.clear);
11
11
  }
12
12
 
13
- protected onDestroy(): void {
14
- this.clear();
15
- }
16
-
17
13
  public set<T = any>(key: string, value: T): void {
18
14
  this._state[key] = value;
19
15
  AppEventBus.emit(`store:${key}`, value);
@@ -27,12 +23,16 @@ export class Store extends Service<Store> {
27
23
  AppEventBus.subscribe(`store:${key}`, callback);
28
24
  }
29
25
 
26
+ public unsubscribe<T = any>(key: string, callback: (value: T) => void): void {
27
+ AppEventBus.off(`store:${key}`, callback);
28
+ }
29
+
30
30
  public clear(): void {
31
31
  this._state = {};
32
32
  }
33
33
 
34
- public unsubscribe<T = any>(key: string, callback: (value: T) => void): void {
35
- AppEventBus.off(`store:${key}`, callback);
34
+ protected onDestroy(): void {
35
+ this.clear();
36
36
  }
37
37
  }
38
38
 
@@ -3,9 +3,9 @@ import { AttributesHelper, IAttributesConfig } from "../helpers/attributes.helpe
3
3
  /**
4
4
  * ```typescript
5
5
  interface IAttributesConfig {
6
- {{#if i18n}}
6
+ {{#if i18n}}
7
7
  i18nKey?: string;
8
- {{/if}}
8
+ {{/if}}
9
9
  textContent?: string;
10
10
  className?: string;
11
11
  style?: string;
@@ -42,30 +42,22 @@ export class AnchorManager {
42
42
  target.appendChild(container);
43
43
  }
44
44
 
45
- private createAnchorContainer() {
46
- const container = document.createElement('div');
45
+ private createAnchorContainer(): HTMLSpanElement {
46
+ const container = document.createElement('span');
47
47
  const anchor = this.createAnchor();
48
-
49
48
  container.appendChild(anchor);
50
-
51
49
  return container;
52
50
  }
53
51
 
54
52
  private createAnchor(): HTMLElement {
55
53
  const anchor = document.createElement('a');
56
-
57
54
  this.setAnchorRef(anchor);
58
-
59
- AttributesHelper.setContent(anchor, this.config);
60
- AttributesHelper.setStyle(anchor, this.config);
61
- AttributesHelper.setPipes(anchor, this.config);
62
-
55
+ AttributesHelper.setAttributes(anchor, this.config);
63
56
  this.handleClick(anchor);
64
-
65
57
  return anchor;
66
58
  }
67
59
 
68
- private setAnchorRef(anchor: HTMLAnchorElement) {
60
+ private setAnchorRef(anchor: HTMLAnchorElement): void {
69
61
  anchor.setAttribute('href', this.config.href);
70
62
  if (this.config.target) {
71
63
  anchor.setAttribute('target', this.config.target);
@@ -81,7 +73,41 @@ export class AnchorManager {
81
73
  anchor?.addEventListener('click', (e) => {
82
74
  e.preventDefault();
83
75
  const target = document.getElementById(id);
84
- target?.scrollIntoView({ behavior: 'smooth' });
76
+ this.scrollToTarget(target);
77
+ this.accessibilityFeatures(target, id);
85
78
  });
86
79
  }
80
+
81
+ private scrollToTarget(target: HTMLElement | null): void {
82
+ const targetPosition = target?.offsetTop;
83
+ window.scrollTo({
84
+ top: targetPosition,
85
+ behavior: 'smooth'
86
+ })
87
+ }
88
+
89
+ private accessibilityFeatures(target: HTMLElement | null, id: string) {
90
+ this.setFocus(target);
91
+ this.setAriaLive(target, id);
92
+ }
93
+
94
+ private setFocus(target: HTMLElement | null): void {
95
+ setTimeout(() => {
96
+ target?.setAttribute('tabindex', '-1');
97
+ target?.focus({ preventScroll: true });
98
+ // Remove tabindex after focus
99
+ target?.addEventListener('blur', () => {
100
+ target?.removeAttribute('tabindex');
101
+ }, { once: true });
102
+ }, 100);
103
+ }
104
+
105
+ private setAriaLive(target: HTMLElement | null, id: string): void {
106
+ const announcement = document.createElement('div');
107
+ announcement.setAttribute('aria-live', 'polite');
108
+ announcement.className = 'sr-only';
109
+ announcement.textContent = `Navigated to ${ target?.textContent || id }`;
110
+ document.body.appendChild(announcement);
111
+ setTimeout(() => document.body.removeChild(announcement), 1000);
112
+ }
87
113
  }
@@ -2,6 +2,7 @@ import { AttributesHelper, IAttributesConfig } from "../helpers/attributes.helpe
2
2
 
3
3
  export interface BaseButton extends IAttributesConfig {
4
4
  callback: () => void;
5
+ label?: string;
5
6
  }
6
7
 
7
8
  export interface ButtonContainerOptions {
@@ -22,10 +23,6 @@ export interface ButtonContainerOptions {
22
23
  pipes?: string;
23
24
  }
24
25
 
25
- interface BaseButton extends IAttributesConfig {
26
- callback: () => void;
27
- }
28
-
29
26
  interface ButtonContainerOptions {
30
27
  containerClassName?: string;
31
28
  containerStyles?: string;
@@ -61,47 +58,71 @@ export class ButtonManager {
61
58
  container.style = this.containerOptions.containerStyles || '';
62
59
 
63
60
  this.buttons.forEach((config, index) => {
64
- const button = this.createButton(config, index);
65
- container.appendChild(button);
66
- this.bindButtonCallback(config, index);
61
+ if (config.label) {
62
+ return this.createButtonWithCheckbox(config, index, container);
63
+ } else {
64
+ const button = this.createButton(config, index);
65
+ container.appendChild(button);
66
+ this.bindButtonCallback(config, index);
67
+ }
67
68
  });
68
69
 
69
70
  return container;
70
71
  }
71
72
 
73
+ public appendTo(target: HTMLElement): void {
74
+ const container = this.createButtonContainer();
75
+ if (container) {
76
+ target.appendChild(container);
77
+ }
78
+ }
79
+
80
+ private createButtonWithCheckbox(config: BaseButton, index: number, container: HTMLElement): HTMLElement {
81
+ if (!config.label) return container;
82
+ const checkbox = document.createElement('input');
83
+ checkbox.setAttribute('type', 'checkbox');
84
+ checkbox.setAttribute('id', config.label);
85
+ checkbox.hidden = true;
86
+ container.appendChild(checkbox);
87
+
88
+ const label = document.createElement('label');
89
+ label.setAttribute('for', config.label);
90
+
91
+ const button = this.createButton(config, index);
92
+ label.appendChild(button);
93
+
94
+ container.appendChild(label);
95
+ this.bindButtonCallback(config, index, checkbox);
96
+ return container;
97
+ }
98
+
72
99
  private createButton(config: BaseButton, index: number): HTMLElement {
73
100
  const button = document.createElement('button');
74
101
 
75
- AttributesHelper.setContent(button, config);
76
- AttributesHelper.setStyle(button, config);
77
- AttributesHelper.setPipes(button, config);
102
+ AttributesHelper.setAttributes(button, config);
78
103
 
79
104
  this.setDataEvent(button, index);
80
105
 
81
106
  return button;
82
107
  }
83
108
 
84
- private setDataEvent(button: HTMLButtonElement, index: number) {
109
+ private setDataEvent(button: HTMLButtonElement, index: number): void {
85
110
  button.setAttribute('data-event', `click:onButtonClick_${index}`);
86
111
  }
87
112
 
88
- private bindButtonCallback(config: BaseButton, index: number): void {
113
+ private bindButtonCallback(config: BaseButton, index: number, checkbox?: HTMLInputElement): void {
89
114
  const methodName = `onButtonClick_${index}`;
90
115
 
91
116
  this.component[methodName] = () => {
92
117
  if (typeof config.callback === 'function') {
93
118
  config.callback();
119
+ if (checkbox) {
120
+ checkbox.checked = !checkbox.checked;
121
+ }
94
122
  }
95
123
  };
96
124
  }
97
125
 
98
- public appendTo(target: HTMLElement): void {
99
- const container = this.createButtonContainer();
100
- if (container) {
101
- target.appendChild(container);
102
- }
103
- }
104
-
105
126
  /** Note : doesn't track container
106
127
  */
107
128
  public updateButtons(newButtons: BaseButton[]): void {
@@ -10,21 +10,27 @@ export interface IAttributesConfig {
10
10
  }
11
11
 
12
12
  export class AttributesHelper {
13
- static setContent(element: HTMLElement, config: IAttributesConfig) {
13
+ public static setAttributes(element: HTMLElement, config: IAttributesConfig): void {
14
+ this.setContent(element, config);
15
+ this.setPipes(element, config);
16
+ this.setStyle(element, config);
17
+ }
18
+
19
+ private static setContent(element: HTMLElement, config: IAttributesConfig): void {
14
20
  element.textContent = config.textContent || '';
15
- {{#if i18n}}
21
+ {{#if i18n}}
16
22
  if (config.i18nKey) {
17
23
  element.setAttribute('data-i18n', config.i18nKey);
18
24
  }
19
- {{/if}}
25
+ {{/if}}
20
26
  }
21
27
 
22
- static setPipes(element: HTMLElement, config: IAttributesConfig) {
28
+ private static setPipes(element: HTMLElement, config: IAttributesConfig): void {
23
29
  if (!config.pipes) return;
24
30
  element.setAttribute('data-pipe', config.pipes);
25
31
  }
26
32
 
27
- static setStyle(element: HTMLElement, config: IAttributesConfig) {
33
+ private static setStyle(element: HTMLElement, config: IAttributesConfig): void {
28
34
  const classes = [
29
35
  config.className
30
36
  ].filter(Boolean).join(' ');
@@ -1,6 +1,6 @@
1
1
  import { AppPipeRegistry } from "../../core/index.js";
2
2
 
3
- export const registerPipes = () => {
3
+ export const registerPipes = (): void => {
4
4
  // currency
5
5
  AppPipeRegistry.register('currency', (
6
6
  value: number,