react-native-drum-picker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +252 -0
  4. package/android/build.gradle +68 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/drumpicker/DrumPickerAdapter.kt +108 -0
  7. package/android/src/main/java/com/drumpicker/DrumPickerChangeEvent.kt +25 -0
  8. package/android/src/main/java/com/drumpicker/DrumPickerDefaults.kt +19 -0
  9. package/android/src/main/java/com/drumpicker/DrumPickerPackage.kt +17 -0
  10. package/android/src/main/java/com/drumpicker/DrumPickerView.kt +583 -0
  11. package/android/src/main/java/com/drumpicker/DrumPickerViewManager.kt +108 -0
  12. package/lib/module/DateDrumPicker.js +153 -0
  13. package/lib/module/DrumPicker.js +6 -0
  14. package/lib/module/DrumPicker.native.js +63 -0
  15. package/lib/module/DrumPickerViewNativeComponent.ts +31 -0
  16. package/lib/module/dateDrumPickerLogic.js +82 -0
  17. package/lib/module/index.js +5 -0
  18. package/lib/module/package.json +1 -0
  19. package/lib/module/types.js +4 -0
  20. package/lib/typescript/package.json +1 -0
  21. package/lib/typescript/src/DateDrumPicker.d.ts +32 -0
  22. package/lib/typescript/src/DrumPicker.d.ts +3 -0
  23. package/lib/typescript/src/DrumPicker.native.d.ts +3 -0
  24. package/lib/typescript/src/DrumPickerViewNativeComponent.d.ts +25 -0
  25. package/lib/typescript/src/dateDrumPickerLogic.d.ts +20 -0
  26. package/lib/typescript/src/index.d.ts +5 -0
  27. package/lib/typescript/src/types.d.ts +24 -0
  28. package/package.json +189 -0
  29. package/react-native.config.js +13 -0
  30. package/src/DateDrumPicker.tsx +267 -0
  31. package/src/DrumPicker.native.tsx +68 -0
  32. package/src/DrumPicker.tsx +7 -0
  33. package/src/DrumPickerViewNativeComponent.ts +31 -0
  34. package/src/dateDrumPickerLogic.ts +95 -0
  35. package/src/index.tsx +15 -0
  36. package/src/types.ts +25 -0
