@thelacanians/vue-native-cli 0.4.12 → 0.4.13

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.
Files changed (69) hide show
  1. package/dist/cli.js +1 -1
  2. package/native/android/.editorconfig +25 -0
  3. package/native/android/VueNativeCore/build.gradle.kts +25 -1
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +17 -10
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +5 -5
  6. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +13 -13
  7. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +27 -27
  8. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +6 -4
  9. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +1 -1
  10. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +24 -12
  11. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +5 -2
  12. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +7 -7
  13. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +12 -12
  14. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +0 -1
  15. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +5 -2
  16. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +5 -2
  17. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +3 -2
  18. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +5 -3
  19. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +5 -2
  20. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +5 -2
  21. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +5 -2
  22. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +3 -3
  23. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +3 -3
  24. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +0 -1
  25. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +9 -3
  26. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +7 -5
  27. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +5 -2
  28. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +4 -1
  29. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +77 -21
  30. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +20 -5
  31. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +12 -3
  32. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +5 -2
  33. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +88 -23
  34. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +24 -11
  35. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +7 -2
  36. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +24 -12
  37. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +14 -11
  38. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +79 -24
  39. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +10 -7
  40. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +5 -5
  41. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +17 -8
  42. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +20 -5
  43. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +4 -1
  44. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +12 -3
  45. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +4 -1
  46. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +24 -6
  47. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +13 -5
  48. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +8 -2
  49. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +17 -8
  50. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +20 -5
  51. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +16 -4
  52. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +6 -3
  53. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +4 -2
  54. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +26 -8
  55. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +127 -84
  56. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +26 -26
  57. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +1 -1
  58. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentRegistryTest.kt +173 -0
  59. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +436 -0
  60. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleRegistryTest.kt +251 -0
  61. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/StyleEngineTest.kt +482 -0
  62. package/native/android/build.gradle.kts +1 -0
  63. package/native/ios/.swiftlint.yml +62 -0
  64. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +4 -1
  65. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentRegistryTests.swift +237 -0
  66. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeBridgeOperationTests.swift +398 -0
  67. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleRegistryTests.swift +203 -0
  68. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/StyleEngineTests.swift +381 -0
  69. package/package.json +1 -1
