ngx-dev-toolbar 0.0.2-2 → 0.0.2-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/components/icons/icon.component.ts +42 -57
- package/src/components/icons/icon.models.ts +2 -1
- package/src/components/icons/translate-icon.component.ts +23 -0
- package/src/components/select/select.component.scss +25 -6
- package/src/components/tool-button/tool-button.component.scss +3 -3
- package/src/components/tool-button/tool-button.component.ts +3 -4
- package/src/components/toolbar-tool/toolbar-tool.component.ts +13 -5
- package/src/components/window/window.component.scss +17 -14
- package/src/components/window/window.models.ts +1 -1
- package/src/dev-toolbar-state.service.ts +6 -1
- package/src/dev-toolbar.component.scss +2 -1
- package/src/dev-toolbar.component.ts +19 -16
- package/src/index.ts +8 -0
- package/src/models/dev-tools.interface.ts +19 -0
- package/src/styles.scss +2 -0
- package/src/tools/feature-flags-tool/feature-flags-internal.service.ts +96 -0
- package/src/tools/feature-flags-tool/feature-flags-tool.component.ts +10 -3
- package/src/tools/feature-flags-tool/feature-flags.service.ts +13 -97
- package/src/tools/{settings-tool/settings-tool.component.ts → home-tool/home-tool.component.ts} +21 -25
- package/src/tools/language-tool/language-internal.service.ts +51 -0
- package/src/tools/language-tool/language-tool.component.scss +7 -0
- package/src/tools/language-tool/language-tool.component.ts +75 -0
- package/src/tools/language-tool/language.models.ts +4 -0
- package/src/tools/language-tool/language.service.ts +26 -0
- package/src/tools/index.ts +0 -5
- /package/src/tools/{settings-tool/settings-tool.component.scss → home-tool/home-tool.component.scss} +0 -0
- /package/src/tools/{settings-tool → home-tool}/settings.models.ts +0 -0
- /package/src/tools/{settings-tool → home-tool}/settings.service.spec.ts +0 -0
- /package/src/tools/{settings-tool → home-tool}/settings.service.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-dev-toolbar",
|
|
3
|
-
"version": "0.0.2-
|
|
3
|
+
"version": "0.0.2-4",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": "^18.0.0 || ^19.0.0",
|
|
6
6
|
"vite": "^5.0.0",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"@nx/vite": "20.3.0",
|
|
9
9
|
"@angular/forms": "^18.0.0 || ^19.0.0",
|
|
10
10
|
"@angular/animations": "^18.0.0 || ^19.0.0",
|
|
11
|
+
"@angular/cdk": "^18.0.0 || ^19.0.0",
|
|
11
12
|
"rxjs": "~7.8.0"
|
|
12
13
|
},
|
|
13
14
|
"sideEffects": false
|
|
@@ -24,6 +24,7 @@ import { StarIconComponent } from './star-icon.component';
|
|
|
24
24
|
import { SunIconComponent } from './sun-icon.component';
|
|
25
25
|
import { TerminalIconComponent } from './terminal-icon.component';
|
|
26
26
|
import { ToggleLeftIconComponent } from './toggle-left-icon.component';
|
|
27
|
+
import { TranslateIconComponent } from './translate-icon.component';
|
|
27
28
|
import { UsersIconComponent } from './users-icon.component';
|
|
28
29
|
|
|
29
30
|
@Component({
|
|
@@ -48,65 +49,49 @@ import { UsersIconComponent } from './users-icon.component';
|
|
|
48
49
|
UsersIconComponent,
|
|
49
50
|
SunIconComponent,
|
|
50
51
|
MoonIconComponent,
|
|
52
|
+
TranslateIconComponent,
|
|
51
53
|
],
|
|
52
54
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
53
55
|
template: `
|
|
54
|
-
@switch (name()) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
@case ('terminal') {
|
|
95
|
-
<ndt-terminal-icon [fill]="fill()" />
|
|
96
|
-
}
|
|
97
|
-
@case ('toggle-left') {
|
|
98
|
-
<ndt-toggle-left-icon [fill]="fill()" />
|
|
99
|
-
}
|
|
100
|
-
@case ('user') {
|
|
101
|
-
<ndt-users-icon [fill]="fill()" />
|
|
102
|
-
}
|
|
103
|
-
@case ('sun') {
|
|
104
|
-
<ndt-sun-icon [fill]="fill()" />
|
|
105
|
-
}
|
|
106
|
-
@case ('moon') {
|
|
107
|
-
<ndt-moon-icon [fill]="fill()" />
|
|
108
|
-
}
|
|
109
|
-
}
|
|
56
|
+
@switch (name()) { @case ('angular') {
|
|
57
|
+
<ndt-angular-icon />
|
|
58
|
+
} @case ('bug') {
|
|
59
|
+
<ndt-bug-icon [fill]="fill()" />
|
|
60
|
+
} @case ('code') {
|
|
61
|
+
<ndt-code-icon [fill]="fill()" />
|
|
62
|
+
} @case ('database') {
|
|
63
|
+
<ndt-database-icon [fill]="fill()" />
|
|
64
|
+
} @case ('gauge') {
|
|
65
|
+
<ndt-gauge-icon [fill]="fill()" />
|
|
66
|
+
} @case ('gear') {
|
|
67
|
+
<ndt-gear-icon [fill]="fill()" />
|
|
68
|
+
} @case ('git-branch') {
|
|
69
|
+
<ndt-git-branch-icon [fill]="fill()" />
|
|
70
|
+
} @case ('layout') {
|
|
71
|
+
<ndt-layout-icon [fill]="fill()" />
|
|
72
|
+
} @case ('lighting') {
|
|
73
|
+
<ndt-lighting-icon [fill]="fill()" />
|
|
74
|
+
} @case ('network') {
|
|
75
|
+
<ndt-network-icon [fill]="fill()" />
|
|
76
|
+
} @case ('puzzle') {
|
|
77
|
+
<ndt-puzzle-icon [fill]="fill()" />
|
|
78
|
+
} @case ('refresh') {
|
|
79
|
+
<ndt-refresh-icon [fill]="fill()" />
|
|
80
|
+
} @case ('star') {
|
|
81
|
+
<ndt-star-icon [fill]="fill()" />
|
|
82
|
+
} @case ('terminal') {
|
|
83
|
+
<ndt-terminal-icon [fill]="fill()" />
|
|
84
|
+
} @case ('toggle-left') {
|
|
85
|
+
<ndt-toggle-left-icon [fill]="fill()" />
|
|
86
|
+
} @case ('user') {
|
|
87
|
+
<ndt-users-icon [fill]="fill()" />
|
|
88
|
+
} @case ('sun') {
|
|
89
|
+
<ndt-sun-icon [fill]="fill()" />
|
|
90
|
+
} @case ('moon') {
|
|
91
|
+
<ndt-moon-icon [fill]="fill()" />
|
|
92
|
+
} @case ('translate') {
|
|
93
|
+
<ndt-translate-icon [fill]="fill()" />
|
|
94
|
+
} }
|
|
110
95
|
`,
|
|
111
96
|
})
|
|
112
97
|
export class DevToolbarIconComponent {
|
|
@@ -115,6 +100,6 @@ export class DevToolbarIconComponent {
|
|
|
115
100
|
name = input.required<IconName>();
|
|
116
101
|
|
|
117
102
|
fill = computed(() =>
|
|
118
|
-
this.stateService.theme() === 'dark' ? '#FFFFFF' : '#000000'
|
|
103
|
+
this.stateService.theme() === 'dark' ? '#FFFFFF' : '#000000'
|
|
119
104
|
);
|
|
120
105
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'ndt-translate-icon',
|
|
5
|
+
standalone: true,
|
|
6
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7
|
+
template: `
|
|
8
|
+
<svg
|
|
9
|
+
[attr.fill]="fill()"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
width="24"
|
|
12
|
+
height="24"
|
|
13
|
+
viewBox="0 0 256 256"
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d="M250.73,210.63l-56-112a12,12,0,0,0-21.46,0l-20.52,41A84.2,84.2,0,0,1,114,126.22,107.48,107.48,0,0,0,139.33,68H160a12,12,0,0,0,0-24H108V32a12,12,0,0,0-24,0V44H32a12,12,0,0,0,0,24h83.13A83.69,83.69,0,0,1,96,110.35,84,84,0,0,1,83.6,91a12,12,0,1,0-21.81,10A107.55,107.55,0,0,0,78,126.24,83.54,83.54,0,0,1,32,140a12,12,0,0,0,0,24,107.47,107.47,0,0,0,64-21.07,108.4,108.4,0,0,0,45.39,19.44l-24.13,48.26a12,12,0,1,0,21.46,10.73L151.41,196h65.17l12.68,25.36a12,12,0,1,0,21.47-10.73ZM163.41,172,184,130.83,204.58,172Z"
|
|
17
|
+
></path>
|
|
18
|
+
</svg>
|
|
19
|
+
`,
|
|
20
|
+
})
|
|
21
|
+
export class TranslateIconComponent {
|
|
22
|
+
fill = input<string>('#FFFF');
|
|
23
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
@use '../../styles' as devtools;
|
|
2
|
+
|
|
3
|
+
select.select {
|
|
2
4
|
width: 100%;
|
|
3
5
|
cursor: pointer;
|
|
4
6
|
min-width: 100px;
|
|
@@ -45,19 +47,36 @@
|
|
|
45
47
|
&:invalid {
|
|
46
48
|
border-color: var(--devtools-border-error, #ff4444);
|
|
47
49
|
}
|
|
50
|
+
|
|
51
|
+
option {
|
|
52
|
+
background-color: var(--devtools-bg-primary);
|
|
53
|
+
color: var(--devtools-text-primary);
|
|
54
|
+
padding: var(--devtools-spacing-sm);
|
|
55
|
+
font-size: var(--devtools-font-size-sm);
|
|
56
|
+
|
|
57
|
+
&:hover,
|
|
58
|
+
&:focus {
|
|
59
|
+
background-color: var(--devtools-background-hover);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
/* Style for Firefox */
|
|
51
65
|
@-moz-document url-prefix() {
|
|
52
|
-
.select {
|
|
66
|
+
select.select {
|
|
53
67
|
text-indent: 0.01px;
|
|
54
68
|
text-overflow: '';
|
|
55
69
|
padding-right: 2.5em;
|
|
70
|
+
|
|
71
|
+
option {
|
|
72
|
+
background-color: var(--devtools-bg-primary) !important;
|
|
73
|
+
color: var(--devtools-text-primary) !important;
|
|
74
|
+
}
|
|
56
75
|
}
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
/* Style for Webkit browsers */
|
|
60
|
-
.select::-ms-expand {
|
|
79
|
+
select.select::-ms-expand {
|
|
61
80
|
display: none;
|
|
62
81
|
}
|
|
63
82
|
|
|
@@ -67,9 +86,9 @@ select::-ms-expand {
|
|
|
67
86
|
}
|
|
68
87
|
|
|
69
88
|
/* For Webkit browsers (Chrome, Safari) */
|
|
70
|
-
select:-webkit-autofill,
|
|
71
|
-
select:-webkit-autofill:hover,
|
|
72
|
-
select:-webkit-autofill:focus {
|
|
89
|
+
select.select:-webkit-autofill,
|
|
90
|
+
select.select:-webkit-autofill:hover,
|
|
91
|
+
select.select:-webkit-autofill:focus {
|
|
73
92
|
-webkit-box-shadow: 0 0 0px 1000px var(--devtools-bg-primary) inset !important;
|
|
74
93
|
-webkit-text-fill-color: var(--devtools-text-primary) !important;
|
|
75
94
|
}
|
|
@@ -16,13 +16,13 @@ $dimensions: devtools.$dimensions;
|
|
|
16
16
|
opacity: 0.5;
|
|
17
17
|
position: relative;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
&--active {
|
|
20
20
|
background: var(--devtools-hover-bg);
|
|
21
21
|
opacity: 1;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
background:
|
|
24
|
+
&:hover {
|
|
25
|
+
background: var(--devtools-hover-bg);
|
|
26
26
|
opacity: 1;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -23,7 +23,6 @@ import { DevToolbarStateService } from '../../dev-toolbar-state.service';
|
|
|
23
23
|
template: `
|
|
24
24
|
<button
|
|
25
25
|
class="tool-button"
|
|
26
|
-
[class.tool-button--toolbar-visible]="isToolbarVisible()"
|
|
27
26
|
[class.tool-button--active]="isActive()"
|
|
28
27
|
[class.tool-button--focus]="isFocused()"
|
|
29
28
|
(mouseenter)="onMouseEnter()"
|
|
@@ -74,9 +73,9 @@ export class DevToolbarToolButtonComponent {
|
|
|
74
73
|
// Inputs
|
|
75
74
|
readonly title = input.required<string>();
|
|
76
75
|
readonly toolId = input.required<string>();
|
|
76
|
+
|
|
77
77
|
// Outputs
|
|
78
|
-
|
|
79
|
-
readonly click = output<void>();
|
|
78
|
+
readonly open = output<void>();
|
|
80
79
|
|
|
81
80
|
// Signals
|
|
82
81
|
readonly isActive = computed(
|
|
@@ -102,7 +101,7 @@ export class DevToolbarToolButtonComponent {
|
|
|
102
101
|
// Public methods
|
|
103
102
|
onClick(): void {
|
|
104
103
|
this.isFocused.set(false);
|
|
105
|
-
this.
|
|
104
|
+
this.open.emit();
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
onMouseEnter(): void {
|
|
@@ -81,21 +81,29 @@ export class DevToolbarToolComponent {
|
|
|
81
81
|
);
|
|
82
82
|
height = computed(() => {
|
|
83
83
|
switch (this.windowConfig().size) {
|
|
84
|
-
case '
|
|
85
|
-
return
|
|
84
|
+
case 'small':
|
|
85
|
+
return 320;
|
|
86
86
|
case 'medium':
|
|
87
87
|
return 480;
|
|
88
|
+
case 'tall':
|
|
89
|
+
return 620;
|
|
90
|
+
case 'large':
|
|
91
|
+
return 620;
|
|
88
92
|
default:
|
|
89
|
-
return
|
|
93
|
+
return 480;
|
|
90
94
|
}
|
|
91
95
|
});
|
|
92
96
|
|
|
93
97
|
width = computed(() => {
|
|
94
98
|
switch (this.windowConfig().size) {
|
|
95
|
-
case '
|
|
96
|
-
return
|
|
99
|
+
case 'small':
|
|
100
|
+
return 320;
|
|
97
101
|
case 'medium':
|
|
98
102
|
return 480;
|
|
103
|
+
case 'tall':
|
|
104
|
+
return 480;
|
|
105
|
+
case 'large':
|
|
106
|
+
return 620;
|
|
99
107
|
default:
|
|
100
108
|
return 400;
|
|
101
109
|
}
|
|
@@ -28,20 +28,6 @@
|
|
|
28
28
|
justify-content: space-between;
|
|
29
29
|
align-items: flex-start;
|
|
30
30
|
|
|
31
|
-
&__content {
|
|
32
|
-
display: flex;
|
|
33
|
-
flex-direction: column;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
&__controls {
|
|
37
|
-
display: flex;
|
|
38
|
-
gap: var(--devtools-spacing-sm);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
&__description {
|
|
42
|
-
font-size: var(--devtools-font-size-sm);
|
|
43
|
-
color: var(--devtools-text-muted);
|
|
44
|
-
}
|
|
45
31
|
|
|
46
32
|
&__title {
|
|
47
33
|
display: flex;
|
|
@@ -60,6 +46,23 @@
|
|
|
60
46
|
letter-spacing: 0.5px;
|
|
61
47
|
}
|
|
62
48
|
}
|
|
49
|
+
&__description {
|
|
50
|
+
font-size: var(--devtools-font-size-sm);
|
|
51
|
+
color: var(--devtools-text-muted);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
&__content {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&__controls {
|
|
61
|
+
display: flex;
|
|
62
|
+
gap: var(--devtools-spacing-sm);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
.content {
|
|
@@ -22,12 +22,17 @@ export class DevToolbarStateService {
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
// Selectors
|
|
25
|
-
readonly isVisible = computed(
|
|
25
|
+
readonly isVisible = computed(
|
|
26
|
+
() => !this.state().isHidden || this.hasActiveTool()
|
|
27
|
+
);
|
|
26
28
|
readonly isDarkTheme = computed(() => this.state().theme === 'dark');
|
|
27
29
|
readonly activeToolId = computed(() => this.state().activeToolId);
|
|
28
30
|
readonly hasActiveTool = computed(() => this.state().activeToolId !== null);
|
|
29
31
|
readonly error = computed(() => this.state().error);
|
|
30
32
|
readonly theme = computed(() => this.state().theme);
|
|
33
|
+
/**
|
|
34
|
+
* The delay to hide the toolbar
|
|
35
|
+
*/
|
|
31
36
|
readonly delay = computed(() => this.state().delay);
|
|
32
37
|
|
|
33
38
|
// State updates
|
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
transform: translateX(-50%);
|
|
10
10
|
display: flex;
|
|
11
11
|
pointer-events: auto;
|
|
12
|
-
background: var(--devtools-bg-
|
|
12
|
+
background: var(--devtools-bg-primary);
|
|
13
13
|
border: 1px solid var(--devtools-border-primary);
|
|
14
14
|
border-radius: map.get(map.get(devtools.$dimensions, border-radius), full);
|
|
15
15
|
box-shadow: var(--devtools-shadow-toolbar);
|
|
16
16
|
height: map.get(devtools.$dimensions, toolbar-height);
|
|
17
|
+
overflow: hidden;
|
|
17
18
|
|
|
18
19
|
&--active {
|
|
19
20
|
opacity: 1;
|
|
@@ -5,29 +5,33 @@ import {
|
|
|
5
5
|
transition,
|
|
6
6
|
trigger,
|
|
7
7
|
} from '@angular/animations';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Component,
|
|
10
|
+
DestroyRef,
|
|
11
|
+
OnInit,
|
|
12
|
+
inject,
|
|
13
|
+
isDevMode,
|
|
14
|
+
} from '@angular/core';
|
|
9
15
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
16
|
import { fromEvent } from 'rxjs';
|
|
11
17
|
import { filter, throttleTime } from 'rxjs/operators';
|
|
12
|
-
import { DevToolbarIconComponent } from './components/icons/icon.component';
|
|
13
|
-
import { DevToolbarToolButtonComponent } from './components/tool-button/tool-button.component';
|
|
14
18
|
import { DevToolbarStateService } from './dev-toolbar-state.service';
|
|
15
19
|
import { DevToolbarFeatureFlagsToolComponent } from './tools/feature-flags-tool/feature-flags-tool.component';
|
|
16
|
-
import {
|
|
17
|
-
import { SettingsService } from './tools/
|
|
20
|
+
import { DevToolbarHomeToolComponent } from './tools/home-tool/home-tool.component';
|
|
21
|
+
import { SettingsService } from './tools/home-tool/settings.service';
|
|
22
|
+
import { DevToolbarLanguageToolComponent } from './tools/language-tool/language-tool.component';
|
|
18
23
|
|
|
19
24
|
@Component({
|
|
20
25
|
standalone: true,
|
|
21
26
|
selector: 'ndt-toolbar',
|
|
22
27
|
styleUrls: ['./dev-toolbar.component.scss'],
|
|
23
28
|
imports: [
|
|
24
|
-
|
|
29
|
+
DevToolbarHomeToolComponent,
|
|
30
|
+
DevToolbarLanguageToolComponent,
|
|
25
31
|
DevToolbarFeatureFlagsToolComponent,
|
|
26
|
-
DevToolbarSettingsToolComponent,
|
|
27
|
-
DevToolbarIconComponent,
|
|
28
32
|
],
|
|
29
|
-
|
|
30
33
|
template: `
|
|
34
|
+
@if (isDevMode) {
|
|
31
35
|
<div
|
|
32
36
|
aria-label="Developer tools"
|
|
33
37
|
role="toolbar"
|
|
@@ -37,15 +41,12 @@ import { SettingsService } from './tools/settings-tool/settings.service';
|
|
|
37
41
|
[class.dev-toolbar--active]="state.isVisible()"
|
|
38
42
|
(mouseenter)="onMouseEnter()"
|
|
39
43
|
>
|
|
40
|
-
<ndt-tool
|
|
41
|
-
|
|
42
|
-
</ndt-tool-button>
|
|
43
|
-
<ndt-tool-button title="Performance" toolId="ndt-performance">
|
|
44
|
-
<ndt-icon name="gauge" />
|
|
45
|
-
</ndt-tool-button>
|
|
44
|
+
<ndt-home-tool />
|
|
45
|
+
<ndt-language-tool />
|
|
46
46
|
<ndt-feature-flags-tool />
|
|
47
|
-
<
|
|
47
|
+
<ng-content />
|
|
48
48
|
</div>
|
|
49
|
+
}
|
|
49
50
|
`,
|
|
50
51
|
animations: [
|
|
51
52
|
trigger('toolbarState', [
|
|
@@ -72,6 +73,8 @@ export class DevToolbarComponent implements OnInit {
|
|
|
72
73
|
destroyRef = inject(DestroyRef);
|
|
73
74
|
settingsService = inject(SettingsService);
|
|
74
75
|
|
|
76
|
+
isDevMode = isDevMode();
|
|
77
|
+
|
|
75
78
|
private keyboardShortcut = fromEvent<KeyboardEvent>(window, 'keydown')
|
|
76
79
|
.pipe(
|
|
77
80
|
filter((event) => event.ctrlKey && event.shiftKey && event.key === 'D'),
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export * from './components/icons/icon.component';
|
|
2
|
+
export * from './components/icons/icon.models';
|
|
3
|
+
export * from './components/toolbar-tool/toolbar-tool.component';
|
|
4
|
+
export * from './components/toolbar-tool/toolbar-tool.models';
|
|
5
|
+
export * from './components/window/window.models';
|
|
1
6
|
export * from './dev-toolbar.component';
|
|
7
|
+
export * from './models/dev-tools.interface';
|
|
2
8
|
export * from './tools/feature-flags-tool/feature-flags.models';
|
|
3
9
|
export * from './tools/feature-flags-tool/feature-flags.service';
|
|
10
|
+
export * from './tools/language-tool/language.models';
|
|
11
|
+
export * from './tools/language-tool/language.service';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface that should be implemented by any tool service that is used in the dev toolbar
|
|
5
|
+
*/
|
|
6
|
+
export interface DevToolsService<OptionType> {
|
|
7
|
+
/**
|
|
8
|
+
* Sets the available options that will be displayed in the tool on the dev toolbar
|
|
9
|
+
* @param options The options to be displayed
|
|
10
|
+
*/
|
|
11
|
+
setAvailableOptions(options: OptionType[]): void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gets the values that were forced/modified through the tool on the dev toolbar.
|
|
15
|
+
* If the tool only supports a single option, the returned array will have a single element.
|
|
16
|
+
* @returns Observable of forced values array
|
|
17
|
+
*/
|
|
18
|
+
getForcedValues(): Observable<OptionType[]>;
|
|
19
|
+
}
|
package/src/styles.scss
CHANGED
|
@@ -240,6 +240,7 @@ $z-indices: (
|
|
|
240
240
|
h5 {
|
|
241
241
|
font-weight: 600;
|
|
242
242
|
color: var(--devtools-text-primary);
|
|
243
|
+
margin: 0;
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
h1 {
|
|
@@ -265,6 +266,7 @@ $z-indices: (
|
|
|
265
266
|
|
|
266
267
|
p {
|
|
267
268
|
line-height: 1.5em;
|
|
269
|
+
margin: 0;
|
|
268
270
|
}
|
|
269
271
|
--devtools-note-background: #{map.get(
|
|
270
272
|
map.get(map.get($colors, light), annotation),
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { BehaviorSubject, Observable, combineLatest, map } from 'rxjs';
|
|
4
|
+
import { DevToolsStorageService } from '../../utils/storage.service';
|
|
5
|
+
import { Flag } from './feature-flags.models';
|
|
6
|
+
|
|
7
|
+
interface ForcedFlagsState {
|
|
8
|
+
enabled: string[];
|
|
9
|
+
disabled: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@Injectable({ providedIn: 'root' })
|
|
13
|
+
export class DevToolbarInternalFeatureFlagService {
|
|
14
|
+
private readonly STORAGE_KEY = 'feature-flags';
|
|
15
|
+
private storageService = inject(DevToolsStorageService);
|
|
16
|
+
|
|
17
|
+
private appFlags$ = new BehaviorSubject<Flag[]>([]);
|
|
18
|
+
private forcedFlagsSubject = new BehaviorSubject<ForcedFlagsState>({
|
|
19
|
+
enabled: [],
|
|
20
|
+
disabled: [],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
private readonly forcedFlags$ = this.forcedFlagsSubject.asObservable();
|
|
24
|
+
|
|
25
|
+
public flags$: Observable<Flag[]> = combineLatest([
|
|
26
|
+
this.appFlags$,
|
|
27
|
+
this.forcedFlags$,
|
|
28
|
+
]).pipe(
|
|
29
|
+
map(([appFlags, { enabled, disabled }]) => {
|
|
30
|
+
return appFlags.map((flag) => ({
|
|
31
|
+
...flag,
|
|
32
|
+
isForced: enabled.includes(flag.id) || disabled.includes(flag.id),
|
|
33
|
+
isEnabled: enabled.includes(flag.id),
|
|
34
|
+
}));
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
public flags = toSignal(this.flags$, { initialValue: [] });
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
this.loadForcedFlags();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setAppFlags(flags: Flag[]): void {
|
|
45
|
+
this.appFlags$.next(flags);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getAppFlags(): Observable<Flag[]> {
|
|
49
|
+
return this.appFlags$.asObservable();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getForcedFlags(): Observable<Flag[]> {
|
|
53
|
+
return this.flags$.pipe(
|
|
54
|
+
map((flags) => flags.filter((flag) => flag.isForced))
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setFlag(flagId: string, isEnabled: boolean): void {
|
|
59
|
+
const { enabled, disabled } = this.forcedFlagsSubject.value;
|
|
60
|
+
|
|
61
|
+
const newEnabled = enabled.filter((id) => id !== flagId);
|
|
62
|
+
const newDisabled = disabled.filter((id) => id !== flagId);
|
|
63
|
+
|
|
64
|
+
if (isEnabled) {
|
|
65
|
+
newEnabled.push(flagId);
|
|
66
|
+
} else {
|
|
67
|
+
newDisabled.push(flagId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const newState = { enabled: newEnabled, disabled: newDisabled };
|
|
71
|
+
this.forcedFlagsSubject.next(newState);
|
|
72
|
+
this.storageService.set(this.STORAGE_KEY, newState);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
removeFlagOverride(flagId: string): void {
|
|
76
|
+
const { enabled, disabled } = this.forcedFlagsSubject.value;
|
|
77
|
+
|
|
78
|
+
const newState = {
|
|
79
|
+
enabled: enabled.filter((id) => id !== flagId),
|
|
80
|
+
disabled: disabled.filter((id) => id !== flagId),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this.forcedFlagsSubject.next(newState);
|
|
84
|
+
this.storageService.set(this.STORAGE_KEY, newState);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private loadForcedFlags(): void {
|
|
88
|
+
const savedFlags = this.storageService.get<ForcedFlagsState>(
|
|
89
|
+
this.STORAGE_KEY
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (savedFlags) {
|
|
93
|
+
this.forcedFlagsSubject.next(savedFlags);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -10,8 +10,8 @@ import { DevToolbarInputComponent } from '../../components/input/input.component
|
|
|
10
10
|
import { DevToolbarSelectComponent } from '../../components/select/select.component';
|
|
11
11
|
import { DevToolbarToolComponent } from '../../components/toolbar-tool/toolbar-tool.component';
|
|
12
12
|
import { WindowSize } from '../../components/window/window.models';
|
|
13
|
+
import { DevToolbarInternalFeatureFlagService } from './feature-flags-internal.service';
|
|
13
14
|
import { FeatureFlagFilter, Flag } from './feature-flags.models';
|
|
14
|
-
import { DevToolbarFeatureFlagsService } from './feature-flags.service';
|
|
15
15
|
|
|
16
16
|
@Component({
|
|
17
17
|
selector: 'ndt-feature-flags-tool',
|
|
@@ -47,6 +47,10 @@ import { DevToolbarFeatureFlagsService } from './feature-flags.service';
|
|
|
47
47
|
<div class="empty">
|
|
48
48
|
<p>No flags found</p>
|
|
49
49
|
</div>
|
|
50
|
+
} @else if (hasNoFilteredFlags()) {
|
|
51
|
+
<div class="empty">
|
|
52
|
+
<p>No flags found matching your filter</p>
|
|
53
|
+
</div>
|
|
50
54
|
} @else {
|
|
51
55
|
<div class="flag-list">
|
|
52
56
|
@for (flag of filteredFlags(); track flag.id) {
|
|
@@ -169,7 +173,7 @@ import { DevToolbarFeatureFlagsService } from './feature-flags.service';
|
|
|
169
173
|
})
|
|
170
174
|
export class DevToolbarFeatureFlagsToolComponent {
|
|
171
175
|
// Injects
|
|
172
|
-
private readonly featureFlags = inject(
|
|
176
|
+
private readonly featureFlags = inject(DevToolbarInternalFeatureFlagService);
|
|
173
177
|
|
|
174
178
|
// Signals
|
|
175
179
|
protected readonly activeFilter = signal<FeatureFlagFilter>('all');
|
|
@@ -197,6 +201,9 @@ export class DevToolbarFeatureFlagsToolComponent {
|
|
|
197
201
|
return matchesSearch && matchesFilter;
|
|
198
202
|
});
|
|
199
203
|
});
|
|
204
|
+
protected readonly hasNoFilteredFlags = computed(
|
|
205
|
+
() => this.filteredFlags().length === 0
|
|
206
|
+
);
|
|
200
207
|
|
|
201
208
|
// Other properties
|
|
202
209
|
protected readonly windowConfig = {
|
|
@@ -216,7 +223,7 @@ export class DevToolbarFeatureFlagsToolComponent {
|
|
|
216
223
|
];
|
|
217
224
|
|
|
218
225
|
protected readonly flagValueOptions = [
|
|
219
|
-
{ value: 'not-forced', label: '
|
|
226
|
+
{ value: 'not-forced', label: 'Not Forced' },
|
|
220
227
|
{ value: 'off', label: 'Forced Off (false)' },
|
|
221
228
|
{ value: 'on', label: 'Forced On (true)' },
|
|
222
229
|
];
|
|
@@ -1,110 +1,26 @@
|
|
|
1
1
|
import { Injectable, inject } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { DevToolsService } from '../../models/dev-tools.interface';
|
|
4
|
+
import { DevToolbarInternalFeatureFlagService } from './feature-flags-internal.service';
|
|
5
5
|
import { Flag } from './feature-flags.models';
|
|
6
6
|
|
|
7
|
-
interface ForcedFlagsState {
|
|
8
|
-
enabled: string[];
|
|
9
|
-
disabled: string[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
7
|
@Injectable({ providedIn: 'root' })
|
|
13
|
-
export class
|
|
14
|
-
private
|
|
15
|
-
private storageService = inject(DevToolsStorageService);
|
|
8
|
+
export class DevToolbarFeatureFlagService implements DevToolsService<Flag> {
|
|
9
|
+
private internalService = inject(DevToolbarInternalFeatureFlagService);
|
|
16
10
|
|
|
17
11
|
/**
|
|
18
|
-
*
|
|
12
|
+
* Sets the available flags that will be displayed in the tool on the dev toolbar
|
|
13
|
+
* @param flags The flags to be displayed
|
|
19
14
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Forced flags are the flags that are currently forced in the app
|
|
24
|
-
*/
|
|
25
|
-
private forcedFlagsSubject = new BehaviorSubject<ForcedFlagsState>({
|
|
26
|
-
enabled: [],
|
|
27
|
-
disabled: [],
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
private readonly forcedFlags$ = this.forcedFlagsSubject.asObservable();
|
|
31
|
-
/**
|
|
32
|
-
* Flags with the forced flag status
|
|
33
|
-
*/
|
|
34
|
-
public flags$: Observable<Flag[]> = combineLatest([
|
|
35
|
-
this.appFlags$,
|
|
36
|
-
this.forcedFlags$,
|
|
37
|
-
]).pipe(
|
|
38
|
-
map(([appFlags, { enabled, disabled }]) => {
|
|
39
|
-
return appFlags.map((flag) => ({
|
|
40
|
-
...flag,
|
|
41
|
-
isForced: enabled.includes(flag.id) || disabled.includes(flag.id),
|
|
42
|
-
isEnabled: enabled.includes(flag.id),
|
|
43
|
-
}));
|
|
44
|
-
})
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
public flags = toSignal(this.flags$, { initialValue: [] });
|
|
48
|
-
|
|
49
|
-
constructor() {
|
|
50
|
-
this.loadForcedFlags();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public set(flags: Flag[]): void {
|
|
54
|
-
this.appFlags$.next(flags);
|
|
15
|
+
setAvailableOptions(flags: Flag[]): void {
|
|
16
|
+
this.internalService.setAppFlags(flags);
|
|
55
17
|
}
|
|
56
18
|
|
|
57
19
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* @param flags - The flags to set
|
|
20
|
+
* Gets the flags that were forced/modified through the tool on the dev toolbar
|
|
21
|
+
* @returns Observable of forced flags array
|
|
61
22
|
*/
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public getAppFlags(): Observable<Flag[]> {
|
|
67
|
-
return this.appFlags$.asObservable();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public setFlag(flagId: string, isEnabled: boolean): void {
|
|
71
|
-
const { enabled, disabled } = this.forcedFlagsSubject.value;
|
|
72
|
-
|
|
73
|
-
// Remove from both arrays first
|
|
74
|
-
const newEnabled = enabled.filter((id) => id !== flagId);
|
|
75
|
-
const newDisabled = disabled.filter((id) => id !== flagId);
|
|
76
|
-
|
|
77
|
-
// Add to appropriate array
|
|
78
|
-
if (isEnabled) {
|
|
79
|
-
newEnabled.push(flagId);
|
|
80
|
-
} else {
|
|
81
|
-
newDisabled.push(flagId);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const newState = { enabled: newEnabled, disabled: newDisabled };
|
|
85
|
-
this.forcedFlagsSubject.next(newState);
|
|
86
|
-
this.storageService.set(this.STORAGE_KEY, newState);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public removeFlagOverride(flagId: string): void {
|
|
90
|
-
const { enabled, disabled } = this.forcedFlagsSubject.value;
|
|
91
|
-
|
|
92
|
-
const newState = {
|
|
93
|
-
enabled: enabled.filter((id) => id !== flagId),
|
|
94
|
-
disabled: disabled.filter((id) => id !== flagId),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
this.forcedFlagsSubject.next(newState);
|
|
98
|
-
this.storageService.set(this.STORAGE_KEY, newState);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private loadForcedFlags(): void {
|
|
102
|
-
const savedFlags = this.storageService.get<ForcedFlagsState>(
|
|
103
|
-
this.STORAGE_KEY
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
if (savedFlags) {
|
|
107
|
-
this.forcedFlagsSubject.next(savedFlags);
|
|
108
|
-
}
|
|
23
|
+
getForcedValues(): Observable<Flag[]> {
|
|
24
|
+
return this.internalService.getForcedFlags();
|
|
109
25
|
}
|
|
110
26
|
}
|
package/src/tools/{settings-tool/settings-tool.component.ts → home-tool/home-tool.component.ts}
RENAMED
|
@@ -14,15 +14,11 @@ import { SettingsService } from './settings.service';
|
|
|
14
14
|
type ThemeType = 'light' | 'dark';
|
|
15
15
|
|
|
16
16
|
@Component({
|
|
17
|
-
selector: 'ndt-
|
|
17
|
+
selector: 'ndt-home-tool',
|
|
18
18
|
standalone: true,
|
|
19
19
|
imports: [DevToolbarToolComponent, FormsModule, DevToolbarButtonComponent],
|
|
20
20
|
template: `
|
|
21
|
-
<ndt-toolbar-tool
|
|
22
|
-
[windowConfig]="windowConfig"
|
|
23
|
-
title="Settings"
|
|
24
|
-
icon="gear"
|
|
25
|
-
>
|
|
21
|
+
<ndt-toolbar-tool [windowConfig]="windowConfig" title="Home" icon="angular">
|
|
26
22
|
<section class="settings">
|
|
27
23
|
<div class="instruction">
|
|
28
24
|
<div class="instruction__label">
|
|
@@ -34,18 +30,15 @@ type ThemeType = 'light' | 'dark';
|
|
|
34
30
|
<div class="instruction__control">
|
|
35
31
|
<div class="theme">
|
|
36
32
|
<ndt-button
|
|
37
|
-
[isActive]="
|
|
38
|
-
(click)="
|
|
33
|
+
[isActive]="true"
|
|
34
|
+
(click)="onToggleTheme()"
|
|
39
35
|
variant="icon"
|
|
40
|
-
ariaLabel="
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
variant="icon"
|
|
47
|
-
ariaLabel="Switch to dark theme"
|
|
48
|
-
icon="moon"
|
|
36
|
+
[ariaLabel]="
|
|
37
|
+
state.isDarkTheme()
|
|
38
|
+
? 'Switch to light theme'
|
|
39
|
+
: 'Switch to dark theme'
|
|
40
|
+
"
|
|
41
|
+
[icon]="state.isDarkTheme() ? 'sun' : 'moon'"
|
|
49
42
|
/>
|
|
50
43
|
</div>
|
|
51
44
|
</div>
|
|
@@ -53,24 +46,27 @@ type ThemeType = 'light' | 'dark';
|
|
|
53
46
|
</section>
|
|
54
47
|
</ndt-toolbar-tool>
|
|
55
48
|
`,
|
|
56
|
-
styleUrls: ['./
|
|
49
|
+
styleUrls: ['./home-tool.component.scss'],
|
|
57
50
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
58
51
|
})
|
|
59
|
-
export class
|
|
52
|
+
export class DevToolbarHomeToolComponent {
|
|
60
53
|
state = inject(DevToolbarStateService);
|
|
61
54
|
settingsService = inject(SettingsService);
|
|
62
55
|
|
|
63
56
|
readonly badge = input<string | number>();
|
|
57
|
+
readonly title = `Angular Dev Toolbar`;
|
|
64
58
|
readonly windowConfig: WindowConfig = {
|
|
65
|
-
title:
|
|
59
|
+
title: this.title,
|
|
66
60
|
isClosable: true,
|
|
67
|
-
id: 'ndt-
|
|
68
|
-
|
|
61
|
+
id: 'ndt-home',
|
|
62
|
+
size: 'medium',
|
|
63
|
+
description: '',
|
|
69
64
|
isBeta: true,
|
|
70
65
|
};
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
67
|
+
onToggleTheme(): void {
|
|
68
|
+
const newTheme: ThemeType = this.state.isDarkTheme() ? 'light' : 'dark';
|
|
69
|
+
this.settingsService.setSettings({ isDarkMode: newTheme === 'dark' });
|
|
70
|
+
this.state.setTheme(newTheme);
|
|
75
71
|
}
|
|
76
72
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core';
|
|
2
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { BehaviorSubject, Observable, map } from 'rxjs';
|
|
4
|
+
import { DevToolsStorageService } from '../../utils/storage.service';
|
|
5
|
+
import { Language } from './language.models';
|
|
6
|
+
|
|
7
|
+
@Injectable({ providedIn: 'root' })
|
|
8
|
+
export class DevToolbarInternalLanguageService {
|
|
9
|
+
private readonly STORAGE_KEY = 'language';
|
|
10
|
+
private readonly storageService = inject(DevToolsStorageService);
|
|
11
|
+
|
|
12
|
+
private languages$ = new BehaviorSubject<Language[]>([]);
|
|
13
|
+
private forcedLanguage$ = new BehaviorSubject<Language | null>(null);
|
|
14
|
+
|
|
15
|
+
public languages = toSignal(this.languages$, { initialValue: [] });
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
this.loadForcedLanguage();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setAppLanguages(languages: Language[]): void {
|
|
22
|
+
this.languages$.next(languages);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getAppLanguages(): Observable<Language[]> {
|
|
26
|
+
return this.languages$.asObservable();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setForcedLanguage(language: Language): void {
|
|
30
|
+
this.forcedLanguage$.next(language);
|
|
31
|
+
this.storageService.set(this.STORAGE_KEY, language);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getForcedLanguage(): Observable<Language[]> {
|
|
35
|
+
return this.forcedLanguage$.pipe(
|
|
36
|
+
map((language) => (language ? [language] : []))
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
removeForcedLanguage(): void {
|
|
41
|
+
this.forcedLanguage$.next(null);
|
|
42
|
+
this.storageService.remove(this.STORAGE_KEY);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private loadForcedLanguage(): void {
|
|
46
|
+
const savedLanguage = this.storageService.get<Language>(this.STORAGE_KEY);
|
|
47
|
+
if (savedLanguage) {
|
|
48
|
+
this.forcedLanguage$.next(savedLanguage);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Component, inject, signal } from '@angular/core';
|
|
2
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
|
+
import { firstValueFrom, map } from 'rxjs';
|
|
4
|
+
import { DevToolbarSelectComponent } from '../../components/select/select.component';
|
|
5
|
+
import { DevToolbarToolComponent } from '../../components/toolbar-tool/toolbar-tool.component';
|
|
6
|
+
import { WindowSize } from '../../components/window/window.models';
|
|
7
|
+
import { DevToolbarInternalLanguageService } from './language-internal.service';
|
|
8
|
+
import { Language } from './language.models';
|
|
9
|
+
|
|
10
|
+
@Component({
|
|
11
|
+
selector: 'ndt-language-tool',
|
|
12
|
+
standalone: true,
|
|
13
|
+
imports: [DevToolbarToolComponent, DevToolbarSelectComponent],
|
|
14
|
+
styleUrls: ['./language-tool.component.scss'],
|
|
15
|
+
template: `
|
|
16
|
+
<ndt-toolbar-tool
|
|
17
|
+
title="Languages"
|
|
18
|
+
icon="translate"
|
|
19
|
+
[windowConfig]="windowConfig"
|
|
20
|
+
>
|
|
21
|
+
<div class="language-select">
|
|
22
|
+
<label for="language-select">Language</label>
|
|
23
|
+
<ndt-select
|
|
24
|
+
id="language-select"
|
|
25
|
+
[value]="activeLanguage()"
|
|
26
|
+
[options]="languageOptions()"
|
|
27
|
+
[size]="'medium'"
|
|
28
|
+
(valueChange)="onLanguageChange($event ?? '')"
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</ndt-toolbar-tool>
|
|
32
|
+
`,
|
|
33
|
+
})
|
|
34
|
+
export class DevToolbarLanguageToolComponent {
|
|
35
|
+
private readonly languageService = inject(DevToolbarInternalLanguageService);
|
|
36
|
+
|
|
37
|
+
protected readonly windowConfig = {
|
|
38
|
+
title: 'Languages',
|
|
39
|
+
description: 'Set the language for your current session',
|
|
40
|
+
isClosable: true,
|
|
41
|
+
size: 'small' as WindowSize,
|
|
42
|
+
id: 'ndt-language',
|
|
43
|
+
isBeta: true,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
activeLanguage = signal<string>('not-forced');
|
|
47
|
+
|
|
48
|
+
languageOptions = toSignal(
|
|
49
|
+
this.languageService.getAppLanguages().pipe(
|
|
50
|
+
map((languages) => [
|
|
51
|
+
{ value: 'not-forced', label: 'Not Forced' },
|
|
52
|
+
...languages.map(({ id: value, name: label }) => ({
|
|
53
|
+
value,
|
|
54
|
+
label,
|
|
55
|
+
})),
|
|
56
|
+
])
|
|
57
|
+
),
|
|
58
|
+
{ initialValue: [] }
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
async onLanguageChange(language: string): Promise<void> {
|
|
62
|
+
if (language === 'not-forced' || !language) {
|
|
63
|
+
this.languageService.removeForcedLanguage();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const languages = await firstValueFrom(
|
|
68
|
+
this.languageService.getAppLanguages()
|
|
69
|
+
);
|
|
70
|
+
const selectedLanguage = languages.find(({ id }) => id === language);
|
|
71
|
+
if (selectedLanguage) {
|
|
72
|
+
this.languageService.setForcedLanguage(selectedLanguage as Language);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { inject, Injectable } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { DevToolsService } from '../../models/dev-tools.interface';
|
|
4
|
+
import { DevToolbarInternalLanguageService } from './language-internal.service';
|
|
5
|
+
import { Language } from './language.models';
|
|
6
|
+
|
|
7
|
+
@Injectable({ providedIn: 'root' })
|
|
8
|
+
export class DevToolbarLanguageService implements DevToolsService<Language> {
|
|
9
|
+
private internalService = inject(DevToolbarInternalLanguageService);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sets the available languages that will be displayed in the tool on the dev toolbar
|
|
13
|
+
* @param languages The languages to be displayed
|
|
14
|
+
*/
|
|
15
|
+
setAvailableOptions(languages: Language[]): void {
|
|
16
|
+
this.internalService.setAppLanguages(languages);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Gets the languages that were forced/modified through the tool on the dev toolbar
|
|
21
|
+
* @returns Observable of forced languages array
|
|
22
|
+
*/
|
|
23
|
+
getForcedValues(): Observable<Language[]> {
|
|
24
|
+
return this.internalService.getForcedLanguage();
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/tools/index.ts
DELETED
/package/src/tools/{settings-tool/settings-tool.component.scss → home-tool/home-tool.component.scss}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|