ngx-dev-toolbar 0.0.2-1 → 0.0.2-2

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 (50) hide show
  1. package/package.json +5 -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 +120 -0
  13. package/src/components/icons/icon.models.ts +20 -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/users-icon.component.ts +27 -0
  25. package/src/components/input/input.component.ts +66 -0
  26. package/src/components/select/select.component.scss +83 -0
  27. package/src/components/select/select.component.ts +40 -0
  28. package/src/components/tool-button/tool-button.component.scss +67 -0
  29. package/src/components/tool-button/tool-button.component.ts +127 -0
  30. package/src/components/toolbar-tool/toolbar-tool.component.scss +9 -0
  31. package/src/components/toolbar-tool/toolbar-tool.component.ts +120 -0
  32. package/src/components/toolbar-tool/toolbar-tool.models.ts +9 -0
  33. package/src/components/window/window.component.scss +96 -0
  34. package/src/components/window/window.component.ts +79 -0
  35. package/src/components/window/window.models.ts +28 -0
  36. package/src/dev-toolbar-state.service.ts +84 -0
  37. package/src/dev-toolbar.component.scss +21 -0
  38. package/src/dev-toolbar.component.ts +102 -0
  39. package/src/index.ts +3 -1
  40. package/src/styles.scss +361 -0
  41. package/src/tools/feature-flags-tool/feature-flags-tool.component.ts +255 -0
  42. package/src/tools/feature-flags-tool/feature-flags.models.ts +10 -0
  43. package/src/tools/feature-flags-tool/feature-flags.service.ts +110 -0
  44. package/src/tools/index.ts +5 -0
  45. package/src/tools/settings-tool/settings-tool.component.scss +61 -0
  46. package/src/tools/settings-tool/settings-tool.component.ts +76 -0
  47. package/src/tools/settings-tool/settings.models.ts +3 -0
  48. package/src/tools/settings-tool/settings.service.spec.ts +59 -0
  49. package/src/tools/settings-tool/settings.service.ts +21 -0
  50. package/src/utils/storage.service.ts +19 -0