@@ -62,10 +62,22 @@ object StyleEngine {
62
62
  val bg = ensureBackground(view)
63
63
  val radii = bg.cornerRadii ?: FloatArray(8) { 0f }
64
64
  when (key) {
65
- "borderTopLeftRadius" -> { radii[0] = px; radii[1] = px }
66
- "borderTopRightRadius" -> { radii[2] = px; radii[3] = px }
67
- "borderBottomRightRadius" -> { radii[4] = px; radii[5] = px }
68
- "borderBottomLeftRadius" -> { radii[6] = px; radii[7] = px }
65
+ "borderTopLeftRadius" -> {
66
+ radii[0] = px
67
+ radii[1] = px
68
+ }
69
+ "borderTopRightRadius" -> {
70
+ radii[2] = px
71
+ radii[3] = px
72
+ }
73
+ "borderBottomRightRadius" -> {
74
+ radii[4] = px
75
+ radii[5] = px
76
+ }
77
+ "borderBottomLeftRadius" -> {
78
+ radii[6] = px
79
+ radii[7] = px
80
+ }
69
81
  }
70
82
  bg.cornerRadii = radii
71
83
  view.background = view.background
@@ -124,37 +136,52 @@ object StyleEngine {
124
136
  }
125
137
 
126
138
  // --- Margin (stored in FlexProps, applied when inserted into parent) ---
127
- "margin" -> updateFlexProps(view) { fp -> val px = dpToPx(ctx, toFloat(value, 0f)).toInt(); fp.copy(marginLeft = px, marginTop = px, marginRight = px, marginBottom = px) }
128
- "marginHorizontal" -> updateFlexProps(view) { fp -> val px = dpToPx(ctx, toFloat(value, 0f)).toInt(); fp.copy(marginLeft = px, marginRight = px) }
129
- "marginVertical" -> updateFlexProps(view) { fp -> val px = dpToPx(ctx, toFloat(value, 0f)).toInt(); fp.copy(marginTop = px, marginBottom = px) }
130
- "marginLeft" -> updateFlexProps(view) { fp -> fp.copy(marginLeft = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
131
- "marginRight" -> updateFlexProps(view) { fp -> fp.copy(marginRight = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
132
- "marginTop" -> updateFlexProps(view) { fp -> fp.copy(marginTop = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
133
- "marginBottom" -> updateFlexProps(view) { fp -> fp.copy(marginBottom = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
134
- "marginStart" -> updateFlexProps(view) { fp -> fp.copy(marginStart = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
135
- "marginEnd" -> updateFlexProps(view) { fp -> fp.copy(marginEnd = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
139
+ "margin" -> updateFlexProps(view) { fp ->
140
+ val px = dpToPx(ctx, toFloat(value, 0f)).toInt()
141
+ fp.copy(marginLeft = px, marginTop = px, marginRight = px, marginBottom = px)
142
+ }
143
+ "marginHorizontal" -> updateFlexProps(view) { fp ->
144
+ val px = dpToPx(ctx, toFloat(value, 0f)).toInt()
145
+ fp.copy(marginLeft = px, marginRight = px)
146
+ }
147
+ "marginVertical" -> updateFlexProps(view) { fp ->
148
+ val px = dpToPx(ctx, toFloat(value, 0f)).toInt()
149
+ fp.copy(marginTop = px, marginBottom = px)
150
+ }
151
+ "marginLeft" -> updateFlexProps(view) { fp -> fp.copy(marginLeft = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
152
+ "marginRight" -> updateFlexProps(view) { fp -> fp.copy(marginRight = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
153
+ "marginTop" -> updateFlexProps(view) { fp -> fp.copy(marginTop = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
154
+ "marginBottom" -> updateFlexProps(view) { fp -> fp.copy(marginBottom = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
155
+ "marginStart" -> updateFlexProps(view) { fp -> fp.copy(marginStart = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
156
+ "marginEnd" -> updateFlexProps(view) { fp -> fp.copy(marginEnd = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
136
157
 
137
158
  // --- Dimensions ---
138
159
  "width" -> {
139
160
  val pct = parsePercent(value)
140
- if (pct != null) updateFlexProps(view) { fp -> fp.copy(width = ViewGroup.LayoutParams.WRAP_CONTENT, widthPercent = pct) }
141
- else updateFlexProps(view) { fp -> fp.copy(width = parseDimension(ctx, value), widthPercent = -1f) }
161
+ if (pct != null) {
162
+ updateFlexProps(view) { fp -> fp.copy(width = ViewGroup.LayoutParams.WRAP_CONTENT, widthPercent = pct) }
163
+ } else {
164
+ updateFlexProps(view) { fp -> fp.copy(width = parseDimension(ctx, value), widthPercent = -1f) }
165
+ }
142
166
  }
143
167
  "height" -> {
144
168
  val pct = parsePercent(value)
145
- if (pct != null) updateFlexProps(view) { fp -> fp.copy(height = ViewGroup.LayoutParams.WRAP_CONTENT, heightPercent = pct) }
146
- else updateFlexProps(view) { fp -> fp.copy(height = parseDimension(ctx, value), heightPercent = -1f) }
169
+ if (pct != null) {
170
+ updateFlexProps(view) { fp -> fp.copy(height = ViewGroup.LayoutParams.WRAP_CONTENT, heightPercent = pct) }
171
+ } else {
172
+ updateFlexProps(view) { fp -> fp.copy(height = parseDimension(ctx, value), heightPercent = -1f) }
173
+ }
147
174
  }
148
- "minWidth" -> updateFlexProps(view) { fp -> fp.copy(minWidth = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
175
+ "minWidth" -> updateFlexProps(view) { fp -> fp.copy(minWidth = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
149
176
  "minHeight" -> updateFlexProps(view) { fp -> fp.copy(minHeight = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
150
- "maxWidth" -> updateFlexProps(view) { fp -> fp.copy(maxWidth = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
177
+ "maxWidth" -> updateFlexProps(view) { fp -> fp.copy(maxWidth = dpToPx(ctx, toFloat(value, 0f)).toInt()) }
151
178
 
152
179
  // --- Flex props (stored in FlexProps, applied when inserted) ---
153
180
  "flex" -> {
154
181
  val f = toFloat(value, 0f)
155
182
  updateFlexProps(view) { fp -> fp.copy(flexGrow = f, flexShrink = 1f, width = 0, height = 0) }
156
183
  }
157
- "flexGrow" -> updateFlexProps(view) { fp -> fp.copy(flexGrow = toFloat(value, 0f)) }
184
+ "flexGrow" -> updateFlexProps(view) { fp -> fp.copy(flexGrow = toFloat(value, 0f)) }
158
185
  "flexShrink" -> updateFlexProps(view) { fp -> fp.copy(flexShrink = toFloat(value, 1f)) }
159
186
  "flexBasis" -> {
160
187
  if (value == "auto" || value == null) {
@@ -170,8 +197,8 @@ object StyleEngine {
170
197
  }
171
198
  }
172
199
  }
173
- "alignSelf" -> updateFlexProps(view) { fp -> fp.copy(alignSelf = parseAlignSelf(value)) }
174
- "order" -> updateFlexProps(view) { fp -> fp.copy(order = toInt(value, 1)) }
200
+ "alignSelf" -> updateFlexProps(view) { fp -> fp.copy(alignSelf = parseAlignSelf(value)) }
201
+ "order" -> updateFlexProps(view) { fp -> fp.copy(order = toInt(value, 1)) }
175
202
 
176
203
  // --- Parent Flex props (applied to FlexboxLayout itself) ---
177
204
  "flexDirection" -> {
@@ -281,8 +308,11 @@ object StyleEngine {
281
308
  view.visibility = if (value == "none") View.GONE else View.VISIBLE
282
309
  }
283
310
  "visible" -> {
284
- if (value == false || value == "false") view.visibility = View.INVISIBLE
285
- else view.visibility = View.VISIBLE
311
+ if (value == false || value == "false") {
312
+ view.visibility = View.INVISIBLE
313
+ } else {
314
+ view.visibility = View.VISIBLE
315
+ }
286
316
  }
287
317
  "hidden" -> {
288
318
  view.visibility = if (value == true || value == "true") View.INVISIBLE else View.VISIBLE
@@ -332,15 +362,15 @@ object StyleEngine {
332
362
  override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
333
363
  super.onInitializeAccessibilityNodeInfo(host, info)
334
364
  when (role) {
335
- "button" -> info.className = "android.widget.Button"
336
- "link" -> info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK)
337
- "header" -> info.isHeading = true
338
- "image" -> info.className = "android.widget.ImageView"
339
- "text" -> info.className = "android.widget.TextView"
340
- "search" -> info.className = "android.widget.EditText"
365
+ "button" -> info.className = "android.widget.Button"
366
+ "link" -> info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK)
367
+ "header" -> info.isHeading = true
368
+ "image" -> info.className = "android.widget.ImageView"
369
+ "text" -> info.className = "android.widget.TextView"
370
+ "search" -> info.className = "android.widget.EditText"
341
371
  "adjustable" -> info.className = "android.widget.SeekBar"
342
- "tab" -> info.roleDescription = "tab"
343
- "none" -> { /* no special role */ }
372
+ "tab" -> info.roleDescription = "tab"
373
+ "none" -> { /* no special role */ }
344
374
  }
345
375
  }
346
376
  })
@@ -363,13 +393,16 @@ object StyleEngine {
363
393
  view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
364
394
  }
365
395
  "accessible" -> view.importantForAccessibility =
366
- if (value == true) View.IMPORTANT_FOR_ACCESSIBILITY_YES
367
- else View.IMPORTANT_FOR_ACCESSIBILITY_NO
396
+ if (value == true) {
397
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES
398
+ } else {
399
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
400
+ }
368
401
  "importantForAccessibility" -> {
369
402
  view.importantForAccessibility = when (value) {
370
403
  "auto" -> View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
371
- "yes" -> View.IMPORTANT_FOR_ACCESSIBILITY_YES
372
- "no" -> View.IMPORTANT_FOR_ACCESSIBILITY_NO
404
+ "yes" -> View.IMPORTANT_FOR_ACCESSIBILITY_YES
405
+ "no" -> View.IMPORTANT_FOR_ACCESSIBILITY_NO
373
406
  "no-hide-descendants" -> View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
374
407
  else -> View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
375
408
  }
@@ -390,23 +423,28 @@ object StyleEngine {
390
423
  "fontWeight" -> {
391
424
  val current = tv.typeface ?: android.graphics.Typeface.DEFAULT
392
425
  tv.setTypeface(current,
393
- if (value == "bold" || (value as? Number)?.toInt() ?: 0 >= 600)
426
+ if (value == "bold" || (value as? Number)?.toInt() ?: 0 >= 600) {
394
427
  android.graphics.Typeface.BOLD
395
- else android.graphics.Typeface.NORMAL
428
+ } else {
429
+ android.graphics.Typeface.NORMAL
430
+ }
396
431
  )
397
432
  }
398
433
  "fontStyle" -> {
399
434
  val current = tv.typeface ?: android.graphics.Typeface.DEFAULT
400
435
  tv.setTypeface(current,
401
- if (value == "italic") android.graphics.Typeface.ITALIC
402
- else android.graphics.Typeface.NORMAL
436
+ if (value == "italic") {
437
+ android.graphics.Typeface.ITALIC
438
+ } else {
439
+ android.graphics.Typeface.NORMAL
440
+ }
403
441
  )
404
442
  }
405
443
  "textAlign" -> tv.textAlignment = when (value) {
406
444
  "center" -> View.TEXT_ALIGNMENT_CENTER
407
- "right" -> View.TEXT_ALIGNMENT_VIEW_END
408
- "left" -> View.TEXT_ALIGNMENT_VIEW_START
409
- else -> View.TEXT_ALIGNMENT_INHERIT
445
+ "right" -> View.TEXT_ALIGNMENT_VIEW_END
446
+ "left" -> View.TEXT_ALIGNMENT_VIEW_START
447
+ else -> View.TEXT_ALIGNMENT_INHERIT
410
448
  }
411
449
  "lineHeight" -> {
412
450
  val px = dpToPx(ctx, toFloat(value, 0f))
@@ -419,20 +457,20 @@ object StyleEngine {
419
457
  }
420
458
  "textDecorationLine" -> {
421
459
  tv.paintFlags = when (value) {
422
- "underline" -> tv.paintFlags or android.graphics.Paint.UNDERLINE_TEXT_FLAG
460
+ "underline" -> tv.paintFlags or android.graphics.Paint.UNDERLINE_TEXT_FLAG
423
461
  "line-through" -> tv.paintFlags or android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
424
- "none" -> (tv.paintFlags and android.graphics.Paint.UNDERLINE_TEXT_FLAG.inv()
462
+ "none" -> (tv.paintFlags and android.graphics.Paint.UNDERLINE_TEXT_FLAG.inv()
425
463
  and android.graphics.Paint.STRIKE_THRU_TEXT_FLAG.inv())
426
- else -> tv.paintFlags
464
+ else -> tv.paintFlags
427
465
  }
428
466
  }
429
467
  "textTransform" -> {
430
468
  val t = tv.text?.toString() ?: ""
431
469
  tv.text = when (value) {
432
- "uppercase" -> t.uppercase()
433
- "lowercase" -> t.lowercase()
470
+ "uppercase" -> t.uppercase()
471
+ "lowercase" -> t.lowercase()
434
472
  "capitalize" -> t.split(" ").joinToString(" ") { it.replaceFirstChar(Char::uppercase) }
435
- else -> t
473
+ else -> t
436
474
  }
437
475
  }
438
476
  "numberOfLines" -> {
@@ -528,7 +566,9 @@ object StyleEngine {
528
566
  s == "gray" || s == "grey" -> Color.GRAY
529
567
  else -> Color.parseColor(s)
530
568
  }
531
- } catch (e: Exception) { null }
569
+ } catch (e: Exception) {
570
+ null
571
+ }
532
572
  }
533
573
 
534
574
  // -- Dimension helpers --------------------------------------------------------
@@ -546,8 +586,11 @@ object StyleEngine {
546
586
  value is String && value.endsWith("%") -> {
547
587
  // 100% maps to MATCH_PARENT; other percentages are handled via widthPercent/heightPercent
548
588
  // in the "width"/"height" cases above — this fallback covers minWidth/minHeight
549
- if (value == "100%") ViewGroup.LayoutParams.MATCH_PARENT
550
- else ViewGroup.LayoutParams.WRAP_CONTENT
589
+ if (value == "100%") {
590
+ ViewGroup.LayoutParams.MATCH_PARENT
591
+ } else {
592
+ ViewGroup.LayoutParams.WRAP_CONTENT
593
+ }
551
594
  }
552
595
  value == "auto" || value == null -> ViewGroup.LayoutParams.WRAP_CONTENT
553
596
  else -> dpToPx(context, toFloat(value, 0f)).toInt()
@@ -564,20 +607,20 @@ object StyleEngine {
564
607
 
565
608
  fun toFloat(value: Any?, default: Float): Float = when (value) {
566
609
  is Double -> value.toFloat()
567
- is Float -> value
568
- is Int -> value.toFloat()
569
- is Long -> value.toFloat()
610
+ is Float -> value
611
+ is Int -> value.toFloat()
612
+ is Long -> value.toFloat()
570
613
  is String -> value.toFloatOrNull() ?: default
571
- else -> default
614
+ else -> default
572
615
  }
573
616
 
574
617
  fun toInt(value: Any?, default: Int): Int = when (value) {
575
- is Int -> value
618
+ is Int -> value
576
619
  is Double -> value.toInt()
577
- is Float -> value.toInt()
578
- is Long -> value.toInt()
620
+ is Float -> value.toInt()
621
+ is Long -> value.toInt()
579
622
  is String -> value.toIntOrNull() ?: default
580
- else -> default
623
+ else -> default
581
624
  }
582
625
 
583
626
  // -- GradientDrawable management ----------------------------------------------
@@ -593,51 +636,51 @@ object StyleEngine {
593
636
  // -- Flex enum parsing --------------------------------------------------------
594
637
 
595
638
  private fun parseFlexDirection(value: Any?) = when (value) {
596
- "row" -> FlexDirection.ROW
597
- "row-reverse" -> FlexDirection.ROW_REVERSE
639
+ "row" -> FlexDirection.ROW
640
+ "row-reverse" -> FlexDirection.ROW_REVERSE
598
641
  "column-reverse" -> FlexDirection.COLUMN_REVERSE
599
- else -> FlexDirection.COLUMN
642
+ else -> FlexDirection.COLUMN
600
643
  }
601
644
 
602
645
  private fun parseFlexWrap(value: Any?) = when (value) {
603
- "wrap" -> FlexWrap.WRAP
646
+ "wrap" -> FlexWrap.WRAP
604
647
  "wrap-reverse" -> FlexWrap.WRAP_REVERSE
605
- else -> FlexWrap.NOWRAP
648
+ else -> FlexWrap.NOWRAP
606
649
  }
607
650
 
608
651
  private fun parseAlignItems(value: Any?) = when (value) {
609
652
  "flex-start" -> AlignItems.FLEX_START
610
- "flex-end" -> AlignItems.FLEX_END
611
- "center" -> AlignItems.CENTER
612
- "baseline" -> AlignItems.BASELINE
613
- else -> AlignItems.STRETCH
653
+ "flex-end" -> AlignItems.FLEX_END
654
+ "center" -> AlignItems.CENTER
655
+ "baseline" -> AlignItems.BASELINE
656
+ else -> AlignItems.STRETCH
614
657
  }
615
658
 
616
659
  private fun parseAlignContent(value: Any?) = when (value) {
617
- "flex-start" -> AlignContent.FLEX_START
618
- "flex-end" -> AlignContent.FLEX_END
619
- "center" -> AlignContent.CENTER
660
+ "flex-start" -> AlignContent.FLEX_START
661
+ "flex-end" -> AlignContent.FLEX_END
662
+ "center" -> AlignContent.CENTER
620
663
  "space-between" -> AlignContent.SPACE_BETWEEN
621
- "space-around" -> AlignContent.SPACE_AROUND
622
- else -> AlignContent.STRETCH
664
+ "space-around" -> AlignContent.SPACE_AROUND
665
+ else -> AlignContent.STRETCH
623
666
  }
624
667
 
625
668
  private fun parseJustifyContent(value: Any?) = when (value) {
626
- "flex-end" -> JustifyContent.FLEX_END
627
- "center" -> JustifyContent.CENTER
669
+ "flex-end" -> JustifyContent.FLEX_END
670
+ "center" -> JustifyContent.CENTER
628
671
  "space-between" -> JustifyContent.SPACE_BETWEEN
629
- "space-around" -> JustifyContent.SPACE_AROUND
630
- "space-evenly" -> JustifyContent.SPACE_EVENLY
631
- else -> JustifyContent.FLEX_START
672
+ "space-around" -> JustifyContent.SPACE_AROUND
673
+ "space-evenly" -> JustifyContent.SPACE_EVENLY
674
+ else -> JustifyContent.FLEX_START
632
675
  }
633
676
 
634
677
  private fun parseAlignSelf(value: Any?) = when (value) {
635
678
  "flex-start" -> AlignSelf.FLEX_START
636
- "flex-end" -> AlignSelf.FLEX_END
637
- "center" -> AlignSelf.CENTER
638
- "baseline" -> AlignSelf.BASELINE
639
- "stretch" -> AlignSelf.STRETCH
640
- else -> AlignSelf.AUTO
679
+ "flex-end" -> AlignSelf.FLEX_END
680
+ "center" -> AlignSelf.CENTER
681
+ "baseline" -> AlignSelf.BASELINE
682
+ "stretch" -> AlignSelf.STRETCH
683
+ else -> AlignSelf.AUTO
641
684
  }
642
685
 
643
686
  private val FLEX_LAYOUT_KEYS = setOf(
@@ -2,42 +2,42 @@ package com.vuenative.core
2
2
 
3
3
  /** View tag IDs used with View.setTag(id, value) */
4
4
  object Tags {
5
- const val FLEX_PROPS = 0x7F_FF_0001
5
+ const val FLEX_PROPS = 0x7F_FF_0001
6
6
  const val EVENT_HANDLER = 0x7F_FF_0002
7
- const val NODE_ID = 0x7F_FF_0003
8
- const val FACTORY = 0x7F_FF_0004
9
- const val BORDER_COLOR = 0x7F_FF_0010
10
- const val BORDER_WIDTH = 0x7F_FF_0011
11
- const val GAP = 0x7F_FF_0012
12
- const val SHADOW_COLOR = 0x7F_FF_0013
13
- const val SHADOW_OPACITY= 0x7F_FF_0014
7
+ const val NODE_ID = 0x7F_FF_0003
8
+ const val FACTORY = 0x7F_FF_0004
9
+ const val BORDER_COLOR = 0x7F_FF_0010
10
+ const val BORDER_WIDTH = 0x7F_FF_0011
11
+ const val GAP = 0x7F_FF_0012
12
+ const val SHADOW_COLOR = 0x7F_FF_0013
13
+ const val SHADOW_OPACITY = 0x7F_FF_0014
14
14
  const val SHADOW_RADIUS = 0x7F_FF_0015
15
15
  const val SHADOW_OFFSET_X = 0x7F_FF_0016
16
16
  const val SHADOW_OFFSET_Y = 0x7F_FF_0017
17
- const val POSITION = 0x7F_FF_0020
18
- const val POSITION_TOP = 0x7F_FF_0021
19
- const val POSITION_LEFT = 0x7F_FF_0022
17
+ const val POSITION = 0x7F_FF_0020
18
+ const val POSITION_TOP = 0x7F_FF_0021
19
+ const val POSITION_LEFT = 0x7F_FF_0022
20
20
  const val POSITION_RIGHT = 0x7F_FF_0023
21
- const val POSITION_BOTTOM= 0x7F_FF_0024
21
+ const val POSITION_BOTTOM = 0x7F_FF_0024
22
22
  const val INTERNAL_PROPS = 0x7F_FF_0030
23
23
  }
24
24
 
25
25
  /** Alias used throughout the codebase */
26
- const val TAG_FLEX_PROPS = Tags.FLEX_PROPS
26
+ const val TAG_FLEX_PROPS = Tags.FLEX_PROPS
27
27
  const val TAG_EVENT_HANDLER = Tags.EVENT_HANDLER
28
- const val TAG_NODE_ID = Tags.NODE_ID
29
- const val TAG_FACTORY = Tags.FACTORY
30
- const val TAG_BORDER_COLOR = Tags.BORDER_COLOR
31
- const val TAG_BORDER_WIDTH = Tags.BORDER_WIDTH
32
- const val TAG_GAP = Tags.GAP
33
- const val TAG_SHADOW_COLOR = Tags.SHADOW_COLOR
34
- const val TAG_SHADOW_OPACITY = Tags.SHADOW_OPACITY
35
- const val TAG_SHADOW_RADIUS = Tags.SHADOW_RADIUS
28
+ const val TAG_NODE_ID = Tags.NODE_ID
29
+ const val TAG_FACTORY = Tags.FACTORY
30
+ const val TAG_BORDER_COLOR = Tags.BORDER_COLOR
31
+ const val TAG_BORDER_WIDTH = Tags.BORDER_WIDTH
32
+ const val TAG_GAP = Tags.GAP
33
+ const val TAG_SHADOW_COLOR = Tags.SHADOW_COLOR
34
+ const val TAG_SHADOW_OPACITY = Tags.SHADOW_OPACITY
35
+ const val TAG_SHADOW_RADIUS = Tags.SHADOW_RADIUS
36
36
  const val TAG_SHADOW_OFFSET_X = Tags.SHADOW_OFFSET_X
37
37
  const val TAG_SHADOW_OFFSET_Y = Tags.SHADOW_OFFSET_Y
38
- const val TAG_POSITION = Tags.POSITION
39
- const val TAG_POSITION_TOP = Tags.POSITION_TOP
40
- const val TAG_POSITION_LEFT = Tags.POSITION_LEFT
41
- const val TAG_POSITION_RIGHT = Tags.POSITION_RIGHT
38
+ const val TAG_POSITION = Tags.POSITION
39
+ const val TAG_POSITION_TOP = Tags.POSITION_TOP
40
+ const val TAG_POSITION_LEFT = Tags.POSITION_LEFT
41
+ const val TAG_POSITION_RIGHT = Tags.POSITION_RIGHT
42
42
  const val TAG_POSITION_BOTTOM = Tags.POSITION_BOTTOM
43
- const val TAG_INTERNAL_PROPS = Tags.INTERNAL_PROPS
43
+ const val TAG_INTERNAL_PROPS = Tags.INTERNAL_PROPS
@@ -4,10 +4,10 @@ import android.content.Intent
4
4
  import android.graphics.Color
5
5
  import android.os.Build
6
6
  import android.os.Bundle
7
+ import android.util.Log
7
8
  import android.view.View
8
9
  import android.view.ViewGroup
9
10
  import android.widget.FrameLayout
10
- import android.util.Log
11
11
  import androidx.appcompat.app.AppCompatActivity
12
12
 
13
13
  /**
@@ -0,0 +1,173 @@
1
+ package com.vuenative.core
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import android.widget.TextView
6
+ import androidx.test.core.app.ApplicationProvider
7
+ import com.google.android.flexbox.FlexboxLayout
8
+ import org.junit.Assert.assertEquals
9
+ import org.junit.Assert.assertNotNull
10
+ import org.junit.Assert.assertNull
11
+ import org.junit.Assert.assertTrue
12
+ import org.junit.Before
13
+ import org.junit.Test
14
+ import org.junit.runner.RunWith
15
+ import org.robolectric.RobolectricTestRunner
16
+ import org.robolectric.annotation.Config
17
+
18
+ @RunWith(RobolectricTestRunner::class)
19
+ @Config(sdk = [34])
20
+ class ComponentRegistryTest {
21
+
22
+ private lateinit var context: Context
23
+ private lateinit var registry: ComponentRegistry
24
+
25
+ @Before
26
+ fun setUp() {
27
+ // Reset singleton via reflection for test isolation
28
+ val field = ComponentRegistry::class.java.getDeclaredField("instance")
29
+ field.isAccessible = true
30
+ field.set(null, null)
31
+
32
+ context = ApplicationProvider.getApplicationContext()
33
+ registry = ComponentRegistry.getInstance(context)
34
+ }
35
+
36
+ // -------------------------------------------------------------------------
37
+ // All 28 default component types should be registered
38
+ // -------------------------------------------------------------------------
39
+
40
+ @Test
41
+ fun testAllComponentsRegistered() {
42
+ val expectedTypes = listOf(
43
+ "VView", "VText", "VButton", "VInput", "VSwitch",
44
+ "VActivityIndicator", "VScrollView", "VImage",
45
+ "VKeyboardAvoiding", "VSafeArea", "VSlider", "VList",
46
+ "VModal", "VAlertDialog", "VStatusBar", "VWebView",
47
+ "VProgressBar", "VPicker", "VSegmentedControl", "VActionSheet",
48
+ "VRefreshControl", "VPressable", "VSectionList", "VCheckbox",
49
+ "VRadio", "VDropdown", "VVideo", "__ROOT__"
50
+ )
51
+
52
+ for (type in expectedTypes) {
53
+ val view = registry.createView(type)
54
+ assertNotNull("createView('$type') should return non-null", view)
55
+ }
56
+ }
57
+
58
+ // -------------------------------------------------------------------------
59
+ // Unknown type returns null
60
+ // -------------------------------------------------------------------------
61
+
62
+ @Test
63
+ fun testUnknownTypeReturnsNull() {
64
+ val view = registry.createView("NonExistent")
65
+ assertNull("Unknown type should return null", view)
66
+ }
67
+
68
+ // -------------------------------------------------------------------------
69
+ // Factory is stored on view via tag
70
+ // -------------------------------------------------------------------------
71
+
72
+ @Test
73
+ fun testFactoryStoredOnView() {
74
+ val view = registry.createView("VView")!!
75
+ val factory = registry.factoryForView(view)
76
+ assertNotNull("factoryForView should return non-null after createView", factory)
77
+ }
78
+
79
+ // -------------------------------------------------------------------------
80
+ // factoryForType
81
+ // -------------------------------------------------------------------------
82
+
83
+ @Test
84
+ fun testFactoryForType() {
85
+ val factory = registry.factoryForType("VView")
86
+ assertNotNull("factoryForType('VView') should return non-null", factory)
87
+ assertTrue("VView factory should be VViewFactory", factory is VViewFactory)
88
+ }
89
+
90
+ @Test
91
+ fun testFactoryForTypeUnknown() {
92
+ val factory = registry.factoryForType("NonExistent")
93
+ assertNull("factoryForType for unknown type should return null", factory)
94
+ }
95
+
96
+ // -------------------------------------------------------------------------
97
+ // Register a custom factory
98
+ // -------------------------------------------------------------------------
99
+
100
+ @Test
101
+ fun testRegisterCustomFactory() {
102
+ val customFactory = object : NativeComponentFactory {
103
+ override fun createView(context: Context): View = View(context)
104
+ override fun updateProp(view: View, key: String, value: Any?) {}
105
+ override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {}
106
+ override fun removeEventListener(view: View, event: String) {}
107
+ }
108
+
109
+ registry.register("CustomComponent", customFactory)
110
+ val view = registry.createView("CustomComponent")
111
+ assertNotNull("Custom component should be created", view)
112
+
113
+ val retrievedFactory = registry.factoryForType("CustomComponent")
114
+ assertEquals("Retrieved factory should be the custom factory", customFactory, retrievedFactory)
115
+ }
116
+
117
+ // -------------------------------------------------------------------------
118
+ // VText creates a TextView
119
+ // -------------------------------------------------------------------------
120
+
121
+ @Test
122
+ fun testVTextCreatesTextView() {
123
+ val view = registry.createView("VText")
124
+ assertNotNull(view)
125
+ assertTrue("VText should create a TextView", view is TextView)
126
+ }
127
+
128
+ // -------------------------------------------------------------------------
129
+ // VView creates a FlexboxLayout
130
+ // -------------------------------------------------------------------------
131
+
132
+ @Test
133
+ fun testVViewCreatesFlexboxLayout() {
134
+ val view = registry.createView("VView")
135
+ assertNotNull(view)
136
+ assertTrue("VView should create a FlexboxLayout", view is FlexboxLayout)
137
+ }
138
+
139
+ // -------------------------------------------------------------------------
140
+ // Singleton behavior
141
+ // -------------------------------------------------------------------------
142
+
143
+ @Test
144
+ fun testSingletonReturnsSameInstance() {
145
+ val instance1 = ComponentRegistry.getInstance(context)
146
+ val instance2 = ComponentRegistry.getInstance(context)
147
+ assertTrue("getInstance should return the same instance", instance1 === instance2)
148
+ }
149
+
150
+ // -------------------------------------------------------------------------
151
+ // Register overwrites existing factory
152
+ // -------------------------------------------------------------------------
153
+
154
+ @Test
155
+ fun testRegisterOverwritesExistingFactory() {
156
+ val customFactory = object : NativeComponentFactory {
157
+ override fun createView(context: Context): View = TextView(context)
158
+ override fun updateProp(view: View, key: String, value: Any?) {}
159
+ override fun addEventListener(view: View, event: String, handler: (Any?) -> Unit) {}
160
+ override fun removeEventListener(view: View, event: String) {}
161
+ }
162
+
163
+ // Overwrite the default VView factory
164
+ registry.register("VView", customFactory)
165
+
166
+ val factory = registry.factoryForType("VView")
167
+ assertEquals("Factory should be the newly registered one", customFactory, factory)
168
+
169
+ // The view created should now be a TextView (from custom factory), not FlexboxLayout
170
+ val view = registry.createView("VView")
171
+ assertTrue("Overwritten VView should create a TextView", view is TextView)
172
+ }
173
+ }