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,361 @@
1
+ @use 'sass:map';
2
+ // Color primitives
3
+ $color-black-900: rgb(17, 24, 39); // darkest
4
+ $color-black-800: rgb(19, 21, 26);
5
+ $color-gray-700: rgb(55, 65, 81);
6
+ $color-gray-600: rgb(107, 114, 128);
7
+ $color-gray-500: rgb(156, 163, 175);
8
+ $color-gray-300: rgb(229, 231, 235);
9
+ $color-gray-200: rgb(243, 244, 246);
10
+ $color-white: rgb(255, 255, 255);
11
+ $color-red-600: rgb(220, 38, 38); // dark red
12
+ $color-red-500: rgb(239, 68, 68); // light red
13
+ $color-blue-100: rgb(219, 234, 254);
14
+ $color-blue-600: rgb(37, 99, 235);
15
+ $color-yellow-100: rgb(254, 249, 195);
16
+ $color-yellow-600: rgb(202, 138, 4);
17
+ $color-red-100: rgb(254, 226, 226);
18
+ // Colors and Themes
19
+ $colors: (
20
+ dark: (
21
+ background: (
22
+ primary: $color-black-900,
23
+ gradient:
24
+ linear-gradient(
25
+ 180deg,
26
+ $color-black-800 0%,
27
+ rgba($color-black-800, 0.88) 100%
28
+ ),
29
+ ),
30
+ text: (
31
+ primary: $color-white,
32
+ secondary: $color-gray-300,
33
+ muted: $color-gray-500,
34
+ ),
35
+ border: (
36
+ primary: #343841,
37
+ subtle: rgba($color-white, 0.1),
38
+ ),
39
+ hover: (
40
+ background: rgba($color-white, 0.12),
41
+ danger: $color-red-600,
42
+ ),
43
+ annotation: (
44
+ note: (
45
+ background: rgba($color-blue-600, 0.15),
46
+ border: rgba($color-blue-600, 0.3),
47
+ ),
48
+ warning: (
49
+ background: rgba($color-yellow-600, 0.15),
50
+ border: rgba($color-yellow-600, 0.3),
51
+ ),
52
+ error: (
53
+ background: rgba($color-red-600, 0.15),
54
+ border: rgba($color-red-600, 0.3),
55
+ ),
56
+ ),
57
+ ),
58
+ light: (
59
+ background: (
60
+ primary: $color-white,
61
+ gradient:
62
+ linear-gradient(
63
+ 180deg,
64
+ $color-gray-200 0%,
65
+ rgba($color-gray-200, 0.88) 100%
66
+ ),
67
+ ),
68
+ text: (
69
+ primary: $color-black-900,
70
+ secondary: $color-gray-700,
71
+ muted: $color-gray-600,
72
+ ),
73
+ border: (
74
+ primary: #e5e7eb,
75
+ subtle: rgba($color-black-900, 0.1),
76
+ ),
77
+ hover: (
78
+ background: rgba($color-black-900, 0.05),
79
+ danger: $color-red-500,
80
+ ),
81
+ annotation: (
82
+ note: (
83
+ background: $color-blue-100,
84
+ border: rgba($color-blue-600, 0.2),
85
+ ),
86
+ warning: (
87
+ background: $color-yellow-100,
88
+ border: rgba($color-yellow-600, 0.2),
89
+ ),
90
+ error: (
91
+ background: $color-red-100,
92
+ border: rgba($color-red-600, 0.2),
93
+ ),
94
+ ),
95
+ ),
96
+ );
97
+
98
+ // Shadows
99
+ $shadow-themes: (
100
+ dark: (
101
+ toolbar: 0 2px 8px rgba(19, 21, 26, 0.3),
102
+ tooltip: (
103
+ 0 0 0 1px rgba(255, 255, 255, 0.1),
104
+ 0 4px 8px rgba(0, 0, 0, 0.4),
105
+ 0 2px 4px rgba(0, 0, 0, 0.3),
106
+ ),
107
+ window: (
108
+ 0px 0px 0px 0px rgba(19, 21, 26, 0.3),
109
+ 0px 1px 2px 0px rgba(19, 21, 26, 0.29),
110
+ 0px 4px 4px 0px rgba(19, 21, 26, 0.26),
111
+ 0px 10px 6px 0px rgba(19, 21, 26, 0.15),
112
+ 0px 17px 7px 0px rgba(19, 21, 26, 0.04),
113
+ 0px 26px 7px 0px rgba(19, 21, 26, 0.01),
114
+ ),
115
+ ),
116
+ light: (
117
+ toolbar: 0 2px 8px rgba(156, 163, 175, 0.2),
118
+ tooltip: (
119
+ 0 0 0 1px rgba(17, 24, 39, 0.05),
120
+ 0 4px 8px rgba(107, 114, 128, 0.15),
121
+ 0 2px 4px rgba(107, 114, 128, 0.1),
122
+ ),
123
+ window: (
124
+ 0px 0px 0px 0px rgba(156, 163, 175, 0.1),
125
+ 0px 1px 2px 0px rgba(156, 163, 175, 0.12),
126
+ 0px 4px 4px 0px rgba(156, 163, 175, 0.1),
127
+ 0px 10px 6px 0px rgba(156, 163, 175, 0.08),
128
+ 0px 17px 7px 0px rgba(156, 163, 175, 0.05),
129
+ 0px 26px 7px 0px rgba(156, 163, 175, 0.02),
130
+ ),
131
+ ),
132
+ );
133
+
134
+ // Typography
135
+ $font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
136
+ 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
137
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
138
+
139
+ // Add font size variables
140
+ $font-sizes: (
141
+ xs: 0.75rem,
142
+ sm: 0.875rem,
143
+ md: 1rem,
144
+ lg: 1.25rem,
145
+ xl: 2rem,
146
+ );
147
+
148
+ // Dimensions
149
+ $dimensions: (
150
+ toolbar-height: 40px,
151
+ toolbar-button-width: 44px,
152
+ window-max-width: 640px,
153
+ window-max-height: 480px,
154
+ border-radius: (
155
+ small: 4px,
156
+ medium: 8px,
157
+ large: 12px,
158
+ full: 9999px,
159
+ ),
160
+ );
161
+
162
+ // Transitions
163
+ $transitions: (
164
+ default: all 0.2s ease-out,
165
+ smooth: all 0.2s ease-in-out,
166
+ );
167
+
168
+ // Z-indices
169
+ $z-indices: (
170
+ toolbar: 999999,
171
+ window: 999999999,
172
+ );
173
+
174
+ .dev-toolbar {
175
+ // Border radius variables
176
+ --devtools-border-radius-small: #{map.get(
177
+ map.get($dimensions, border-radius),
178
+ small
179
+ )};
180
+ --devtools-border-radius-medium: #{map.get(
181
+ map.get($dimensions, border-radius),
182
+ medium
183
+ )};
184
+ --devtools-border-radius-large: #{map.get(
185
+ map.get($dimensions, border-radius),
186
+ large
187
+ )};
188
+
189
+ // Transition variables
190
+ --devtools-transition-default: #{map.get($transitions, default)};
191
+ --devtools-transition-smooth: #{map.get($transitions, smooth)};
192
+
193
+ // Default light theme
194
+ --devtools-bg-primary: #{map.get(map.get($colors, light), background, primary)};
195
+ --devtools-bg-gradient: #{map.get(
196
+ map.get($colors, light),
197
+ background,
198
+ gradient
199
+ )};
200
+ --devtools-text-primary: #{map.get(map.get($colors, light), text, primary)};
201
+ --devtools-text-secondary: #{map.get(map.get($colors, light), text, secondary)};
202
+ --devtools-text-muted: #{map.get(map.get($colors, light), text, muted)};
203
+ --devtools-border-primary: #{map.get(map.get($colors, light), border, primary)};
204
+ --devtools-border-subtle: #{map.get(map.get($colors, light), border, subtle)};
205
+ --devtools-hover-bg: #{map.get(map.get($colors, light), hover, background)};
206
+ --devtools-hover-danger: #{map.get(map.get($colors, light), hover, danger)};
207
+ --devtools-shadow-toolbar: #{map.get(map.get($shadow-themes, light), toolbar)};
208
+ --devtools-shadow-tooltip: #{map.get(map.get($shadow-themes, light), tooltip)};
209
+ --devtools-shadow-window: #{map.get(map.get($shadow-themes, light), window)};
210
+
211
+ // Add these spacing variables
212
+ --devtools-spacing-xs: 4px;
213
+ --devtools-spacing-sm: 8px;
214
+ --devtools-spacing-md: 16px;
215
+ --devtools-window-padding: 24px;
216
+
217
+ // Add font size CSS variables
218
+ --devtools-font-size-xs: #{map.get($font-sizes, xs)};
219
+ --devtools-font-size-sm: #{map.get($font-sizes, sm)};
220
+ --devtools-font-size-md: #{map.get($font-sizes, md)};
221
+ --devtools-font-size-lg: #{map.get($font-sizes, lg)};
222
+ --devtools-font-size-xl: #{map.get($font-sizes, xl)};
223
+
224
+ // Background colors
225
+ --devtools-background-secondary: var(--devtools-bg-primary);
226
+ --devtools-background-hover: var(--devtools-hover-bg);
227
+
228
+ // Primary color for active states
229
+ --devtools-primary: #df30d4; // You can adjust this color value
230
+ --devtools-text-on-primary: #{$color-white};
231
+
232
+ // Border color
233
+ --devtools-border-color: var(--devtools-border-primary);
234
+
235
+ // Typography styles
236
+ h1,
237
+ h2,
238
+ h3,
239
+ h4,
240
+ h5 {
241
+ font-weight: 600;
242
+ color: var(--devtools-text-primary);
243
+ }
244
+
245
+ h1 {
246
+ font-size: var(--devtools-font-size-xl);
247
+ }
248
+ h2 {
249
+ font-size: var(--devtools-font-size-lg);
250
+ }
251
+ h3 {
252
+ font-size: var(--devtools-font-size-md);
253
+ }
254
+ h4 {
255
+ font-size: var(--devtools-font-size-sm);
256
+ }
257
+ h5 {
258
+ font-size: var(--devtools-font-size-xs);
259
+ }
260
+
261
+ hr {
262
+ border: 1px solid var(--devtools-border-subtle);
263
+ margin: 1em 0;
264
+ }
265
+
266
+ p {
267
+ line-height: 1.5em;
268
+ }
269
+ --devtools-note-background: #{map.get(
270
+ map.get(map.get($colors, light), annotation),
271
+ note,
272
+ background
273
+ )};
274
+ --devtools-note-border: #{map.get(
275
+ map.get(map.get($colors, light), annotation),
276
+ note,
277
+ border
278
+ )};
279
+ --devtools-warning-background: #{map.get(
280
+ map.get(map.get($colors, light), annotation),
281
+ warning,
282
+ background
283
+ )};
284
+ --devtools-warning-border: #{map.get(
285
+ map.get(map.get($colors, light), annotation),
286
+ warning,
287
+ border
288
+ )};
289
+ --devtools-error-background: #{map.get(
290
+ map.get(map.get($colors, light), annotation),
291
+ error,
292
+ background
293
+ )};
294
+ --devtools-error-border: #{map.get(
295
+ map.get(map.get($colors, light), annotation),
296
+ error,
297
+ border
298
+ )};
299
+
300
+ &[data-theme='dark'] {
301
+ --devtools-bg-primary: #{map.get(
302
+ map.get($colors, dark),
303
+ background,
304
+ primary
305
+ )};
306
+ --devtools-bg-gradient: #{map.get(
307
+ map.get($colors, dark),
308
+ background,
309
+ gradient
310
+ )};
311
+ --devtools-text-primary: #{map.get(map.get($colors, dark), text, primary)};
312
+ --devtools-text-secondary: #{map.get(
313
+ map.get($colors, dark),
314
+ text,
315
+ secondary
316
+ )};
317
+ --devtools-text-muted: #{map.get(map.get($colors, dark), text, muted)};
318
+ --devtools-border-primary: #{map.get(
319
+ map.get($colors, dark),
320
+ border,
321
+ primary
322
+ )};
323
+ --devtools-border-subtle: #{map.get(map.get($colors, dark), border, subtle)};
324
+ --devtools-hover-bg: #{map.get(map.get($colors, dark), hover, background)};
325
+ --devtools-hover-danger: #{map.get(map.get($colors, dark), hover, danger)};
326
+ --devtools-shadow-toolbar: #{map.get(map.get($shadow-themes, dark), toolbar)};
327
+ --devtools-shadow-tooltip: #{map.get(map.get($shadow-themes, dark), tooltip)};
328
+ --devtools-shadow-window: #{map.get(map.get($shadow-themes, dark), window)};
329
+
330
+ --devtools-note-background: #{map.get(
331
+ map.get(map.get($colors, dark), annotation),
332
+ note,
333
+ background
334
+ )};
335
+ --devtools-note-border: #{map.get(
336
+ map.get(map.get($colors, dark), annotation),
337
+ note,
338
+ border
339
+ )};
340
+ --devtools-warning-background: #{map.get(
341
+ map.get(map.get($colors, dark), annotation),
342
+ warning,
343
+ background
344
+ )};
345
+ --devtools-warning-border: #{map.get(
346
+ map.get(map.get($colors, dark), annotation),
347
+ warning,
348
+ border
349
+ )};
350
+ --devtools-error-background: #{map.get(
351
+ map.get(map.get($colors, dark), annotation),
352
+ error,
353
+ background
354
+ )};
355
+ --devtools-error-border: #{map.get(
356
+ map.get(map.get($colors, dark), annotation),
357
+ error,
358
+ border
359
+ )};
360
+ }
361
+ }
@@ -0,0 +1,255 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ computed,
5
+ inject,
6
+ signal,
7
+ } from '@angular/core';
8
+ import { FormsModule } from '@angular/forms';
9
+ import { DevToolbarInputComponent } from '../../components/input/input.component';
10
+ import { DevToolbarSelectComponent } from '../../components/select/select.component';
11
+ import { DevToolbarToolComponent } from '../../components/toolbar-tool/toolbar-tool.component';
12
+ import { WindowSize } from '../../components/window/window.models';
13
+ import { FeatureFlagFilter, Flag } from './feature-flags.models';
14
+ import { DevToolbarFeatureFlagsService } from './feature-flags.service';
15
+
16
+ @Component({
17
+ selector: 'ndt-feature-flags-tool',
18
+ standalone: true,
19
+ imports: [
20
+ FormsModule,
21
+ DevToolbarToolComponent,
22
+ DevToolbarInputComponent,
23
+ DevToolbarSelectComponent,
24
+ ],
25
+ template: `
26
+ <ndt-toolbar-tool
27
+ [windowConfig]="windowConfig"
28
+ title="Feature Flags"
29
+ icon="toggle-left"
30
+ >
31
+ <div class="container">
32
+ <div class="header">
33
+ <ndt-input
34
+ [value]="searchQuery()"
35
+ (valueChange)="onSearchChange($event)"
36
+ placeholder="Search..."
37
+ />
38
+ <ndt-select
39
+ [value]="activeFilter()"
40
+ [options]="filterOptions"
41
+ [size]="'medium'"
42
+ (valueChange)="onFilterChange($event)"
43
+ />
44
+ </div>
45
+
46
+ @if (hasNoFlags()) {
47
+ <div class="empty">
48
+ <p>No flags found</p>
49
+ </div>
50
+ } @else {
51
+ <div class="flag-list">
52
+ @for (flag of filteredFlags(); track flag.id) {
53
+ <div class="flag">
54
+ <div class="info">
55
+ <h3>{{ flag.name }}</h3>
56
+ <p>{{ flag?.description }}</p>
57
+ </div>
58
+
59
+ <ndt-select
60
+ [value]="getFlagValue(flag)"
61
+ [options]="flagValueOptions"
62
+ [ariaLabel]="'Set value for ' + flag.name"
63
+ (valueChange)="onFlagChange(flag.id, $event ?? '')"
64
+ size="small"
65
+ />
66
+ </div>
67
+ }
68
+ </div>
69
+ }
70
+ </div>
71
+ </ndt-toolbar-tool>
72
+ `,
73
+ styles: [
74
+ `
75
+ .container {
76
+ display: flex;
77
+ flex-direction: column;
78
+ height: 100%;
79
+ }
80
+
81
+ .header {
82
+ flex-shrink: 0;
83
+ display: flex;
84
+ gap: var(--devtools-spacing-sm);
85
+ margin-bottom: var(--devtools-spacing-md);
86
+
87
+ ndt-input {
88
+ flex: 0.65;
89
+ }
90
+
91
+ ndt-select {
92
+ flex: 0.35;
93
+ }
94
+ }
95
+
96
+ .empty {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: var(--devtools-spacing-md);
100
+ flex: 1;
101
+ min-height: 0;
102
+ justify-content: center;
103
+ align-items: center;
104
+ border: 1px solid var(--devtools-warning-border);
105
+ border-radius: var(--devtools-border-radius-medium);
106
+ padding: var(--devtools-spacing-md);
107
+ background: var(--devtools-warning-background);
108
+ color: var(--devtools-text-muted);
109
+ }
110
+
111
+ .flag-list {
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: var(--devtools-spacing-md);
115
+ flex: 1;
116
+ min-height: 0;
117
+ overflow-y: auto;
118
+ padding-right: var(--devtools-spacing-sm);
119
+
120
+ &::-webkit-scrollbar {
121
+ width: 8px;
122
+ }
123
+
124
+ &::-webkit-scrollbar-track {
125
+ background: var(--devtools-background-secondary);
126
+ border-radius: 4px;
127
+ }
128
+
129
+ &::-webkit-scrollbar-thumb {
130
+ background: var(--devtools-border-primary);
131
+ border-radius: 4px;
132
+
133
+ &:hover {
134
+ background: var(--devtools-hover-bg);
135
+ }
136
+ }
137
+
138
+ scrollbar-width: thin;
139
+ scrollbar-color: var(--devtools-border-primary)
140
+ var(--devtools-background-secondary);
141
+ }
142
+
143
+ .flag {
144
+ display: flex;
145
+ flex-direction: row;
146
+ gap: var(--devtools-spacing-sm);
147
+ background: var(--devtools-background-secondary);
148
+ .info {
149
+ flex: 0 0 65%;
150
+ h3 {
151
+ margin: 0;
152
+ font-size: var(--devtools-font-size-md);
153
+ color: var(--devtools-text-primary);
154
+ }
155
+
156
+ p {
157
+ font-size: var(--devtools-font-size-xs);
158
+ color: var(--devtools-text-muted);
159
+ }
160
+ }
161
+
162
+ ndt-select {
163
+ flex: 0 0 35%;
164
+ }
165
+ }
166
+ `,
167
+ ],
168
+ changeDetection: ChangeDetectionStrategy.OnPush,
169
+ })
170
+ export class DevToolbarFeatureFlagsToolComponent {
171
+ // Injects
172
+ private readonly featureFlags = inject(DevToolbarFeatureFlagsService);
173
+
174
+ // Signals
175
+ protected readonly activeFilter = signal<FeatureFlagFilter>('all');
176
+ protected readonly searchQuery = signal<string>('');
177
+
178
+ protected readonly flags = this.featureFlags.flags;
179
+ protected readonly hasNoFlags = computed(() => this.flags().length === 0);
180
+ protected readonly filteredFlags = computed(() => {
181
+ return this.flags().filter((flag) => {
182
+ const searchTerm = this.searchQuery().toLowerCase();
183
+ const flagName = flag.name.toLowerCase();
184
+ const flagDescription = flag.description?.toLowerCase() ?? '';
185
+
186
+ const matchesSearch =
187
+ !this.searchQuery() ||
188
+ flagName.toLowerCase().includes(searchTerm.toLowerCase()) ||
189
+ flagDescription.toLowerCase().includes(searchTerm.toLowerCase());
190
+
191
+ const matchesFilter =
192
+ this.activeFilter() === 'all' ||
193
+ (this.activeFilter() === 'forced' && flag.isForced) ||
194
+ (this.activeFilter() === 'enabled' && flag.isEnabled) ||
195
+ (this.activeFilter() === 'disabled' && !flag.isEnabled);
196
+
197
+ return matchesSearch && matchesFilter;
198
+ });
199
+ });
200
+
201
+ // Other properties
202
+ protected readonly windowConfig = {
203
+ title: 'Feature Flags',
204
+ description: 'Manage the feature flags for your current session',
205
+ isClosable: true,
206
+ size: 'tall' as WindowSize,
207
+ id: 'ndt-feature-flags',
208
+ isBeta: true,
209
+ };
210
+
211
+ protected readonly filterOptions = [
212
+ { value: 'all', label: 'All Flags' },
213
+ { value: 'forced', label: 'Forced' },
214
+ { value: 'enabled', label: 'Enabled' },
215
+ { value: 'disabled', label: 'Disabled' },
216
+ ];
217
+
218
+ protected readonly flagValueOptions = [
219
+ { value: 'not-forced', label: 'Select an override' },
220
+ { value: 'off', label: 'Forced Off (false)' },
221
+ { value: 'on', label: 'Forced On (true)' },
222
+ ];
223
+
224
+ // Public methods
225
+ onFilterChange(value: string | undefined): void {
226
+ const filter = this.filterOptions.find((f) => f.value === value);
227
+ if (filter) {
228
+ this.activeFilter.set(filter.value as FeatureFlagFilter);
229
+ }
230
+ }
231
+
232
+ onFlagChange(flagId: string, value: string): void {
233
+ switch (value) {
234
+ case 'not-forced':
235
+ this.featureFlags.removeFlagOverride(flagId);
236
+ break;
237
+ case 'on':
238
+ this.featureFlags.setFlag(flagId, true);
239
+ break;
240
+ case 'off':
241
+ this.featureFlags.setFlag(flagId, false);
242
+ break;
243
+ }
244
+ }
245
+
246
+ onSearchChange(query: string): void {
247
+ this.searchQuery.set(query);
248
+ }
249
+
250
+ // Protected methods
251
+ protected getFlagValue(flag: Flag): string {
252
+ if (!flag.isForced) return '';
253
+ return flag.isEnabled ? 'on' : 'off';
254
+ }
255
+ }
@@ -0,0 +1,10 @@
1
+ export interface Flag {
2
+ id: string;
3
+ name: string;
4
+ description?: string;
5
+ link?: string;
6
+ isEnabled: boolean;
7
+ isForced: boolean;
8
+ }
9
+
10
+ export type FeatureFlagFilter = 'all' | 'forced' | 'enabled' | 'disabled';