src/app/core/admin/admin-entity/admin-entity-general-settings/conditional-color-config/conditional-color-config.component.ts
A form control for configuring conditional colors based on entity fields.
CustomFormControlDirective<
string | ColorMapping[]
>
| changeDetection | ChangeDetectionStrategy.OnPush |
| providers |
ConditionalColorConfigComponent
|
| selector | app-conditional-color-config |
| standalone | true |
| imports |
MatFormFieldModule
MatButtonModule
FontAwesomeModule
MatTooltipModule
|
| styleUrls | ./conditional-color-config.component.scss |
| templateUrl | ./conditional-color-config.component.html |
MatFormFieldModule
MatButtonModule
FontAwesomeModule
MatTooltipModule
ColorInputComponent
ConditionalColorSectionComponent
CustomFormControlDirective<
string | ColorMapping[]
>
Properties |
|
Methods |
Inputs |
Outputs |
Accessors |
| entityConstructor | |
Type : EntityConstructor
|
|
| Required : true | |
| isConditionalMode | |
Type : boolean
|
|
Default value : false
|
|
| aria-describedby | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| disabled | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| ngControl | |
Type : any
|
|
Default value : inject(NgControl, { optional: true, self: true })
|
|
|
Inherited from
CustomFormControlDirective
|
|
| placeholder | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| required | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| value | |
Type : T
|
|
|
Inherited from
CustomFormControlDirective
|
|
| isConditionalModeChange | |
Type : boolean
|
|
| valueChange | |
Type : EventEmitter
|
|
|
Inherited from
CustomFormControlDirective
|
|
| addConditionalColorSection |
addConditionalColorSection()
|
|
Add a new conditional color section
Returns :
void
|
| deleteConditionalColorSection | ||||||
deleteConditionalColorSection(sectionIndex: number)
|
||||||
|
Delete a conditional color section
Parameters :
Returns :
void
|
| onConditionChange | |||||||||
onConditionChange(section: ColorMapping, updatedConditions: any)
|
|||||||||
|
Handle any change in conditional sections that requires value update
Parameters :
Returns :
void
|
| onStaticColorChange | ||||||
onStaticColorChange(newColor: string)
|
||||||
|
Update the static/default color
Parameters :
Returns :
void
|
| updateConditionalSectionColor |
updateConditionalSectionColor(sectionIndex: number, newColor: string)
|
|
Update the color for a conditional section
Returns :
void
|
| blur |
blur()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| focus |
focus()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| onContainerClick | ||||||
onContainerClick(event: MouseEvent)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDescribedByIds | ||||||
setDescribedByIds(ids: string[])
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDisabledState | ||||||
setDisabledState(isDisabled: boolean)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| writeValue | |||||||||||||||
writeValue(value: T, notifyFormControl: unknown)
|
|||||||||||||||
|
Inherited from
CustomFormControlDirective
|
|||||||||||||||
|
Implementation for Angular ControlValueAccessor interface that links the form control value to the component value
Parameters :
Returns :
void
|
| controlType |
Type : string
|
Default value : "custom-control"
|
|
Inherited from
CustomFormControlDirective
|
| elementRef |
Type : unknown
|
Default value : inject<ElementRef<HTMLElement>>(ElementRef)
|
|
Inherited from
CustomFormControlDirective
|
| Readonly enabled |
Type : Signal<boolean>
|
Default value : computed(() => !this._disabled())
|
|
Inherited from
CustomFormControlDirective
|
|
Whether the control is currently enabled, as a signal (tracks disabled). |
| errorStateMatcher |
Type : unknown
|
Default value : inject(ErrorStateMatcher)
|
|
Inherited from
CustomFormControlDirective
|
| id |
Type : unknown
|
Default value : `custom-form-control-${CustomFormControlDirective.nextId++}`
|
|
Inherited from
CustomFormControlDirective
|
| Static nextId |
Type : number
|
Default value : 0
|
|
Inherited from
CustomFormControlDirective
|
| onChange |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| onTouched |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| parentForm |
Type : unknown
|
Default value : inject(NgForm, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| parentFormGroup |
Type : unknown
|
Default value : inject(FormGroupDirective, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| stateChanges |
Type : unknown
|
Default value : new Subject<void>()
|
|
Inherited from
CustomFormControlDirective
|
| Readonly valueSignal |
Type : Signal<T>
|
Default value : computed(() => this._value())
|
|
Inherited from
CustomFormControlDirective
|
|
The current value of the control as a signal.
Authoritative in both modes: it reflects the bound |
| staticColor |
getstaticColor()
|
| conditionalColorSections |
getconditionalColorSections()
|
import {
Component,
input,
output,
ChangeDetectionStrategy,
} from "@angular/core";
import {
MatFormFieldModule,
MatFormFieldControl,
} from "@angular/material/form-field";
import { MatButtonModule } from "@angular/material/button";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { MatTooltipModule } from "@angular/material/tooltip";
import { CustomFormControlDirective } from "app/core/common-components/basic-autocomplete/custom-form-control.directive";
import { ColorMapping, EntityConstructor } from "app/core/entity/model/entity";
import { ColorInputComponent } from "#src/app/core/common-components/color-input/color-input.component";
import { ConditionalColorSectionComponent } from "./conditional-color-section/conditional-color-section.component";
/**
* A form control for configuring conditional colors based on entity fields.
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-conditional-color-config",
templateUrl: "./conditional-color-config.component.html",
styleUrls: ["./conditional-color-config.component.scss"],
providers: [
{
provide: MatFormFieldControl,
useExisting: ConditionalColorConfigComponent,
},
],
imports: [
MatFormFieldModule,
MatButtonModule,
FontAwesomeModule,
MatTooltipModule,
ColorInputComponent,
ConditionalColorSectionComponent,
],
})
export class ConditionalColorConfigComponent extends CustomFormControlDirective<
string | ColorMapping[]
> {
entityConstructor = input.required<EntityConstructor>();
isConditionalMode = input<boolean>(false);
isConditionalModeChange = output<boolean>();
// Cached values to avoid recalculating in template
get staticColor(): string {
if (typeof this.value === "string") return this.value;
if (!Array.isArray(this.value)) return "";
return (
this.value.find((m) => !Object.keys(m.condition || {}).length)?.color ||
""
);
}
get conditionalColorSections(): ColorMapping[] {
if (!Array.isArray(this.value)) return [];
return this.value.filter((m) => Object.keys(m.condition || {}).length > 0);
}
/**
* Add a new conditional color section
*/
addConditionalColorSection(): void {
// Enable conditional mode if not already enabled
if (!this.isConditionalMode()) {
this.isConditionalModeChange.emit(true);
}
if (!Array.isArray(this.value)) {
this.value = [{ condition: {}, color: this.staticColor }];
}
// Add new conditional section with one empty condition to start
const newSection: ColorMapping = {
condition: { $or: [{}] },
color: "",
};
this.value = [...this.value, newSection];
this.onChange(this.value);
}
/**
* Delete a conditional color section
*/
deleteConditionalColorSection(sectionIndex: number): void {
if (!Array.isArray(this.value)) return;
const conditionalSections = this.conditionalColorSections;
if (sectionIndex < 0 || sectionIndex >= conditionalSections.length) return;
// Remove the section
const staticMapping = this.value.find(
(m) => !Object.keys(m.condition || {}).length,
);
const remainingSections = conditionalSections.filter(
(_, index) => index !== sectionIndex,
);
this.value = staticMapping
? [staticMapping, ...remainingSections]
: remainingSections;
this.onChange(this.value);
}
/**
* Update the color for a conditional section
*/
updateConditionalSectionColor(sectionIndex: number, newColor: string): void {
const conditionalSections = this.conditionalColorSections;
if (sectionIndex < 0 || sectionIndex >= conditionalSections.length) return;
conditionalSections[sectionIndex].color = newColor;
this.updateValue();
}
/**
* Handle any change in conditional sections that requires value update
*/
onConditionChange(section: ColorMapping, updatedConditions: any): void {
if (!Array.isArray(this.value)) return;
const sectionIndex = this.value.findIndex(
(candidate) => candidate === section,
);
if (sectionIndex < 0) return;
this.value = this.value.map((candidate, index) =>
index === sectionIndex
? { ...candidate, condition: updatedConditions }
: candidate,
);
this.updateValue();
}
/**
* Update the static/default color
*/
onStaticColorChange(newColor: string): void {
if (typeof this.value === "string") {
this.value = newColor;
this.onChange(newColor);
return;
}
if (!Array.isArray(this.value)) {
this.value = [{ condition: {}, color: newColor }];
} else {
// Update or add static mapping
const staticIndex = this.value.findIndex(
(m) => !Object.keys(m.condition || {}).length,
);
if (staticIndex >= 0) {
this.value[staticIndex].color = newColor;
} else {
this.value.unshift({ condition: {}, color: newColor });
}
}
this.onChange(this.value);
}
private updateValue(): void {
this.onChange(this.value);
}
}
<div class="conditional-color-config">
<div class="color-row">
<app-color-input
[value]="staticColor"
(valueChange)="onStaticColorChange($event)"
></app-color-input>
<button
mat-stroked-button
type="button"
(click)="addConditionalColorSection()"
color="accent"
matTooltip="Customize colors dynamically based on data values"
i18n-matTooltip
>
<span i18n>Add Conditional Color</span>
</button>
</div>
@if (isConditionalMode()) {
<div class="conditional-sections flex-column gap-regular padding-regular">
@for (section of conditionalColorSections; track $index) {
<app-conditional-color-section
[section]="section"
[entityConstructor]="entityConstructor()"
(colorChange)="updateConditionalSectionColor($index, $event)"
(deleteSection)="deleteConditionalColorSection($index)"
(conditionChange)="onConditionChange(section, $event)"
></app-conditional-color-section>
}
</div>
}
</div>
./conditional-color-config.component.scss
.conditional-color-config {
width: 100%;
}
.color-row {
display: flex;
gap: 12px;
align-items: center;
width: 100%;
app-color-input {
flex: 1;
}
button {
flex-shrink: 0;
}
}
.conditional-sections {
background-color: var(--mdc-outlined-card-container-color, #fafafa);
border-radius: 8px;
width: 100%;
}
.add-section-container {
display: flex;
gap: 12px;
align-items: center;
justify-content: center;
padding: 16px;
border: 2px solid rgba(0, 0, 0, 0.12);
border-radius: 8px;
background-color: #fafafa;
width: 100%;
}