@use-voltra/ios-client 2.1.0 → 2.2.0

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 (131) hide show
  1. package/README.md +61 -8
  2. package/build/commonjs/dynamic-widget/api.js +15 -0
  3. package/build/commonjs/dynamic-widget/api.js.map +1 -0
  4. package/build/commonjs/index.js +15 -7
  5. package/build/commonjs/index.js.map +1 -1
  6. package/build/commonjs/native/NativeVoltra.js.map +1 -1
  7. package/build/module/dynamic-widget/api.js +10 -0
  8. package/build/module/dynamic-widget/api.js.map +1 -0
  9. package/build/module/index.js +1 -0
  10. package/build/module/index.js.map +1 -1
  11. package/build/module/native/NativeVoltra.js.map +1 -1
  12. package/build/typescript/commonjs/dynamic-widget/api.d.ts +8 -0
  13. package/build/typescript/commonjs/dynamic-widget/api.d.ts.map +1 -0
  14. package/build/typescript/commonjs/index.d.ts +1 -0
  15. package/build/typescript/commonjs/index.d.ts.map +1 -1
  16. package/build/typescript/commonjs/native/NativeVoltra.d.ts +1 -0
  17. package/build/typescript/commonjs/native/NativeVoltra.d.ts.map +1 -1
  18. package/build/typescript/module/dynamic-widget/api.d.ts +8 -0
  19. package/build/typescript/module/dynamic-widget/api.d.ts.map +1 -0
  20. package/build/typescript/module/index.d.ts +1 -0
  21. package/build/typescript/module/index.d.ts.map +1 -1
  22. package/build/typescript/module/native/NativeVoltra.d.ts +1 -0
  23. package/build/typescript/module/native/NativeVoltra.d.ts.map +1 -1
  24. package/expo-plugin/build/cjs/index.js +3 -1
  25. package/expo-plugin/build/cjs/index.js.map +1 -1
  26. package/expo-plugin/build/cjs/ios-widget/index.js +1 -1
  27. package/expo-plugin/build/cjs/ios-widget/index.js.map +1 -1
  28. package/expo-plugin/build/cjs/ios-widget/podfile.js +38 -12
  29. package/expo-plugin/build/cjs/ios-widget/podfile.js.map +1 -1
  30. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js +143 -107
  31. package/expo-plugin/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -1
  32. package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js +30 -22
  33. package/expo-plugin/build/cjs/ios-widget/xcode/configurationList.js.map +1 -1
  34. package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js +50 -0
  35. package/expo-plugin/build/cjs/ios-widget/xcode/fileReferences.js.map +1 -0
  36. package/expo-plugin/build/cjs/ios-widget/xcode/groups.js +45 -43
  37. package/expo-plugin/build/cjs/ios-widget/xcode/groups.js.map +1 -1
  38. package/expo-plugin/build/cjs/ios-widget/xcode/index.js +77 -91
  39. package/expo-plugin/build/cjs/ios-widget/xcode/index.js.map +1 -1
  40. package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js +41 -39
  41. package/expo-plugin/build/cjs/ios-widget/xcode/mainAppSettings.js.map +1 -1
  42. package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js +2 -2
  43. package/expo-plugin/build/cjs/ios-widget/xcode/productFile.js.map +1 -1
  44. package/expo-plugin/build/cjs/ios-widget/xcode/target.js +29 -63
  45. package/expo-plugin/build/cjs/ios-widget/xcode/target.js.map +1 -1
  46. package/expo-plugin/build/esm/index.js +3 -1
  47. package/expo-plugin/build/esm/index.js.map +1 -1
  48. package/expo-plugin/build/esm/ios-widget/index.js +1 -1
  49. package/expo-plugin/build/esm/ios-widget/index.js.map +1 -1
  50. package/expo-plugin/build/esm/ios-widget/podfile.js +36 -11
  51. package/expo-plugin/build/esm/ios-widget/podfile.js.map +1 -1
  52. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js +143 -106
  53. package/expo-plugin/build/esm/ios-widget/xcode/buildPhases.js.map +1 -1
  54. package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js +31 -22
  55. package/expo-plugin/build/esm/ios-widget/xcode/configurationList.js.map +1 -1
  56. package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js +45 -0
  57. package/expo-plugin/build/esm/ios-widget/xcode/fileReferences.js.map +1 -0
  58. package/expo-plugin/build/esm/ios-widget/xcode/groups.js +45 -42
  59. package/expo-plugin/build/esm/ios-widget/xcode/groups.js.map +1 -1
  60. package/expo-plugin/build/esm/ios-widget/xcode/index.js +81 -96
  61. package/expo-plugin/build/esm/ios-widget/xcode/index.js.map +1 -1
  62. package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js +41 -39
  63. package/expo-plugin/build/esm/ios-widget/xcode/mainAppSettings.js.map +1 -1
  64. package/expo-plugin/build/esm/ios-widget/xcode/productFile.js +3 -2
  65. package/expo-plugin/build/esm/ios-widget/xcode/productFile.js.map +1 -1
  66. package/expo-plugin/build/esm/ios-widget/xcode/target.js +28 -62
  67. package/expo-plugin/build/esm/ios-widget/xcode/target.js.map +1 -1
  68. package/expo-plugin/build/types/index.d.ts.map +1 -1
  69. package/expo-plugin/build/types/ios-widget/podfile.d.ts +19 -1
  70. package/expo-plugin/build/types/ios-widget/podfile.d.ts.map +1 -1
  71. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts +3 -7
  72. package/expo-plugin/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -1
  73. package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts +15 -10
  74. package/expo-plugin/build/types/ios-widget/xcode/configurationList.d.ts.map +1 -1
  75. package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts +16 -0
  76. package/expo-plugin/build/types/ios-widget/xcode/fileReferences.d.ts.map +1 -0
  77. package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts +0 -4
  78. package/expo-plugin/build/types/ios-widget/xcode/groups.d.ts.map +1 -1
  79. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts +22 -3
  80. package/expo-plugin/build/types/ios-widget/xcode/index.d.ts.map +1 -1
  81. package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts +16 -4
  82. package/expo-plugin/build/types/ios-widget/xcode/mainAppSettings.d.ts.map +1 -1
  83. package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts +0 -4
  84. package/expo-plugin/build/types/ios-widget/xcode/productFile.d.ts.map +1 -1
  85. package/expo-plugin/build/types/ios-widget/xcode/target.d.ts +11 -26
  86. package/expo-plugin/build/types/ios-widget/xcode/target.d.ts.map +1 -1
  87. package/expo-plugin/src/index.ts +5 -1
  88. package/expo-plugin/src/ios-widget/index.ts +1 -1
  89. package/expo-plugin/src/ios-widget/podfile.ts +46 -13
  90. package/expo-plugin/src/ios-widget/xcode/__fixtures__/fresh.pbxproj +197 -0
  91. package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-existing-extension.pbxproj +321 -0
  92. package/expo-plugin/src/ios-widget/xcode/__fixtures__/with-per-config-signing.pbxproj +203 -0
  93. package/expo-plugin/src/ios-widget/xcode/__snapshots__/applyXcodeChanges.node.test.ts.snap +357 -0
  94. package/expo-plugin/src/ios-widget/xcode/__tests__/pbxConsistency.ts +130 -0
  95. package/expo-plugin/src/ios-widget/xcode/buildPhases.ts +151 -152
  96. package/expo-plugin/src/ios-widget/xcode/configurationList.ts +52 -35
  97. package/expo-plugin/src/ios-widget/xcode/fileReferences.ts +51 -0
  98. package/expo-plugin/src/ios-widget/xcode/groups.ts +52 -54
  99. package/expo-plugin/src/ios-widget/xcode/index.ts +107 -119
  100. package/expo-plugin/src/ios-widget/xcode/mainAppSettings.ts +59 -54
  101. package/expo-plugin/src/ios-widget/xcode/productFile.ts +3 -2
  102. package/expo-plugin/src/ios-widget/xcode/target.ts +31 -83
  103. package/ios/Package.swift +7 -1
  104. package/ios/app/NativeVoltra.mm +19 -0
  105. package/ios/app/VoltraModule.swift +18 -0
  106. package/ios/app/VoltraModuleImpl.swift +16 -0
  107. package/ios/shared/DynamicWidgetPropsCodec.swift +51 -0
  108. package/ios/shared/DynamicWidgetPropsStorage.swift +53 -0
  109. package/ios/shared/DynamicWidgetPropsStore.swift +50 -0
  110. package/ios/shared/DynamicWidgetRenderCoordinator.swift +31 -0
  111. package/ios/shared/DynamicWidgetUpdater.swift +30 -0
  112. package/ios/shared/VoltraConstants.swift +4 -0
  113. package/ios/shared/VoltraJSRenderer.swift +3 -1
  114. package/ios/shared/VoltraNode.swift +6 -2
  115. package/ios/shared/VoltraWidgetDefaults.swift +9 -1
  116. package/ios/target/VoltraClientWidgetRuntime.swift +17 -5
  117. package/ios/target/VoltraHomeWidget.swift +6 -2
  118. package/ios/target/VoltraWidget.swift +3 -1
  119. package/ios/ui/Helpers/VoltraDeepLinkResolver.swift +3 -1
  120. package/ios/ui/Helpers/VoltraProgressDriver.swift +6 -2
  121. package/ios/ui/Layout/VoltraFlexStackLayout.swift +9 -3
  122. package/ios/ui/Style/JSColorParser.swift +24 -8
  123. package/ios/ui/Style/JSGradientParser.swift +36 -12
  124. package/ios/ui/Style/JSStyleParser.swift +36 -12
  125. package/ios/ui/Style/LayoutStyle.swift +12 -4
  126. package/ios/ui/Views/VoltraChart.swift +52 -16
  127. package/ios/ui/Views/VoltraLinearGradient.swift +6 -2
  128. package/package.json +5 -5
  129. package/src/dynamic-widget/api.ts +25 -0
  130. package/src/index.ts +1 -0
  131. package/src/native/NativeVoltra.ts +1 -0
