@cssxjs/css-to-react-native 3.2.0-0 → 3.2.0-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,240 @@
1
+ import camelizeStyleName from 'camelize'
2
+ import { SPACE, COMMA, IDENT, TIME, NONE, VARIABLE } from '../tokenTypes'
3
+
4
+ // Timing function keywords
5
+ const timingFunctionKeywords = [
6
+ 'ease',
7
+ 'linear',
8
+ 'ease-in',
9
+ 'ease-out',
10
+ 'ease-in-out',
11
+ 'step-start',
12
+ 'step-end',
13
+ ]
14
+
15
+ const isTimingFunction = value => {
16
+ if (timingFunctionKeywords.includes(value.toLowerCase())) {
17
+ return true
18
+ }
19
+ // cubic-bezier and steps are handled as functions
20
+ return false
21
+ }
22
+
23
+ const isTime = value => /^[+-]?(?:\d*\.)?\d+(?:ms|s)$/i.test(value)
24
+
25
+ // Helper to parse comma-separated values
26
+ // Returns single value if only one, array if multiple
27
+ const parseCommaSeparatedValues = (tokenStream, parseValue) => {
28
+ const values = []
29
+ let parsingFirst = true
30
+
31
+ while (tokenStream.hasTokens()) {
32
+ if (!parsingFirst) {
33
+ tokenStream.expect(COMMA)
34
+ }
35
+
36
+ // Skip leading/trailing spaces
37
+ if (tokenStream.matches(SPACE)) {
38
+ // continue
39
+ }
40
+
41
+ const value = parseValue(tokenStream)
42
+ values.push(value)
43
+
44
+ // Skip trailing spaces
45
+ if (tokenStream.matches(SPACE)) {
46
+ // continue
47
+ }
48
+
49
+ parsingFirst = false
50
+ }
51
+
52
+ return values.length === 1 ? values[0] : values
53
+ }
54
+
55
+ // Transform for transition-property
56
+ export const transitionProperty = tokenStream => {
57
+ const properties = parseCommaSeparatedValues(tokenStream, ts => {
58
+ const prop = ts.expect(IDENT, NONE)
59
+ // Don't camelize special values like 'all' and 'none'
60
+ return prop === 'all' || prop === 'none' ? prop : camelizeStyleName(prop)
61
+ })
62
+ return { transitionProperty: properties }
63
+ }
64
+
65
+ // Transform for transition-duration
66
+ export const transitionDuration = tokenStream => {
67
+ const durations = parseCommaSeparatedValues(tokenStream, ts =>
68
+ ts.expect(TIME, VARIABLE)
69
+ )
70
+ return { transitionDuration: durations }
71
+ }
72
+
73
+ // Transform for transition-timing-function
74
+ export const transitionTimingFunction = tokenStream => {
75
+ const timingFunctions = parseCommaSeparatedValues(tokenStream, ts => {
76
+ // Check for function (cubic-bezier or steps)
77
+ const funcStream = ts.matchesFunction()
78
+ if (funcStream) {
79
+ const funcName = funcStream.functionName
80
+ const args = []
81
+ while (funcStream.hasTokens()) {
82
+ if (funcStream.matches(SPACE) || funcStream.matches(COMMA)) {
83
+ continue
84
+ }
85
+ const val = funcStream.expect(IDENT, TIME, node => {
86
+ if (node.type === 'word') return node.value
87
+ return null
88
+ })
89
+ args.push(val)
90
+ }
91
+ return `${funcName}(${args.join(', ')})`
92
+ }
93
+ return ts.expect(IDENT)
94
+ })
95
+ return { transitionTimingFunction: timingFunctions }
96
+ }
97
+
98
+ // Transform for transition-delay
99
+ export const transitionDelay = tokenStream => {
100
+ const delays = parseCommaSeparatedValues(tokenStream, ts =>
101
+ ts.expect(TIME, VARIABLE)
102
+ )
103
+ return { transitionDelay: delays }
104
+ }
105
+
106
+ export default tokenStream => {
107
+ // Handle 'none'
108
+ if (tokenStream.matches(NONE)) {
109
+ tokenStream.expectEmpty()
110
+ return {
111
+ transitionProperty: 'none',
112
+ transitionDuration: '0s',
113
+ transitionTimingFunction: 'ease',
114
+ transitionDelay: '0s',
115
+ }
116
+ }
117
+
118
+ const properties = []
119
+ const durations = []
120
+ const timingFunctions = []
121
+ const delays = []
122
+
123
+ let parsingFirst = true
124
+
125
+ while (tokenStream.hasTokens()) {
126
+ if (!parsingFirst) {
127
+ tokenStream.expect(COMMA)
128
+ }
129
+
130
+ // Parse single transition: property duration timing-function delay
131
+ // Order can vary, but typically: property duration [timing-function] [delay]
132
+ let property = null
133
+ let duration = null
134
+ let timingFunction = null
135
+ let delay = null
136
+
137
+ // Skip leading space
138
+ if (tokenStream.matches(SPACE)) {
139
+ // continue
140
+ }
141
+
142
+ // Parse tokens for this transition
143
+ while (tokenStream.hasTokens()) {
144
+ // Check for comma (next transition)
145
+ tokenStream.saveRewindPoint()
146
+ if (tokenStream.matches(SPACE)) {
147
+ if (tokenStream.matches(COMMA)) {
148
+ tokenStream.rewind()
149
+ break
150
+ }
151
+ }
152
+ if (tokenStream.matches(COMMA)) {
153
+ tokenStream.rewind()
154
+ break
155
+ }
156
+ tokenStream.rewind()
157
+
158
+ // Try to match different token types
159
+ if (tokenStream.matches(SPACE)) {
160
+ continue
161
+ }
162
+
163
+ // Match time or variable (for duration/delay) - check before functions
164
+ if (tokenStream.matches(TIME) || tokenStream.matches(VARIABLE)) {
165
+ const value = tokenStream.lastValue
166
+ if (duration === null) {
167
+ duration = value
168
+ } else {
169
+ delay = value
170
+ }
171
+ continue
172
+ }
173
+
174
+ // Check for timing function (cubic-bezier or steps)
175
+ const funcStream = tokenStream.matchesFunction()
176
+ if (funcStream) {
177
+ const funcName = funcStream.functionName
178
+ const args = []
179
+ while (funcStream.hasTokens()) {
180
+ if (funcStream.matches(SPACE) || funcStream.matches(COMMA)) {
181
+ continue
182
+ }
183
+ const val = funcStream.expect(IDENT, TIME, node => {
184
+ if (node.type === 'word') return node.value
185
+ return null
186
+ })
187
+ args.push(val)
188
+ }
189
+ timingFunction = `${funcName}(${args.join(', ')})`
190
+ continue
191
+ }
192
+
193
+ // Match word or time
194
+ if (tokenStream.matches(IDENT)) {
195
+ const value = tokenStream.lastValue
196
+ if (isTimingFunction(value)) {
197
+ timingFunction = value
198
+ } else {
199
+ property = value
200
+ }
201
+ continue
202
+ }
203
+
204
+ // Try to match as word for property names like 'all', 'opacity', etc.
205
+ const wordMatch = tokenStream.expect(node => {
206
+ if (node.type === 'word') return node.value
207
+ return null
208
+ })
209
+ if (isTime(wordMatch)) {
210
+ if (duration === null) {
211
+ duration = wordMatch
212
+ } else {
213
+ delay = wordMatch
214
+ }
215
+ } else if (isTimingFunction(wordMatch)) {
216
+ timingFunction = wordMatch
217
+ } else {
218
+ property = wordMatch
219
+ }
220
+ }
221
+
222
+ // Apply defaults and camelize property name
223
+ const propName = property || 'all'
224
+ properties.push(propName === 'all' ? 'all' : camelizeStyleName(propName))
225
+ durations.push(duration || '0s')
226
+ timingFunctions.push(timingFunction || 'ease')
227
+ delays.push(delay || '0s')
228
+
229
+ parsingFirst = false
230
+ }
231
+
232
+ // Return single values if only one transition, arrays if multiple
233
+ const isSingle = properties.length === 1
234
+ return {
235
+ transitionProperty: isSingle ? properties[0] : properties,
236
+ transitionDuration: isSingle ? durations[0] : durations,
237
+ transitionTimingFunction: isSingle ? timingFunctions[0] : timingFunctions,
238
+ transitionDelay: isSingle ? delays[0] : delays,
239
+ }
240
+ }
@@ -9,7 +9,7 @@ import {
9
9
  } from '../tokenTypes'
