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,27 @@
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ndt-sun-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="M128,60a68,68,0,1,0,68,68A68.07,68.07,0,0,0,128,60Z"
17
+ opacity="0.2"
18
+ />
19
+ <path
20
+ d="M128,44a8,8,0,0,0,8-8V16a8,8,0,0,0-16,0V36A8,8,0,0,0,128,44ZM57.31,68.69a8,8,0,0,0,11.32-11.32L54.63,43.37A8,8,0,0,0,43.31,54.69ZM44,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H36A8,8,0,0,0,44,128Zm24.63,59.31-14,14a8,8,0,0,0,11.32,11.32l14-14a8,8,0,0,0-11.32-11.32ZM128,212a8,8,0,0,0-8,8v20a8,8,0,0,0,16,0V220A8,8,0,0,0,128,212Zm70.69-24.69a8,8,0,0,0-11.32,11.32l14,14a8,8,0,0,0,11.32-11.32ZM240,120H220a8,8,0,0,0,0,16h20a8,8,0,0,0,0-16Zm-24.69-62.63-14,14a8,8,0,0,0,11.32,11.32l14-14a8,8,0,0,0-11.32-11.32ZM128,76a52,52,0,1,0,52,52A52.06,52.06,0,0,0,128,76Zm0,88a36,36,0,1,1,36-36A36,36,0,0,1,128,164Z"
21
+ />
22
+ </svg>
23
+ `,
24
+ })
25
+ export class SunIconComponent {
26
+ fill = input<string>('#FFFF');
27
+ }
@@ -0,0 +1,27 @@
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ndt-terminal-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="M224,56V200a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8H216A8,8,0,0,1,224,56Z"
17
+ opacity="0.2"
18
+ ></path>
19
+ <path
20
+ d="M128,128a8,8,0,0,1-3,6.25l-40,32a8,8,0,1,1-10-12.5L107.19,128,75,102.25a8,8,0,1,1,10-12.5l40,32A8,8,0,0,1,128,128Zm48,24H136a8,8,0,0,0,0,16h40a8,8,0,0,0,0-16Zm56-96V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56ZM216,200V56H40V200H216Z"
21
+ ></path>
22
+ </svg>
23
+ `,
24
+ })
25
+ export class TerminalIconComponent {
26
+ fill = input<string>('#FFFF');
27
+ }
@@ -0,0 +1,27 @@
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ndt-toggle-left-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="M112,128A32,32,0,1,1,80,96,32,32,0,0,1,112,128Z"
17
+ opacity="0.2"
18
+ ></path>
19
+ <path
20
+ d="M176,56H80a72,72,0,0,0,0,144h96a72,72,0,0,0,0-144Zm0,128H80A56,56,0,0,1,80,72h96a56,56,0,0,1,0,112ZM80,88a40,40,0,1,0,40,40A40,40,0,0,0,80,88Zm0,64a24,24,0,1,1,24-24A24,24,0,0,1,80,152Z"
21
+ ></path>
22
+ </svg>
23
+ `,
24
+ })
25
+ export class ToggleLeftIconComponent {
26
+ fill = input<string>('#FFFF');
27
+ }
@@ -0,0 +1,27 @@
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'ndt-users-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="M224,128a95.76,95.76,0,0,1-31.8,71.37A72,72,0,0,0,128,160a40,40,0,1,0-40-40,40,40,0,0,0,40,40,72,72,0,0,0-64.2,39.37h0A96,96,0,1,1,224,128Z"
17
+ opacity="0.2"
18
+ ></path>
19
+ <path
20
+ d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24ZM74.08,197.5a64,64,0,0,1,107.84,0,87.83,87.83,0,0,1-107.84,0ZM96,120a32,32,0,1,1,32,32A32,32,0,0,1,96,120Zm97.76,66.41a79.66,79.66,0,0,0-36.06-28.75,48,48,0,1,0-59.4,0,79.66,79.66,0,0,0-36.06,28.75,88,88,0,1,1,131.52,0Z"
21
+ ></path>
22
+ </svg>
23
+ `,
24
+ })
25
+ export class UsersIconComponent {
26
+ fill = input<string>('#FFFF');
27
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ input,
5
+ model,
6
+ } from '@angular/core';
7
+ import { FormsModule } from '@angular/forms';
8
+
9
+ @Component({
10
+ selector: 'ndt-input',
11
+ standalone: true,
12
+ imports: [FormsModule],
13
+ template: `
14
+ <input
15
+ [attr.aria-label]="ariaLabel()"
16
+ [type]="type()"
17
+ [class]="inputClass()"
18
+ [ngModel]="value()"
19
+ [placeholder]="placeholder()"
20
+ (ngModelChange)="value.set($event)"
21
+ />
22
+ `,
23
+ styles: [
24
+ `
25
+ :host {
26
+ display: block;
27
+ }
28
+
29
+ .input {
30
+ width: 100%;
31
+ padding: var(--devtools-spacing-sm) var(--devtools-spacing-md);
32
+ border: 1px solid var(--devtools-border-primary);
33
+ border-radius: var(--devtools-border-radius-small);
34
+ background-color: var(--devtools-bg-primary);
35
+ color: var(--devtools-text-primary);
36
+ font-size: var(--devtools-font-size-sm);
37
+ transition: var(--devtools-transition-default);
38
+
39
+ &::placeholder {
40
+ color: var(--devtools-text-muted);
41
+ }
42
+
43
+ &:focus {
44
+ outline: none;
45
+ border-color: var(--devtools-primary);
46
+ box-shadow: 0 0 0 1px var(--devtools-primary);
47
+ }
48
+
49
+ &:disabled {
50
+ background-color: var(--devtools-background-secondary);
51
+ cursor: not-allowed;
52
+ color: var(--devtools-text-muted);
53
+ }
54
+ }
55
+ `,
56
+ ],
57
+ changeDetection: ChangeDetectionStrategy.OnPush,
58
+ })
59
+ export class DevToolbarInputComponent {
60
+ value = model.required<string>();
61
+
62
+ type = input<string>('text');
63
+ placeholder = input<string>('');
64
+ ariaLabel = input<string>('');
65
+ inputClass = input<string>('input');
66
+ }
@@ -0,0 +1,83 @@
1
+ .select {
2
+ width: 100%;
3
+ cursor: pointer;
4
+ min-width: 100px;
5
+ display: flex;
6
+ align-items: center;
7
+ padding: var(--devtools-spacing-sm) var(--devtools-spacing-md);
8
+ border: 1px solid var(--devtools-border-primary);
9
+ border-radius: var(--devtools-border-radius-small);
10
+ background-color: var(--devtools-bg-primary);
11
+ color: var(--devtools-text-primary);
12
+ font-size: var(--devtools-font-size-sm);
13
+ gap: var(--devtools-spacing-xs);
14
+ appearance: none;
15
+ background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23666666%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
16
+ background-repeat: no-repeat;
17
+ background-position: right var(--devtools-spacing-sm) center;
18
+ background-size: 0.65em auto;
19
+ padding-right: 2.5em;
20
+ transition: var(--devtools-transition-default);
21
+
22
+ &.small {
23
+ padding: calc(var(--devtools-spacing-xs) / 2) var(--devtools-spacing-sm);
24
+ font-size: var(--devtools-font-size-sm);
25
+ height: 24px;
26
+ padding-right: 2em;
27
+ }
28
+
29
+ &:hover {
30
+ background-color: var(--devtools-background-hover);
31
+ }
32
+
33
+ &:focus {
34
+ outline: none;
35
+ border-color: var(--devtools-primary);
36
+ box-shadow: 0 0 0 1px var(--devtools-primary);
37
+ }
38
+
39
+ &:disabled {
40
+ opacity: 0.6;
41
+ cursor: not-allowed;
42
+ background-color: var(--devtools-background-disabled);
43
+ }
44
+
45
+ &:invalid {
46
+ border-color: var(--devtools-border-error, #ff4444);
47
+ }
48
+ }
49
+
50
+ /* Style for Firefox */
51
+ @-moz-document url-prefix() {
52
+ .select {
53
+ text-indent: 0.01px;
54
+ text-overflow: '';
55
+ padding-right: 2.5em;
56
+ }
57
+ }
58
+
59
+ /* Style for Webkit browsers */
60
+ .select::-ms-expand {
61
+ display: none;
62
+ }
63
+
64
+ /* Additional browser-specific overrides */
65
+ select::-ms-expand {
66
+ display: none;
67
+ }
68
+
69
+ /* For Webkit browsers (Chrome, Safari) */
70
+ select:-webkit-autofill,
71
+ select:-webkit-autofill:hover,
72
+ select:-webkit-autofill:focus {
73
+ -webkit-box-shadow: 0 0 0px 1000px var(--devtools-bg-primary) inset !important;
74
+ -webkit-text-fill-color: var(--devtools-text-primary) !important;
75
+ }
76
+
77
+ /* For Firefox */
78
+ @-moz-document url-prefix() {
79
+ .select option {
80
+ background-color: var(--devtools-bg-primary) !important;
81
+ color: var(--devtools-text-primary) !important;
82
+ }
83
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ input,
5
+ model,
6
+ } from '@angular/core';
7
+ import { FormsModule } from '@angular/forms';
8
+
9
+ export interface SelectOption {
10
+ value: string;
11
+ label: string;
12
+ }
13
+
14
+ @Component({
15
+ selector: 'ndt-select',
16
+ standalone: true,
17
+ imports: [FormsModule],
18
+ template: `
19
+ <select
20
+ class="select"
21
+ [attr.aria-label]="ariaLabel()"
22
+ [class.small]="size() === 'small'"
23
+ [ngModel]="value()"
24
+ (ngModelChange)="value.set($event)"
25
+ >
26
+ @for (option of options(); track option.value) {
27
+ <option role="option" [value]="option.value">{{ option.label }}</option>
28
+ }
29
+ </select>
30
+ `,
31
+ styleUrls: ['./select.component.scss'],
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ })
34
+ export class DevToolbarSelectComponent {
35
+ value = model<string>();
36
+ options = input.required<SelectOption[]>();
37
+ ariaLabel = input<string>('');
38
+ label = input<string>('');
39
+ size = input<'small' | 'medium'>('medium');
40
+ }
@@ -0,0 +1,67 @@
1
+ @use '../../styles' as devtools;
2
+ @use 'sass:map';
3
+ $dimensions: devtools.$dimensions;
4
+
5
+ .tool-button {
6
+ display: flex;
7
+ justify-content: center;
8
+ align-items: center;
9
+ width: map.get($dimensions, toolbar-button-width);
10
+ height: map.get($dimensions, toolbar-height);
11
+ border: 0;
12
+ background: transparent;
13
+ color: var(--devtools-text-primary);
14
+ transition: var(--devtools-transition-default);
15
+ cursor: pointer;
16
+ opacity: 0.5;
17
+ position: relative;
18
+
19
+ &:hover {
20
+ background: var(--devtools-hover-bg);
21
+ opacity: 1;
22
+ }
23
+
24
+ &--toolbar-visible {
25
+ background: transparent;
26
+ opacity: 1;
27
+ }
28
+
29
+ ::ng-deep svg {
30
+ width: 24px;
31
+ height: 24px;
32
+ display: block;
33
+ margin: auto;
34
+ }
35
+
36
+ &__badge {
37
+ position: absolute;
38
+ top: -0.25rem;
39
+ right: -0.25rem;
40
+ background-color: var(--devtools-hover-danger);
41
+ color: var(--devtools-text-primary);
42
+ border-radius: var(--devtools-border-radius-full);
43
+ min-width: 1rem;
44
+ height: 1rem;
45
+ font-size: 0.75rem;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ padding: 0.125rem;
50
+ }
51
+ }
52
+
53
+ .tooltip {
54
+ position: absolute;
55
+ bottom: calc(100% + 1.2rem);
56
+ left: 50%;
57
+ transform: translateX(-50%);
58
+ background: var(--devtools-bg-primary);
59
+ color: var(--devtools-text-primary);
60
+ padding: 0.5rem 0.75rem;
61
+ border-radius: var(--devtools-border-radius-medium);
62
+ font-size: 0.75rem;
63
+ white-space: nowrap;
64
+ pointer-events: none;
65
+ z-index: 1000;
66
+ box-shadow: var(--devtools-shadow-tooltip);
67
+ }
@@ -0,0 +1,127 @@
1
+ import {
2
+ animate,
3
+ state,
4
+ style,
5
+ transition,
6
+ trigger,
7
+ } from '@angular/animations';
8
+ import {
9
+ ChangeDetectionStrategy,
10
+ Component,
11
+ ElementRef,
12
+ computed,
13
+ inject,
14
+ input,
15
+ output,
16
+ signal,
17
+ } from '@angular/core';
18
+ import { DevToolbarStateService } from '../../dev-toolbar-state.service';
19
+
20
+ @Component({
21
+ selector: 'ndt-tool-button',
22
+ standalone: true,
23
+ template: `
24
+ <button
25
+ class="tool-button"
26
+ [class.tool-button--toolbar-visible]="isToolbarVisible()"
27
+ [class.tool-button--active]="isActive()"
28
+ [class.tool-button--focus]="isFocused()"
29
+ (mouseenter)="onMouseEnter()"
30
+ (focusin)="onFocus()"
31
+ (focusout)="onBlur()"
32
+ (mouseleave)="onMouseLeave()"
33
+ (keydown.escape)="onEscape()"
34
+ >
35
+ @if (isTooltipVisible()) {
36
+ <span
37
+ class="tooltip"
38
+ [@tooltipAnimation]="tooltipState ? 'visible' : 'hidden'"
39
+ >
40
+ {{ tooltip() }}
41
+ </span>
42
+ }
43
+ <ng-content />
44
+ </button>
45
+ `,
46
+ styleUrls: ['./tool-button.component.scss'],
47
+ animations: [
48
+ trigger('tooltipAnimation', [
49
+ state(
50
+ 'hidden',
51
+ style({
52
+ opacity: 0,
53
+ transform: 'translateX(-50%) translateY(1rem)',
54
+ })
55
+ ),
56
+ state(
57
+ 'visible',
58
+ style({
59
+ opacity: 1,
60
+ transform: 'translateX(-50%) translateY(0)',
61
+ })
62
+ ),
63
+ transition('hidden => visible', [animate('200ms ease-out')]),
64
+ transition('visible => hidden', [animate('150ms ease-in')]),
65
+ ]),
66
+ ],
67
+ changeDetection: ChangeDetectionStrategy.OnPush,
68
+ })
69
+ export class DevToolbarToolButtonComponent {
70
+ // Injects
71
+ private readonly state = inject(DevToolbarStateService);
72
+ private readonly elementRef = inject(ElementRef);
73
+
74
+ // Inputs
75
+ readonly title = input.required<string>();
76
+ readonly toolId = input.required<string>();
77
+ // Outputs
78
+ // eslint-disable-next-line @angular-eslint/no-output-native
79
+ readonly click = output<void>();
80
+
81
+ // Signals
82
+ readonly isActive = computed(
83
+ () => this.state.activeToolId() === this.toolId()
84
+ );
85
+ readonly isToolbarVisible = this.state.isVisible;
86
+
87
+ readonly isFocused = signal(false);
88
+ readonly tooltip = computed(
89
+ () =>
90
+ this.elementRef.nativeElement.parentElement?.getAttribute(
91
+ 'data-tooltip'
92
+ ) ?? ''
93
+ );
94
+ readonly isTooltipVisible = computed(
95
+ () => this.tooltip() && !this.isActive()
96
+ );
97
+
98
+ // Properties
99
+ protected tooltipState = false;
100
+ private readonly hideDelay = 3000;
101
+
102
+ // Public methods
103
+ onClick(): void {
104
+ this.isFocused.set(false);
105
+ this.click.emit();
106
+ }
107
+
108
+ onMouseEnter(): void {
109
+ this.tooltipState = true;
110
+ }
111
+
112
+ onMouseLeave(): void {
113
+ this.tooltipState = false;
114
+ }
115
+
116
+ onEscape(): void {
117
+ this.isFocused.set(false);
118
+ }
119
+
120
+ onFocus(): void {
121
+ this.isFocused.set(true);
122
+ }
123
+
124
+ onBlur(): void {
125
+ this.isFocused.set(false);
126
+ }
127
+ }
@@ -0,0 +1,9 @@
1
+ @use '../../styles' as devtools;
2
+
3
+ .tool {
4
+ position: relative;
5
+ }
6
+
7
+ .trigger {
8
+ cursor: pointer;
9
+ }
@@ -0,0 +1,120 @@
1
+ import { CdkConnectedOverlay, OverlayModule } from '@angular/cdk/overlay';
2
+ import {
3
+ ChangeDetectionStrategy,
4
+ Component,
5
+ ContentChild,
6
+ ElementRef,
7
+ ViewChild,
8
+ computed,
9
+ inject,
10
+ input,
11
+ } from '@angular/core';
12
+ import { DevToolbarStateService } from '../../dev-toolbar-state.service';
13
+ import { DevToolbarIconComponent } from '../icons/icon.component';
14
+ import { IconName } from '../icons/icon.models';
15
+ import { DevToolbarToolButtonComponent } from '../tool-button/tool-button.component';
16
+ import { DevToolbarWindowComponent } from '../window/window.component';
17
+ import { WindowConfig } from '../window/window.models';
18
+
19
+ @Component({
20
+ selector: 'ndt-toolbar-tool',
21
+ standalone: true,
22
+ imports: [
23
+ CdkConnectedOverlay,
24
+ OverlayModule,
25
+ DevToolbarWindowComponent,
26
+ DevToolbarToolButtonComponent,
27
+ DevToolbarIconComponent,
28
+ ],
29
+ template: `
30
+ <div #trigger="cdkOverlayOrigin" class="dev-toolbar-tool" cdkOverlayOrigin>
31
+ <div
32
+ class="dev-toolbar-tool__icon"
33
+ (click)="onOpen()"
34
+ (keydown.enter)="onOpen()"
35
+ (keydown.space)="onOpen()"
36
+ tabindex="0"
37
+ >
38
+ <div [attr.data-tooltip]="title()">
39
+ @if (icon()) {
40
+ <ndt-tool-button [title]="title()" [toolId]="windowConfig().id">
41
+ <ndt-icon [name]="icon()" />
42
+ </ndt-tool-button>
43
+ } @else {
44
+ <ng-content select="ndt-tool-button"></ng-content>
45
+ }
46
+ </div>
47
+ </div>
48
+
49
+ @if (isActive()) {
50
+ <ng-template
51
+ #contentTemplate
52
+ [cdkConnectedOverlayOrigin]="trigger"
53
+ [cdkConnectedOverlayOpen]="isActive()"
54
+ [cdkConnectedOverlayPositions]="positions()"
55
+ [cdkConnectedOverlayWidth]="width()"
56
+ [cdkConnectedOverlayHeight]="height()"
57
+ cdkConnectedOverlay
58
+ >
59
+ <ndt-window [config]="windowConfig()" (close)="onClose()">
60
+ <ng-content />
61
+ </ndt-window>
62
+ </ng-template>
63
+ }
64
+ </div>
65
+ `,
66
+ styleUrl: './toolbar-tool.component.scss',
67
+ changeDetection: ChangeDetectionStrategy.OnPush,
68
+ })
69
+ export class DevToolbarToolComponent {
70
+ state = inject(DevToolbarStateService);
71
+ @ViewChild('trigger') trigger!: ElementRef;
72
+
73
+ @ContentChild(DevToolbarToolButtonComponent)
74
+ buttonComponent!: DevToolbarToolButtonComponent;
75
+
76
+ windowConfig = input.required<WindowConfig>();
77
+ icon = input.required<IconName>();
78
+ title = input.required<string>();
79
+ isActive = computed(
80
+ () => this.state.activeToolId() === this.windowConfig().id
81
+ );
82
+ height = computed(() => {
83
+ switch (this.windowConfig().size) {
84
+ case 'tall':
85
+ return 620;
86
+ case 'medium':
87
+ return 480;
88
+ default:
89
+ return 400;
90
+ }
91
+ });
92
+
93
+ width = computed(() => {
94
+ switch (this.windowConfig().size) {
95
+ case 'tall':
96
+ return 520;
97
+ case 'medium':
98
+ return 480;
99
+ default:
100
+ return 400;
101
+ }
102
+ });
103
+ positions = computed(() => [
104
+ {
105
+ originX: 'center' as const,
106
+ originY: 'center' as const,
107
+ overlayX: 'center' as const,
108
+ overlayY: 'center' as const,
109
+ offsetY: -(Math.ceil(this.height() / 2) + 36),
110
+ },
111
+ ]);
112
+
113
+ onOpen(): void {
114
+ this.state.setActiveTool(this.windowConfig().id);
115
+ }
116
+
117
+ onClose(): void {
118
+ this.state.setActiveTool(null);
119
+ }
120
+ }
@@ -0,0 +1,9 @@
1
+ import { IconName } from '../icons/icon.models';
2
+ import { WindowConfig } from '../window/window.models';
3
+
4
+ export interface DevToolbarToolConfig {
5
+ icon: IconName;
6
+ name: string;
7
+ windowConfig: WindowConfig;
8
+ isBeta?: boolean;
9
+ }