@thelacanians/vue-native-cli 0.4.15 → 0.6.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.
Files changed (116) hide show
  1. package/dist/cli.js +329 -15
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  6. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  7. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  8. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  9. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  13. package/native/android/gradle.properties +1 -0
  14. package/native/android/gradlew +1 -1
  15. package/native/ios/VueNativeCore/Package.swift +1 -1
  16. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
  17. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
  18. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  19. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  23. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  24. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  25. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  26. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  27. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  28. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  29. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  30. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  31. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  32. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  33. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  34. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  35. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  36. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  94. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  95. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  96. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  97. package/native/shared/VueNativeShared/Package.swift +14 -0
  98. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  99. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  100. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  101. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  102. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  103. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  104. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  115. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  116. package/package.json +8 -2
@@ -0,0 +1,276 @@
1
+ import AppKit
2
+ import ObjectiveC
3
+
4
+ /// Factory for VOutlineView — macOS-specific tree/outline view component.
5
+ /// Maps to NSOutlineView wrapped in an NSScrollView.
6
+ ///
7
+ /// Props:
8
+ /// - data: [{ id, label, children?: [...] }]
9
+ /// - expandAll: Bool
10
+ /// - selectionMode: "single" | "multiple" | "none"
11
+ ///
12
+ /// Events:
13
+ /// - select -> { id, label }
14
+ /// - expand -> { id }
15
+ /// - collapse -> { id }
16
+ final class VOutlineViewFactory: NativeComponentFactory {
17
+
18
+ // MARK: - Associated object keys
19
+
20
+ private static var dataSourceKey: UInt8 = 0
21
+ private static var selectHandlerKey: UInt8 = 0
22
+ private static var expandHandlerKey: UInt8 = 0
23
+ private static var collapseHandlerKey: UInt8 = 0
24
+ private static var outlineViewKey: UInt8 = 0
25
+
26
+ // MARK: - NativeComponentFactory
27
+
28
+ func createView() -> NSView {
29
+ let scrollView = NSScrollView()
30
+ scrollView.wantsLayer = true
31
+ scrollView.hasVerticalScroller = true
32
+ scrollView.hasHorizontalScroller = false
33
+ scrollView.autohidesScrollers = true
34
+ scrollView.drawsBackground = false
35
+
36
+ let outlineView = NSOutlineView()
37
+ outlineView.headerView = nil
38
+ outlineView.usesAlternatingRowBackgroundColors = false
39
+ outlineView.allowsMultipleSelection = false
40
+ outlineView.selectionHighlightStyle = .regular
41
+
42
+ let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("label"))
43
+ column.title = ""
44
+ column.resizingMask = .autoresizingMask
45
+ outlineView.addTableColumn(column)
46
+ outlineView.outlineTableColumn = column
47
+
48
+ scrollView.documentView = outlineView
49
+
50
+ // Create and attach data source/delegate
51
+ let handler = OutlineHandler(outlineView: outlineView, scrollView: scrollView)
52
+ outlineView.dataSource = handler
53
+ outlineView.delegate = handler
54
+
55
+ objc_setAssociatedObject(
56
+ scrollView, &VOutlineViewFactory.dataSourceKey,
57
+ handler, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
58
+ )
59
+ objc_setAssociatedObject(
60
+ scrollView, &VOutlineViewFactory.outlineViewKey,
61
+ outlineView, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
62
+ )
63
+
64
+ scrollView.ensureLayoutNode()
65
+ return scrollView
66
+ }
67
+
68
+ func updateProp(view: NSView, key: String, value: Any?) {
69
+ let outlineView = objc_getAssociatedObject(
70
+ view, &VOutlineViewFactory.outlineViewKey
71
+ ) as? NSOutlineView
72
+
73
+ let handler = objc_getAssociatedObject(
74
+ view, &VOutlineViewFactory.dataSourceKey
75
+ ) as? OutlineHandler
76
+
77
+ switch key {
78
+ case "data":
79
+ guard let dataArray = value as? [[String: Any]] else { return }
80
+ let nodes = OutlineNode.parse(dataArray)
81
+ handler?.rootNodes = nodes
82
+ outlineView?.reloadData()
83
+
84
+ // If expandAll was previously set, re-expand
85
+ if handler?.shouldExpandAll == true {
86
+ outlineView?.expandItem(nil, expandChildren: true)
87
+ }
88
+
89
+ case "expandAll":
90
+ let expand = (value as? Bool) ?? false
91
+ handler?.shouldExpandAll = expand
92
+ if expand {
93
+ outlineView?.expandItem(nil, expandChildren: true)
94
+ } else {
95
+ outlineView?.collapseItem(nil, collapseChildren: true)
96
+ }
97
+
98
+ case "selectionMode":
99
+ guard let outlineView = outlineView else { return }
100
+ if let mode = value as? String {
101
+ switch mode {
102
+ case "multiple":
103
+ outlineView.allowsMultipleSelection = true
104
+ outlineView.allowsEmptySelection = true
105
+ case "none":
106
+ outlineView.allowsMultipleSelection = false
107
+ outlineView.allowsEmptySelection = true
108
+ outlineView.selectionHighlightStyle = .none
109
+ default: // "single"
110
+ outlineView.allowsMultipleSelection = false
111
+ outlineView.allowsEmptySelection = true
112
+ outlineView.selectionHighlightStyle = .regular
113
+ }
114
+ }
115
+
116
+ default:
117
+ StyleEngine.apply(key: key, value: value, to: view)
118
+ }
119
+ }
120
+
121
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
122
+ let outlineHandler = objc_getAssociatedObject(
123
+ view, &VOutlineViewFactory.dataSourceKey
124
+ ) as? OutlineHandler
125
+
126
+ switch event {
127
+ case "select":
128
+ outlineHandler?.onSelect = handler
129
+
130
+ case "expand":
131
+ outlineHandler?.onExpand = handler
132
+
133
+ case "collapse":
134
+ outlineHandler?.onCollapse = handler
135
+
136
+ default:
137
+ break
138
+ }
139
+ }
140
+
141
+ func removeEventListener(view: NSView, event: String) {
142
+ let outlineHandler = objc_getAssociatedObject(
143
+ view, &VOutlineViewFactory.dataSourceKey
144
+ ) as? OutlineHandler
145
+
146
+ switch event {
147
+ case "select":
148
+ outlineHandler?.onSelect = nil
149
+
150
+ case "expand":
151
+ outlineHandler?.onExpand = nil
152
+
153
+ case "collapse":
154
+ outlineHandler?.onCollapse = nil
155
+
156
+ default:
157
+ break
158
+ }
159
+ }
160
+ }
161
+
162
+ // MARK: - OutlineNode
163
+
164
+ private final class OutlineNode {
165
+ let id: String
166
+ let label: String
167
+ var children: [OutlineNode]
168
+
169
+ init(id: String, label: String, children: [OutlineNode] = []) {
170
+ self.id = id
171
+ self.label = label
172
+ self.children = children
173
+ }
174
+
175
+ static func parse(_ array: [[String: Any]]) -> [OutlineNode] {
176
+ return array.map { dict in
177
+ let id = (dict["id"] as? String) ?? UUID().uuidString
178
+ let label = (dict["label"] as? String) ?? ""
179
+ let childDicts = dict["children"] as? [[String: Any]] ?? []
180
+ let children = parse(childDicts)
181
+ return OutlineNode(id: id, label: label, children: children)
182
+ }
183
+ }
184
+ }
185
+
186
+ // MARK: - OutlineHandler
187
+
188
+ private final class OutlineHandler: NSObject,
189
+ NSOutlineViewDataSource, NSOutlineViewDelegate
190
+ {
191
+ var rootNodes: [OutlineNode] = []
192
+ var shouldExpandAll: Bool = false
193
+
194
+ var onSelect: ((Any?) -> Void)?
195
+ var onExpand: ((Any?) -> Void)?
196
+ var onCollapse: ((Any?) -> Void)?
197
+
198
+ private weak var outlineView: NSOutlineView?
199
+ private weak var scrollView: NSScrollView?
200
+
201
+ init(outlineView: NSOutlineView, scrollView: NSScrollView) {
202
+ self.outlineView = outlineView
203
+ self.scrollView = scrollView
204
+ super.init()
205
+ }
206
+
207
+ // MARK: - NSOutlineViewDataSource
208
+
209
+ func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
210
+ if let node = item as? OutlineNode {
211
+ return node.children.count
212
+ }
213
+ return rootNodes.count
214
+ }
215
+
216
+ func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
217
+ if let node = item as? OutlineNode {
218
+ return node.children[index]
219
+ }
220
+ return rootNodes[index]
221
+ }
222
+
223
+ func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
224
+ if let node = item as? OutlineNode {
225
+ return !node.children.isEmpty
226
+ }
227
+ return false
228
+ }
229
+
230
+ // MARK: - NSOutlineViewDelegate
231
+
232
+ func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
233
+ guard let node = item as? OutlineNode else { return nil }
234
+
235
+ let cellId = NSUserInterfaceItemIdentifier("OutlineCell")
236
+ let cellView: NSTableCellView
237
+
238
+ if let reused = outlineView.makeView(withIdentifier: cellId, owner: nil) as? NSTableCellView {
239
+ cellView = reused
240
+ } else {
241
+ cellView = NSTableCellView()
242
+ cellView.identifier = cellId
243
+
244
+ let textField = NSTextField(labelWithString: "")
245
+ textField.translatesAutoresizingMaskIntoConstraints = false
246
+ cellView.addSubview(textField)
247
+ cellView.textField = textField
248
+
249
+ NSLayoutConstraint.activate([
250
+ textField.leadingAnchor.constraint(equalTo: cellView.leadingAnchor, constant: 4),
251
+ textField.trailingAnchor.constraint(equalTo: cellView.trailingAnchor, constant: -4),
252
+ textField.centerYAnchor.constraint(equalTo: cellView.centerYAnchor),
253
+ ])
254
+ }
255
+
256
+ cellView.textField?.stringValue = node.label
257
+ return cellView
258
+ }
259
+
260
+ func outlineViewSelectionDidChange(_ notification: Notification) {
261
+ guard let outlineView = outlineView else { return }
262
+ let row = outlineView.selectedRow
263
+ guard row >= 0, let node = outlineView.item(atRow: row) as? OutlineNode else { return }
264
+ onSelect?(["id": node.id, "label": node.label])
265
+ }
266
+
267
+ func outlineViewItemDidExpand(_ notification: Notification) {
268
+ guard let node = notification.userInfo?["NSObject"] as? OutlineNode else { return }
269
+ onExpand?(["id": node.id])
270
+ }
271
+
272
+ func outlineViewItemDidCollapse(_ notification: Notification) {
273
+ guard let node = notification.userInfo?["NSObject"] as? OutlineNode else { return }
274
+ onCollapse?(["id": node.id])
275
+ }
276
+ }
@@ -0,0 +1,134 @@
1
+ import AppKit
2
+ import ObjectiveC
3
+
4
+ /// Factory for VPicker — a popup button for selecting from a list of items.
5
+ /// Maps to NSPopUpButton.
6
+ final class VPickerFactory: NativeComponentFactory {
7
+
8
+ private static var changeHandlerKey: UInt8 = 0
9
+ nonisolated(unsafe) static var itemsKey: UInt8 = 0
10
+
11
+ // MARK: - NativeComponentFactory
12
+
13
+ func createView() -> NSView {
14
+ let popup = NSPopUpButton()
15
+ popup.pullsDown = false
16
+ popup.wantsLayer = true
17
+ popup.ensureLayoutNode()
18
+ return popup
19
+ }
20
+
21
+ func updateProp(view: NSView, key: String, value: Any?) {
22
+ guard let popup = view as? NSPopUpButton else {
23
+ StyleEngine.apply(key: key, value: value, to: view)
24
+ return
25
+ }
26
+
27
+ switch key {
28
+ case "items":
29
+ popup.removeAllItems()
30
+ var storedItems: [[String: Any]] = []
31
+
32
+ if let strings = value as? [String] {
33
+ popup.addItems(withTitles: strings)
34
+ storedItems = strings.map { ["label": $0, "value": $0] }
35
+ } else if let items = value as? [[String: Any]] {
36
+ for item in items {
37
+ let label = item["label"] as? String ?? item["value"] as? String ?? ""
38
+ popup.addItem(withTitle: label)
39
+ storedItems.append(item)
40
+ }
41
+ }
42
+ objc_setAssociatedObject(popup, &VPickerFactory.itemsKey, storedItems, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
43
+
44
+ case "selectedIndex":
45
+ if let idx = value as? Int {
46
+ if idx >= 0 && idx < popup.numberOfItems {
47
+ popup.selectItem(at: idx)
48
+ }
49
+ } else if let idx = value as? Double {
50
+ let i = Int(idx)
51
+ if i >= 0 && i < popup.numberOfItems {
52
+ popup.selectItem(at: i)
53
+ }
54
+ }
55
+
56
+ case "selectedValue":
57
+ if let val = value as? String,
58
+ let items = objc_getAssociatedObject(popup, &VPickerFactory.itemsKey) as? [[String: Any]] {
59
+ if let idx = items.firstIndex(where: { ($0["value"] as? String) == val }) {
60
+ popup.selectItem(at: idx)
61
+ }
62
+ }
63
+
64
+ case "enabled":
65
+ if let enabled = value as? Bool {
66
+ popup.isEnabled = enabled
67
+ } else {
68
+ popup.isEnabled = true
69
+ }
70
+
71
+ case "disabled":
72
+ if let disabled = value as? Bool {
73
+ popup.isEnabled = !disabled
74
+ } else if let disabled = value as? Int {
75
+ popup.isEnabled = disabled == 0
76
+ } else {
77
+ popup.isEnabled = true
78
+ }
79
+
80
+ default:
81
+ StyleEngine.apply(key: key, value: value, to: view)
82
+ }
83
+ }
84
+
85
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
86
+ guard let popup = view as? NSPopUpButton, event == "change" else { return }
87
+
88
+ let proxy = PickerActionProxy(popup: popup, handler: handler)
89
+ popup.target = proxy
90
+ popup.action = #selector(PickerActionProxy.selectionChanged(_:))
91
+ objc_setAssociatedObject(
92
+ popup,
93
+ &VPickerFactory.changeHandlerKey,
94
+ proxy,
95
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
96
+ )
97
+ }
98
+
99
+ func removeEventListener(view: NSView, event: String) {
100
+ guard let popup = view as? NSPopUpButton, event == "change" else { return }
101
+
102
+ popup.target = nil
103
+ popup.action = nil
104
+ objc_setAssociatedObject(popup, &VPickerFactory.changeHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
105
+ }
106
+ }
107
+
108
+ // MARK: - PickerActionProxy
109
+
110
+ /// Target-action proxy that routes NSPopUpButton selection changes to a closure handler.
111
+ final class PickerActionProxy: NSObject {
112
+
113
+ private weak var popup: NSPopUpButton?
114
+ private let handler: (Any?) -> Void
115
+
116
+ init(popup: NSPopUpButton, handler: @escaping (Any?) -> Void) {
117
+ self.popup = popup
118
+ self.handler = handler
119
+ }
120
+
121
+ @objc func selectionChanged(_ sender: NSPopUpButton) {
122
+ let index = sender.indexOfSelectedItem
123
+ let items = objc_getAssociatedObject(sender, &VPickerFactory.itemsKey) as? [[String: Any]]
124
+ let item = items?[safe: index]
125
+ let label = sender.titleOfSelectedItem ?? ""
126
+ let value = item?["value"] as? String ?? label
127
+
128
+ handler([
129
+ "selectedIndex": index,
130
+ "value": value,
131
+ "label": label
132
+ ])
133
+ }
134
+ }
@@ -0,0 +1,120 @@
1
+ import AppKit
2
+ import ObjectiveC
3
+
4
+ /// Factory for VPressable — custom mouse-tracking view for pressable interactions.
5
+ /// Maps to a PressableView (ClickableView subclass) with press, longPress,
6
+ /// pressIn (mouseDown), and pressOut (mouseUp) events.
7
+ final class VPressableFactory: NativeComponentFactory {
8
+
9
+ // MARK: - Associated object keys
10
+
11
+ private static var pressInHandlerKey: UInt8 = 0
12
+ private static var pressOutHandlerKey: UInt8 = 0
13
+
14
+ // MARK: - NativeComponentFactory
15
+
16
+ func createView() -> NSView {
17
+ let pressable = PressableView()
18
+ pressable.ensureLayoutNode()
19
+ return pressable
20
+ }
21
+
22
+ func updateProp(view: NSView, key: String, value: Any?) {
23
+ guard let pressable = view as? PressableView else {
24
+ StyleEngine.apply(key: key, value: value, to: view)
25
+ return
26
+ }
27
+
28
+ switch key {
29
+ case "disabled":
30
+ if let disabled = value as? Bool {
31
+ pressable.isDisabled = disabled
32
+ } else if let disabled = value as? Int {
33
+ pressable.isDisabled = disabled != 0
34
+ } else {
35
+ pressable.isDisabled = false
36
+ }
37
+
38
+ case "activeOpacity":
39
+ if let opacity = value as? Double {
40
+ pressable.activeOpacity = CGFloat(opacity)
41
+ } else if let opacity = value as? Int {
42
+ pressable.activeOpacity = CGFloat(opacity)
43
+ } else {
44
+ pressable.activeOpacity = 0.7
45
+ }
46
+
47
+ default:
48
+ StyleEngine.apply(key: key, value: value, to: view)
49
+ }
50
+ }
51
+
52
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
53
+ guard let pressable = view as? PressableView else { return }
54
+
55
+ switch event {
56
+ case "press":
57
+ pressable.onPress = {
58
+ handler(nil)
59
+ }
60
+
61
+ case "longPress":
62
+ pressable.onLongPress = {
63
+ handler(nil)
64
+ }
65
+
66
+ case "pressIn":
67
+ pressable.onPressIn = {
68
+ handler(nil)
69
+ }
70
+
71
+ case "pressOut":
72
+ pressable.onPressOut = {
73
+ handler(nil)
74
+ }
75
+
76
+ default:
77
+ break
78
+ }
79
+ }
80
+
81
+ func removeEventListener(view: NSView, event: String) {
82
+ guard let pressable = view as? PressableView else { return }
83
+
84
+ switch event {
85
+ case "press":
86
+ pressable.onPress = nil
87
+ case "longPress":
88
+ pressable.onLongPress = nil
89
+ case "pressIn":
90
+ pressable.onPressIn = nil
91
+ case "pressOut":
92
+ pressable.onPressOut = nil
93
+ default:
94
+ break
95
+ }
96
+ }
97
+ }
98
+
99
+ // MARK: - PressableView
100
+
101
+ /// ClickableView subclass that adds pressIn (mouseDown) and pressOut (mouseUp) callbacks.
102
+ /// Provides the full set of press interaction events for the VPressable component.
103
+ final class PressableView: ClickableView {
104
+
105
+ /// Called immediately when the mouse button goes down.
106
+ var onPressIn: (() -> Void)?
107
+
108
+ /// Called when the mouse button is released (regardless of position).
109
+ var onPressOut: (() -> Void)?
110
+
111
+ override func mouseDown(with event: NSEvent) {
112
+ onPressIn?()
113
+ super.mouseDown(with: event)
114
+ }
115
+
116
+ override func mouseUp(with event: NSEvent) {
117
+ onPressOut?()
118
+ super.mouseUp(with: event)
119
+ }
120
+ }
@@ -0,0 +1,71 @@
1
+ import AppKit
2
+
3
+ /// Factory for VProgressBar — a determinate/indeterminate progress indicator.
4
+ /// Maps to NSProgressIndicator with bar style.
5
+ final class VProgressBarFactory: NativeComponentFactory {
6
+
7
+ // MARK: - NativeComponentFactory
8
+
9
+ func createView() -> NSView {
10
+ let indicator = NSProgressIndicator()
11
+ indicator.style = .bar
12
+ indicator.isIndeterminate = false
13
+ indicator.minValue = 0
14
+ indicator.maxValue = 1
15
+ indicator.doubleValue = 0
16
+ indicator.wantsLayer = true
17
+ indicator.ensureLayoutNode()
18
+ return indicator
19
+ }
20
+
21
+ func updateProp(view: NSView, key: String, value: Any?) {
22
+ guard let indicator = view as? NSProgressIndicator else {
23
+ StyleEngine.apply(key: key, value: value, to: view)
24
+ return
25
+ }
26
+
27
+ switch key {
28
+ case "progress", "value":
29
+ if let val = value as? Double {
30
+ indicator.isIndeterminate = false
31
+ indicator.doubleValue = val
32
+ } else if let val = value as? Int {
33
+ indicator.isIndeterminate = false
34
+ indicator.doubleValue = Double(val)
35
+ }
36
+
37
+ case "progressTintColor":
38
+ if let colorStr = value as? String {
39
+ indicator.wantsLayer = true
40
+ indicator.layer?.backgroundColor = NSColor.fromHex(colorStr).cgColor
41
+ }
42
+
43
+ case "trackTintColor":
44
+ // No direct API; would require custom drawing
45
+ break
46
+
47
+ case "animated", "indeterminate":
48
+ let indeterminate: Bool
49
+ if let val = value as? Bool {
50
+ indeterminate = val
51
+ } else if let val = value as? Int {
52
+ indeterminate = val != 0
53
+ } else {
54
+ indeterminate = false
55
+ }
56
+ indicator.isIndeterminate = indeterminate
57
+ if indeterminate {
58
+ indicator.startAnimation(nil)
59
+ } else {
60
+ indicator.stopAnimation(nil)
61
+ }
62
+
63
+ default:
64
+ StyleEngine.apply(key: key, value: value, to: view)
65
+ }
66
+ }
67
+
68
+ func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
69
+ // No events for progress bar
70
+ }
71
+ }