@onekeyfe/react-native-native-list 3.0.105

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 (181) hide show
  1. package/README.md +342 -0
  2. package/android/CMakeLists.txt +20 -0
  3. package/android/build.gradle +105 -0
  4. package/android/gradle.properties +4 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  7. package/android/src/main/java/com/onekey/nativelist/HybridNativeList.kt +125 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +165 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +147 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListPackage.kt +25 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +2939 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +1794 -0
  13. package/common/fonts/Roobert-Bold.ttf +0 -0
  14. package/common/fonts/Roobert-Medium.ttf +0 -0
  15. package/common/fonts/Roobert-Regular.ttf +0 -0
  16. package/common/fonts/Roobert-SemiBold.ttf +0 -0
  17. package/docs/DESIGN.md +129 -0
  18. package/ios/HybridNativeList.swift +131 -0
  19. package/ios/NativeListCell.swift +2858 -0
  20. package/ios/NativeListDesignAssets.swift +110 -0
  21. package/ios/NativeListModels.swift +215 -0
  22. package/ios/RNCNativeListView.swift +1737 -0
  23. package/ios/Resources/NativeListIcons.xcassets/Contents.json +1 -0
  24. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/Contents.json +1 -0
  25. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/icon.svg +1 -0
  26. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/Contents.json +1 -0
  27. package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/icon.svg +1 -0
  28. package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/Contents.json +1 -0
  29. package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/icon.svg +1 -0
  30. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/Contents.json +1 -0
  31. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/icon.svg +1 -0
  32. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/Contents.json +1 -0
  33. package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/icon.svg +1 -0
  34. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/Contents.json +1 -0
  35. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/icon.svg +1 -0
  36. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/Contents.json +1 -0
  37. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/icon.svg +1 -0
  38. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/Contents.json +1 -0
  39. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/icon.svg +1 -0
  40. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/Contents.json +1 -0
  41. package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/icon.svg +1 -0
  42. package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/Contents.json +1 -0
  43. package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/icon.svg +1 -0
  44. package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/Contents.json +1 -0
  45. package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/icon.svg +1 -0
  46. package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/Contents.json +1 -0
  47. package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/icon.svg +1 -0
  48. package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/Contents.json +1 -0
  49. package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/icon.svg +1 -0
  50. package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/Contents.json +1 -0
  51. package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/icon.svg +1 -0
  52. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/Contents.json +1 -0
  53. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/icon.svg +1 -0
  54. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/Contents.json +1 -0
  55. package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/icon.svg +1 -0
  56. package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/Contents.json +1 -0
  57. package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/icon.svg +1 -0
  58. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/Contents.json +1 -0
  59. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/icon.svg +1 -0
  60. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/Contents.json +1 -0
  61. package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/icon.svg +1 -0
  62. package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/Contents.json +1 -0
  63. package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/icon.svg +1 -0
  64. package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/Contents.json +1 -0
  65. package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/icon.svg +1 -0
  66. package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/Contents.json +1 -0
  67. package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/icon.svg +1 -0
  68. package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/Contents.json +1 -0
  69. package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/icon.svg +1 -0
  70. package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/Contents.json +1 -0
  71. package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/icon.svg +1 -0
  72. package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/Contents.json +1 -0
  73. package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/icon.svg +1 -0
  74. package/lib/module/NativeList.js +183 -0
  75. package/lib/module/NativeList.nitro.js +4 -0
  76. package/lib/module/NativeList.types.js +4 -0
  77. package/lib/module/NativeList.web.js +167 -0
  78. package/lib/module/index.js +6 -0
  79. package/lib/module/models.js +2 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/scrolling.js +121 -0
  82. package/lib/module/selection.js +85 -0
  83. package/lib/module/validation.js +562 -0
  84. package/lib/module/web/NativeListWebEngine.js +2238 -0
  85. package/lib/nitrogen/generated/.gitattributes +1 -0
  86. package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  87. package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +201 -0
  88. package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +84 -0
  89. package/lib/nitrogen/generated/android/c++/JNativeListScrollAlignment.hpp +64 -0
  90. package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +104 -0
  91. package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.hpp +54 -0
  92. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_std__string.kt +78 -0
  93. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +177 -0
  94. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/NativeListScrollAlignment.kt +25 -0
  95. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/nativelistOnLoad.kt +35 -0
  96. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +96 -0
  97. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListStateUpdater.kt +23 -0
  98. package/lib/nitrogen/generated/android/nativelist+autolinking.cmake +83 -0
  99. package/lib/nitrogen/generated/android/nativelist+autolinking.gradle +27 -0
  100. package/lib/nitrogen/generated/android/nativelistOnLoad.cpp +58 -0
  101. package/lib/nitrogen/generated/android/nativelistOnLoad.hpp +34 -0
  102. package/lib/nitrogen/generated/ios/NativeListModule+autolinking.rb +62 -0
  103. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +41 -0
  104. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +91 -0
  105. package/lib/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Umbrella.hpp +49 -0
  106. package/lib/nitrogen/generated/ios/NativeListModuleAutolinking.mm +33 -0
  107. package/lib/nitrogen/generated/ios/NativeListModuleAutolinking.swift +26 -0
  108. package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.cpp +11 -0
  109. package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +180 -0
  110. package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +180 -0
  111. package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  112. package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +69 -0
  113. package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +447 -0
  114. package/lib/nitrogen/generated/ios/swift/NativeListScrollAlignment.swift +48 -0
  115. package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +43 -0
  116. package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +87 -0
  117. package/lib/nitrogen/generated/shared/c++/NativeListScrollAlignment.hpp +84 -0
  118. package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +46 -0
  119. package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +103 -0
  120. package/lib/nitrogen/generated/shared/json/NativeListConfig.json +16 -0
  121. package/lib/typescript/package.json +1 -0
  122. package/lib/typescript/src/NativeList.d.ts +18 -0
  123. package/lib/typescript/src/NativeList.nitro.d.ts +29 -0
  124. package/lib/typescript/src/NativeList.types.d.ts +87 -0
  125. package/lib/typescript/src/NativeList.web.d.ts +19 -0
  126. package/lib/typescript/src/index.d.ts +7 -0
  127. package/lib/typescript/src/models.d.ts +456 -0
  128. package/lib/typescript/src/scrolling.d.ts +32 -0
  129. package/lib/typescript/src/selection.d.ts +27 -0
  130. package/lib/typescript/src/validation.d.ts +7 -0
  131. package/lib/typescript/src/web/NativeListWebEngine.d.ts +203 -0
  132. package/nitro.json +23 -0
  133. package/nitrogen/generated/.gitattributes +1 -0
  134. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  135. package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +201 -0
  136. package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +84 -0
  137. package/nitrogen/generated/android/c++/JNativeListScrollAlignment.hpp +64 -0
  138. package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +104 -0
  139. package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.hpp +54 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/Func_void_std__string.kt +78 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +177 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/NativeListScrollAlignment.kt +25 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/nativelistOnLoad.kt +35 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListManager.kt +96 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/views/HybridNativeListStateUpdater.kt +23 -0
  146. package/nitrogen/generated/android/nativelist+autolinking.cmake +83 -0
  147. package/nitrogen/generated/android/nativelist+autolinking.gradle +27 -0
  148. package/nitrogen/generated/android/nativelistOnLoad.cpp +58 -0
  149. package/nitrogen/generated/android/nativelistOnLoad.hpp +34 -0
  150. package/nitrogen/generated/ios/NativeListModule+autolinking.rb +62 -0
  151. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.cpp +41 -0
  152. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Bridge.hpp +91 -0
  153. package/nitrogen/generated/ios/NativeListModule-Swift-Cxx-Umbrella.hpp +49 -0
  154. package/nitrogen/generated/ios/NativeListModuleAutolinking.mm +33 -0
  155. package/nitrogen/generated/ios/NativeListModuleAutolinking.swift +26 -0
  156. package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.cpp +11 -0
  157. package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +180 -0
  158. package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +180 -0
  159. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  160. package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +69 -0
  161. package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +447 -0
  162. package/nitrogen/generated/ios/swift/NativeListScrollAlignment.swift +48 -0
  163. package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +43 -0
  164. package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +87 -0
  165. package/nitrogen/generated/shared/c++/NativeListScrollAlignment.hpp +84 -0
  166. package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +46 -0
  167. package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +103 -0
  168. package/nitrogen/generated/shared/json/NativeListConfig.json +16 -0
  169. package/package.json +126 -0
  170. package/react-native-native-list.podspec +38 -0
  171. package/react-native.config.js +19 -0
  172. package/src/NativeList.nitro.ts +48 -0
  173. package/src/NativeList.tsx +298 -0
  174. package/src/NativeList.types.ts +125 -0
  175. package/src/NativeList.web.tsx +258 -0
  176. package/src/index.ts +35 -0
  177. package/src/models.ts +604 -0
  178. package/src/scrolling.ts +189 -0
  179. package/src/selection.ts +159 -0
  180. package/src/validation.ts +877 -0
  181. package/src/web/NativeListWebEngine.ts +3767 -0
