ngx-dev-toolbar 0.0.2-1 → 0.0.2-3

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 (57) hide show
  1. package/package.json +6 -3
  2. package/project.json +1 -1
  3. package/src/components/button/button.component.scss +36 -0
  4. package/src/components/button/button.component.ts +36 -0
  5. package/src/components/icons/angular-icon.component.ts +35 -0
  6. package/src/components/icons/bug-icon.component.ts +27 -0
  7. package/src/components/icons/code-icon.component.ts +24 -0
  8. package/src/components/icons/database-icon.component.ts +27 -0
  9. package/src/components/icons/gauge-icon.component.ts +27 -0
  10. package/src/components/icons/gear-icon.component.ts +27 -0
  11. package/src/components/icons/git-branch-icon.component.ts +27 -0
  12. package/src/components/icons/icon.component.ts +105 -0
  13. package/src/components/icons/icon.models.ts +21 -0
  14. package/src/components/icons/layout-icon.component.ts +24 -0
  15. package/src/components/icons/lighting-icon.component.ts +24 -0
  16. package/src/components/icons/moon-icon.component.ts +27 -0
  17. package/src/components/icons/network-icon.component.ts +27 -0
  18. package/src/components/icons/puzzle-icon.component.ts +27 -0
  19. package/src/components/icons/refresh-icon.component.ts +27 -0
  20. package/src/components/icons/star-icon.component.ts +27 -0
  21. package/src/components/icons/sun-icon.component.ts +27 -0
  22. package/src/components/icons/terminal-icon.component.ts +27 -0
  23. package/src/components/icons/toggle-left-icon.component.ts +27 -0
  24. package/src/components/icons/translate-icon.component.ts +23 -0
  25. package/src/components/icons/users-icon.component.ts +27 -0
  26. package/src/components/input/input.component.ts +66 -0
  27. package/src/components/select/select.component.scss +102 -0
  28. package/src/components/select/select.component.ts +40 -0
  29. package/src/components/tool-button/tool-button.component.scss +67 -0
  30. package/src/components/tool-button/tool-button.component.ts +126 -0
  31. package/src/components/toolbar-tool/toolbar-tool.component.scss +9 -0
  32. package/src/components/toolbar-tool/toolbar-tool.component.ts +128 -0
  33. package/src/components/toolbar-tool/toolbar-tool.models.ts +9 -0
  34. package/src/components/window/window.component.scss +99 -0
  35. package/src/components/window/window.component.ts +79 -0
  36. package/src/components/window/window.models.ts +28 -0
  37. package/src/dev-toolbar-state.service.ts +89 -0
  38. package/src/dev-toolbar.component.scss +22 -0
  39. package/src/dev-toolbar.component.ts +104 -0
  40. package/src/index.ts +6 -1
  41. package/src/models/dev-tools.interface.ts +19 -0
  42. package/src/styles.scss +363 -0
  43. package/src/tools/feature-flags-tool/feature-flags-internal.service.ts +96 -0
  44. package/src/tools/feature-flags-tool/feature-flags-tool.component.ts +262 -0
  45. package/src/tools/feature-flags-tool/feature-flags.models.ts +10 -0
  46. package/src/tools/feature-flags-tool/feature-flags.service.ts +26 -0
  47. package/src/tools/home-tool/home-tool.component.scss +61 -0
  48. package/src/tools/home-tool/home-tool.component.ts +72 -0
  49. package/src/tools/home-tool/settings.models.ts +3 -0
  50. package/src/tools/home-tool/settings.service.spec.ts +59 -0
  51. package/src/tools/home-tool/settings.service.ts +21 -0
  52. package/src/tools/language-tool/language-internal.service.ts +51 -0
  53. package/src/tools/language-tool/language-tool.component.scss +7 -0
  54. package/src/tools/language-tool/language-tool.component.ts +75 -0
  55. package/src/tools/language-tool/language.models.ts +4 -0
  56. package/src/tools/language-tool/language.service.ts +26 -0
  57. package/src/utils/storage.service.ts +19 -0
