11/**
22 * Smart merge utility for StyleObject values
3- * Handles array properties (like transform) by concatenating instead of overwriting
3+ * Handles transform arrays with "last wins" semantics for same transform types
44 */
55
6- import type { StyleObject } from "../types/core" ;
6+ import type { StyleObject , TransformStyle } from "../types/core" ;
77
88/**
9- * Properties that should be merged as arrays (concatenated) rather than overwritten
9+ * Get the transform type key from a transform object
10+ * e.g., { rotate: '45deg' } -> 'rotate', { scale: 1.1 } -> 'scale'
1011 */
11- const ARRAY_MERGE_PROPERTIES = new Set < string > ( [ "transform" ] ) ;
12+ function getTransformType ( transform : TransformStyle ) : string {
13+ return Object . keys ( transform ) [ 0 ] ;
14+ }
1215
1316/**
14- * Merge two StyleObject instances, handling array properties specially
17+ * Merge transform arrays with "last wins" semantics for duplicate transform types.
18+ * Different transform types are combined, but if the same type appears twice,
19+ * the later one replaces the earlier one (matching Tailwind CSS behavior).
20+ *
21+ * @example
22+ * // Different types are combined
23+ * mergeTransforms([{ rotate: '45deg' }], [{ scale: 1.1 }])
24+ * // => [{ rotate: '45deg' }, { scale: 1.1 }]
25+ *
26+ * @example
27+ * // Same type: last wins
28+ * mergeTransforms([{ rotate: '45deg' }], [{ rotate: '90deg' }])
29+ * // => [{ rotate: '90deg' }]
30+ */
31+ function mergeTransforms ( target : TransformStyle [ ] , source : TransformStyle [ ] ) : TransformStyle [ ] {
32+ // Build result by processing target first, then source
33+ // For each source transform, replace any existing transform of the same type
34+ const result : TransformStyle [ ] = [ ...target ] ;
35+
36+ for ( const sourceTransform of source ) {
37+ const sourceType = getTransformType ( sourceTransform ) ;
38+ const existingIndex = result . findIndex ( ( t ) => getTransformType ( t ) === sourceType ) ;
39+
40+ if ( existingIndex !== - 1 ) {
41+ // Replace existing transform of same type (last wins)
42+ result [ existingIndex ] = sourceTransform ;
43+ } else {
44+ // Add new transform type
45+ result . push ( sourceTransform ) ;
46+ }
47+ }
48+
49+ return result ;
50+ }
51+
52+ /**
53+ * Merge two StyleObject instances, handling transform arrays specially
1554 *
1655 * @param target - The target object to merge into (mutated)
1756 * @param source - The source object to merge from
@@ -23,30 +62,38 @@ const ARRAY_MERGE_PROPERTIES = new Set<string>(["transform"]);
2362 * // => { margin: 4, padding: 8 }
2463 *
2564 * @example
26- * // Array properties ( transform) are concatenated
65+ * // Different transform types are combined
2766 * mergeStyles(
2867 * { transform: [{ rotate: '45deg' }] },
2968 * { transform: [{ scale: 1.1 }] }
3069 * )
3170 * // => { transform: [{ rotate: '45deg' }, { scale: 1.1 }] }
71+ *
72+ * @example
73+ * // Same transform type: last wins (Tailwind parity)
74+ * mergeStyles(
75+ * { transform: [{ rotate: '45deg' }] },
76+ * { transform: [{ rotate: '90deg' }] }
77+ * )
78+ * // => { transform: [{ rotate: '90deg' }] }
3279 */
3380export function mergeStyles ( target : StyleObject , source : StyleObject ) : StyleObject {
3481 for ( const key in source ) {
3582 if ( Object . prototype . hasOwnProperty . call ( source , key ) ) {
3683 const sourceValue = source [ key ] ;
3784
38- // Handle array merge properties (like transform)
39- if ( ARRAY_MERGE_PROPERTIES . has ( key ) && Array . isArray ( sourceValue ) ) {
85+ // Handle transform arrays specially
86+ if ( key === "transform" && Array . isArray ( sourceValue ) ) {
4087 const targetValue = target [ key ] ;
4188 if ( Array . isArray ( targetValue ) ) {
42- // Concatenate arrays
43- ( target as Record < string , unknown > ) [ key ] = [ ... targetValue , ... sourceValue ] ;
89+ // Merge transforms with "last wins" for same types
90+ target . transform = mergeTransforms ( targetValue , sourceValue ) ;
4491 } else {
4592 // No existing array, just assign
4693 target [ key ] = sourceValue ;
4794 }
4895 } else {
49- // Standard Object.assign behavior for non-array properties
96+ // Standard Object.assign behavior for non-transform properties
5097 target [ key ] = sourceValue ;
5198 }
5299 }
0 commit comments