@@ -0,0 +1,110 @@
1
+ import CoreText
2
+ import UIKit
3
+
4
+ private final class NativeListResourceToken {}
5
+
6
+ enum NativeListFontWeight {
7
+ case regular
8
+ case medium
9
+ case semibold
10
+ case bold
11
+
12
+ fileprivate var postScriptName: String {
13
+ switch self {
14
+ case .regular: return "Roobert-Regular"
15
+ case .medium: return "Roobert-Medium"
16
+ case .semibold: return "Roobert-SemiBold"
17
+ case .bold: return "Roobert-Bold"
18
+ }
19
+ }
20
+
21
+ fileprivate var systemWeight: UIFont.Weight {
22
+ switch self {
23
+ case .regular: return .regular
24
+ case .medium: return .medium
25
+ case .semibold: return .semibold
26
+ case .bold: return .bold
27
+ }
28
+ }
29
+ }
30
+
31
+ private enum NativeListResources {
32
+ static let bundle: Bundle = {
33
+ let owners = [Bundle(for: NativeListResourceToken.self), .main]
34
+ for owner in owners {
35
+ if let url = owner.url(forResource: "NativeListResources", withExtension: "bundle"),
36
+ let bundle = Bundle(url: url) {
37
+ return bundle
38
+ }
39
+ }
40
+ return Bundle(for: NativeListResourceToken.self)
41
+ }()
42
+
43
+ static let registerFonts: Void = {
44
+ ["Regular", "Medium", "SemiBold", "Bold"].forEach { weight in
45
+ let directURL = bundle.url(forResource: "Roobert-\(weight)", withExtension: "ttf")
46
+ let nestedURL = bundle.url(
47
+ forResource: "Roobert-\(weight)",
48
+ withExtension: "ttf",
49
+ subdirectory: "fonts"
50
+ )
51
+ guard let url = directURL ?? nestedURL else { return }
52
+ CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
53
+ }
54
+ }()
55
+ }
56
+
57
+ func nativeListFont(ofSize size: CGFloat, weight: NativeListFontWeight = .regular) -> UIFont {
58
+ _ = NativeListResources.registerFonts
59
+ return UIFont(name: weight.postScriptName, size: size)
60
+ ?? UIFont.systemFont(ofSize: size, weight: weight.systemWeight)
61
+ }
62
+
63
+ func nativeListTabularFont(
64
+ ofSize size: CGFloat,
65
+ weight: NativeListFontWeight = .regular
66
+ ) -> UIFont {
67
+ let font = nativeListFont(ofSize: size, weight: weight)
68
+ let settings: [[UIFontDescriptor.FeatureKey: Int]] = [[
69
+ .type: kNumberSpacingType,
70
+ .selector: kMonospacedNumbersSelector,
71
+ ]]
72
+ let descriptor = font.fontDescriptor.addingAttributes([
73
+ .featureSettings: settings,
74
+ ])
75
+ return UIFont(descriptor: descriptor, size: size)
76
+ }
77
+
78
+ func nativeListIcon(named name: String) -> UIImage? {
79
+ let assetName: String
80
+ switch name {
81
+ case "ArrowBottomOutline": assetName = "onekey_arrow_bottom"
82
+ case "ArrowTopOutline": assetName = "onekey_arrow_top"
83
+ case "ChartTrendingUpOutline": assetName = "onekey_chart_trending_up"
84
+ case "SwapHorOutline": assetName = "onekey_swap_hor"
85
+ case "ShieldExclamationOutline": assetName = "onekey_shield_exclamation"
86
+ case "InfoCircleOutline": assetName = "onekey_info_circle"
87
+ case "SpeakerPromoteOutline": assetName = "onekey_speaker_promote"
88
+ case "PlusCircleOutline": assetName = "onekey_plus_circle"
89
+ case "PlusSmallOutline": assetName = "onekey_plus_small"
90
+ case "DotHorOutline": assetName = "onekey_dot_hor"
91
+ case "PencilOutline": assetName = "onekey_pencil"
92
+ case "MinusCircleSolid": assetName = "onekey_minus_circle_solid"
93
+ case "MinusCircleOutline": assetName = "onekey_minus_circle"
94
+ case "ChevronRightSmallOutline": assetName = "onekey_chevron_right_small"
95
+ case "DragOutline": assetName = "onekey_drag"
96
+ case "StarOutline": assetName = "onekey_star"
97
+ case "StarSolid": assetName = "onekey_star_solid"
98
+ case "ChevronGrabberVerOutline": assetName = "onekey_chevron_grabber_ver"
99
+ case "ChevronBottomOutline": assetName = "onekey_chevron_bottom"
100
+ case "ChevronTopOutline": assetName = "onekey_chevron_top"
101
+ case "CheckboxCheckedCustom": assetName = "onekey_checkbox_checked"
102
+ case "CheckboxIndeterminateCustom": assetName = "onekey_checkbox_indeterminate"
103
+ case "ErrorSolid": assetName = "onekey_error_solid"
104
+ case "QuestionmarkSolid": assetName = "onekey_questionmark_solid"
105
+ case "ImageSquareWavesOutline": assetName = "onekey_image_square_waves"
106
+ default: return nil
107
+ }
108
+ return UIImage(named: assetName, in: NativeListResources.bundle, compatibleWith: nil)?
109
+ .withRenderingMode(.alwaysTemplate)
110
+ }
@@ -0,0 +1,215 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ struct NativeListItem {
5
+ let key: String
6
+ let type: String
7
+ let sectionKey: String?
8
+ let revision: Int
9
+ let data: [String: Any]
10
+ let content: String
11
+
12
+ var isSelectable: Bool {
13
+ let disabled = data["disabled"] as? Bool ?? false
14
+ return !disabled && Self.selectableTypes.contains(type)
15
+ }
16
+
17
+ var isReorderable: Bool {
18
+ guard !data.bool("disabled") else { return false }
19
+ if type == "rail", data.bool("draggable") { return true }
20
+ if type == "identity", data.bool("draggable") { return true }
21
+ if type == "walletGroup", data.bool("draggable") { return true }
22
+ return data.dictionaries("trailing").contains { $0.string("kind") == "drag" }
23
+ }
24
+
25
+ init(data: [String: Any]) throws {
26
+ guard let key = data["key"] as? String, !key.isEmpty,
27
+ let type = data["type"] as? String, !type.isEmpty else {
28
+ throw NativeListModelError.invalidRow
29
+ }
30
+ self.key = key
31
+ self.type = type
32
+ self.sectionKey = data["sectionKey"] as? String
33
+ self.revision = data["revision"] as? Int ?? 0
34
+ self.data = data
35
+ let jsonData = try JSONSerialization.data(withJSONObject: data, options: [.sortedKeys])
36
+ self.content = String(data: jsonData, encoding: .utf8) ?? ""
37
+ }
38
+
39
+ private static let selectableTypes: Set<String> = [
40
+ "identity", "rail", "activity", "message", "dataRow", "mediaTile", "metricCard",
41
+ ]
42
+ }
43
+
44
+ struct NativeListConfig {
45
+ let generation: Int
46
+ let layout: String
47
+ let orientation: String
48
+ let gridColumns: Int
49
+ let stickyHeaders: Bool
50
+ let contentPadding: CGFloat
51
+ let contentPaddingHorizontal: CGFloat
52
+ let contentPaddingTop: CGFloat
53
+ let contentPaddingBottom: CGFloat
54
+ let itemSpacing: CGFloat
55
+ let selectionMode: String
56
+ let rowPressToggles: Bool
57
+ var selectedKeys: Set<String>
58
+ let reorderable: Bool
59
+ let pullToRefresh: Bool
60
+ var refreshing: Bool
61
+ let loadMore: Bool
62
+ let endReachedThreshold: Double
63
+ let sectionIndexEnabled: Bool
64
+ let sectionIndexHapticsEnabled: Bool
65
+ let theme: [String: Any]?
66
+ let fixedFooter: NativeListItem?
67
+ var items: [NativeListItem]
68
+
69
+ static func parse(json: String) throws -> NativeListConfig {
70
+ guard let bytes = json.data(using: .utf8),
71
+ let root = try JSONSerialization.jsonObject(with: bytes) as? [String: Any],
72
+ root["schemaVersion"] as? Int == 1,
73
+ let generation = root["generation"] as? Int,
74
+ let layoutData = root["layout"] as? [String: Any],
75
+ let layout = layoutData["kind"] as? String,
76
+ let rowData = root["rows"] as? [[String: Any]] else {
77
+ throw NativeListModelError.invalidSnapshot
78
+ }
79
+
80
+ var items = try rowData.map(NativeListItem.init)
81
+ let keys = Set(items.map(\.key))
82
+ guard keys.count == items.count else { throw NativeListModelError.duplicateKey }
83
+ if items.isEmpty, let emptyData = root["emptyState"] as? [String: Any] {
84
+ items = [try NativeListItem(data: emptyData)]
85
+ }
86
+
87
+ let selection = root["selection"] as? [String: Any]
88
+ let selectedKeys = Set(selection?["selectedKeys"] as? [String] ?? [])
89
+ guard selectedKeys.isSubset(of: keys) else { throw NativeListModelError.unknownSelectionKey }
90
+ let capabilities = root["capabilities"] as? [String: Any]
91
+ let sectionIndex = capabilities?["sectionIndex"] as? [String: Any]
92
+ let footer = try (root["fixedFooter"] as? [String: Any]).map(NativeListItem.init)
93
+
94
+ let contentPadding = max(0, CGFloat(layoutData.double("contentPadding")))
95
+ let contentPaddingHorizontal = layoutData["contentPaddingHorizontal"] == nil
96
+ ? contentPadding
97
+ : max(0, CGFloat(layoutData.double("contentPaddingHorizontal")))
98
+ let contentPaddingTop = layoutData["contentPaddingTop"] == nil
99
+ ? contentPadding
100
+ : max(0, CGFloat(layoutData.double("contentPaddingTop")))
101
+ let contentPaddingBottom = layoutData["contentPaddingBottom"] == nil
102
+ ? contentPadding
103
+ : max(0, CGFloat(layoutData.double("contentPaddingBottom")))
104
+
105
+ return NativeListConfig(
106
+ generation: generation,
107
+ layout: layout,
108
+ orientation: layoutData["orientation"] as? String ?? "vertical",
109
+ gridColumns: min(4, max(1, layoutData["gridColumns"] as? Int ?? 1)),
110
+ stickyHeaders: layoutData["stickyHeaders"] as? Bool ?? false,
111
+ contentPadding: contentPadding,
112
+ contentPaddingHorizontal: contentPaddingHorizontal,
113
+ contentPaddingTop: contentPaddingTop,
114
+ contentPaddingBottom: contentPaddingBottom,
115
+ itemSpacing: max(0, layoutData["itemSpacing"] as? CGFloat ?? 0),
116
+ selectionMode: selection?["mode"] as? String ?? "none",
117
+ rowPressToggles: selection?["rowPressToggles"] as? Bool ?? false,
118
+ selectedKeys: selectedKeys,
119
+ reorderable: capabilities?["reorderable"] as? Bool ?? false,
120
+ pullToRefresh: capabilities?["pullToRefresh"] as? Bool ?? false,
121
+ refreshing: capabilities?["refreshing"] as? Bool ?? false,
122
+ loadMore: capabilities?["loadMore"] as? Bool ?? false,
123
+ endReachedThreshold: capabilities?["endReachedThreshold"] as? Double ?? 0.2,
124
+ sectionIndexEnabled: sectionIndex?["enabled"] as? Bool ?? false,
125
+ sectionIndexHapticsEnabled: sectionIndex?["hapticsEnabled"] as? Bool ?? true,
126
+ theme: root["theme"] as? [String: Any],
127
+ fixedFooter: footer,
128
+ items: items
129
+ )
130
+ }
131
+ }
132
+
133
+ enum NativeListModelError: Error {
134
+ case invalidSnapshot
135
+ case invalidRow
136
+ case duplicateKey
137
+ case unknownSelectionKey
138
+ }
139
+
140
+ struct NativeSelectionTarget {
141
+ let scope: String
142
+ let key: String?
143
+ }
144
+
145
+ extension Dictionary where Key == String, Value == Any {
146
+ func string(_ key: String, default fallback: String = "") -> String {
147
+ self[key] as? String ?? fallback
148
+ }
149
+
150
+ func bool(_ key: String, default fallback: Bool = false) -> Bool {
151
+ self[key] as? Bool ?? fallback
152
+ }
153
+
154
+ func int(_ key: String, default fallback: Int = 0) -> Int {
155
+ if let value = self[key] as? Int { return value }
156
+ if let value = self[key] as? NSNumber { return value.intValue }
157
+ return fallback
158
+ }
159
+
160
+ func double(_ key: String, default fallback: Double = 0) -> Double {
161
+ if let value = self[key] as? Double { return value }
162
+ if let value = self[key] as? NSNumber { return value.doubleValue }
163
+ return fallback
164
+ }
165
+
166
+ func dictionary(_ key: String) -> [String: Any]? {
167
+ self[key] as? [String: Any]
168
+ }
169
+
170
+ func dictionaries(_ key: String) -> [[String: Any]] {
171
+ self[key] as? [[String: Any]] ?? []
172
+ }
173
+ }
174
+
175
+ extension UIColor {
176
+ convenience init(nativeListHex value: String, fallback: UIColor) {
177
+ let value = value.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
178
+ var hex: UInt64 = 0
179
+ guard Scanner(string: value).scanHexInt64(&hex) else {
180
+ self.init(cgColor: fallback.cgColor)
181
+ return
182
+ }
183
+ let red: CGFloat
184
+ let green: CGFloat
185
+ let blue: CGFloat
186
+ let alpha: CGFloat
187
+ switch value.count {
188
+ case 8:
189
+ red = CGFloat((hex >> 24) & 0xff) / 255
190
+ green = CGFloat((hex >> 16) & 0xff) / 255
191
+ blue = CGFloat((hex >> 8) & 0xff) / 255
192
+ alpha = CGFloat(hex & 0xff) / 255
193
+ case 6:
194
+ red = CGFloat((hex >> 16) & 0xff) / 255
195
+ green = CGFloat((hex >> 8) & 0xff) / 255
196
+ blue = CGFloat(hex & 0xff) / 255
197
+ alpha = 1
198
+ default:
199
+ self.init(cgColor: fallback.cgColor)
200
+ return
201
+ }
202
+ self.init(red: red, green: green, blue: blue, alpha: alpha)
203
+ }
204
+ }
205
+
206
+ func nativeListColor(
207
+ _ theme: [String: Any]?,
208
+ _ key: String,
209
+ _ fallback: String
210
+ ) -> UIColor {
211
+ UIColor(
212
+ nativeListHex: theme?[key] as? String ?? fallback,
213
+ fallback: UIColor(nativeListHex: fallback, fallback: .black)
214
+ )
215
+ }