11import { DOCUMENT } from '@angular/common' ;
22import { inject , Pipe } from '@angular/core' ;
33
4+ const RGBA_REGEX = / r g b a ? \( ( \d + ) , \s * ( \d + ) , \s * ( \d + ) , ? \s * ( \d * \. ? \d + ) ? \) / ;
5+ const DEFAULT_COLOR = 0x000000 ;
6+
47@Pipe ( { name : 'hexify' , pure : true , standalone : true } )
58export class NgtHexify {
69 private document = inject ( DOCUMENT , { optional : true } ) ;
710 private ctx ?: CanvasRenderingContext2D | null ;
11+ private cache : Record < string , number > = { } ;
812
913 /**
1014 * transforms a:
@@ -17,45 +21,66 @@ export class NgtHexify {
1721 * @param value
1822 */
1923 transform ( value : string ) : number {
20- if ( value == null ) return 0x000000 ;
24+ if ( value == null ) return DEFAULT_COLOR ;
2125
2226 if ( value . startsWith ( '#' ) ) {
23- return this . hexStringToNumber ( value ) ;
27+ if ( ! this . cache [ value ] ) {
28+ this . cache [ value ] = this . hexStringToNumber ( value ) ;
29+ }
30+ return this . cache [ value ] ;
2431 }
2532
2633 if ( ! this . ctx ) {
2734 this . ctx = this . document ?. createElement ( 'canvas' ) . getContext ( '2d' ) ;
2835 }
2936
30- if ( ! this . ctx ) return 0x000000 ;
37+ if ( ! this . ctx ) {
38+ console . warn ( '[NGT] hexify: canvas context is not available' ) ;
39+ return DEFAULT_COLOR ;
40+ }
3141
3242 this . ctx . fillStyle = value ;
3343 const computedValue = this . ctx . fillStyle ;
3444
3545 if ( computedValue . startsWith ( '#' ) ) {
36- return this . hexStringToNumber ( computedValue ) ;
46+ if ( ! this . cache [ computedValue ] ) {
47+ this . cache [ computedValue ] = this . hexStringToNumber ( computedValue ) ;
48+ }
49+ return this . cache [ computedValue ] ;
3750 }
3851
39- if ( ! computedValue . startsWith ( 'rgba' ) ) return 0x000000 ;
52+ if ( ! computedValue . startsWith ( 'rgba' ) ) {
53+ console . warn ( `[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${ computedValue } ` ) ;
54+ return DEFAULT_COLOR ;
55+ }
4056
41- const regex = / r g b a ? \( ( \d + ) , \s * ( \d + ) , \s * ( \d + ) , ? \s * ( \d * \. ? \d + ) ? \) / ;
42- const match = computedValue . match ( regex ) ;
43- if ( ! match ) return 0x000000 ;
57+ const match = computedValue . match ( RGBA_REGEX ) ;
58+ if ( ! match ) {
59+ console . warn ( `[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${ computedValue } ` ) ;
60+ return DEFAULT_COLOR ;
61+ }
4462
4563 const r = parseInt ( match [ 1 ] , 10 ) ;
4664 const g = parseInt ( match [ 2 ] , 10 ) ;
4765 const b = parseInt ( match [ 3 ] , 10 ) ;
4866 const a = match [ 4 ] ? parseFloat ( match [ 4 ] ) : 1.0 ;
4967
50- // Convert the components to hex strings
51- const hexR = this . componentToHex ( r ) ;
52- const hexG = this . componentToHex ( g ) ;
53- const hexB = this . componentToHex ( b ) ;
54- const hexA = this . componentToHex ( Math . round ( a * 255 ) ) ;
68+ const cacheKey = `${ r } :${ g } :${ b } :${ a } ` ;
69+
70+ // check result from cache
71+ if ( ! this . cache [ cacheKey ] ) {
72+ // Convert the components to hex strings
73+ const hexR = this . componentToHex ( r ) ;
74+ const hexG = this . componentToHex ( g ) ;
75+ const hexB = this . componentToHex ( b ) ;
76+ const hexA = this . componentToHex ( Math . round ( a * 255 ) ) ;
77+
78+ // Combine the hex components into a single hex string
79+ const hex = `#${ hexR } ${ hexG } ${ hexB } ${ hexA } ` ;
80+ this . cache [ cacheKey ] = this . hexStringToNumber ( hex ) ;
81+ }
5582
56- // Combine the hex components into a single hex string
57- const hex = `#${ hexR } ${ hexG } ${ hexB } ${ hexA } ` ;
58- return this . hexStringToNumber ( hex ) ;
83+ return this . cache [ cacheKey ] ;
5984 }
6085
6186 private hexStringToNumber ( hexString : string ) : number {
0 commit comments