@@ -2,14 +2,14 @@ import {CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf} fr
22import {
33 ChangeDetectionStrategy ,
44 Component ,
5- OnInit ,
65 ChangeDetectorRef ,
7- OnChanges ,
8- SimpleChanges ,
96 inject ,
107 input ,
118 output ,
129 viewChild ,
10+ computed ,
11+ linkedSignal ,
12+ effect ,
1313} from '@angular/core' ;
1414import { WHITE } from '../color' ;
1515import {
@@ -39,7 +39,7 @@ import {MatIcon} from '@angular/material/icon';
3939 MatIcon ,
4040 ] ,
4141} )
42- export class ColorTableEditorComponent implements OnInit , OnChanges {
42+ export class ColorTableEditorComponent {
4343 private ref = inject ( ChangeDetectorRef ) ;
4444
4545 // Symbology to use for creating color tabs
@@ -52,26 +52,58 @@ export class ColorTableEditorComponent implements OnInit, OnChanges {
5252
5353 readonly virtualScrollViewport = viewChild . required ( CdkVirtualScrollViewport ) ;
5454
55- colorAttributes : Array < ColorAttributeInput > = [ ] ;
56- colorHints ?: ColorAttributeInputHinter ;
57-
58- ngOnInit ( ) : void {
59- this . updateColorAttributes ( ) ;
55+ readonly colorAttributes = linkedSignal < Array < ColorBreakpoint > , Array < ColorAttributeInput > > ( {
56+ source : ( ) => this . colorTable ( ) ,
57+ computation : ( colorTable : Array < ColorBreakpoint > ) =>
58+ colorTable . map ( ( color : ColorBreakpoint ) => {
59+ return { key : color . value . toString ( ) , value : color . color } ;
60+ } ) ,
61+ equal : ( a , b ) => {
62+ if ( a . length !== b . length ) {
63+ return false ;
64+ }
65+ for ( let i = 0 ; i < a . length ; i ++ ) {
66+ if ( a [ i ] . key !== b [ i ] . key ) {
67+ return false ;
68+ }
69+ if ( ! a [ i ] . value . equals ( b [ i ] . value ) ) {
70+ return false ;
71+ }
72+ }
73+ return true ;
74+ } ,
75+ } ) ;
76+ readonly colorHints = computed < ColorAttributeInputHinter | undefined > ( ( ) => {
6077 const measurement = this . measurement ( ) ;
78+
6179 if ( measurement instanceof ClassificationMeasurement ) {
62- this . colorHints = measurement as ColorAttributeInputHinter ;
80+ return measurement as ColorAttributeInputHinter ;
6381 }
64- }
6582
66- ngOnChanges ( _changes : SimpleChanges ) : void {
67- this . updateColorAttributes ( ) ;
68- }
83+ return undefined ;
84+ } ) ;
85+
86+ constructor ( ) {
87+ effect ( ( ) => {
88+ const colorAttributes = this . colorAttributes ( ) ;
89+ let hadError = false ;
90+
91+ const colorTable : Array < ColorBreakpoint > = colorAttributes . map ( ( color : ColorAttributeInput ) => {
92+ const value = Number ( color . key ) ;
93+
94+ if ( isNaN ( value ) ) {
95+ hadError = true ;
96+ }
6997
70- updateColorAttributes ( ) : void {
71- this . colorAttributes = this . colorTable ( ) . map ( ( color : ColorBreakpoint ) => {
72- return { key : color . value . toString ( ) , value : color . color } ;
98+ return new ColorBreakpoint ( value , color . value ) ;
99+ } ) ;
100+
101+ if ( hadError ) {
102+ return ;
103+ }
104+
105+ this . colorTableChanged . emit ( colorTable ) ;
73106 } ) ;
74- this . ref . detectChanges ( ) ;
75107 }
76108
77109 /**
@@ -81,75 +113,43 @@ export class ColorTableEditorComponent implements OnInit, OnChanges {
81113 * the time of updating.
82114 */
83115 updateColorAt ( index : number , color : ColorAttributeInput ) : void {
84- this . colorAttributes . splice ( index , 1 , color ) ;
85-
86- // TODO: only sort if necessary
87- this . sortColorAttributeInputs ( ) ;
116+ const colorAttributes = [ ...this . colorAttributes ( ) ] ; // copy
117+ colorAttributes . splice ( index , 1 , color ) ;
88118
89- this . emitColorTable ( ) ;
119+ this . colorAttributes . set ( sorted ( colorAttributes ) ) ;
90120 }
91121
92122 removeColorAt ( index : number ) : void {
93- this . colorAttributes . splice ( index , 1 ) ;
94- this . colorAttributes = [ ... this . colorAttributes ] ; // new array
123+ const colorAttributes = [ ... this . colorAttributes ( ) ] ; // copy
124+ colorAttributes . splice ( index , 1 ) ;
95125
96- setTimeout ( ( ) => this . ref . detectChanges ( ) ) ;
97-
98- this . emitColorTable ( ) ;
99- }
100-
101- /**
102- * Sort allColors by raster layer values, so ColorAttributeInputs are displayed in the correct order.
103- * Only called by parent when apply is pressed, so Inputs don't jump around while user is editing.
104- */
105- sortColorAttributeInputs ( ) : void {
106- this . colorAttributes = this . colorAttributes . sort ( ( a : ColorAttributeInput , b : ColorAttributeInput ) =>
107- Math . sign ( parseFloat ( a . key ) - parseFloat ( b . key ) ) ,
108- ) ;
109-
110- setTimeout ( ( ) => this . ref . markForCheck ( ) ) ;
126+ this . colorAttributes . set ( colorAttributes ) ;
111127 }
112128
113129 appendColor ( ) : void {
130+ const colorAttributes = this . colorAttributes ( ) ;
131+
114132 let newValue ;
115- if ( this . colorAttributes . length ) {
133+ if ( colorAttributes . length ) {
116134 // Determine a value so that the new tab will appear at the bottom of the list.
117- newValue = parseFloat ( this . colorAttributes [ this . colorAttributes . length - 1 ] . key ) + 1 ;
135+ newValue = parseFloat ( colorAttributes [ colorAttributes . length - 1 ] . key ) + 1 ;
118136 } else {
119137 newValue = 0 ;
120138 }
121139
122- this . colorAttributes = [ ...this . colorAttributes , { key : newValue . toString ( ) , value : WHITE } ] ;
123-
124- // TODO: do we need that?
125- this . sortColorAttributeInputs ( ) ;
140+ this . colorAttributes . set ( sorted ( [ ...colorAttributes , { key : newValue . toString ( ) , value : WHITE } ] ) ) ;
126141
127142 setTimeout ( ( ) => this . virtualScrollViewport ( ) . scrollTo ( { bottom : 0 } ) , 0 ) ; // Delay of 0 to include new tab in scroll
128-
129- this . emitColorTable ( ) ;
130143 }
131144
132145 isNoNumber ( index : number ) : boolean {
133- return isNaN ( Number ( this . colorAttributes [ index ] . key ) ) ;
134- }
135-
136- emitColorTable ( ) : void {
137- let hadError = false ;
138-
139- const colorTable : Array < ColorBreakpoint > = this . colorAttributes . map ( ( color : ColorAttributeInput ) => {
140- const value = Number ( color . key ) ;
141-
142- if ( isNaN ( value ) ) {
143- hadError = true ;
144- }
145-
146- return new ColorBreakpoint ( value , color . value ) ;
147- } ) ;
148-
149- if ( hadError ) {
150- return ;
151- }
152-
153- this . colorTableChanged . emit ( colorTable ) ;
146+ return isNaN ( Number ( this . colorAttributes ( ) [ index ] . key ) ) ;
154147 }
155148}
149+
150+ /**
151+ * Sort allColors by raster layer values, so ColorAttributeInputs are displayed in the correct order.
152+ * Only called by parent when apply is pressed, so Inputs don't jump around while user is editing.
153+ */
154+ const sorted = ( colorAttributes : Array < ColorAttributeInput > ) : Array < ColorAttributeInput > =>
155+ colorAttributes . sort ( ( a : ColorAttributeInput , b : ColorAttributeInput ) => Math . sign ( parseFloat ( a . key ) - parseFloat ( b . key ) ) ) ;
0 commit comments