11import type { TwTheme } from '../tw-config' ;
22import type { DependentStyle , ParseContext , Style , StyleIR } from '../types' ;
3- import { isString , Unit } from '../types' ;
3+ import { Unit } from '../types' ;
44import {
5+ complete ,
56 parseNumericValue ,
67 parseStyleVal ,
78 parseUnconfigged ,
@@ -10,6 +11,7 @@ import {
1011
1112type Axis = `x` | `y` | `z` | ``;
1213type Property = `scale` | `rotate` | `skew` | `translate`;
14+ type Position = `left` | `center` | `right` | `top` | `bottom`;
1315
1416export function scale (
1517 value : string ,
@@ -124,13 +126,7 @@ export function translate(
124126 ? parseStyleVal ( configValue , context )
125127 : parseUnconfigged ( value , context ) ;
126128
127- // support for percentage values in translate was only added in RN 0.75
128- // source: https://reactnative.dev/blog/2024/08/12/release-0.75#percentage-values-in-translation
129- // since the support of this package starts at RN 0.62.2
130- // we need to filter out percentages which are non-numeric values
131- return styleVal === null || isString ( styleVal )
132- ? null
133- : createStyle ( styleVal , `translate` , translateAxis ) ;
129+ return styleVal === null ? null : createStyle ( styleVal , `translate` , translateAxis ) ;
134130}
135131
136132export function transformNone ( ) : StyleIR {
@@ -142,6 +138,75 @@ export function transformNone(): StyleIR {
142138 } ;
143139}
144140
141+ export function origin (
142+ value : string ,
143+ context : ParseContext = { } ,
144+ config ?: TwTheme [ 'transformOrigin' ] ,
145+ ) : StyleIR | null {
146+ const configValue = config ?. [ value ] ;
147+
148+ if ( configValue ) {
149+ return complete ( { transformOrigin : configValue } ) ;
150+ }
151+
152+ if ( isArbitraryValue ( value ) ) {
153+ const values = value . slice ( 1 , - 1 ) . split ( `_` ) ;
154+
155+ if ( values . length === 0 || values . length > 3 ) {
156+ return null ;
157+ }
158+
159+ // with a single value, the value must be one of the positions, a percentage or a pixel value
160+ if ( values . length === 1 ) {
161+ const parsedValue = parseOriginValue (
162+ values [ 0 ] ,
163+ [ `left` , `center` , `right` , `top` , `bottom` ] ,
164+ [ Unit . percent , Unit . px ] ,
165+ context ,
166+ ) ;
167+ return parsedValue === null ? null : complete ( { transformOrigin : [ parsedValue ] } ) ;
168+ }
169+
170+ // with two or three values, the first value must be 'left' / 'center' / 'right', a percentage or a pixel value
171+ const xOffset = parseOriginValue (
172+ values [ 0 ] ,
173+ [ `left` , `center` , `right` ] ,
174+ [ Unit . percent , Unit . px ] ,
175+ context ,
176+ ) ;
177+
178+ if ( xOffset === null ) {
179+ return null ;
180+ }
181+
182+ // with two or three values, the second value must be 'top' / 'center' / 'bottom', a percentage or a pixel value
183+ const yOffset = parseOriginValue (
184+ values [ 1 ] ,
185+ [ `top` , `center` , `bottom` ] ,
186+ [ Unit . percent , Unit . px ] ,
187+ context ,
188+ ) ;
189+
190+ if ( yOffset === null ) {
191+ return null ;
192+ }
193+
194+ // with three values, the third value must be a pixel value
195+ const zOffset = parseOriginValue ( values [ 2 ] , [ ] , [ Unit . px ] , context ) ;
196+
197+ if ( zOffset === null && values . length === 3 ) {
198+ return null ;
199+ }
200+
201+ return complete ( {
202+ transformOrigin :
203+ zOffset === null ? [ xOffset , yOffset ] : [ xOffset , yOffset , zOffset ] ,
204+ } ) ;
205+ }
206+
207+ return null ;
208+ }
209+
145210function createStyle (
146211 styleVal : string | number ,
147212 property : Property ,
@@ -166,6 +231,27 @@ function createStyle(
166231 } ;
167232}
168233
169- function isArbitraryValue ( value : string ) : boolean {
234+ export function isArbitraryValue ( value : string ) : boolean {
170235 return value . startsWith ( `[` ) && value . endsWith ( `]` ) ;
171236}
237+
238+ function parseOriginValue (
239+ value : string | undefined ,
240+ allowedPositions : Position [ ] ,
241+ allowedUnits : Unit [ ] ,
242+ context : ParseContext = { } ,
243+ ) : string | number | null {
244+ if ( ! value ) {
245+ return null ;
246+ }
247+
248+ if ( allowedPositions . includes ( value as any ) ) {
249+ return value ;
250+ }
251+
252+ const parsedValue = parseNumericValue ( value , context ) ;
253+
254+ return parsedValue === null || ! allowedUnits . includes ( parsedValue [ 1 ] )
255+ ? null
256+ : toStyleVal ( parsedValue [ 0 ] , parsedValue [ 1 ] , context ) ;
257+ }
0 commit comments