10
10
 
11
11
  export const directionFactory = ({
12
- types = [LENGTH, UNSUPPORTED_LENGTH_UNIT, PERCENT],
12
+ types = [LENGTH, UNSUPPORTED_LENGTH_UNIT, PERCENT, VARIABLE],
13
13
  directions = ['Top', 'Right', 'Bottom', 'Left'],
14
14
  prefix = '',
15
15
  suffix = '',
@@ -68,10 +68,13 @@ export const parseShadow = tokenStream => {
68
68
  offsetX === undefined &&
69
69
  tokenStream.matches(LENGTH, UNSUPPORTED_LENGTH_UNIT)
70
70
  ) {
71
+ // First offset must be a concrete LENGTH to distinguish from color
71
72
  offsetX = tokenStream.lastValue
72
73
  tokenStream.expect(SPACE)
73
- offsetY = tokenStream.expect(LENGTH, UNSUPPORTED_LENGTH_UNIT)
74
+ // Second offset and radius can be VARIABLE
75
+ offsetY = tokenStream.expect(LENGTH, UNSUPPORTED_LENGTH_UNIT, VARIABLE)
74
76
 
77
+ // Try to match optional blur-radius (concrete LENGTH only here)
75
78
  tokenStream.saveRewindPoint()
76
79
  if (
77
80
  tokenStream.matches(SPACE) &&
@@ -81,6 +84,25 @@ export const parseShadow = tokenStream => {
81
84
  } else {
82
85
  tokenStream.rewind()
83
86
  }
87
+ } else if (
88
+ offsetX !== undefined &&
89
+ radius === undefined &&
90
+ color === undefined &&
91
+ tokenStream.matches(VARIABLE)
92
+ ) {
93
+ // VARIABLE after offsets - could be radius or color
94
+ // Peek ahead to determine which
95
+ const potentialValue = tokenStream.lastValue
96
+ tokenStream.saveRewindPoint()
97
+ if (tokenStream.matches(SPACE) && tokenStream.hasTokens()) {
98
+ // There's more content - this VARIABLE is the radius
99
+ tokenStream.rewind()
100
+ radius = potentialValue
101
+ } else {
102
+ // No more content - this VARIABLE is the color
103
+ tokenStream.rewind()
104
+ color = potentialValue
105
+ }
84
106
  } else if (
85
107
  color === undefined &&
86
108
  (tokenStream.matches(COLOR) || tokenStream.matches(VARIABLE))