@@ -0,0 +1,96 @@
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
+ &__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
+
46
+ &__title {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: var(--devtools-spacing-sm);
50
+
51
+ .beta-tag {
52
+ font-size: var(--devtools-font-size-xxs, 0.65rem);
53
+ background: var(--devtools-purple, #8b5cf6);
54
+ color: var(--devtools-text-on-primary);
55
+ padding: 1px 4px;
56
+ margin-left: var(--devtools-spacing-xs);
57
+ border-radius: var(--devtools-border-radius-small);
58
+ font-weight: 500;
59
+ text-transform: uppercase;
60
+ letter-spacing: 0.5px;
61
+ }
62
+ }
63
+ }
64
+
65
+ .content {
66
+ flex: 1;
67
+ overflow: auto;
68
+ }
69
+
70
+ .divider {
71
+ height: 1px;
72
+ background-color: var(--devtools-border-primary);
73
+ margin-bottom: var(--devtools-spacing-md);
74
+ margin-top: var(--devtools-spacing-md);
75
+ }
76
+
77
+ .control {
78
+ background: none;
79
+ border: none;
80
+ color: var(--devtools-text-secondary);
81
+ cursor: pointer;
82
+ padding: var(--devtools-spacing-xs) var(--devtools-spacing-sm);
83
+ border-radius: var(--devtools-border-radius-small);
84
+ font-size: var(--devtools-font-size-md);
85
+ line-height: 1;
86
+ transition: var(--devtools-transition-smooth);
87
+
88
+ &:hover {
89
+ background: var(--devtools-hover-bg);
90
+ color: var(--devtools-text-primary);
91
+ }
92
+
93
+ &--close:hover {
94
+ background: var(--devtools-hover-danger);
95
+ }
96
+ }
@@ -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 = 'tall' | 'medium';
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,84 @@
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(() => !this.state().isHidden);
26
+ readonly isDarkTheme = computed(() => this.state().theme === 'dark');
27
+ readonly activeToolId = computed(() => this.state().activeToolId);
28
+ readonly hasActiveTool = computed(() => this.state().activeToolId !== null);
29
+ readonly error = computed(() => this.state().error);
30
+ readonly theme = computed(() => this.state().theme);
31
+ readonly delay = computed(() => this.state().delay);
32
+
33
+ // State updates
34
+ setVisibility(isVisible: boolean): void {
35
+ if (isVisible) {
36
+ this.state.update((state) => ({
37
+ ...state,
38
+ isHidden: false,
39
+ }));
40
+ } else {
41
+ if (this.activeToolId() === null) {
42
+ setTimeout(() => {
43
+ this.state.update((state) => ({
44
+ ...state,
45
+ isHidden: true,
46
+ }));
47
+ }, this.state().delay);
48
+ }
49
+ }
50
+ }
51
+
52
+ setTheme(theme: 'light' | 'dark'): void {
53
+ this.state.update((state) => ({
54
+ ...state,
55
+ theme,
56
+ }));
57
+ }
58
+
59
+ setActiveTool(toolId: string | null): void {
60
+ this.state.update((state) => ({
61
+ ...state,
62
+ activeToolId: toolId,
63
+ }));
64
+
65
+ if (toolId === null) {
66
+ this.setVisibility(false);
67
+ } else {
68
+ this.setVisibility(true);
69
+ }
70
+ }
71
+
72
+ // Public actions
73
+ toggleTool(toolId: string | null): void {
74
+ const currentToolId = this.activeToolId();
75
+ this.setActiveTool(currentToolId === toolId ? null : toolId);
76
+ }
77
+
78
+ toggleVisibility(): void {
79
+ this.state.update((state) => ({
80
+ ...state,
81
+ isHidden: !state.isHidden,
82
+ }));
83
+ }
84
+ }
@@ -0,0 +1,21 @@
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-gradient);
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
+
18
+ &--active {
19
+ opacity: 1;
20
+ }
21
+ }
@@ -0,0 +1,102 @@
1
+ import {
2
+ animate,
3
+ state,
4
+ style,
5
+ transition,
6
+ trigger,
7
+ } from '@angular/animations';
8
+ import { Component, DestroyRef, OnInit, inject } from '@angular/core';
9
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
+ import { fromEvent } from 'rxjs';
11
+ 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
+ import { DevToolbarStateService } from './dev-toolbar-state.service';
15
+ import { DevToolbarFeatureFlagsToolComponent } from './tools/feature-flags-tool/feature-flags-tool.component';
16
+ import { DevToolbarSettingsToolComponent } from './tools/settings-tool/settings-tool.component';
17
+ import { SettingsService } from './tools/settings-tool/settings.service';
18
+
19
+ @Component({
20
+ standalone: true,
21
+ selector: 'ndt-toolbar',
22
+ styleUrls: ['./dev-toolbar.component.scss'],
23
+ imports: [
24
+ DevToolbarToolButtonComponent,
25
+ DevToolbarFeatureFlagsToolComponent,
26
+ DevToolbarSettingsToolComponent,
27
+ DevToolbarIconComponent,
28
+ ],
29
+
30
+ template: `
31
+ <div
32
+ aria-label="Developer tools"
33
+ role="toolbar"
34
+ class="dev-toolbar"
35
+ [@toolbarState]="state.isVisible() ? 'visible' : 'hidden'"
36
+ [attr.data-theme]="state.theme()"
37
+ [class.dev-toolbar--active]="state.isVisible()"
38
+ (mouseenter)="onMouseEnter()"
39
+ >
40
+ <ndt-tool-button title="Home" toolId="ndt-home">
41
+ <ndt-icon name="angular" />
42
+ </ndt-tool-button>
43
+ <ndt-tool-button title="Performance" toolId="ndt-performance">
44
+ <ndt-icon name="gauge" />
45
+ </ndt-tool-button>
46
+ <ndt-feature-flags-tool />
47
+ <ndt-settings-tool />
48
+ </div>
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
+ private keyboardShortcut = fromEvent<KeyboardEvent>(window, 'keydown')
76
+ .pipe(
77
+ filter((event) => event.ctrlKey && event.shiftKey && event.key === 'D'),
78
+ takeUntilDestroyed(this.destroyRef)
79
+ )
80
+ .subscribe(() => this.toggleDevTools());
81
+
82
+ private mouseLeave = fromEvent<MouseEvent>(document, 'mouseleave')
83
+ .pipe(throttleTime(3000), takeUntilDestroyed(this.destroyRef))
84
+ .subscribe(() => this.onMouseLeave());
85
+
86
+ ngOnInit(): void {
87
+ const settings = this.settingsService.getSettings();
88
+ this.state.setTheme(settings.isDarkMode ? 'dark' : 'light');
89
+ }
90
+
91
+ onMouseEnter(): void {
92
+ this.state.setVisibility(true);
93
+ }
94
+
95
+ onMouseLeave(): void {
96
+ setTimeout(() => this.state.setVisibility(false), this.state.delay());
97
+ }
98
+
99
+ private toggleDevTools(): void {
100
+ this.state.setVisibility(!this.state.isVisible());
101
+ }
102
+ }
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
- export * from './lib/ngx-dev-toolbar/ngx-dev-toolbar.component';
1
+ export * from './dev-toolbar.component';
2
+ export * from './tools/feature-flags-tool/feature-flags.models';
3
+ export * from './tools/feature-flags-tool/feature-flags.service';