@@ -8,7 +8,9 @@ enum JSColorParser {
8
8
  // Optimize: standard trim and lowercase
9
9
  let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
10
10
 
11
- if trimmed.isEmpty { return nil }
11
+ if trimmed.isEmpty {
12
+ return nil
13
+ }
12
14
 
13
15
  // 1. Hex (with or without #)
14
16
  if trimmed.hasPrefix("#") {
@@ -57,7 +59,9 @@ enum JSColorParser {
57
59
  guard let string = value as? String else { return nil }
58
60
 
59
61
  let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
60
- if trimmed.isEmpty { return nil }
62
+ if trimmed.isEmpty {
63
+ return nil
64
+ }
61
65
 
62
66
  if trimmed.hasPrefix("#") {
63
67
  return parseHexComponents(trimmed)
@@ -477,7 +481,9 @@ enum JSColorParser {
477
481
 
478
482
  private static func normalizeUnit(_ value: Double) -> Double {
479
483
  var result = value.truncatingRemainder(dividingBy: 1)
480
- if result < 0 { result += 1 }
484
+ if result < 0 {
485
+ result += 1
486
+ }
481
487
  return result
482
488
  }
483
489
 
@@ -506,12 +512,22 @@ enum JSColorParser {
506
512
  /// Helper function for HSL to RGB conversion
507
513
  private static func hueToRgb(p: Double, q: Double, t: Double) -> Double {
508
514
  var t = t
509
- if t < 0 { t += 1 }
510
- if t > 1 { t -= 1 }
515
+ if t < 0 {
516
+ t += 1
517
+ }
518
+ if t > 1 {
519
+ t -= 1
520
+ }
511
521
 
512
- if t < 1.0 / 6.0 { return p + (q - p) * 6 * t }
513
- if t < 1.0 / 2.0 { return q }
514
- if t < 2.0 / 3.0 { return p + (q - p) * (2.0 / 3.0 - t) * 6 }
522
+ if t < 1.0 / 6.0 {
523
+ return p + (q - p) * 6 * t
524
+ }
525
+ if t < 1.0 / 2.0 {
526
+ return q
527
+ }
528
+ if t < 2.0 / 3.0 {
529
+ return p + (q - p) * (2.0 / 3.0 - t) * 6
530
+ }
515
531
  return p
516
532
  }
517
533
  }
@@ -200,12 +200,16 @@ enum JSGradientParser {
200
200
  depth += 1
201
201
  } else if char == ")" {
202
202
  depth -= 1
203
- if depth < 0 { return [] }
203
+ if depth < 0 {
204
+ return []
205
+ }
204
206
  }
205
207
 
206
208
  if char == ",", depth == 0 {
207
209
  let token = current.trimmingCharacters(in: .whitespacesAndNewlines)
208
- if token.isEmpty { return [] }
210
+ if token.isEmpty {
211
+ return []
212
+ }
209
213
  args.append(token)
210
214
  current = ""
211
215
  } else {
@@ -216,7 +220,9 @@ enum JSGradientParser {
216
220
  guard depth == 0 else { return [] }
217
221
 
218
222
  let token = current.trimmingCharacters(in: .whitespacesAndNewlines)
219
- if token.isEmpty { return [] }
223
+ if token.isEmpty {
224
+ return []
225
+ }
220
226
  args.append(token)
221
227
  return args
222
228
  }
@@ -231,7 +237,9 @@ enum JSGradientParser {
231
237
  depth += 1
232
238
  } else if char == ")" {
233
239
  depth -= 1
234
- if depth < 0 { return [] }
240
+ if depth < 0 {
241
+ return []
242
+ }
235
243
  }
236
244
 
237
245
  if char.isWhitespace, depth == 0 {
@@ -269,10 +277,14 @@ enum JSGradientParser {
269
277
  for word in words {
270
278
  switch word {
271
279
  case "left", "right":
272
- if horizontal != nil { return nil }
280
+ if horizontal != nil {
281
+ return nil
282
+ }
273
283
  horizontal = word
274
284
  case "top", "bottom":
275
- if vertical != nil { return nil }
285
+ if vertical != nil {
286
+ return nil
287
+ }
276
288
  vertical = word
277
289
  default:
278
290
  return nil
@@ -418,10 +430,14 @@ enum JSGradientParser {
418
430
  for token in tokens {
419
431
  switch token {
420
432
  case "left":
421
- if x != nil { return nil }
433
+ if x != nil {
434
+ return nil
435
+ }
422
436
  x = 0
423
437
  case "right":
424
- if x != nil { return nil }
438
+ if x != nil {
439
+ return nil
440
+ }
425
441
  x = 1
426
442
  case "center":
427
443
  if x == nil {
@@ -432,10 +448,14 @@ enum JSGradientParser {
432
448
  return nil
433
449
  }
434
450
  case "top":
435
- if y != nil { return nil }
451
+ if y != nil {
452
+ return nil
453
+ }
436
454
  y = 0
437
455
  case "bottom":
438
- if y != nil { return nil }
456
+ if y != nil {
457
+ return nil
458
+ }
439
459
  y = 1
440
460
  default:
441
461
  return nil
@@ -613,8 +633,12 @@ enum JSGradientParser {
613
633
  guard !stops.isEmpty else { return [] }
614
634
 
615
635
  var positions = stops.map(\.position)
616
- if positions.first == nil { positions[0] = 0 }
617
- if positions.last == nil { positions[positions.count - 1] = 1 }
636
+ if positions.first == nil {
637
+ positions[0] = 0
638
+ }
639
+ if positions.last == nil {
640
+ positions[positions.count - 1] = 1
641
+ }
618
642
 
619
643
  var lastDefined: Double?
620
644
  for idx in positions.indices {
@@ -2,16 +2,24 @@ import SwiftUI
2
2
 
3
3
  enum JSStyleParser {
4
4
  static func number(_ value: Any?) -> CGFloat? {
5
- if let v = value as? CGFloat { return v }
6
- if let v = value as? Double { return CGFloat(v) }
7
- if let v = value as? Int { return CGFloat(v) }
5
+ if let v = value as? CGFloat {
6
+ return v
7
+ }
8
+ if let v = value as? Double {
9
+ return CGFloat(v)
10
+ }
11
+ if let v = value as? Int {
12
+ return CGFloat(v)
13
+ }
8
14
  // Handle "20.5" strings if necessary, though rarer in raw JSON objects
9
15
  return nil
10
16
  }
11
17
 
12
18
  static func sizeValue(_ value: Any?) -> SizeValue? {
13
19
  // Numeric → Fixed
14
- if let n = number(value) { return .fixed(n) }
20
+ if let n = number(value) {
21
+ return .fixed(n)
22
+ }
15
23
  // String → Fill / Wrap / try numeric
16
24
  guard let str = value as? String else { return nil }
17
25
  switch str {
@@ -19,7 +27,9 @@ enum JSStyleParser {
19
27
  case "auto": return .wrap
20
28
  default:
21
29
  // Try parsing as numeric string
22
- if let n = Double(str) { return .fixed(CGFloat(n)) }
30
+ if let n = Double(str) {
31
+ return .fixed(CGFloat(n))
32
+ }
23
33
  return nil
24
34
  }
25
35
  }
@@ -50,8 +60,12 @@ enum JSStyleParser {
50
60
  static func boolean(_ value: Any?) -> Bool {
51
61
  guard let value = value else { return false }
52
62
 
53
- if let bool = value as? Bool { return bool }
54
- if let int = value as? Int { return int == 1 }
63
+ if let bool = value as? Bool {
64
+ return bool
65
+ }
66
+ if let int = value as? Int {
67
+ return int == 1
68
+ }
55
69
  if let string = value as? String {
56
70
  return string.lowercased() == "true"
57
71
  }
@@ -107,9 +121,15 @@ enum JSStyleParser {
107
121
  let hasUnderline = string.contains("underline")
108
122
  let hasLineThrough = string.contains("line-through") || string.contains("strikethrough")
109
123
 
110
- if hasUnderline, hasLineThrough { return .underlineLineThrough }
111
- if hasUnderline { return .underline }
112
- if hasLineThrough { return .lineThrough }
124
+ if hasUnderline, hasLineThrough {
125
+ return .underlineLineThrough
126
+ }
127
+ if hasUnderline {
128
+ return .underline
129
+ }
130
+ if hasLineThrough {
131
+ return .lineThrough
132
+ }
113
133
  return .none
114
134
  }
115
135
 
@@ -128,8 +148,12 @@ enum JSStyleParser {
128
148
  static func overflow(_ value: Any?) -> Overflow? {
129
149
  guard let string = (value as? String)?.lowercased() else { return nil }
130
150
 
131
- if string == "hidden" { return .hidden }
132
- if string == "visible" { return .visible }
151
+ if string == "hidden" {
152
+ return .hidden
153
+ }
154
+ if string == "visible" {
155
+ return .visible
156
+ }
133
157
  return nil
134
158
  }
135
159
 
@@ -112,15 +112,23 @@ struct LayoutModifier: ViewModifier {
112
112
 
113
113
  /// Derive effective ideal width: explicit width, then flexBasis, then nil
114
114
  private var effectiveIdealWidth: CGFloat? {
115
- if let w = widthValue { return w }
116
- if case let .fixed(v) = style.flexBasis { return v }
115
+ if let w = widthValue {
116
+ return w
117
+ }
118
+ if case let .fixed(v) = style.flexBasis {
119
+ return v
120
+ }
117
121
  return nil
118
122
  }
119
123
 
120
124
  /// Derive effective ideal height: explicit height, then flexBasis, then nil
121
125
  private var effectiveIdealHeight: CGFloat? {
122
- if let h = heightValue { return h }
123
- if case let .fixed(v) = style.flexBasis { return v }
126
+ if let h = heightValue {
127
+ return h
128
+ }
129
+ if case let .fixed(v) = style.flexBasis {
130
+ return v
131
+ }
124
132
  return nil
125
133
  }
126
134
 
@@ -49,7 +49,9 @@ public struct VoltraChart: VoltraView {
49
49
  }
50
50
 
51
51
  private func bool(_ v: JSONValue) -> Bool? {
52
- if case let .bool(value) = v { return value }
52
+ if case let .bool(value) = v {
53
+ return value
54
+ }
53
55
  return nil
54
56
  }
55
57
 
@@ -60,16 +62,22 @@ public struct VoltraChart: VoltraView {
60
62
  else { return nil }
61
63
 
62
64
  let props = dict.compactMapValues { jsonValue(from: $0) }
63
- if props.isEmpty { return nil }
65
+ if props.isEmpty {
66
+ return nil
67
+ }
64
68
 
65
69
  let visible = props["v"].flatMap(bool) ?? props["visible"].flatMap(bool)
66
70
  let color = (props["c"] ?? props["color"]).flatMap {
67
- if case let .string(value) = $0 { return JSColorParser.parse(value) }
71
+ if case let .string(value) = $0 {
72
+ return JSColorParser.parse(value)
73
+ }
68
74
  return nil
69
75
  }
70
76
  let lineWidth = (props["lw"] ?? props["lineWidth"]).flatMap(num).map { CGFloat($0) }
71
77
  let dash = ((props["d"] ?? props["dash"]).flatMap {
72
- if case let .array(values) = $0 { return values.compactMap(num).map { CGFloat($0) } }
78
+ if case let .array(values) = $0 {
79
+ return values.compactMap(num).map { CGFloat($0) }
80
+ }
73
81
  return nil
74
82
  })
75
83
 
@@ -140,7 +148,11 @@ public struct VoltraChart: VoltraView {
140
148
  default: return 0
141
149
  }
142
150
  }()
143
- let series: String? = pt.count >= 3 ? { if case let .string(s) = pt[2] { return s }; return nil }() : nil
151
+ let series: String? = pt.count >= 3 ? {
152
+ if case let .string(s) = pt[2] {
153
+ return s
154
+ }; return nil
155
+ }() : nil
144
156
  switch pt[0] {
145
157
  case let .string(x): return (x, nil, y, series)
146
158
  case let .double(x): return (nil, x, y, series)
@@ -186,12 +198,24 @@ public struct VoltraChart: VoltraView {
186
198
 
187
199
  @ChartContentBuilder
188
200
  private func buildMark(_ m: WireMark) -> some ChartContent {
189
- if m.type == "bar" { barContent(m) }
190
- if m.type == "line" { lineContent(m) }
191
- if m.type == "area" { areaContent(m) }
192
- if m.type == "point" { pointContent(m) }
193
- if m.type == "rule" { ruleContent(m) }
194
- if #available(iOS 17.0, macOS 14.0, *), m.type == "sector" { sectorContent(m) }
201
+ if m.type == "bar" {
202
+ barContent(m)
203
+ }
204
+ if m.type == "line" {
205
+ lineContent(m)
206
+ }
207
+ if m.type == "area" {
208
+ areaContent(m)
209
+ }
210
+ if m.type == "point" {
211
+ pointContent(m)
212
+ }
213
+ if m.type == "rule" {
214
+ ruleContent(m)
215
+ }
216
+ if #available(iOS 17.0, macOS 14.0, *), m.type == "sector" {
217
+ sectorContent(m)
218
+ }
195
219
  }
196
220
 
197
221
  // MARK: Bar
@@ -208,10 +232,16 @@ public struct VoltraChart: VoltraView {
208
232
  let (xStr, xNum, y, series) = xy(pt)
209
233
  let cr: CGFloat? = props["cr"].flatMap(num).map { CGFloat($0) }
210
234
  let barWidth: MarkDimension = {
211
- if let v = props["w"].flatMap(num) { return .fixed(CGFloat(v)) }
235
+ if let v = props["w"].flatMap(num) {
236
+ return .fixed(CGFloat(v))
237
+ }
212
238
  return .automatic
213
239
  }()
214
- let grouped: Bool = { if case let .string(s)? = props["stk"] { return s == "grouped" }; return false }()
240
+ let grouped: Bool = {
241
+ if case let .string(s)? = props["stk"] {
242
+ return s == "grouped"
243
+ }; return false
244
+ }()
215
245
 
216
246
  if let x = xStr {
217
247
  if let series {
@@ -555,15 +585,21 @@ public struct VoltraChart: VoltraView {
555
585
  case let .string(category) = pt[1]
556
586
  {
557
587
  let inner: MarkDimension = {
558
- if let v = props["ir"].flatMap(num) { return .ratio(CGFloat(v)) }
588
+ if let v = props["ir"].flatMap(num) {
589
+ return .ratio(CGFloat(v))
590
+ }
559
591
  return .automatic
560
592
  }()
561
593
  let outer: MarkDimension = {
562
- if let v = props["or"].flatMap(num) { return .ratio(CGFloat(v)) }
594
+ if let v = props["or"].flatMap(num) {
595
+ return .ratio(CGFloat(v))
596
+ }
563
597
  return .automatic
564
598
  }()
565
599
  let inset: CGFloat = {
566
- if let v = props["agin"].flatMap(num) { return CGFloat(v) }
600
+ if let v = props["agin"].flatMap(num) {
601
+ return CGFloat(v)
602
+ }
567
603
  return 0
568
604
  }()
569
605
 
@@ -57,12 +57,16 @@ public struct VoltraLinearGradient: VoltraView {
57
57
  }
58
58
  }
59
59
  }
60
- if !stops.isEmpty { return Gradient(stops: stops) }
60
+ if !stops.isEmpty {
61
+ return Gradient(stops: stops)
62
+ }
61
63
  }
62
64
  if let colorsStr = params.colors {
63
65
  let parts = colorsStr.split(separator: "|").map(String.init)
64
66
  let colors: [Color] = parts.compactMap { JSColorParser.parse($0) }
65
- if !colors.isEmpty { return Gradient(colors: colors) }
67
+ if !colors.isEmpty {
68
+ return Gradient(colors: colors)
69
+ }
66
70
  }
67
71
  // Fallback neutral gradient
68
72
  return Gradient(colors: [Color.black.opacity(0.25), Color.black.opacity(0.05)])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@use-voltra/ios-client",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Client-only Voltra APIs for iOS",
5
5
  "main": "./build/commonjs/index.js",
6
6
  "module": "./build/module/index.js",
@@ -39,9 +39,9 @@
39
39
  "@expo/plist": "^0.3.5",
40
40
  "dedent": "^1.7.1",
41
41
  "xcode": "^3.0.1",
42
- "@use-voltra/compiler": "^2.1.0",
43
- "@use-voltra/ios": "^2.1.0",
44
- "@use-voltra/expo-plugin": "^2.1.0"
42
+ "@use-voltra/compiler": "^2.2.0",
43
+ "@use-voltra/expo-plugin": "^2.2.0",
44
+ "@use-voltra/ios": "^2.2.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "expo": "*",
@@ -125,7 +125,7 @@
125
125
  "format:swift:fix": "swiftformat ios",
126
126
  "lint": "oxlint src expo-plugin/src",
127
127
  "typecheck": "tsc -p tsconfig.typecheck.json --noEmit && tsc -p expo-plugin/tsconfig.typecheck.json --noEmit",
128
- "test": "jest --config expo-plugin/jest.config.js",
128
+ "test": "jest --config jest.dynamic-widget.config.js && jest --config expo-plugin/jest.config.js",
129
129
  "test:swift": "cd ios && swift test"
130
130
  }
131
131
  }
@@ -0,0 +1,25 @@
1
+ import { getNativeVoltra } from '../native/NativeVoltra.js'
2
+ import { assertRunningOnApple } from '../utils/assertRunningOnApple.js'
3
+
4
+ export type DynamicWidgetPropsValue =
5
+ | string
6
+ | number
7
+ | boolean
8
+ | null
9
+ | ReadonlyArray<DynamicWidgetPropsValue>
10
+ | Readonly<{ [dynamicWidgetPropName: string]: DynamicWidgetPropsValue }>
11
+
12
+ export type DynamicWidgetProps = Readonly<{
13
+ [dynamicWidgetPropName: string]: DynamicWidgetPropsValue
14
+ }>
15
+
16
+ export const updateDynamicWidget = async (
17
+ dynamicWidgetId: string,
18
+ dynamicWidgetProps: DynamicWidgetProps
19
+ ): Promise<void> => {
20
+ if (!assertRunningOnApple()) return Promise.resolve()
21
+
22
+ const dynamicWidgetPropsJson = JSON.stringify(dynamicWidgetProps)
23
+
24
+ return getNativeVoltra().updateDynamicWidget(dynamicWidgetId, dynamicWidgetPropsJson)
25
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export {
5
5
  } from './components/VoltraLiveActivityPreview.js'
6
6
  export { VoltraView, type VoltraViewProps } from './components/VoltraView.js'
7
7
  export { VoltraWidgetPreview, type VoltraWidgetPreviewProps } from './components/VoltraWidgetPreview.js'
8
+ export { type DynamicWidgetProps, type DynamicWidgetPropsValue, updateDynamicWidget } from './dynamic-widget/api.js'
8
9
  export * from './events.js'
9
10
  export { isGlassSupported, isHeadless, useIsHeadless } from './helpers.js'
10
11
  export { logger, type VoltraLogLevel } from './logger.js'
@@ -100,6 +100,7 @@ export interface Spec extends TurboModule {
100
100
  preloadImages(images: PreloadImageOptions[]): Promise<PreloadImagesResult>
101
101
  reloadLiveActivities(activityNames?: string[] | null): Promise<void>
102
102
  clearPreloadedImages(keys?: string[] | null): Promise<void>
103
+ updateDynamicWidget(dynamicWidgetId: string, dynamicWidgetPropsJson: string): Promise<void>
103
104
  updateWidget(widgetId: string, jsonString: string, options: UpdateWidgetOptions): Promise<void>
104
105
  scheduleWidget(widgetId: string, timelineJson: string): Promise<void>
105
106
  reloadWidgets(widgetIds?: string[] | null): Promise<void>