react-native-platform-components 0.8.10 → 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 +68 -891
- package/android/src/main/java/com/platformcomponents/PCDatePickerView.kt +17 -3
- package/android/src/main/java/com/platformcomponents/PCImageLoader.kt +88 -0
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +292 -31
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +74 -8
- package/android/src/main/java/com/platformcomponents/PCSelectionMenuView.kt +2 -1
- package/android/src/main/java/com/platformcomponents/PCThemeSupport.kt +94 -0
- package/android/src/main/res/values/styles.xml +40 -0
- package/ios/PCImageLoader.swift +58 -0
- package/ios/PCSegmentedControl.mm +85 -25
- package/ios/PCSegmentedControl.swift +231 -308
- package/lib/commonjs/DatePickerNativeComponent.ts +4 -2
- package/lib/commonjs/SegmentedControl.js +138 -6
- package/lib/commonjs/SegmentedControl.js.map +1 -1
- package/lib/commonjs/SegmentedControlNativeComponent.ts +62 -8
- package/lib/commonjs/SelectionMenuNativeComponent.ts +4 -2
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/module/DatePickerNativeComponent.ts +4 -2
- package/lib/module/SegmentedControl.js +138 -7
- package/lib/module/SegmentedControl.js.map +1 -1
- package/lib/module/SegmentedControlNativeComponent.ts +62 -8
- package/lib/module/SelectionMenuNativeComponent.ts +4 -2
- package/lib/module/index.web.js.map +1 -1
- package/lib/typescript/commonjs/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/commonjs/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts +4 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts +2 -2
- package/lib/typescript/commonjs/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts +111 -6
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +46 -7
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts +2 -2
- package/lib/typescript/commonjs/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.web.d.ts +36 -1
- package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
- package/lib/typescript/module/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/module/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/module/docs/sidebars.d.ts +4 -0
- package/lib/typescript/module/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/module/src/DatePicker.d.ts +1 -1
- package/lib/typescript/module/src/DatePicker.d.ts.map +1 -1
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts +2 -2
- package/lib/typescript/module/src/DatePickerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControl.d.ts +111 -6
- package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +46 -7
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenu.d.ts +1 -1
- package/lib/typescript/module/src/SelectionMenu.d.ts.map +1 -1
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts +2 -2
- package/lib/typescript/module/src/SelectionMenuNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +6 -6
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/index.web.d.ts +36 -1
- package/lib/typescript/module/src/index.web.d.ts.map +1 -1
- package/package.json +62 -37
- package/plugin/build/index.cjs +58 -17
- package/plugin/build/index.d.ts +32 -15
- package/plugin/build/index.d.ts.map +1 -1
- package/plugin/tsconfig.json +9 -4
- package/src/DatePickerNativeComponent.ts +4 -2
- package/src/SegmentedControl.tsx +238 -11
- package/src/SegmentedControlNativeComponent.ts +62 -8
- package/src/SelectionMenuNativeComponent.ts +4 -2
- package/src/index.web.tsx +34 -1
|
@@ -6,16 +6,63 @@ struct PCSegmentedControlSegment {
|
|
|
6
6
|
let label: String
|
|
7
7
|
let value: String
|
|
8
8
|
let disabled: Bool
|
|
9
|
-
|
|
9
|
+
/// "", "sfSymbol", "drawable" (ignored on iOS) or "image"
|
|
10
|
+
let iconType: String
|
|
11
|
+
let iconName: String
|
|
12
|
+
let iconUri: String
|
|
13
|
+
let iconScale: CGFloat
|
|
14
|
+
let iconTinted: Bool
|
|
15
|
+
let badge: String
|
|
16
|
+
let accessibilityLabel: String
|
|
17
|
+
|
|
18
|
+
var hasIcon: Bool { iconType == "sfSymbol" || iconType == "image" }
|
|
19
|
+
|
|
20
|
+
/// What VoiceOver announces for the segment, badge included.
|
|
21
|
+
var spokenLabel: String {
|
|
22
|
+
let base = accessibilityLabel.isEmpty ? label : accessibilityLabel
|
|
23
|
+
return badge.isEmpty ? base : "\(base), \(badge)"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// A capsule badge drawn over a segment's top-right corner.
|
|
28
|
+
private final class PCBadgeLabel: UILabel {
|
|
29
|
+
static let height: CGFloat = 16
|
|
30
|
+
static let horizontalPadding: CGFloat = 5
|
|
31
|
+
|
|
32
|
+
override init(frame: CGRect) {
|
|
33
|
+
super.init(frame: frame)
|
|
34
|
+
font = .systemFont(ofSize: 11, weight: .semibold)
|
|
35
|
+
textAlignment = .center
|
|
36
|
+
layer.cornerRadius = Self.height / 2
|
|
37
|
+
clipsToBounds = true
|
|
38
|
+
isUserInteractionEnabled = false
|
|
39
|
+
// The segment announces the badge; don't expose it twice
|
|
40
|
+
isAccessibilityElement = false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
44
|
+
|
|
45
|
+
override var intrinsicContentSize: CGSize {
|
|
46
|
+
let base = super.intrinsicContentSize
|
|
47
|
+
return CGSize(
|
|
48
|
+
width: max(Self.height, base.width + Self.horizontalPadding * 2),
|
|
49
|
+
height: Self.height
|
|
50
|
+
)
|
|
51
|
+
}
|
|
10
52
|
}
|
|
11
53
|
|
|
12
54
|
@objcMembers
|
|
13
55
|
public final class PCSegmentedControlView: UIControl {
|
|
14
56
|
// MARK: - Props (set from ObjC++)
|
|
15
57
|
|
|
16
|
-
/// ObjC++ sets this as an array of dictionaries
|
|
58
|
+
/// ObjC++ sets this as an array of dictionaries; see `rebuildControl` for keys.
|
|
17
59
|
public var segments: [Any] = [] { didSet { rebuildControl() } }
|
|
18
60
|
|
|
61
|
+
/// "auto" | "labeled" | "unlabeled"
|
|
62
|
+
public var labelVisibility: String = "auto" {
|
|
63
|
+
didSet { if oldValue != labelVisibility { rebuildControl() } }
|
|
64
|
+
}
|
|
65
|
+
|
|
19
66
|
/// Controlled selection by value. "" = no selection.
|
|
20
67
|
public var selectedValue: String = "" { didSet { updateSelection() } }
|
|
21
68
|
|
|
@@ -30,18 +77,50 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
30
77
|
didSet { control.apportionsSegmentWidthsByContent = apportionsSegmentWidthsByContent }
|
|
31
78
|
}
|
|
32
79
|
|
|
33
|
-
///
|
|
34
|
-
public var
|
|
80
|
+
/// Background of the selected segment
|
|
81
|
+
public var selectedSegmentColor: UIColor? {
|
|
82
|
+
didSet { control.selectedSegmentTintColor = selectedSegmentColor }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Text / icon color of the selected segment
|
|
86
|
+
public var activeTintColor: UIColor? { didSet { updateTitleAttributes() } }
|
|
87
|
+
|
|
88
|
+
/// Text / icon color of unselected segments
|
|
89
|
+
public var inactiveTintColor: UIColor? { didSet { updateTitleAttributes() } }
|
|
90
|
+
|
|
91
|
+
/// Label font; nil keeps the system font
|
|
92
|
+
public var labelFont: UIFont? {
|
|
93
|
+
didSet {
|
|
94
|
+
updateTitleAttributes()
|
|
95
|
+
invalidateIntrinsicContentSize()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Badge background; nil = system red
|
|
100
|
+
public var badgeBackgroundColor: UIColor? { didSet { updateBadgeColors() } }
|
|
101
|
+
|
|
102
|
+
/// Badge text color; nil = white
|
|
103
|
+
public var badgeTextColor: UIColor? { didSet { updateBadgeColors() } }
|
|
35
104
|
|
|
36
105
|
// MARK: - Events back to ObjC++
|
|
37
106
|
|
|
38
107
|
public var onSelect: ((Int, String) -> Void)? // (index, value)
|
|
39
108
|
|
|
109
|
+
/// Called when content changed after layout (an icon finished loading),
|
|
110
|
+
/// so the Fabric measurement can be refreshed.
|
|
111
|
+
public var onNeedsRemeasure: (() -> Void)?
|
|
112
|
+
|
|
40
113
|
// MARK: - Internal
|
|
41
114
|
|
|
42
115
|
private let control = UISegmentedControl()
|
|
43
116
|
private var parsedSegments: [PCSegmentedControlSegment] = []
|
|
44
117
|
|
|
118
|
+
/// Bumped on every rebuild so late image loads can't touch a stale control.
|
|
119
|
+
private var rebuildGeneration = 0
|
|
120
|
+
|
|
121
|
+
/// Badge label per segment index (only segments with a badge have one).
|
|
122
|
+
private var badgeLabels: [Int: PCBadgeLabel] = [:]
|
|
123
|
+
|
|
45
124
|
// MARK: - Init
|
|
46
125
|
|
|
47
126
|
public override init(frame: CGRect) {
|
|
@@ -55,6 +134,9 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
55
134
|
}
|
|
56
135
|
|
|
57
136
|
private func setup() {
|
|
137
|
+
// Badges overhang the top edge slightly
|
|
138
|
+
clipsToBounds = false
|
|
139
|
+
|
|
58
140
|
control.translatesAutoresizingMaskIntoConstraints = false
|
|
59
141
|
addSubview(control)
|
|
60
142
|
|
|
@@ -82,25 +164,28 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
82
164
|
private func rebuildControl() {
|
|
83
165
|
parsedSegments = segments.compactMap { any in
|
|
84
166
|
guard let dict = any as? [String: Any] else { return nil }
|
|
85
|
-
let
|
|
86
|
-
let value = (dict["value"] as? String) ?? ""
|
|
87
|
-
let disabled = (dict["disabled"] as? String) == "disabled"
|
|
88
|
-
let icon = (dict["icon"] as? String) ?? ""
|
|
167
|
+
let scale = (dict["iconScale"] as? NSNumber).map { CGFloat(truncating: $0) } ?? 1
|
|
89
168
|
return PCSegmentedControlSegment(
|
|
90
|
-
label: label,
|
|
91
|
-
value: value,
|
|
92
|
-
disabled: disabled,
|
|
93
|
-
|
|
169
|
+
label: (dict["label"] as? String) ?? "",
|
|
170
|
+
value: (dict["value"] as? String) ?? "",
|
|
171
|
+
disabled: (dict["disabled"] as? String) == "disabled",
|
|
172
|
+
iconType: (dict["iconType"] as? String) ?? "",
|
|
173
|
+
iconName: (dict["iconName"] as? String) ?? "",
|
|
174
|
+
iconUri: (dict["iconUri"] as? String) ?? "",
|
|
175
|
+
iconScale: scale > 0 ? scale : 1,
|
|
176
|
+
iconTinted: (dict["iconTinted"] as? String) != "false",
|
|
177
|
+
badge: (dict["badge"] as? String) ?? "",
|
|
178
|
+
accessibilityLabel: (dict["accessibilityLabel"] as? String) ?? ""
|
|
94
179
|
)
|
|
95
180
|
}
|
|
96
181
|
|
|
182
|
+
rebuildGeneration += 1
|
|
183
|
+
let generation = rebuildGeneration
|
|
97
184
|
control.removeAllSegments()
|
|
98
185
|
|
|
99
186
|
for (index, segment) in parsedSegments.enumerated() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
let sfImage = UIImage(systemName: segment.icon) {
|
|
103
|
-
control.insertSegment(with: sfImage, at: index, animated: false)
|
|
187
|
+
if let image = iconImage(for: segment, generation: generation) {
|
|
188
|
+
control.insertSegment(with: image, at: index, animated: false)
|
|
104
189
|
} else {
|
|
105
190
|
control.insertSegment(withTitle: segment.label, at: index, animated: false)
|
|
106
191
|
}
|
|
@@ -109,10 +194,122 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
109
194
|
control.setEnabled(!segment.disabled, forSegmentAt: index)
|
|
110
195
|
}
|
|
111
196
|
|
|
197
|
+
rebuildBadges()
|
|
112
198
|
updateSelection()
|
|
113
199
|
invalidateIntrinsicContentSize()
|
|
114
200
|
}
|
|
115
201
|
|
|
202
|
+
// MARK: - Badges
|
|
203
|
+
|
|
204
|
+
private func rebuildBadges() {
|
|
205
|
+
badgeLabels.values.forEach { $0.removeFromSuperview() }
|
|
206
|
+
badgeLabels = [:]
|
|
207
|
+
|
|
208
|
+
for (index, segment) in parsedSegments.enumerated() where !segment.badge.isEmpty {
|
|
209
|
+
let label = PCBadgeLabel()
|
|
210
|
+
label.text = segment.badge
|
|
211
|
+
addSubview(label)
|
|
212
|
+
badgeLabels[index] = label
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
updateBadgeColors()
|
|
216
|
+
setNeedsLayout()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private func updateBadgeColors() {
|
|
220
|
+
for label in badgeLabels.values {
|
|
221
|
+
label.backgroundColor = badgeBackgroundColor ?? .systemRed
|
|
222
|
+
label.textColor = badgeTextColor ?? .white
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// Frames of the individual segments in the control's coordinate space.
|
|
227
|
+
/// UISegmentedControl lays each segment out as its own subview, so their
|
|
228
|
+
/// frames honor proportional widths; if the hierarchy ever looks different
|
|
229
|
+
/// fall back to equal division.
|
|
230
|
+
private func segmentFrames() -> [CGRect] {
|
|
231
|
+
let count = control.numberOfSegments
|
|
232
|
+
guard count > 0 else { return [] }
|
|
233
|
+
|
|
234
|
+
let segmentViews = control.subviews
|
|
235
|
+
.filter { NSStringFromClass(type(of: $0)) == "UISegment" }
|
|
236
|
+
.sorted { $0.frame.minX < $1.frame.minX }
|
|
237
|
+
if segmentViews.count == count {
|
|
238
|
+
return segmentViews.map { $0.frame }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let width = control.bounds.width / CGFloat(count)
|
|
242
|
+
return (0..<count).map {
|
|
243
|
+
CGRect(x: CGFloat($0) * width, y: 0, width: width, height: control.bounds.height)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private func layoutBadges() {
|
|
248
|
+
guard !badgeLabels.isEmpty else { return }
|
|
249
|
+
control.layoutIfNeeded()
|
|
250
|
+
let frames = segmentFrames()
|
|
251
|
+
|
|
252
|
+
for (index, label) in badgeLabels {
|
|
253
|
+
guard index < frames.count else {
|
|
254
|
+
label.isHidden = true
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
label.isHidden = false
|
|
258
|
+
let segment = convert(frames[index], from: control)
|
|
259
|
+
let size = label.intrinsicContentSize
|
|
260
|
+
label.frame = CGRect(
|
|
261
|
+
x: segment.maxX - size.width - 2,
|
|
262
|
+
y: segment.minY - size.height / 4,
|
|
263
|
+
width: size.width,
|
|
264
|
+
height: size.height
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
public override func layoutSubviews() {
|
|
270
|
+
super.layoutSubviews()
|
|
271
|
+
layoutBadges()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// Resolves the image shown for a segment, or nil when the segment shows
|
|
275
|
+
/// its title. UISegmentedControl shows either a title or an image, so
|
|
276
|
+
/// "labeled" always wins and "auto" / "unlabeled" prefer the icon.
|
|
277
|
+
///
|
|
278
|
+
/// Images that are still loading return nil; the segment starts with its
|
|
279
|
+
/// title and switches to the image once it arrives.
|
|
280
|
+
private func iconImage(for segment: PCSegmentedControlSegment, generation: Int) -> UIImage? {
|
|
281
|
+
guard segment.hasIcon, labelVisibility != "labeled" else { return nil }
|
|
282
|
+
|
|
283
|
+
switch segment.iconType {
|
|
284
|
+
case "sfSymbol":
|
|
285
|
+
return UIImage(systemName: segment.iconName).map { decorate($0, for: segment) }
|
|
286
|
+
|
|
287
|
+
case "image":
|
|
288
|
+
let cached = PCImageLoader.shared.image(uri: segment.iconUri, scale: segment.iconScale) { [weak self] image in
|
|
289
|
+
guard let self, let image,
|
|
290
|
+
self.rebuildGeneration == generation,
|
|
291
|
+
let index = self.parsedSegments.firstIndex(where: { $0.value == segment.value }),
|
|
292
|
+
index < self.control.numberOfSegments else { return }
|
|
293
|
+
self.control.setImage(self.decorate(image, for: segment), forSegmentAt: index)
|
|
294
|
+
self.invalidateIntrinsicContentSize()
|
|
295
|
+
self.onNeedsRemeasure?()
|
|
296
|
+
}
|
|
297
|
+
return cached.map { decorate($0, for: segment) }
|
|
298
|
+
|
|
299
|
+
default:
|
|
300
|
+
return nil
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/// Applies the rendering mode and the VoiceOver label. Rendering-mode
|
|
305
|
+
/// variants are fresh instances, so the label never leaks onto shared
|
|
306
|
+
/// system images.
|
|
307
|
+
private func decorate(_ image: UIImage, for segment: PCSegmentedControlSegment) -> UIImage {
|
|
308
|
+
let rendered = image.withRenderingMode(segment.iconTinted ? .alwaysTemplate : .alwaysOriginal)
|
|
309
|
+
rendered.accessibilityLabel = segment.spokenLabel
|
|
310
|
+
return rendered
|
|
311
|
+
}
|
|
312
|
+
|
|
116
313
|
private func updateSelection() {
|
|
117
314
|
if selectedValue.isEmpty {
|
|
118
315
|
control.selectedSegmentIndex = UISegmentedControl.noSegment
|
|
@@ -129,12 +326,25 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
129
326
|
alpha = enabled ? 1.0 : 0.5
|
|
130
327
|
}
|
|
131
328
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
329
|
+
/// UISegmentedControl draws template images with the same foreground
|
|
330
|
+
/// color as the title for that state, so these attributes style icons too.
|
|
331
|
+
private func updateTitleAttributes() {
|
|
332
|
+
var normal: [NSAttributedString.Key: Any] = [:]
|
|
333
|
+
var selected: [NSAttributedString.Key: Any] = [:]
|
|
334
|
+
|
|
335
|
+
if let labelFont {
|
|
336
|
+
normal[.font] = labelFont
|
|
337
|
+
selected[.font] = labelFont
|
|
137
338
|
}
|
|
339
|
+
if let inactiveTintColor {
|
|
340
|
+
normal[.foregroundColor] = inactiveTintColor
|
|
341
|
+
}
|
|
342
|
+
if let activeTintColor {
|
|
343
|
+
selected[.foregroundColor] = activeTintColor
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
control.setTitleTextAttributes(normal.isEmpty ? nil : normal, for: .normal)
|
|
347
|
+
control.setTitleTextAttributes(selected.isEmpty ? nil : selected, for: .selected)
|
|
138
348
|
}
|
|
139
349
|
|
|
140
350
|
// MARK: - Sizing
|
|
@@ -167,290 +377,3 @@ public final class PCSegmentedControlView: UIControl {
|
|
|
167
377
|
)
|
|
168
378
|
}
|
|
169
379
|
}
|
|
170
|
-
|
|
171
|
-
// MARK: - UIColor parsing extension
|
|
172
|
-
|
|
173
|
-
private extension UIColor {
|
|
174
|
-
/// Parses color strings in React Native compatible formats:
|
|
175
|
-
/// - Hex: #RGB, #RRGGBB, #RRGGBBAA
|
|
176
|
-
/// - RGB: rgb(r, g, b) where r, g, b are 0-255
|
|
177
|
-
/// - RGBA: rgba(r, g, b, a) where r, g, b are 0-255 and a is 0-1
|
|
178
|
-
/// - HSL: hsl(h, s%, l%) where h is 0-360, s and l are 0-100
|
|
179
|
-
/// - HSLA: hsla(h, s%, l%, a) where h is 0-360, s and l are 0-100, a is 0-1
|
|
180
|
-
/// - Named colors: red, blue, transparent, etc. (CSS named colors)
|
|
181
|
-
convenience init?(hex: String) {
|
|
182
|
-
let trimmed = hex.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
183
|
-
|
|
184
|
-
// Try named colors first
|
|
185
|
-
if let namedColor = UIColor.namedColors[trimmed] {
|
|
186
|
-
self.init(red: namedColor.r, green: namedColor.g, blue: namedColor.b, alpha: namedColor.a)
|
|
187
|
-
return
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Try rgba format: rgba(r, g, b, a)
|
|
191
|
-
if trimmed.hasPrefix("rgba(") && trimmed.hasSuffix(")") {
|
|
192
|
-
let inner = String(trimmed.dropFirst(5).dropLast(1))
|
|
193
|
-
let components = inner.split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
|
194
|
-
guard components.count == 4,
|
|
195
|
-
let r = Double(components[0]),
|
|
196
|
-
let g = Double(components[1]),
|
|
197
|
-
let b = Double(components[2]),
|
|
198
|
-
let a = Double(components[3]) else { return nil }
|
|
199
|
-
self.init(
|
|
200
|
-
red: CGFloat(r) / 255.0,
|
|
201
|
-
green: CGFloat(g) / 255.0,
|
|
202
|
-
blue: CGFloat(b) / 255.0,
|
|
203
|
-
alpha: CGFloat(a)
|
|
204
|
-
)
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Try rgb format: rgb(r, g, b)
|
|
209
|
-
if trimmed.hasPrefix("rgb(") && trimmed.hasSuffix(")") {
|
|
210
|
-
let inner = String(trimmed.dropFirst(4).dropLast(1))
|
|
211
|
-
let components = inner.split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
|
212
|
-
guard components.count == 3,
|
|
213
|
-
let r = Double(components[0]),
|
|
214
|
-
let g = Double(components[1]),
|
|
215
|
-
let b = Double(components[2]) else { return nil }
|
|
216
|
-
self.init(
|
|
217
|
-
red: CGFloat(r) / 255.0,
|
|
218
|
-
green: CGFloat(g) / 255.0,
|
|
219
|
-
blue: CGFloat(b) / 255.0,
|
|
220
|
-
alpha: 1.0
|
|
221
|
-
)
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Try hsla format: hsla(h, s%, l%, a)
|
|
226
|
-
if trimmed.hasPrefix("hsla(") && trimmed.hasSuffix(")") {
|
|
227
|
-
let inner = String(trimmed.dropFirst(5).dropLast(1))
|
|
228
|
-
let components = inner.split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces).replacingOccurrences(of: "%", with: "") }
|
|
229
|
-
guard components.count == 4,
|
|
230
|
-
let h = Double(components[0]),
|
|
231
|
-
let s = Double(components[1]),
|
|
232
|
-
let l = Double(components[2]),
|
|
233
|
-
let a = Double(components[3]) else { return nil }
|
|
234
|
-
let rgb = UIColor.hslToRgb(h: h / 360.0, s: s / 100.0, l: l / 100.0)
|
|
235
|
-
self.init(red: rgb.r, green: rgb.g, blue: rgb.b, alpha: CGFloat(a))
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Try hsl format: hsl(h, s%, l%)
|
|
240
|
-
if trimmed.hasPrefix("hsl(") && trimmed.hasSuffix(")") {
|
|
241
|
-
let inner = String(trimmed.dropFirst(4).dropLast(1))
|
|
242
|
-
let components = inner.split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces).replacingOccurrences(of: "%", with: "") }
|
|
243
|
-
guard components.count == 3,
|
|
244
|
-
let h = Double(components[0]),
|
|
245
|
-
let s = Double(components[1]),
|
|
246
|
-
let l = Double(components[2]) else { return nil }
|
|
247
|
-
let rgb = UIColor.hslToRgb(h: h / 360.0, s: s / 100.0, l: l / 100.0)
|
|
248
|
-
self.init(red: rgb.r, green: rgb.g, blue: rgb.b, alpha: 1.0)
|
|
249
|
-
return
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Fall back to hex parsing
|
|
253
|
-
var hexSanitized = trimmed.replacingOccurrences(of: "#", with: "")
|
|
254
|
-
|
|
255
|
-
var rgb: UInt64 = 0
|
|
256
|
-
guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
|
|
257
|
-
|
|
258
|
-
let length = hexSanitized.count
|
|
259
|
-
if length == 6 {
|
|
260
|
-
self.init(
|
|
261
|
-
red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
|
|
262
|
-
green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
|
|
263
|
-
blue: CGFloat(rgb & 0x0000FF) / 255.0,
|
|
264
|
-
alpha: 1.0
|
|
265
|
-
)
|
|
266
|
-
} else if length == 8 {
|
|
267
|
-
self.init(
|
|
268
|
-
red: CGFloat((rgb & 0xFF000000) >> 24) / 255.0,
|
|
269
|
-
green: CGFloat((rgb & 0x00FF0000) >> 16) / 255.0,
|
|
270
|
-
blue: CGFloat((rgb & 0x0000FF00) >> 8) / 255.0,
|
|
271
|
-
alpha: CGFloat(rgb & 0x000000FF) / 255.0
|
|
272
|
-
)
|
|
273
|
-
} else {
|
|
274
|
-
return nil
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// HSL to RGB conversion
|
|
279
|
-
private static func hslToRgb(h: Double, s: Double, l: Double) -> (r: CGFloat, g: CGFloat, b: CGFloat) {
|
|
280
|
-
if s == 0 {
|
|
281
|
-
return (CGFloat(l), CGFloat(l), CGFloat(l))
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
|
285
|
-
let p = 2 * l - q
|
|
286
|
-
|
|
287
|
-
func hueToRgb(_ p: Double, _ q: Double, _ t: Double) -> Double {
|
|
288
|
-
var t = t
|
|
289
|
-
if t < 0 { t += 1 }
|
|
290
|
-
if t > 1 { t -= 1 }
|
|
291
|
-
if t < 1/6 { return p + (q - p) * 6 * t }
|
|
292
|
-
if t < 1/2 { return q }
|
|
293
|
-
if t < 2/3 { return p + (q - p) * (2/3 - t) * 6 }
|
|
294
|
-
return p
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return (
|
|
298
|
-
CGFloat(hueToRgb(p, q, h + 1/3)),
|
|
299
|
-
CGFloat(hueToRgb(p, q, h)),
|
|
300
|
-
CGFloat(hueToRgb(p, q, h - 1/3))
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// CSS named colors (React Native compatible)
|
|
305
|
-
private static let namedColors: [String: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat)] = [
|
|
306
|
-
"transparent": (0, 0, 0, 0),
|
|
307
|
-
"aliceblue": (240/255, 248/255, 255/255, 1),
|
|
308
|
-
"antiquewhite": (250/255, 235/255, 215/255, 1),
|
|
309
|
-
"aqua": (0, 255/255, 255/255, 1),
|
|
310
|
-
"aquamarine": (127/255, 255/255, 212/255, 1),
|
|
311
|
-
"azure": (240/255, 255/255, 255/255, 1),
|
|
312
|
-
"beige": (245/255, 245/255, 220/255, 1),
|
|
313
|
-
"bisque": (255/255, 228/255, 196/255, 1),
|
|
314
|
-
"black": (0, 0, 0, 1),
|
|
315
|
-
"blanchedalmond": (255/255, 235/255, 205/255, 1),
|
|
316
|
-
"blue": (0, 0, 255/255, 1),
|
|
317
|
-
"blueviolet": (138/255, 43/255, 226/255, 1),
|
|
318
|
-
"brown": (165/255, 42/255, 42/255, 1),
|
|
319
|
-
"burlywood": (222/255, 184/255, 135/255, 1),
|
|
320
|
-
"cadetblue": (95/255, 158/255, 160/255, 1),
|
|
321
|
-
"chartreuse": (127/255, 255/255, 0, 1),
|
|
322
|
-
"chocolate": (210/255, 105/255, 30/255, 1),
|
|
323
|
-
"coral": (255/255, 127/255, 80/255, 1),
|
|
324
|
-
"cornflowerblue": (100/255, 149/255, 237/255, 1),
|
|
325
|
-
"cornsilk": (255/255, 248/255, 220/255, 1),
|
|
326
|
-
"crimson": (220/255, 20/255, 60/255, 1),
|
|
327
|
-
"cyan": (0, 255/255, 255/255, 1),
|
|
328
|
-
"darkblue": (0, 0, 139/255, 1),
|
|
329
|
-
"darkcyan": (0, 139/255, 139/255, 1),
|
|
330
|
-
"darkgoldenrod": (184/255, 134/255, 11/255, 1),
|
|
331
|
-
"darkgray": (169/255, 169/255, 169/255, 1),
|
|
332
|
-
"darkgreen": (0, 100/255, 0, 1),
|
|
333
|
-
"darkgrey": (169/255, 169/255, 169/255, 1),
|
|
334
|
-
"darkkhaki": (189/255, 183/255, 107/255, 1),
|
|
335
|
-
"darkmagenta": (139/255, 0, 139/255, 1),
|
|
336
|
-
"darkolivegreen": (85/255, 107/255, 47/255, 1),
|
|
337
|
-
"darkorange": (255/255, 140/255, 0, 1),
|
|
338
|
-
"darkorchid": (153/255, 50/255, 204/255, 1),
|
|
339
|
-
"darkred": (139/255, 0, 0, 1),
|
|
340
|
-
"darksalmon": (233/255, 150/255, 122/255, 1),
|
|
341
|
-
"darkseagreen": (143/255, 188/255, 143/255, 1),
|
|
342
|
-
"darkslateblue": (72/255, 61/255, 139/255, 1),
|
|
343
|
-
"darkslategray": (47/255, 79/255, 79/255, 1),
|
|
344
|
-
"darkslategrey": (47/255, 79/255, 79/255, 1),
|
|
345
|
-
"darkturquoise": (0, 206/255, 209/255, 1),
|
|
346
|
-
"darkviolet": (148/255, 0, 211/255, 1),
|
|
347
|
-
"deeppink": (255/255, 20/255, 147/255, 1),
|
|
348
|
-
"deepskyblue": (0, 191/255, 255/255, 1),
|
|
349
|
-
"dimgray": (105/255, 105/255, 105/255, 1),
|
|
350
|
-
"dimgrey": (105/255, 105/255, 105/255, 1),
|
|
351
|
-
"dodgerblue": (30/255, 144/255, 255/255, 1),
|
|
352
|
-
"firebrick": (178/255, 34/255, 34/255, 1),
|
|
353
|
-
"floralwhite": (255/255, 250/255, 240/255, 1),
|
|
354
|
-
"forestgreen": (34/255, 139/255, 34/255, 1),
|
|
355
|
-
"fuchsia": (255/255, 0, 255/255, 1),
|
|
356
|
-
"gainsboro": (220/255, 220/255, 220/255, 1),
|
|
357
|
-
"ghostwhite": (248/255, 248/255, 255/255, 1),
|
|
358
|
-
"gold": (255/255, 215/255, 0, 1),
|
|
359
|
-
"goldenrod": (218/255, 165/255, 32/255, 1),
|
|
360
|
-
"gray": (128/255, 128/255, 128/255, 1),
|
|
361
|
-
"green": (0, 128/255, 0, 1),
|
|
362
|
-
"greenyellow": (173/255, 255/255, 47/255, 1),
|
|
363
|
-
"grey": (128/255, 128/255, 128/255, 1),
|
|
364
|
-
"honeydew": (240/255, 255/255, 240/255, 1),
|
|
365
|
-
"hotpink": (255/255, 105/255, 180/255, 1),
|
|
366
|
-
"indianred": (205/255, 92/255, 92/255, 1),
|
|
367
|
-
"indigo": (75/255, 0, 130/255, 1),
|
|
368
|
-
"ivory": (255/255, 255/255, 240/255, 1),
|
|
369
|
-
"khaki": (240/255, 230/255, 140/255, 1),
|
|
370
|
-
"lavender": (230/255, 230/255, 250/255, 1),
|
|
371
|
-
"lavenderblush": (255/255, 240/255, 245/255, 1),
|
|
372
|
-
"lawngreen": (124/255, 252/255, 0, 1),
|
|
373
|
-
"lemonchiffon": (255/255, 250/255, 205/255, 1),
|
|
374
|
-
"lightblue": (173/255, 216/255, 230/255, 1),
|
|
375
|
-
"lightcoral": (240/255, 128/255, 128/255, 1),
|
|
376
|
-
"lightcyan": (224/255, 255/255, 255/255, 1),
|
|
377
|
-
"lightgoldenrodyellow": (250/255, 250/255, 210/255, 1),
|
|
378
|
-
"lightgray": (211/255, 211/255, 211/255, 1),
|
|
379
|
-
"lightgreen": (144/255, 238/255, 144/255, 1),
|
|
380
|
-
"lightgrey": (211/255, 211/255, 211/255, 1),
|
|
381
|
-
"lightpink": (255/255, 182/255, 193/255, 1),
|
|
382
|
-
"lightsalmon": (255/255, 160/255, 122/255, 1),
|
|
383
|
-
"lightseagreen": (32/255, 178/255, 170/255, 1),
|
|
384
|
-
"lightskyblue": (135/255, 206/255, 250/255, 1),
|
|
385
|
-
"lightslategray": (119/255, 136/255, 153/255, 1),
|
|
386
|
-
"lightslategrey": (119/255, 136/255, 153/255, 1),
|
|
387
|
-
"lightsteelblue": (176/255, 196/255, 222/255, 1),
|
|
388
|
-
"lightyellow": (255/255, 255/255, 224/255, 1),
|
|
389
|
-
"lime": (0, 255/255, 0, 1),
|
|
390
|
-
"limegreen": (50/255, 205/255, 50/255, 1),
|
|
391
|
-
"linen": (250/255, 240/255, 230/255, 1),
|
|
392
|
-
"magenta": (255/255, 0, 255/255, 1),
|
|
393
|
-
"maroon": (128/255, 0, 0, 1),
|
|
394
|
-
"mediumaquamarine": (102/255, 205/255, 170/255, 1),
|
|
395
|
-
"mediumblue": (0, 0, 205/255, 1),
|
|
396
|
-
"mediumorchid": (186/255, 85/255, 211/255, 1),
|
|
397
|
-
"mediumpurple": (147/255, 112/255, 219/255, 1),
|
|
398
|
-
"mediumseagreen": (60/255, 179/255, 113/255, 1),
|
|
399
|
-
"mediumslateblue": (123/255, 104/255, 238/255, 1),
|
|
400
|
-
"mediumspringgreen": (0, 250/255, 154/255, 1),
|
|
401
|
-
"mediumturquoise": (72/255, 209/255, 204/255, 1),
|
|
402
|
-
"mediumvioletred": (199/255, 21/255, 133/255, 1),
|
|
403
|
-
"midnightblue": (25/255, 25/255, 112/255, 1),
|
|
404
|
-
"mintcream": (245/255, 255/255, 250/255, 1),
|
|
405
|
-
"mistyrose": (255/255, 228/255, 225/255, 1),
|
|
406
|
-
"moccasin": (255/255, 228/255, 181/255, 1),
|
|
407
|
-
"navajowhite": (255/255, 222/255, 173/255, 1),
|
|
408
|
-
"navy": (0, 0, 128/255, 1),
|
|
409
|
-
"oldlace": (253/255, 245/255, 230/255, 1),
|
|
410
|
-
"olive": (128/255, 128/255, 0, 1),
|
|
411
|
-
"olivedrab": (107/255, 142/255, 35/255, 1),
|
|
412
|
-
"orange": (255/255, 165/255, 0, 1),
|
|
413
|
-
"orangered": (255/255, 69/255, 0, 1),
|
|
414
|
-
"orchid": (218/255, 112/255, 214/255, 1),
|
|
415
|
-
"palegoldenrod": (238/255, 232/255, 170/255, 1),
|
|
416
|
-
"palegreen": (152/255, 251/255, 152/255, 1),
|
|
417
|
-
"paleturquoise": (175/255, 238/255, 238/255, 1),
|
|
418
|
-
"palevioletred": (219/255, 112/255, 147/255, 1),
|
|
419
|
-
"papayawhip": (255/255, 239/255, 213/255, 1),
|
|
420
|
-
"peachpuff": (255/255, 218/255, 185/255, 1),
|
|
421
|
-
"peru": (205/255, 133/255, 63/255, 1),
|
|
422
|
-
"pink": (255/255, 192/255, 203/255, 1),
|
|
423
|
-
"plum": (221/255, 160/255, 221/255, 1),
|
|
424
|
-
"powderblue": (176/255, 224/255, 230/255, 1),
|
|
425
|
-
"purple": (128/255, 0, 128/255, 1),
|
|
426
|
-
"rebeccapurple": (102/255, 51/255, 153/255, 1),
|
|
427
|
-
"red": (255/255, 0, 0, 1),
|
|
428
|
-
"rosybrown": (188/255, 143/255, 143/255, 1),
|
|
429
|
-
"royalblue": (65/255, 105/255, 225/255, 1),
|
|
430
|
-
"saddlebrown": (139/255, 69/255, 19/255, 1),
|
|
431
|
-
"salmon": (250/255, 128/255, 114/255, 1),
|
|
432
|
-
"sandybrown": (244/255, 164/255, 96/255, 1),
|
|
433
|
-
"seagreen": (46/255, 139/255, 87/255, 1),
|
|
434
|
-
"seashell": (255/255, 245/255, 238/255, 1),
|
|
435
|
-
"sienna": (160/255, 82/255, 45/255, 1),
|
|
436
|
-
"silver": (192/255, 192/255, 192/255, 1),
|
|
437
|
-
"skyblue": (135/255, 206/255, 235/255, 1),
|
|
438
|
-
"slateblue": (106/255, 90/255, 205/255, 1),
|
|
439
|
-
"slategray": (112/255, 128/255, 144/255, 1),
|
|
440
|
-
"slategrey": (112/255, 128/255, 144/255, 1),
|
|
441
|
-
"snow": (255/255, 250/255, 250/255, 1),
|
|
442
|
-
"springgreen": (0, 255/255, 127/255, 1),
|
|
443
|
-
"steelblue": (70/255, 130/255, 180/255, 1),
|
|
444
|
-
"tan": (210/255, 180/255, 140/255, 1),
|
|
445
|
-
"teal": (0, 128/255, 128/255, 1),
|
|
446
|
-
"thistle": (216/255, 191/255, 216/255, 1),
|
|
447
|
-
"tomato": (255/255, 99/255, 71/255, 1),
|
|
448
|
-
"turquoise": (64/255, 224/255, 208/255, 1),
|
|
449
|
-
"violet": (238/255, 130/255, 238/255, 1),
|
|
450
|
-
"wheat": (245/255, 222/255, 179/255, 1),
|
|
451
|
-
"white": (255/255, 255/255, 255/255, 1),
|
|
452
|
-
"whitesmoke": (245/255, 245/255, 245/255, 1),
|
|
453
|
-
"yellow": (255/255, 255/255, 0, 1),
|
|
454
|
-
"yellowgreen": (154/255, 205/255, 50/255, 1)
|
|
455
|
-
]
|
|
456
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// DatePickerNativeComponent.ts
|
|
2
|
-
import type { CodegenTypes, ViewProps } from 'react-native';
|
|
2
|
+
import type { CodegenTypes, HostComponent, ViewProps } from 'react-native';
|
|
3
3
|
import { codegenNativeComponent } from 'react-native';
|
|
4
4
|
|
|
5
5
|
export type TimestampMs = CodegenTypes.Double;
|
|
@@ -69,4 +69,6 @@ export interface NativeProps extends ViewProps, CommonProps {
|
|
|
69
69
|
onClosed?: CodegenTypes.BubblingEventHandler<Readonly<{}>>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export default codegenNativeComponent<NativeProps>(
|
|
72
|
+
export default codegenNativeComponent<NativeProps>(
|
|
73
|
+
'PCDatePicker'
|
|
74
|
+
) as HostComponent<NativeProps>;
|