@@ -0,0 +1,99 @@
1
+ @use '../../styles' as *;
2
+ @use 'sass:map';
3
+
4
+ :host {
5
+ display: block;
6
+ width: 100%;
7
+ }
8
+
9
+ .window {
10
+ box-sizing: border-box;
11
+ display: flex;
12
+ flex-direction: column;
13
+ width: 100%;
14
+ height: 100%;
15
+ background: var(--devtools-bg-primary);
16
+ border: 1px solid var(--devtools-border-primary);
17
+ border-radius: var(--devtools-border-radius-large);
18
+ padding: var(--devtools-window-padding);
19
+ font-family: $font-family;
20
+ color: var(--devtools-text-secondary);
21
+ z-index: #{map.get($z-indices, window)};
22
+ box-shadow: var(--devtools-shadow-window);
23
+ }
24
+
25
+ .header {
26
+ display: flex;
27
+ flex-direction: row;
28
+ justify-content: space-between;
29
+ align-items: flex-start;
30
+
31
+
32
+ &__title {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: var(--devtools-spacing-sm);
36
+
37
+ .beta-tag {
38
+ font-size: var(--devtools-font-size-xxs, 0.65rem);
39
+ background: var(--devtools-purple, #8b5cf6);
40
+ color: var(--devtools-text-on-primary);
41
+ padding: 1px 4px;
42
+ margin-left: var(--devtools-spacing-xs);
43
+ border-radius: var(--devtools-border-radius-small);
44
+ font-weight: 500;
45
+ text-transform: uppercase;
46
+ letter-spacing: 0.5px;
47
+ }
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
+
66
+ }
67
+
68
+ .content {
69
+ flex: 1;
70
+ overflow: auto;
71
+ }
72
+
73
+ .divider {
74
+ height: 1px;
75
+ background-color: var(--devtools-border-primary);
76
+ margin-bottom: var(--devtools-spacing-md);
77
+ margin-top: var(--devtools-spacing-md);
78
+ }
79
+
80
+ .control {
81
+ background: none;
82
+ border: none;
83
+ color: var(--devtools-text-secondary);
84
+ cursor: pointer;
85
+ padding: var(--devtools-spacing-xs) var(--devtools-spacing-sm);
86
+ border-radius: var(--devtools-border-radius-small);
87
+ font-size: var(--devtools-font-size-md);
88
+ line-height: 1;
89
+ transition: var(--devtools-transition-smooth);
90
+
91
+ &:hover {
92
+ background: var(--devtools-hover-bg);
93
+ color: var(--devtools-text-primary);
94
+ }
95
+
96
+ &--close:hover {
97
+ background: var(--devtools-hover-danger);
98
+ }
99
+ }
@@ -0,0 +1,79 @@
1
+ import { Component, computed, inject, input, output } from '@angular/core';
2
+ import { DevToolbarStateService } from '../../dev-toolbar-state.service';
3
+ import { WindowConfig } from './window.models';
4
+
5
+ @Component({
6
+ selector: 'ndt-window',
7
+ standalone: true,
8
+ template: `
9
+ <div class="window dev-toolbar" [attr.data-theme]="theme()">
10
+ <div class="header">
11
+ <div class="header__content">
12
+ <div class="header__title">
13
+ <h1>{{ config().title }}</h1>
14
+ @if (config().isBeta) {
15
+ <span class="beta-tag">BETA</span>
16
+ }
17
+ </div>
18
+ <p class="header__description">{{ config().description }}</p>
19
+ </div>
20
+ <div class="header__controls">
21
+ @if (config().isMinimizable) {
22
+ <button
23
+ aria-label="Minimize"
24
+ class="control"
25
+ (click)="onMinimize()"
26
+ >
27
+
28
+ </button>
29
+ }
30
+ @if (config().isMaximizable) {
31
+ <button
32
+ aria-label="Maximize"
33
+ class="control"
34
+ (click)="onMaximize()"
35
+ >
36
+
37
+ </button>
38
+ }
39
+ @if (config().isClosable) {
40
+ <button
41
+ aria-label="Close"
42
+ class="control control--close"
43
+ (click)="onClose()"
44
+ >
45
+ ×
46
+ </button>
47
+ }
48
+ </div>
49
+ </div>
50
+
51
+ <div class="divider"></div>
52
+ <div class="content">
53
+ <ng-content></ng-content>
54
+ </div>
55
+ </div>
56
+ `,
57
+ styleUrls: ['./window.component.scss'],
58
+ })
59
+ export class DevToolbarWindowComponent {
60
+ readonly devToolbarStateService = inject(DevToolbarStateService);
61
+ readonly config = input.required<WindowConfig>();
62
+ readonly close = output<void>();
63
+ readonly maximize = output<void>();
64
+ readonly minimize = output<void>();
65
+
66
+ readonly theme = computed(() => this.devToolbarStateService.theme());
67
+
68
+ protected onClose(): void {
69
+ this.close.emit();
70
+ }
71
+
72
+ protected onMaximize(): void {
73
+ this.maximize.emit();
74
+ }
75
+
76
+ protected onMinimize(): void {
77
+ this.minimize.emit();
78
+ }
79
+ }
@@ -0,0 +1,28 @@
1
+ export type WindowPlacement =
2
+ | 'bottom-left'
3
+ | 'bottom-center'
4
+ | 'bottom-right'
5
+ | 'top-left'
6
+ | 'top-center'
7
+ | 'top-right';
8
+
9
+ export type WindowSize = 'small' | 'medium' | 'tall' | 'large';
10
+ export interface WindowConfig {
11
+ id: string;
12
+ /**
13
+ * The title of the window, this can be different from the name of the tool
14
+ */
15
+ title: string;
16
+ description?: string;
17
+ isClosable?: boolean;
18
+ isMaximizable?: boolean;
19
+ isMinimizable?: boolean;
20
+ placement?: WindowPlacement;
21
+ size?: WindowSize;
22
+ isBeta?: boolean;
23
+ }
24
+
25
+ export interface WindowPosition {
26
+ x: number;
27
+ y: number;
28
+ }
@@ -0,0 +1,89 @@
1
+ import { Injectable, computed, signal } from '@angular/core';
2
+
3
+ interface DevToolbarState {
4
+ isHidden: boolean;
5
+ activeToolId: string | null;
6
+ error: string | null;
7
+ theme: 'light' | 'dark';
8
+ delay: number;
9
+ }
10
+
11
+ @Injectable({
12
+ providedIn: 'root',
13
+ })
14
+ export class DevToolbarStateService {
15
+ // Initial state
16
+ private state = signal<DevToolbarState>({
17
+ isHidden: true,
18
+ activeToolId: null,
19
+ delay: 3000,
20
+ error: null,
21
+ theme: 'dark',
22
+ });
23
+
24
+ // Selectors
25
+ readonly isVisible = computed(
26
+ () => !this.state().isHidden || this.hasActiveTool()
27
+ );
28
+ readonly isDarkTheme = computed(() => this.state().theme === 'dark');
29
+ readonly activeToolId = computed(() => this.state().activeToolId);
30
+ readonly hasActiveTool = computed(() => this.state().activeToolId !== null);
31
+ readonly error = computed(() => this.state().error);
32
+ readonly theme = computed(() => this.state().theme);
33
+ /**
34
+ * The delay to hide the toolbar
35
+ */
36
+ readonly delay = computed(() => this.state().delay);
37
+
38
+ // State updates
39
+ setVisibility(isVisible: boolean): void {
40
+ if (isVisible) {
41
+ this.state.update((state) => ({
42
+ ...state,
43
+ isHidden: false,
44
+ }));
45
+ } else {
46
+ if (this.activeToolId() === null) {
47
+ setTimeout(() => {
48
+ this.state.update((state) => ({
49
+ ...state,
50
+ isHidden: true,
51
+ }));
52
+ }, this.state().delay);
53
+ }
54
+ }
55
+ }
56
+
57
+ setTheme(theme: 'light' | 'dark'): void {
58
+ this.state.update((state) => ({
59
+ ...state,
60
+ theme,
61
+ }));
62
+ }
63
+
64
+ setActiveTool(toolId: string | null): void {
65
+ this.state.update((state) => ({
66
+ ...state,
67
+ activeToolId: toolId,
68
+ }));
69
+
70
+ if (toolId === null) {
71
+ this.setVisibility(false);
72
+ } else {
73
+ this.setVisibility(true);
74
+ }
75
+ }
76
+
77
+ // Public actions
78
+ toggleTool(toolId: string | null): void {
79
+ const currentToolId = this.activeToolId();
80
+ this.setActiveTool(currentToolId === toolId ? null : toolId);
81
+ }
82
+
83
+ toggleVisibility(): void {
84
+ this.state.update((state) => ({
85
+ ...state,
86
+ isHidden: !state.isHidden,
87
+ }));
88
+ }
89
+ }
@@ -0,0 +1,22 @@
1
+ @use './styles' as devtools;
2
+ @use 'sass:map';
3
+
4
+ .dev-toolbar {
5
+ position: fixed;
6
+ bottom: 0;
7
+ left: 50%;
8
+ z-index: map.get(devtools.$z-indices, toolbar);
9
+ transform: translateX(-50%);
10
+ display: flex;
11
+ pointer-events: auto;
12
+ background: var(--devtools-bg-primary);
13
+ border: 1px solid var(--devtools-border-primary);
14
+ border-radius: map.get(map.get(devtools.$dimensions, border-radius), full);
15
+ box-shadow: var(--devtools-shadow-toolbar);
16
+ height: map.get(devtools.$dimensions, toolbar-height);
17
+ overflow: hidden;
18
+
19
+ &--active {
20
+ opacity: 1;
21
+ }
22
+ }
@@ -0,0 +1,104 @@
1
+ import {
2
+ animate,
3
+ state,
4
+ style,
5
+ transition,
6
+ trigger,
7
+ } from '@angular/animations';
8
+ import {
9
+ Component,
10
+ DestroyRef,
11
+ OnInit,
12
+ inject,
13
+ isDevMode,
14
+ } from '@angular/core';
15
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
16
+ import { fromEvent } from 'rxjs';
17
+ import { filter, throttleTime } from 'rxjs/operators';
18
+ import { DevToolbarStateService } from './dev-toolbar-state.service';
19
+ import { DevToolbarFeatureFlagsToolComponent } from './tools/feature-flags-tool/feature-flags-tool.component';
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';
23
+
24
+ @Component({
25
+ standalone: true,
26
+ selector: 'ndt-toolbar',
27
+ styleUrls: ['./dev-toolbar.component.scss'],
28
+ imports: [
29
+ DevToolbarHomeToolComponent,
30
+ DevToolbarLanguageToolComponent,
31
+ DevToolbarFeatureFlagsToolComponent,
32
+ ],
33
+ template: `
34
+ @if (isDevMode) {
35
+ <div
36
+ aria-label="Developer tools"
37
+ role="toolbar"
38
+ class="dev-toolbar"
39
+ [@toolbarState]="state.isVisible() ? 'visible' : 'hidden'"
40
+ [attr.data-theme]="state.theme()"
41
+ [class.dev-toolbar--active]="state.isVisible()"
42
+ (mouseenter)="onMouseEnter()"
43
+ >
44
+ <ndt-home-tool />
45
+ <ndt-language-tool />
46
+ <ndt-feature-flags-tool />
47
+ </div>
48
+ }
49
+ `,
50
+ animations: [
51
+ trigger('toolbarState', [
52
+ state(
53
+ 'hidden',
54
+ style({
55
+ transform: 'translate(-50%, calc(100% + -1.2rem))',
56
+ })
57
+ ),
58
+ state(
59
+ 'visible',
60
+ style({
61
+ transform: 'translate(-50%, -1rem)',
62
+ })
63
+ ),
64
+ transition('hidden <=> visible', [
65
+ animate('300ms cubic-bezier(0.4, 0, 0.2, 1)'),
66
+ ]),
67
+ ]),
68
+ ],
69
+ })
70
+ export class DevToolbarComponent implements OnInit {
71
+ state = inject(DevToolbarStateService);
72
+ destroyRef = inject(DestroyRef);
73
+ settingsService = inject(SettingsService);
74
+
75
+ isDevMode = isDevMode();
76
+
77
+ private keyboardShortcut = fromEvent<KeyboardEvent>(window, 'keydown')
78
+ .pipe(
79
+ filter((event) => event.ctrlKey && event.shiftKey && event.key === 'D'),
80
+ takeUntilDestroyed(this.destroyRef)
81
+ )
82
+ .subscribe(() => this.toggleDevTools());
83
+
84
+ private mouseLeave = fromEvent<MouseEvent>(document, 'mouseleave')
85
+ .pipe(throttleTime(3000), takeUntilDestroyed(this.destroyRef))
86
+ .subscribe(() => this.onMouseLeave());
87
+
88
+ ngOnInit(): void {
89
+ const settings = this.settingsService.getSettings();
90
+ this.state.setTheme(settings.isDarkMode ? 'dark' : 'light');
91
+ }
92
+
93
+ onMouseEnter(): void {
94
+ this.state.setVisibility(true);
95
+ }
96
+
97
+ onMouseLeave(): void {
98
+ setTimeout(() => this.state.setVisibility(false), this.state.delay());
99
+ }
100
+
101
+ private toggleDevTools(): void {
102
+ this.state.setVisibility(!this.state.isVisible());
103
+ }
104
+ }
package/src/index.ts CHANGED
@@ -1 +1,6 @@
1
- export * from './lib/ngx-dev-toolbar/ngx-dev-toolbar.component';
1
+ export * from './dev-toolbar.component';
2
+ export * from './models/dev-tools.interface';
3
+ export * from './tools/feature-flags-tool/feature-flags.models';
4
+ export * from './tools/feature-flags-tool/feature-flags.service';
5
+ export * from './tools/language-tool/language.models';
6
+ 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
+ }