@@ -0,0 +1,583 @@
1
+ package com.drumpicker
2
+
3
+ import android.content.Context
4
+ import android.graphics.Color
5
+ import android.util.AttributeSet
6
+ import android.view.View
7
+ import android.widget.FrameLayout
8
+ import androidx.recyclerview.widget.LinearLayoutManager
9
+ import androidx.recyclerview.widget.LinearSnapHelper
10
+ import androidx.recyclerview.widget.RecyclerView
11
+ import com.facebook.react.bridge.ColorPropConverter
12
+ import com.facebook.react.bridge.ReadableArray
13
+ import com.facebook.react.bridge.ReadableType
14
+ import com.facebook.react.bridge.ReactContext
15
+ import com.facebook.react.uimanager.UIManagerHelper
16
+ import com.facebook.react.uimanager.events.EventDispatcher
17
+ import kotlin.math.abs
18
+
19
+ class DrumPickerView @JvmOverloads constructor(
20
+ context: Context,
21
+ attrs: AttributeSet? = null,
22
+ ) : FrameLayout(context, attrs) {
23
+
24
+ private val recyclerView = RecyclerView(context)
25
+ private val topIndicator = View(context)
26
+ private val bottomIndicator = View(context)
27
+ private val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
28
+ private val snapHelper = LinearSnapHelper()
29
+ private val adapter = DrumPickerAdapter { dpToPx(itemHeightDp) }
30
+
31
+ private var items: List<String> = emptyList()
32
+ private var selectedIndex = 0
33
+ private var itemHeightDp = DrumPickerDefaults.ITEM_HEIGHT_DP
34
+ private var visibleItemCount = DrumPickerDefaults.VISIBLE_ITEM_COUNT
35
+ private var textColor = DrumPickerDefaults.TEXT_COLOR
36
+ private var selectedTextColor = DrumPickerDefaults.SELECTED_TEXT_COLOR
37
+ private var textSizeSp = DrumPickerDefaults.TEXT_SIZE_SP
38
+ private var selectedTextSizeSp = DrumPickerDefaults.SELECTED_TEXT_SIZE_SP
39
+ private var showSelectionIndicator = true
40
+ private var selectionIndicatorColor = DrumPickerDefaults.SELECTION_INDICATOR_COLOR
41
+ private var selectionIndicatorHeightDp = DrumPickerDefaults.SELECTION_INDICATOR_HEIGHT_DP
42
+ private var backgroundColor = DrumPickerDefaults.TRANSPARENT
43
+ private var containerBackgroundColor = DrumPickerDefaults.TRANSPARENT
44
+ private var itemBackgroundColor = DrumPickerDefaults.TRANSPARENT
45
+
46
+ private var itemHeightPx = dpToPx(itemHeightDp)
47
+ private var selectionIndicatorHeightPx = dpToPx(selectionIndicatorHeightDp)
48
+ private var lastEmittedIndex = -1
49
+ private var suppressChangeEvent = false
50
+ private var isDetached = false
51
+ private var styleUpdatePosted = false
52
+
53
+ private val styleUpdateRunnable =
54
+ Runnable {
55
+ styleUpdatePosted = false
56
+ if (!isDetached) {
57
+ updateVisibleItemStyles()
58
+ }
59
+ }
60
+
61
+ private val scrollListener =
62
+ object : RecyclerView.OnScrollListener() {
63
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
64
+ if (isDetached) {
65
+ return
66
+ }
67
+ scheduleVisibleItemStyleUpdate()
68
+ }
69
+
70
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
71
+ if (isDetached) {
72
+ return
73
+ }
74
+ when (newState) {
75
+ RecyclerView.SCROLL_STATE_DRAGGING,
76
+ RecyclerView.SCROLL_STATE_SETTLING,
77
+ -> updateVisibleItemStyles()
78
+ RecyclerView.SCROLL_STATE_IDLE -> {
79
+ updateVisibleItemStyles()
80
+ if (suppressChangeEvent) {
81
+ val centerIndex = findSnapCenterIndex()
82
+ if (centerIndex != RecyclerView.NO_POSITION) {
83
+ selectedIndex = centerIndex
84
+ lastEmittedIndex = centerIndex
85
+ }
86
+ suppressChangeEvent = false
87
+ } else {
88
+ updateCenterFromSnap()
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ init {
96
+ setBackgroundColor(DrumPickerDefaults.TRANSPARENT)
97
+ recyclerView.setBackgroundColor(DrumPickerDefaults.TRANSPARENT)
98
+ recyclerView.layoutManager = layoutManager
99
+ recyclerView.adapter = adapter
100
+ recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER
101
+ recyclerView.clipToPadding = false
102
+ snapHelper.attachToRecyclerView(recyclerView)
103
+
104
+ topIndicator.isClickable = false
105
+ topIndicator.isFocusable = false
106
+ bottomIndicator.isClickable = false
107
+ bottomIndicator.isFocusable = false
108
+
109
+ addView(
110
+ recyclerView,
111
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT),
112
+ )
113
+ addView(topIndicator, LayoutParams(LayoutParams.MATCH_PARENT, selectionIndicatorHeightPx))
114
+ addView(bottomIndicator, LayoutParams(LayoutParams.MATCH_PARENT, selectionIndicatorHeightPx))
115
+
116
+ adapter.distanceForPosition = { position -> distanceFromCenterForPosition(position) }
117
+ syncAdapterStyle()
118
+ applyBackgroundColors()
119
+ applyRecyclerPadding()
120
+ updateIndicatorAppearance()
121
+ }
122
+
123
+ fun setItemsProp(value: Any?) {
124
+ val newItems = parseItems(value)
125
+ if (newItems == items) {
126
+ return
127
+ }
128
+
129
+ recyclerView.stopScroll()
130
+ items = newItems
131
+ adapter.updateItems(newItems)
132
+ lastEmittedIndex = -1
133
+
134
+ if (items.isEmpty()) {
135
+ selectedIndex = selectedIndex.coerceAtLeast(0)
136
+ return
137
+ }
138
+
139
+ selectedIndex = selectedIndex.coerceIn(0, items.size - 1)
140
+ runWhenAttached { scrollToSelectedIndex(animated = false, emit = false) }
141
+ }
142
+
143
+ fun setSelectedIndexProp(value: Any?) {
144
+ val index = toInt(value, selectedIndex)
145
+ val safeIndex =
146
+ if (items.isEmpty()) {
147
+ index.coerceAtLeast(0)
148
+ } else {
149
+ index.coerceIn(0, items.size - 1)
150
+ }
151
+ setSelectedIndex(safeIndex)
152
+ }
153
+
154
+ fun setItemHeightProp(value: Any?) {
155
+ setItemHeight(toFloat(value, itemHeightDp))
156
+ }
157
+
158
+ fun setVisibleItemCountProp(value: Any?) {
159
+ setVisibleItemCount(toInt(value, visibleItemCount))
160
+ }
161
+
162
+ fun setTextColorProp(value: Any?) {
163
+ setTextColor(resolveColor(value, textColor))
164
+ }
165
+
166
+ fun setSelectedTextColorProp(value: Any?) {
167
+ setSelectedTextColor(resolveColor(value, selectedTextColor))
168
+ }
169
+
170
+ fun setTextSizeProp(value: Any?) {
171
+ setTextSize(toFloat(value, textSizeSp))
172
+ }
173
+
174
+ fun setSelectedTextSizeProp(value: Any?) {
175
+ setSelectedTextSize(toFloat(value, selectedTextSizeSp))
176
+ }
177
+
178
+ fun setShowSelectionIndicatorProp(value: Any?) {
179
+ showSelectionIndicator = toBoolean(value, true)
180
+ updateIndicatorAppearance()
181
+ requestLayout()
182
+ }
183
+
184
+ fun setSelectionIndicatorColorProp(value: Any?) {
185
+ selectionIndicatorColor = resolveColor(value, selectionIndicatorColor)
186
+ updateIndicatorAppearance()
187
+ }
188
+
189
+ fun setSelectionIndicatorHeightProp(value: Any?) {
190
+ val height = toFloat(value, selectionIndicatorHeightDp)
191
+ if (height <= 0f) {
192
+ return
193
+ }
194
+ selectionIndicatorHeightDp = height
195
+ selectionIndicatorHeightPx = dpToPx(selectionIndicatorHeightDp)
196
+ updateIndicatorAppearance()
197
+ requestLayout()
198
+ }
199
+
200
+ fun setBackgroundColorProp(value: Any?) {
201
+ backgroundColor = resolveBackgroundColor(value, backgroundColor)
202
+ applyBackgroundColors()
203
+ }
204
+
205
+ fun setContainerBackgroundColorProp(value: Any?) {
206
+ containerBackgroundColor = resolveBackgroundColor(value, containerBackgroundColor)
207
+ applyBackgroundColors()
208
+ }
209
+
210
+ fun setItemBackgroundColorProp(value: Any?) {
211
+ itemBackgroundColor = resolveBackgroundColor(value, itemBackgroundColor)
212
+ applyBackgroundColors()
213
+ }
214
+
215
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
216
+ val width = resolveSize(suggestedMinimumWidth, widthMeasureSpec)
217
+ val safeVisibleCount = visibleItemCount.coerceAtLeast(1)
218
+ val height =
219
+ resolveSize(
220
+ (itemHeightPx * safeVisibleCount).coerceAtLeast(suggestedMinimumHeight),
221
+ heightMeasureSpec,
222
+ )
223
+
224
+ val childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
225
+ val childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
226
+ recyclerView.measure(childWidthSpec, childHeightSpec)
227
+ setMeasuredDimension(width, height)
228
+ }
229
+
230
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
231
+ super.onLayout(changed, left, top, right, bottom)
232
+ val width = right - left
233
+ val height = bottom - top
234
+ recyclerView.layout(0, 0, width, height)
235
+ layoutSelectionIndicators(width, height)
236
+ if (changed) {
237
+ applyRecyclerPadding()
238
+ runWhenAttached { scrollToSelectedIndex(animated = false, emit = false) }
239
+ }
240
+ }
241
+
242
+ override fun onAttachedToWindow() {
243
+ super.onAttachedToWindow()
244
+ isDetached = false
245
+ if (recyclerView.adapter == null) {
246
+ recyclerView.adapter = adapter
247
+ }
248
+ snapHelper.attachToRecyclerView(recyclerView)
249
+ recyclerView.addOnScrollListener(scrollListener)
250
+ }
251
+
252
+ override fun onDetachedFromWindow() {
253
+ isDetached = true
254
+ recyclerView.removeCallbacks(styleUpdateRunnable)
255
+ styleUpdatePosted = false
256
+ removeCallbacks(null)
257
+ recyclerView.clearOnScrollListeners()
258
+ snapHelper.attachToRecyclerView(null)
259
+ recyclerView.adapter = null
260
+ super.onDetachedFromWindow()
261
+ }
262
+
263
+ fun setSelectedIndex(index: Int) {
264
+ if (items.isEmpty()) {
265
+ selectedIndex = index.coerceAtLeast(0)
266
+ return
267
+ }
268
+ val clamped = index.coerceIn(0, items.size - 1)
269
+ val snapIndex = findSnapCenterIndex()
270
+ if (clamped == selectedIndex && snapIndex == clamped) {
271
+ return
272
+ }
273
+ selectedIndex = clamped
274
+ scrollToSelectedIndex(animated = false, emit = false)
275
+ }
276
+
277
+ fun setItemHeight(height: Float) {
278
+ if (height <= 0f || height == itemHeightDp) {
279
+ return
280
+ }
281
+ itemHeightDp = height
282
+ itemHeightPx = dpToPx(itemHeightDp)
283
+ adapter.itemHeightPx = itemHeightPx
284
+ adapter.notifyRowMetricsChanged()
285
+ applyRecyclerPadding()
286
+ requestLayout()
287
+ }
288
+
289
+ fun setVisibleItemCount(count: Int) {
290
+ if (count <= 0 || count == visibleItemCount) {
291
+ return
292
+ }
293
+ visibleItemCount = count
294
+ applyRecyclerPadding()
295
+ requestLayout()
296
+ }
297
+
298
+ fun setTextColor(color: Int) {
299
+ textColor = color
300
+ syncAdapterStyle()
301
+ updateVisibleItemStyles()
302
+ }
303
+
304
+ fun setSelectedTextColor(color: Int) {
305
+ selectedTextColor = color
306
+ syncAdapterStyle()
307
+ updateVisibleItemStyles()
308
+ }
309
+
310
+ fun setTextSize(size: Float) {
311
+ if (size <= 0f) {
312
+ return
313
+ }
314
+ textSizeSp = size
315
+ syncAdapterStyle()
316
+ updateVisibleItemStyles()
317
+ }
318
+
319
+ fun setSelectedTextSize(size: Float) {
320
+ if (size <= 0f) {
321
+ return
322
+ }
323
+ selectedTextSizeSp = size
324
+ syncAdapterStyle()
325
+ updateVisibleItemStyles()
326
+ }
327
+
328
+ private fun syncAdapterStyle() {
329
+ adapter.textColor = textColor
330
+ adapter.selectedTextColor = selectedTextColor
331
+ adapter.textSizeSp = textSizeSp
332
+ adapter.selectedTextSizeSp = selectedTextSizeSp
333
+ adapter.itemHeightPx = itemHeightPx
334
+ adapter.itemBackgroundColor = itemBackgroundColor
335
+ resetVisibleStyleBuckets()
336
+ }
337
+
338
+ private fun applyBackgroundColors() {
339
+ setBackgroundColor(backgroundColor)
340
+ recyclerView.setBackgroundColor(containerBackgroundColor)
341
+ adapter.itemBackgroundColor = itemBackgroundColor
342
+ if (adapter.itemCount > 0) {
343
+ adapter.notifyRowMetricsChanged()
344
+ }
345
+ }
346
+
347
+ private fun resetVisibleStyleBuckets() {
348
+ for (i in 0 until recyclerView.childCount) {
349
+ val holder =
350
+ recyclerView.getChildViewHolder(recyclerView.getChildAt(i))
351
+ as? DrumPickerAdapter.ItemViewHolder ?: continue
352
+ holder.lastStyleBucket = Int.MIN_VALUE
353
+ }
354
+ }
355
+
356
+ private fun applyRecyclerPadding() {
357
+ val verticalPadding = (itemHeightPx * (visibleItemCount - 1)) / 2
358
+ recyclerView.setPadding(0, verticalPadding, 0, verticalPadding)
359
+ }
360
+
361
+ private fun layoutSelectionIndicators(width: Int, height: Int) {
362
+ if (!showSelectionIndicator || width == 0 || height == 0) {
363
+ topIndicator.visibility = GONE
364
+ bottomIndicator.visibility = GONE
365
+ return
366
+ }
367
+
368
+ topIndicator.visibility = VISIBLE
369
+ bottomIndicator.visibility = VISIBLE
370
+
371
+ val bandTop = (height - itemHeightPx) / 2
372
+ val bandBottom = bandTop + itemHeightPx
373
+ topIndicator.layout(0, bandTop, width, bandTop + selectionIndicatorHeightPx)
374
+ bottomIndicator.layout(0, bandBottom - selectionIndicatorHeightPx, width, bandBottom)
375
+ }
376
+
377
+ private fun updateIndicatorAppearance() {
378
+ val visibility = if (showSelectionIndicator) VISIBLE else GONE
379
+ topIndicator.visibility = visibility
380
+ bottomIndicator.visibility = visibility
381
+ topIndicator.setBackgroundColor(selectionIndicatorColor)
382
+ bottomIndicator.setBackgroundColor(selectionIndicatorColor)
383
+
384
+ (topIndicator.layoutParams as LayoutParams).height = selectionIndicatorHeightPx
385
+ (bottomIndicator.layoutParams as LayoutParams).height = selectionIndicatorHeightPx
386
+ topIndicator.requestLayout()
387
+ bottomIndicator.requestLayout()
388
+ }
389
+
390
+ private fun scrollToSelectedIndex(animated: Boolean, emit: Boolean) {
391
+ if (isDetached || items.isEmpty()) {
392
+ return
393
+ }
394
+ suppressChangeEvent = !emit
395
+ val index = selectedIndex.coerceIn(0, items.size - 1)
396
+ if (animated) {
397
+ recyclerView.smoothScrollToPosition(index)
398
+ return
399
+ }
400
+
401
+ layoutManager.scrollToPositionWithOffset(index, 0)
402
+ recyclerView.post {
403
+ if (isDetached) {
404
+ return@post
405
+ }
406
+ updateVisibleItemStyles()
407
+ if (emit) {
408
+ maybeEmitChange(index)
409
+ } else {
410
+ lastEmittedIndex = index
411
+ }
412
+ suppressChangeEvent = false
413
+ }
414
+ }
415
+
416
+ private fun runWhenAttached(block: () -> Unit) {
417
+ if (isDetached) {
418
+ return
419
+ }
420
+ post {
421
+ if (isDetached) {
422
+ return@post
423
+ }
424
+ block()
425
+ }
426
+ }
427
+
428
+ private fun updateCenterFromSnap() {
429
+ if (isDetached || items.isEmpty()) {
430
+ return
431
+ }
432
+ val centerIndex = findSnapCenterIndex()
433
+ if (centerIndex == RecyclerView.NO_POSITION) {
434
+ return
435
+ }
436
+
437
+ selectedIndex = centerIndex
438
+ updateVisibleItemStyles()
439
+ maybeEmitChange(centerIndex)
440
+ }
441
+
442
+ private fun findSnapCenterIndex(): Int {
443
+ if (items.isEmpty()) {
444
+ return RecyclerView.NO_POSITION
445
+ }
446
+ val centerView = snapHelper.findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION
447
+ return layoutManager.getPosition(centerView)
448
+ }
449
+
450
+ private fun scheduleVisibleItemStyleUpdate() {
451
+ if (styleUpdatePosted || isDetached) {
452
+ return
453
+ }
454
+ styleUpdatePosted = true
455
+ recyclerView.postOnAnimation(styleUpdateRunnable)
456
+ }
457
+
458
+ private fun distanceFromCenterForPosition(position: Int): Float {
459
+ if (itemHeightPx <= 0 || recyclerView.height == 0) {
460
+ return 2f
461
+ }
462
+ val child = layoutManager.findViewByPosition(position) ?: return 2f
463
+ val pickerCenterY = recyclerView.height / 2f
464
+ val childCenterY = child.top + child.height / 2f
465
+ return abs(childCenterY - pickerCenterY) / itemHeightPx.toFloat()
466
+ }
467
+
468
+ private fun updateVisibleItemStyles() {
469
+ if (isDetached || recyclerView.height == 0 || itemHeightPx <= 0) {
470
+ return
471
+ }
472
+
473
+ val pickerCenterY = recyclerView.height / 2f
474
+ val rowHeight = itemHeightPx.toFloat()
475
+ for (i in 0 until recyclerView.childCount) {
476
+ val child = recyclerView.getChildAt(i)
477
+ val holder = recyclerView.getChildViewHolder(child) as? DrumPickerAdapter.ItemViewHolder
478
+ ?: continue
479
+ val childCenterY = child.top + child.height / 2f
480
+ val distance = abs(childCenterY - pickerCenterY) / rowHeight
481
+ adapter.applyItemStyle(holder, distance)
482
+ }
483
+ }
484
+
485
+ private fun maybeEmitChange(index: Int) {
486
+ if (isDetached || suppressChangeEvent || items.isEmpty()) {
487
+ return
488
+ }
489
+ if (index < 0 || index >= items.size) {
490
+ return
491
+ }
492
+ val clamped = index.coerceIn(0, items.size - 1)
493
+ if (clamped == lastEmittedIndex) {
494
+ selectedIndex = clamped
495
+ return
496
+ }
497
+
498
+ lastEmittedIndex = clamped
499
+ selectedIndex = clamped
500
+
501
+ val reactContext = context as? ReactContext ?: return
502
+ val dispatcher: EventDispatcher? = UIManagerHelper.getEventDispatcher(reactContext)
503
+ dispatcher?.dispatchEvent(
504
+ DrumPickerChangeEvent(
505
+ UIManagerHelper.getSurfaceId(reactContext),
506
+ id,
507
+ clamped,
508
+ items[clamped],
509
+ ),
510
+ )
511
+ }
512
+
513
+ private fun parseItems(value: Any?): List<String> {
514
+ when (value) {
515
+ null -> return emptyList()
516
+ is ReadableArray -> {
517
+ val parsed = ArrayList<String>(value.size())
518
+ for (i in 0 until value.size()) {
519
+ parsed.add(readArrayString(value, i))
520
+ }
521
+ return parsed
522
+ }
523
+ is List<*> -> return value.mapNotNull { it?.toString() }
524
+ is Array<*> -> return value.mapNotNull { it?.toString() }
525
+ else -> return emptyList()
526
+ }
527
+ }
528
+
529
+ private fun readArrayString(array: ReadableArray, index: Int): String =
530
+ when (array.getType(index)) {
531
+ ReadableType.String -> array.getString(index) ?: ""
532
+ ReadableType.Number -> array.getDouble(index).toInt().toString()
533
+ else -> array.getDynamic(index).asString() ?: ""
534
+ }
535
+
536
+ private fun resolveColor(value: Any?, fallback: Int): Int =
537
+ when (value) {
538
+ is Int -> value
539
+ null -> fallback
540
+ else -> ColorPropConverter.getColor(value, context) ?: fallback
541
+ }
542
+
543
+ private fun resolveBackgroundColor(value: Any?, fallback: Int): Int {
544
+ if (value == null) {
545
+ return fallback
546
+ }
547
+ if (value is String && value.equals("transparent", ignoreCase = true)) {
548
+ return Color.TRANSPARENT
549
+ }
550
+ return resolveColor(value, fallback)
551
+ }
552
+
553
+ private fun toBoolean(value: Any?, fallback: Boolean): Boolean =
554
+ when (value) {
555
+ null -> fallback
556
+ is Boolean -> value
557
+ is Number -> value.toInt() != 0
558
+ else -> fallback
559
+ }
560
+
561
+ private fun toInt(value: Any?, fallback: Int): Int =
562
+ when (value) {
563
+ null -> fallback
564
+ is Int -> value
565
+ is Double -> value.toInt()
566
+ is Float -> value.toInt()
567
+ is Number -> value.toInt()
568
+ else -> fallback
569
+ }
570
+
571
+ private fun toFloat(value: Any?, fallback: Float): Float =
572
+ when (value) {
573
+ null -> fallback
574
+ is Float -> value
575
+ is Double -> value.toFloat()
576
+ is Int -> value.toFloat()
577
+ is Number -> value.toFloat()
578
+ else -> fallback
579
+ }
580
+
581
+ private fun dpToPx(dp: Float): Int =
582
+ (dp * resources.displayMetrics.density + 0.5f).toInt()
583
+ }
@@ -0,0 +1,108 @@
1
+ package com.drumpicker
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.module.annotations.ReactModule
5
+ import com.facebook.react.uimanager.ReactStylesDiffMap
6
+ import com.facebook.react.uimanager.SimpleViewManager
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.facebook.react.uimanager.ViewManagerDelegate
9
+ import com.facebook.react.viewmanagers.DrumPickerViewManagerDelegate
10
+ import com.facebook.react.viewmanagers.DrumPickerViewManagerInterface
11
+
12
+ @ReactModule(name = DrumPickerViewManager.NAME)
13
+ class DrumPickerViewManager :
14
+ SimpleViewManager<DrumPickerView>(),
15
+ DrumPickerViewManagerInterface<DrumPickerView> {
16
+ private val delegate: ViewManagerDelegate<DrumPickerView> =
17
+ DrumPickerViewManagerDelegate(this)
18
+
19
+ override fun getDelegate(): ViewManagerDelegate<DrumPickerView>? = delegate
20
+
21
+ override fun getName(): String = NAME
22
+
23
+ public override fun createViewInstance(context: ThemedReactContext): DrumPickerView =
24
+ DrumPickerView(context)
25
+
26
+ override fun updateProperties(view: DrumPickerView, props: ReactStylesDiffMap) {
27
+ for ((key, value) in props.toMap()) {
28
+ when (key) {
29
+ "items" -> view.setItemsProp(value)
30
+ "selectedIndex" -> view.setSelectedIndexProp(value)
31
+ "itemHeight" -> view.setItemHeightProp(value)
32
+ "visibleItemCount" -> view.setVisibleItemCountProp(value)
33
+ "textColor" -> view.setTextColorProp(value)
34
+ "selectedTextColor" -> view.setSelectedTextColorProp(value)
35
+ "textSize" -> view.setTextSizeProp(value)
36
+ "selectedTextSize" -> view.setSelectedTextSizeProp(value)
37
+ "showSelectionIndicator" -> view.setShowSelectionIndicatorProp(value)
38
+ "selectionIndicatorColor" -> view.setSelectionIndicatorColorProp(value)
39
+ "selectionIndicatorHeight" -> view.setSelectionIndicatorHeightProp(value)
40
+ "backgroundColor" -> view.setBackgroundColorProp(value)
41
+ "containerBackgroundColor" -> view.setContainerBackgroundColorProp(value)
42
+ "itemBackgroundColor" -> view.setItemBackgroundColorProp(value)
43
+ else -> delegate.setProperty(view, key, value)
44
+ }
45
+ }
46
+ onAfterUpdateTransaction(view)
47
+ }
48
+
49
+ override fun setItems(view: DrumPickerView?, value: ReadableArray?) {
50
+ view?.setItemsProp(value)
51
+ }
52
+
53
+ override fun setSelectedIndex(view: DrumPickerView?, value: Int) {
54
+ view?.setSelectedIndexProp(value)
55
+ }
56
+
57
+ override fun setItemHeight(view: DrumPickerView?, value: Float) {
58
+ view?.setItemHeightProp(value)
59
+ }
60
+
61
+ override fun setVisibleItemCount(view: DrumPickerView?, value: Int) {
62
+ view?.setVisibleItemCountProp(value)
63
+ }
64
+
65
+ override fun setTextColor(view: DrumPickerView?, value: Int?) {
66
+ view?.setTextColorProp(value)
67
+ }
68
+
69
+ override fun setSelectedTextColor(view: DrumPickerView?, value: Int?) {
70
+ view?.setSelectedTextColorProp(value)
71
+ }
72
+
73
+ override fun setTextSize(view: DrumPickerView?, value: Float) {
74
+ view?.setTextSizeProp(value)
75
+ }
76
+
77
+ override fun setSelectedTextSize(view: DrumPickerView?, value: Float) {
78
+ view?.setSelectedTextSizeProp(value)
79
+ }
80
+
81
+ override fun setShowSelectionIndicator(view: DrumPickerView?, value: Boolean) {
82
+ view?.setShowSelectionIndicatorProp(value)
83
+ }
84
+
85
+ override fun setSelectionIndicatorColor(view: DrumPickerView?, value: Int?) {
86
+ view?.setSelectionIndicatorColorProp(value)
87
+ }
88
+
89
+ override fun setSelectionIndicatorHeight(view: DrumPickerView?, value: Float) {
90
+ view?.setSelectionIndicatorHeightProp(value)
91
+ }
92
+
93
+ override fun setBackgroundColor(view: DrumPickerView?, value: Int?) {
94
+ view?.setBackgroundColorProp(value)
95
+ }
96
+
97
+ override fun setContainerBackgroundColor(view: DrumPickerView?, value: Int?) {
98
+ view?.setContainerBackgroundColorProp(value)
99
+ }
100
+
101
+ override fun setItemBackgroundColor(view: DrumPickerView?, value: Int?) {
102
+ view?.setItemBackgroundColorProp(value)
103
+ }
104
+
105
+ companion object {
106
+ const val NAME = "DrumPickerView"
107
+ }
108
+ }