react-native-tinyui 0.4.0 → 1.0.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.
- package/README.md +383 -53
- package/ios/ConcentricView/TinyuiConcentricView.h +12 -0
- package/ios/ConcentricView/TinyuiConcentricView.mm +60 -0
- package/ios/Core/Tinyui.mm +54 -0
- package/ios/Core/TinyuiComponentClasses.mm +44 -0
- package/ios/LiquidGlassText/TinyuiLiquidGlassTextProvider.swift +64 -79
- package/ios/LiquidGlassText/TinyuiLiquidGlassTextShadowNode.h +46 -0
- package/ios/LiquidGlassText/TinyuiLiquidGlassTextShadowNode.mm +159 -0
- package/ios/LiquidGlassText/TinyuiLiquidGlassTextView.mm +6 -27
- package/ios/LiquidGlassText/TinyuiTextOutlineShape.swift +5 -2
- package/ios/Menu/TinyuiMenuBuilder.swift +238 -0
- package/ios/Menu/TinyuiMenuProvider.swift +51 -85
- package/ios/Menu/TinyuiMenuView.mm +13 -35
- package/ios/Popover/TinyuiPopoverProvider.swift +198 -44
- package/ios/Popover/TinyuiPopoverView.mm +26 -1
- package/ios/SFSymbol/TinyuiSFSymbolEffects.h +9 -0
- package/ios/SFSymbol/TinyuiSFSymbolEffects.mm +180 -0
- package/ios/SFSymbol/TinyuiSFSymbolImage.h +8 -0
- package/ios/SFSymbol/TinyuiSFSymbolImage.mm +141 -0
- package/ios/SFSymbol/TinyuiSFSymbolShadowNode.h +31 -0
- package/ios/SFSymbol/TinyuiSFSymbolShadowNode.mm +30 -0
- package/ios/SFSymbol/TinyuiSFSymbolView.h +4 -0
- package/ios/SFSymbol/TinyuiSFSymbolView.mm +173 -0
- package/ios/Stepper/TinyuiStepperShadowNode.h +32 -0
- package/ios/Stepper/TinyuiStepperShadowNode.mm +28 -0
- package/ios/Stepper/TinyuiStepperView.h +12 -0
- package/ios/Stepper/TinyuiStepperView.mm +128 -0
- package/ios/TipKit/TinyuiTip.swift +31 -0
- package/ios/TipKit/TinyuiTipPopoverProvider.swift +276 -0
- package/ios/TipKit/TinyuiTipPopoverView.h +8 -0
- package/ios/TipKit/TinyuiTipPopoverView.mm +114 -0
- package/ios/TipKit/TinyuiTipsRuntime.swift +148 -0
- package/lib/module/ConcentricView/ConcentricViewNativeComponent.ts +14 -0
- package/lib/module/ConcentricView/index.js +29 -0
- package/lib/module/ConcentricView/index.js.map +1 -0
- package/lib/module/ConcentricView/types.js +4 -0
- package/lib/module/ConcentricView/types.js.map +1 -0
- package/lib/module/LiquidGlassText/LiquidGlassTextNativeComponent.ts +2 -12
- package/lib/module/LiquidGlassText/index.js +60 -35
- package/lib/module/LiquidGlassText/index.js.map +1 -1
- package/lib/module/Menu/MenuNativeComponent.ts +7 -5
- package/lib/module/Menu/index.js +4 -9
- package/lib/module/Menu/index.js.map +1 -1
- package/lib/module/Menu/options.js +4 -12
- package/lib/module/Menu/options.js.map +1 -1
- package/lib/module/NativeTinyui.js.map +1 -1
- package/lib/module/Popover/index.js +4 -2
- package/lib/module/Popover/index.js.map +1 -1
- package/lib/module/SFSymbol/SFSymbolNativeComponent.ts +20 -0
- package/lib/module/SFSymbol/index.js +73 -0
- package/lib/module/SFSymbol/index.js.map +1 -0
- package/lib/module/SFSymbol/options.js +79 -0
- package/lib/module/SFSymbol/options.js.map +1 -0
- package/lib/module/SFSymbol/types.js +4 -0
- package/lib/module/SFSymbol/types.js.map +1 -0
- package/lib/module/Stepper/StepperNativeComponent.ts +29 -0
- package/lib/module/Stepper/index.js +75 -0
- package/lib/module/Stepper/index.js.map +1 -0
- package/lib/module/Stepper/types.js +4 -0
- package/lib/module/Stepper/types.js.map +1 -0
- package/lib/module/TipKit/Popover.js +89 -0
- package/lib/module/TipKit/Popover.js.map +1 -0
- package/lib/module/TipKit/TipPopoverNativeComponent.ts +37 -0
- package/lib/module/TipKit/index.js +28 -0
- package/lib/module/TipKit/index.js.map +1 -0
- package/lib/module/TipKit/types.js +4 -0
- package/lib/module/TipKit/types.js.map +1 -0
- package/lib/module/TipKit/validation.js +24 -0
- package/lib/module/TipKit/validation.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/getNativeTinyui.js.map +1 -1
- package/lib/typescript/src/ConcentricView/ConcentricViewNativeComponent.d.ts +7 -0
- package/lib/typescript/src/ConcentricView/ConcentricViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/ConcentricView/index.d.ts +4 -0
- package/lib/typescript/src/ConcentricView/index.d.ts.map +1 -0
- package/lib/typescript/src/ConcentricView/types.d.ts +6 -0
- package/lib/typescript/src/ConcentricView/types.d.ts.map +1 -0
- package/lib/typescript/src/LiquidGlassText/LiquidGlassTextNativeComponent.d.ts +2 -8
- package/lib/typescript/src/LiquidGlassText/LiquidGlassTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/LiquidGlassText/index.d.ts +1 -1
- package/lib/typescript/src/LiquidGlassText/index.d.ts.map +1 -1
- package/lib/typescript/src/LiquidGlassText/types.d.ts +13 -22
- package/lib/typescript/src/LiquidGlassText/types.d.ts.map +1 -1
- package/lib/typescript/src/Menu/MenuNativeComponent.d.ts +4 -5
- package/lib/typescript/src/Menu/MenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/Menu/index.d.ts +1 -1
- package/lib/typescript/src/Menu/index.d.ts.map +1 -1
- package/lib/typescript/src/Menu/options.d.ts +40 -4
- package/lib/typescript/src/Menu/options.d.ts.map +1 -1
- package/lib/typescript/src/Menu/types.d.ts +16 -4
- package/lib/typescript/src/Menu/types.d.ts.map +1 -1
- package/lib/typescript/src/NativeTinyui.d.ts +2 -0
- package/lib/typescript/src/NativeTinyui.d.ts.map +1 -1
- package/lib/typescript/src/Popover/index.d.ts.map +1 -1
- package/lib/typescript/src/Popover/types.d.ts +2 -0
- package/lib/typescript/src/Popover/types.d.ts.map +1 -1
- package/lib/typescript/src/SFSymbol/SFSymbolNativeComponent.d.ts +11 -0
- package/lib/typescript/src/SFSymbol/SFSymbolNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/SFSymbol/index.d.ts +4 -0
- package/lib/typescript/src/SFSymbol/index.d.ts.map +1 -0
- package/lib/typescript/src/SFSymbol/options.d.ts +86 -0
- package/lib/typescript/src/SFSymbol/options.d.ts.map +1 -0
- package/lib/typescript/src/SFSymbol/types.d.ts +113 -0
- package/lib/typescript/src/SFSymbol/types.d.ts.map +1 -0
- package/lib/typescript/src/Stepper/StepperNativeComponent.d.ts +21 -0
- package/lib/typescript/src/Stepper/StepperNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/Stepper/index.d.ts +4 -0
- package/lib/typescript/src/Stepper/index.d.ts.map +1 -0
- package/lib/typescript/src/Stepper/types.d.ts +24 -0
- package/lib/typescript/src/Stepper/types.d.ts.map +1 -0
- package/lib/typescript/src/TipKit/Popover.d.ts +3 -0
- package/lib/typescript/src/TipKit/Popover.d.ts.map +1 -0
- package/lib/typescript/src/TipKit/TipPopoverNativeComponent.d.ts +38 -0
- package/lib/typescript/src/TipKit/TipPopoverNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/TipKit/index.d.ts +10 -0
- package/lib/typescript/src/TipKit/index.d.ts.map +1 -0
- package/lib/typescript/src/TipKit/types.d.ts +46 -0
- package/lib/typescript/src/TipKit/types.d.ts.map +1 -0
- package/lib/typescript/src/TipKit/validation.d.ts +4 -0
- package/lib/typescript/src/TipKit/validation.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +10 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/getNativeTinyui.d.ts +1 -1
- package/lib/typescript/src/utils/getNativeTinyui.d.ts.map +1 -1
- package/package.json +37 -2
- package/{Tinyui.podspec → react-native-tinyui.podspec} +23 -2
- package/src/ConcentricView/ConcentricViewNativeComponent.ts +14 -0
- package/src/ConcentricView/index.tsx +34 -0
- package/src/ConcentricView/types.ts +6 -0
- package/src/LiquidGlassText/LiquidGlassTextNativeComponent.ts +2 -12
- package/src/LiquidGlassText/index.tsx +73 -39
- package/src/LiquidGlassText/types.ts +24 -50
- package/src/Menu/MenuNativeComponent.ts +7 -5
- package/src/Menu/index.tsx +6 -6
- package/src/Menu/options.ts +4 -18
- package/src/Menu/types.ts +18 -4
- package/src/NativeTinyui.ts +2 -0
- package/src/Popover/index.tsx +8 -3
- package/src/Popover/types.ts +2 -0
- package/src/SFSymbol/SFSymbolNativeComponent.ts +20 -0
- package/src/SFSymbol/index.tsx +89 -0
- package/src/SFSymbol/options.ts +116 -0
- package/src/SFSymbol/types.ts +158 -0
- package/src/Stepper/StepperNativeComponent.ts +29 -0
- package/src/Stepper/index.tsx +91 -0
- package/src/Stepper/types.ts +24 -0
- package/src/TipKit/Popover.tsx +87 -0
- package/src/TipKit/TipPopoverNativeComponent.ts +37 -0
- package/src/TipKit/index.ts +47 -0
- package/src/TipKit/types.ts +57 -0
- package/src/TipKit/validation.ts +39 -0
- package/src/index.tsx +32 -4
- package/src/utils/getNativeTinyui.ts +8 -1
- package/ios/Core/TinyuiHostingHelpers.swift +0 -74
- package/ios/Menu/TinyuiMenuViewSwift.swift +0 -408
- package/ios/Popover/TinyuiPopoverViewSwift.swift +0 -88
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
@objc public protocol TinyuiMenuViewDelegate: AnyObject {
|
|
5
|
+
func onActionPress(id: String, title: String)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Builds UIKit menu elements from the configuration generated in JavaScript. */
|
|
9
|
+
final class TinyuiMenuBuilder {
|
|
10
|
+
private weak var delegate: TinyuiMenuViewDelegate?
|
|
11
|
+
|
|
12
|
+
init(delegate: TinyuiMenuViewDelegate?) {
|
|
13
|
+
self.delegate = delegate
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func buildMenu(title: String, config: [String: Any]) -> UIMenu {
|
|
17
|
+
print("[TinyUI][Menu] config:", config)
|
|
18
|
+
|
|
19
|
+
return UIMenu(
|
|
20
|
+
title: title,
|
|
21
|
+
children: buildChildren(from: dictionaries(in: config, key: "items"))
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private func buildChildren(
|
|
26
|
+
from dictionaries: [[String: Any]],
|
|
27
|
+
inheritedDestructive: Bool = false,
|
|
28
|
+
inheritedDisabled: Bool = false
|
|
29
|
+
) -> [UIMenuElement] {
|
|
30
|
+
// UIKit represents separators by placing adjacent groups in inline menus.
|
|
31
|
+
// Partitioning here also makes consecutive/leading/trailing dividers no-ops.
|
|
32
|
+
let groups = dictionaries.split {
|
|
33
|
+
($0["type"] as? String) == "separator"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if groups.count <= 1 {
|
|
37
|
+
return dictionaries.compactMap {
|
|
38
|
+
buildElement(
|
|
39
|
+
from: $0,
|
|
40
|
+
inheritedDestructive: inheritedDestructive,
|
|
41
|
+
inheritedDisabled: inheritedDisabled
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return groups.compactMap { group in
|
|
47
|
+
let children = group.compactMap {
|
|
48
|
+
buildElement(
|
|
49
|
+
from: $0,
|
|
50
|
+
inheritedDestructive: inheritedDestructive,
|
|
51
|
+
inheritedDisabled: inheritedDisabled
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
guard !children.isEmpty else {
|
|
55
|
+
return nil
|
|
56
|
+
}
|
|
57
|
+
return UIMenu(title: "", options: .displayInline, children: children)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private func buildElement(
|
|
62
|
+
from dictionary: [String: Any],
|
|
63
|
+
inheritedDestructive: Bool,
|
|
64
|
+
inheritedDisabled: Bool
|
|
65
|
+
) -> UIMenuElement? {
|
|
66
|
+
guard !(dictionary["hidden"] as? Bool ?? false) else {
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
switch dictionary["type"] as? String {
|
|
71
|
+
case "action":
|
|
72
|
+
return buildAction(
|
|
73
|
+
from: dictionary,
|
|
74
|
+
inheritedDestructive: inheritedDestructive,
|
|
75
|
+
inheritedDisabled: inheritedDisabled
|
|
76
|
+
)
|
|
77
|
+
case "submenu":
|
|
78
|
+
return buildSubmenu(
|
|
79
|
+
from: dictionary,
|
|
80
|
+
inheritedDestructive: inheritedDestructive,
|
|
81
|
+
inheritedDisabled: inheritedDisabled
|
|
82
|
+
)
|
|
83
|
+
case "section":
|
|
84
|
+
let children = buildChildren(
|
|
85
|
+
from: dictionaries(in: dictionary, key: "children"),
|
|
86
|
+
inheritedDestructive: inheritedDestructive,
|
|
87
|
+
inheritedDisabled: inheritedDisabled
|
|
88
|
+
)
|
|
89
|
+
guard !children.isEmpty else {
|
|
90
|
+
return nil
|
|
91
|
+
}
|
|
92
|
+
return UIMenu(
|
|
93
|
+
title: dictionary["title"] as? String ?? "",
|
|
94
|
+
options: .displayInline,
|
|
95
|
+
children: children
|
|
96
|
+
)
|
|
97
|
+
default:
|
|
98
|
+
return nil
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func buildAction(
|
|
103
|
+
from dictionary: [String: Any],
|
|
104
|
+
inheritedDestructive: Bool,
|
|
105
|
+
inheritedDisabled: Bool
|
|
106
|
+
) -> UIAction {
|
|
107
|
+
let identifier = dictionary["id"] as? String ?? ""
|
|
108
|
+
let title = dictionary["title"] as? String ?? ""
|
|
109
|
+
var attributes: UIMenuElement.Attributes = []
|
|
110
|
+
|
|
111
|
+
if inheritedDisabled || (dictionary["disabled"] as? Bool ?? false) {
|
|
112
|
+
attributes.insert(.disabled)
|
|
113
|
+
}
|
|
114
|
+
if inheritedDestructive || (dictionary["destructive"] as? Bool ?? false) {
|
|
115
|
+
attributes.insert(.destructive)
|
|
116
|
+
}
|
|
117
|
+
if dictionary["hidden"] as? Bool ?? false {
|
|
118
|
+
attributes.insert(.hidden)
|
|
119
|
+
}
|
|
120
|
+
if dictionary["keepOpen"] as? Bool ?? false {
|
|
121
|
+
attributes.insert(.keepsMenuPresented)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return UIAction(
|
|
125
|
+
title: title,
|
|
126
|
+
subtitle: nonEmptyString(dictionary["subtitle"]),
|
|
127
|
+
image: image(from: dictionary),
|
|
128
|
+
identifier: nil,
|
|
129
|
+
discoverabilityTitle: nil,
|
|
130
|
+
attributes: attributes,
|
|
131
|
+
state: state(from: dictionary)
|
|
132
|
+
) { [weak delegate] _ in
|
|
133
|
+
delegate?.onActionPress(id: identifier, title: title)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private func buildSubmenu(
|
|
138
|
+
from dictionary: [String: Any],
|
|
139
|
+
inheritedDestructive: Bool,
|
|
140
|
+
inheritedDisabled: Bool
|
|
141
|
+
) -> UIMenuElement? {
|
|
142
|
+
let isDestructive =
|
|
143
|
+
inheritedDestructive || (dictionary["destructive"] as? Bool ?? false)
|
|
144
|
+
let isDisabled =
|
|
145
|
+
inheritedDisabled || (dictionary["disabled"] as? Bool ?? false)
|
|
146
|
+
let isInline = dictionary["displayInline"] as? Bool ?? false
|
|
147
|
+
|
|
148
|
+
// UIKit doesn't expose disabled/hidden attributes on UIMenu. A disabled
|
|
149
|
+
// hierarchical submenu is therefore represented by a disabled UIAction;
|
|
150
|
+
// inline submenu children can be disabled individually.
|
|
151
|
+
if isDisabled && !isInline {
|
|
152
|
+
var attributes: UIMenuElement.Attributes = [.disabled]
|
|
153
|
+
if isDestructive {
|
|
154
|
+
attributes.insert(.destructive)
|
|
155
|
+
}
|
|
156
|
+
return UIAction(
|
|
157
|
+
title: dictionary["title"] as? String ?? "",
|
|
158
|
+
subtitle: nonEmptyString(dictionary["subtitle"]),
|
|
159
|
+
image: image(from: dictionary),
|
|
160
|
+
identifier: nil,
|
|
161
|
+
discoverabilityTitle: nil,
|
|
162
|
+
attributes: attributes,
|
|
163
|
+
state: .off
|
|
164
|
+
) { _ in }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let children = buildChildren(
|
|
168
|
+
from: dictionaries(in: dictionary, key: "children"),
|
|
169
|
+
inheritedDestructive: isDestructive,
|
|
170
|
+
inheritedDisabled: isDisabled
|
|
171
|
+
)
|
|
172
|
+
guard !children.isEmpty else {
|
|
173
|
+
return nil
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
var options: UIMenu.Options = []
|
|
177
|
+
if isInline {
|
|
178
|
+
options.insert(.displayInline)
|
|
179
|
+
}
|
|
180
|
+
if isDestructive {
|
|
181
|
+
options.insert(.destructive)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return UIMenu(
|
|
185
|
+
title: dictionary["title"] as? String ?? "",
|
|
186
|
+
subtitle: nonEmptyString(dictionary["subtitle"]),
|
|
187
|
+
image: image(from: dictionary),
|
|
188
|
+
identifier: nil,
|
|
189
|
+
options: options,
|
|
190
|
+
children: children
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private func image(from dictionary: [String: Any]) -> UIImage? {
|
|
195
|
+
let image: UIImage?
|
|
196
|
+
if let assetName = nonEmptyString(dictionary["icon"]),
|
|
197
|
+
let assetImage = UIImage(named: assetName) {
|
|
198
|
+
image = assetImage
|
|
199
|
+
} else if let systemName = nonEmptyString(dictionary["systemImage"]) {
|
|
200
|
+
image = UIImage(systemName: systemName)
|
|
201
|
+
} else {
|
|
202
|
+
image = nil
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
guard let image else {
|
|
206
|
+
return nil
|
|
207
|
+
}
|
|
208
|
+
guard let tintColor = dictionary["iconColor"].flatMap(RCTConvert.uiColor) else {
|
|
209
|
+
return image.withRenderingMode(.alwaysTemplate)
|
|
210
|
+
}
|
|
211
|
+
return image.withTintColor(tintColor, renderingMode: .alwaysOriginal)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private func state(from dictionary: [String: Any]) -> UIMenuElement.State {
|
|
215
|
+
switch dictionary["state"] as? String {
|
|
216
|
+
case "on":
|
|
217
|
+
return .on
|
|
218
|
+
case "mixed":
|
|
219
|
+
return .mixed
|
|
220
|
+
default:
|
|
221
|
+
return .off
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private func dictionaries(
|
|
226
|
+
in dictionary: [String: Any],
|
|
227
|
+
key: String
|
|
228
|
+
) -> [[String: Any]] {
|
|
229
|
+
dictionary[key] as? [[String: Any]] ?? []
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private func nonEmptyString(_ value: Any?) -> String? {
|
|
233
|
+
guard let value = value as? String, !value.isEmpty else {
|
|
234
|
+
return nil
|
|
235
|
+
}
|
|
236
|
+
return value
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -1,116 +1,82 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
1
|
import UIKit
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* UIKit
|
|
6
|
-
*
|
|
7
|
-
* `UIHostingController`, mirroring react-native-pager-view's `PagerViewProvider`.
|
|
8
|
-
*
|
|
9
|
-
* The presenting view controller is supplied by the Objective-C++ component
|
|
10
|
-
* view so this file does not depend on React.
|
|
4
|
+
* UIButton-backed UIKit menu trigger used as the Fabric component's content
|
|
5
|
+
* view. UIButton owns the UIMenu presentation and its long-press interaction.
|
|
11
6
|
*/
|
|
12
|
-
@objc public class TinyuiMenuProvider:
|
|
7
|
+
@objc public final class TinyuiMenuProvider: UIButton {
|
|
13
8
|
private weak var delegate: TinyuiMenuViewDelegate?
|
|
14
|
-
private var hostingController: UIHostingController<TinyuiMenuSwiftUIView>?
|
|
15
|
-
private var props = TinyuiMenuProps()
|
|
16
9
|
|
|
17
|
-
/// Presenting view controller, resolved by the component view from its
|
|
18
|
-
/// React view controller hierarchy.
|
|
19
|
-
@objc public weak var parentViewController: UIViewController? {
|
|
20
|
-
didSet {
|
|
21
|
-
if window != nil {
|
|
22
|
-
setupView()
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/// The React Native trigger view rendered as the menu's label.
|
|
28
10
|
@objc public var triggerView: UIView? {
|
|
29
11
|
didSet {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@objc public var title: String = "" {
|
|
42
|
-
didSet {
|
|
43
|
-
props.title = title
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@objc public var disabled: Bool = false {
|
|
48
|
-
didSet {
|
|
49
|
-
props.disabled = disabled
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@objc public var hasPrimaryAction: Bool = false {
|
|
54
|
-
didSet {
|
|
55
|
-
props.hasPrimaryAction = hasPrimaryAction
|
|
12
|
+
guard oldValue !== triggerView else {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
oldValue?.removeFromSuperview()
|
|
16
|
+
if let triggerView {
|
|
17
|
+
addSubview(triggerView)
|
|
18
|
+
}
|
|
19
|
+
setNeedsLayout()
|
|
56
20
|
}
|
|
57
21
|
}
|
|
58
22
|
|
|
59
23
|
@objc public convenience init(delegate: TinyuiMenuViewDelegate) {
|
|
60
|
-
self.init()
|
|
24
|
+
self.init(frame: .zero)
|
|
61
25
|
self.delegate = delegate
|
|
62
26
|
}
|
|
63
27
|
|
|
64
|
-
override public
|
|
65
|
-
super.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
28
|
+
override public init(frame: CGRect) {
|
|
29
|
+
super.init(frame: frame)
|
|
30
|
+
configure()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
required init?(coder: NSCoder) {
|
|
34
|
+
super.init(coder: coder)
|
|
35
|
+
configure()
|
|
69
36
|
}
|
|
70
37
|
|
|
71
38
|
override public func layoutSubviews() {
|
|
72
39
|
super.layoutSubviews()
|
|
73
|
-
|
|
74
|
-
|
|
40
|
+
triggerView?.frame = bounds
|
|
41
|
+
if let triggerView {
|
|
42
|
+
bringSubviewToFront(triggerView)
|
|
75
43
|
}
|
|
76
44
|
}
|
|
77
45
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
guard let parentViewController else {
|
|
85
|
-
return
|
|
46
|
+
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
47
|
+
guard !isHidden, alpha > 0.01, isEnabled, isUserInteractionEnabled,
|
|
48
|
+
self.point(inside: point, with: event) else {
|
|
49
|
+
return nil
|
|
86
50
|
}
|
|
51
|
+
return self
|
|
52
|
+
}
|
|
87
53
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
self.hostingController = hostingController
|
|
92
|
-
|
|
93
|
-
parentViewController.addChild(hostingController)
|
|
94
|
-
hostingController.view.backgroundColor = .clear
|
|
95
|
-
addSubview(hostingController.view)
|
|
96
|
-
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
97
|
-
hostingController.view.pinEdges(to: self)
|
|
98
|
-
hostingController.didMove(toParent: parentViewController)
|
|
54
|
+
private func configure() {
|
|
55
|
+
backgroundColor = .clear
|
|
56
|
+
showsMenuAsPrimaryAction = true
|
|
99
57
|
}
|
|
100
58
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
59
|
+
/** Applies one complete Fabric props snapshot before rebuilding the menu. */
|
|
60
|
+
@objc public func update(
|
|
61
|
+
menuConfig: [String: Any],
|
|
62
|
+
title: String,
|
|
63
|
+
disabled: Bool
|
|
64
|
+
) {
|
|
65
|
+
isEnabled = !disabled
|
|
66
|
+
isUserInteractionEnabled = !disabled
|
|
67
|
+
|
|
68
|
+
// UIButton enables its context menu interaction whenever a menu is assigned,
|
|
69
|
+
// independently of the control's enabled state. Remove it while disabled
|
|
70
|
+
// to block both primary-action and long-press presentation.
|
|
71
|
+
if disabled {
|
|
72
|
+
contextMenuInteraction?.dismissMenu()
|
|
73
|
+
menu = nil
|
|
108
74
|
return
|
|
109
75
|
}
|
|
110
76
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
77
|
+
menu = TinyuiMenuBuilder(delegate: delegate).buildMenu(
|
|
78
|
+
title: title,
|
|
79
|
+
config: menuConfig
|
|
80
|
+
)
|
|
115
81
|
}
|
|
116
82
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#import "TinyuiMenuView.h"
|
|
4
4
|
|
|
5
5
|
#import <React/RCTConversions.h>
|
|
6
|
-
#import <
|
|
6
|
+
#import <react/utils/FollyConvert.h>
|
|
7
7
|
|
|
8
8
|
#import <react/renderer/components/TinyuiSpec/ComponentDescriptors.h>
|
|
9
9
|
#import <react/renderer/components/TinyuiSpec/EventEmitters.h>
|
|
@@ -43,24 +43,15 @@ using namespace facebook::react;
|
|
|
43
43
|
return self;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
- (void)didMoveToWindow
|
|
47
|
-
{
|
|
48
|
-
[super didMoveToWindow];
|
|
49
|
-
if (self.window != nil) {
|
|
50
|
-
_menuProvider.parentViewController = [self reactViewController];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
46
|
#pragma mark - React lifecycle
|
|
55
47
|
|
|
56
48
|
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
57
49
|
index:(NSInteger)index
|
|
58
50
|
{
|
|
59
|
-
// TinyuiMenuView renders its trigger as a child;
|
|
60
|
-
// provider
|
|
51
|
+
// TinyuiMenuView renders its trigger as a child; the UIButton-backed
|
|
52
|
+
// provider uses it as the menu's visual content.
|
|
61
53
|
_triggerView = childComponentView;
|
|
62
54
|
_menuProvider.triggerView = childComponentView;
|
|
63
|
-
[_menuProvider addSubview:childComponentView];
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
@@ -81,17 +72,12 @@ using namespace facebook::react;
|
|
|
81
72
|
const auto &oldMenuProps = *std::static_pointer_cast<TinyuiMenuViewProps const>(_props);
|
|
82
73
|
const auto &newMenuProps = *std::static_pointer_cast<TinyuiMenuViewProps const>(props);
|
|
83
74
|
|
|
84
|
-
if (oldMenuProps.menuConfig != newMenuProps.menuConfig
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (oldMenuProps.hasPrimaryAction != newMenuProps.hasPrimaryAction) {
|
|
91
|
-
_menuProvider.hasPrimaryAction = newMenuProps.hasPrimaryAction;
|
|
92
|
-
}
|
|
93
|
-
if (oldMenuProps.disabled != newMenuProps.disabled) {
|
|
94
|
-
_menuProvider.disabled = newMenuProps.disabled;
|
|
75
|
+
if (oldMenuProps.menuConfig != newMenuProps.menuConfig ||
|
|
76
|
+
oldMenuProps.title != newMenuProps.title ||
|
|
77
|
+
oldMenuProps.disabled != newMenuProps.disabled) {
|
|
78
|
+
[_menuProvider updateWithMenuConfig:convertFollyDynamicToId(newMenuProps.menuConfig)
|
|
79
|
+
title:RCTNSStringFromString(newMenuProps.title)
|
|
80
|
+
disabled:newMenuProps.disabled];
|
|
95
81
|
}
|
|
96
82
|
|
|
97
83
|
[super updateProps:props oldProps:oldProps];
|
|
@@ -99,23 +85,15 @@ using namespace facebook::react;
|
|
|
99
85
|
|
|
100
86
|
#pragma mark - TinyuiMenuViewDelegate
|
|
101
87
|
|
|
102
|
-
- (void)
|
|
103
|
-
{
|
|
104
|
-
if (_eventEmitter == nullptr) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
std::static_pointer_cast<const TinyuiMenuViewEventEmitter>(_eventEmitter)
|
|
108
|
-
->onItemPress(TinyuiMenuViewEventEmitter::OnItemPress{
|
|
109
|
-
.id = std::string(identifier.UTF8String ?: "")});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
- (void)onPrimaryAction
|
|
88
|
+
- (void)onActionPressWithId:(NSString *)identifier title:(NSString *)title
|
|
113
89
|
{
|
|
114
90
|
if (_eventEmitter == nullptr) {
|
|
115
91
|
return;
|
|
116
92
|
}
|
|
117
93
|
std::static_pointer_cast<const TinyuiMenuViewEventEmitter>(_eventEmitter)
|
|
118
|
-
->
|
|
94
|
+
->onActionPress(TinyuiMenuViewEventEmitter::OnActionPress{
|
|
95
|
+
.id = std::string(identifier.UTF8String ?: ""),
|
|
96
|
+
.title = std::string(title.UTF8String ?: "")});
|
|
119
97
|
}
|
|
120
98
|
|
|
121
99
|
@end
|