@onekeyfe/react-native-segment-slider 3.0.40

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/SegmentSlider.podspec +29 -0
  4. package/android/CMakeLists.txt +26 -0
  5. package/android/build.gradle +128 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  8. package/android/src/main/java/com/margelo/nitro/segmentslider/HybridSegmentSlider.kt +364 -0
  9. package/android/src/main/java/com/margelo/nitro/segmentslider/SegmentSliderColorParser.kt +56 -0
  10. package/android/src/main/java/com/margelo/nitro/segmentslider/SegmentSliderPackage.kt +29 -0
  11. package/ios/HybridSegmentSlider.swift +412 -0
  12. package/ios/SegmentSliderColorParser.swift +152 -0
  13. package/lib/module/SegmentSlider.nitro.js +4 -0
  14. package/lib/module/SegmentSlider.nitro.js.map +1 -0
  15. package/lib/module/index.js +43 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/package.json +1 -0
  18. package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  19. package/lib/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  20. package/lib/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
  21. package/lib/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
  22. package/lib/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
  23. package/lib/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
  24. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
  25. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
  26. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
  27. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
  28. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
  29. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
  30. package/lib/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
  31. package/lib/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
  32. package/lib/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
  33. package/lib/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
  34. package/lib/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
  35. package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
  36. package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
  37. package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
  38. package/lib/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
  39. package/lib/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
  40. package/lib/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
  41. package/lib/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
  42. package/lib/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
  43. package/lib/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  44. package/lib/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
  45. package/lib/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
  46. package/lib/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
  47. package/lib/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
  48. package/lib/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
  49. package/lib/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
  50. package/lib/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
  51. package/lib/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
  52. package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  53. package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  54. package/lib/nitrogen/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
  55. package/lib/nitrogen/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
  56. package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
  57. package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
  58. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
  59. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
  60. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
  61. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
  62. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
  63. package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
  64. package/lib/nitrogen/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
  65. package/lib/nitrogen/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
  66. package/lib/nitrogen/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
  67. package/lib/nitrogen/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
  68. package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
  69. package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
  70. package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
  71. package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
  72. package/lib/nitrogen/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
  73. package/lib/nitrogen/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
  74. package/lib/nitrogen/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
  75. package/lib/nitrogen/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
  76. package/lib/nitrogen/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
  77. package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  78. package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
  79. package/lib/nitrogen/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
  80. package/lib/nitrogen/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
  81. package/lib/nitrogen/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
  82. package/lib/nitrogen/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
  83. package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
  84. package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
  85. package/lib/nitrogen/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
  86. package/lib/typescript/package.json +1 -0
  87. package/lib/typescript/src/SegmentSlider.nitro.d.ts +72 -0
  88. package/lib/typescript/src/SegmentSlider.nitro.d.ts.map +1 -0
  89. package/lib/typescript/src/index.d.ts +11 -0
  90. package/lib/typescript/src/index.d.ts.map +1 -0
  91. package/nitro.json +17 -0
  92. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  93. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  94. package/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
  95. package/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
  96. package/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
  97. package/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
  98. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
  99. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
  100. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
  101. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
  102. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
  103. package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
  104. package/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
  105. package/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
  106. package/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
  107. package/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
  108. package/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
  109. package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
  110. package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
  111. package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
  112. package/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
  113. package/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
  114. package/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
  115. package/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
  116. package/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
  117. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  118. package/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
  119. package/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
  120. package/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
  121. package/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
  122. package/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
  123. package/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
  124. package/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
  125. package/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
  126. package/package.json +170 -0
  127. package/src/SegmentSlider.nitro.ts +80 -0
  128. package/src/index.tsx +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 OneKey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @onekeyfe/react-native-segment-slider
2
+
3
+ SegmentSlider view for React Native
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ yarn add @onekeyfe/react-native-segment-slider
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { SegmentSliderView } from '@onekeyfe/react-native-segment-slider';
15
+
16
+ // ...
17
+
18
+ <SegmentSliderView color="#ff0000" style={{ width: 100, height: 100 }} />
19
+ ```
20
+
21
+ ## Contributing
22
+
23
+ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
24
+
25
+ ## License
26
+
27
+ MIT
28
+
29
+ ---
30
+
31
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,29 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "SegmentSlider"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/OneKeyHQ/app-views/react-native-segment-slider.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ "ios/**/*.{swift}",
18
+ "ios/**/*.{m,mm}",
19
+ "cpp/**/*.{hpp,cpp}",
20
+ ]
21
+
22
+ s.dependency 'React-jsi'
23
+ s.dependency 'React-callinvoker'
24
+
25
+ load 'nitrogen/generated/ios/SegmentSlider+autolinking.rb'
26
+ add_nitrogen_files(s)
27
+
28
+ install_modules_dependencies(s)
29
+ end
@@ -0,0 +1,26 @@
1
+ project(segmentslider)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME segmentslider)
5
+ set(CMAKE_VERBOSE_MAKEFILE ON)
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define the C++ library and add the local adapter source.
9
+ add_library(${PACKAGE_NAME} SHARED src/main/cpp/cpp-adapter.cpp)
10
+
11
+ # Add the Nitrogen-generated specs + Nitro core linking. This resolves
12
+ # react-native-nitro-modules via find_package (monorepo-safe), instead of a
13
+ # hardcoded ../node_modules path that breaks once nitro-modules is hoisted.
14
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/segmentslider+autolinking.cmake)
15
+
16
+ # Local includes.
17
+ include_directories("src/main/cpp")
18
+
19
+ find_library(LOG_LIB log)
20
+
21
+ # Link everything together.
22
+ target_link_libraries(
23
+ ${PACKAGE_NAME}
24
+ ${LOG_LIB}
25
+ android
26
+ )
@@ -0,0 +1,128 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['SegmentSlider_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+ def reactNativeArchitectures() {
19
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
20
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
21
+ }
22
+
23
+ apply plugin: "com.android.library"
24
+ apply plugin: "kotlin-android"
25
+ apply from: '../nitrogen/generated/android/segmentslider+autolinking.gradle'
26
+
27
+ apply plugin: "com.facebook.react"
28
+
29
+ def getExtOrIntegerDefault(name) {
30
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["SegmentSlider_" + name]).toInteger()
31
+ }
32
+
33
+ android {
34
+ namespace "com.margelo.nitro.segmentslider"
35
+
36
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
37
+
38
+ defaultConfig {
39
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
40
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
41
+
42
+ externalNativeBuild {
43
+ cmake {
44
+ cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
45
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
46
+ abiFilters (*reactNativeArchitectures())
47
+
48
+ buildTypes {
49
+ debug {
50
+ cppFlags "-O1 -g"
51
+ }
52
+ release {
53
+ cppFlags "-O2"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ externalNativeBuild {
61
+ cmake {
62
+ path "CMakeLists.txt"
63
+ }
64
+ }
65
+
66
+ packagingOptions {
67
+ excludes = [
68
+ "META-INF",
69
+ "META-INF/**",
70
+ "**/libc++_shared.so",
71
+ "**/libfbjni.so",
72
+ "**/libjsi.so",
73
+ "**/libfolly_json.so",
74
+ "**/libfolly_runtime.so",
75
+ "**/libglog.so",
76
+ "**/libhermes.so",
77
+ "**/libhermes-executor-debug.so",
78
+ "**/libhermes_executor.so",
79
+ "**/libreactnative.so",
80
+ "**/libreactnativejni.so",
81
+ "**/libturbomodulejsijni.so",
82
+ "**/libreact_nativemodule_core.so",
83
+ "**/libjscexecutor.so"
84
+ ]
85
+ }
86
+
87
+ buildFeatures {
88
+ buildConfig true
89
+ prefab true
90
+ }
91
+
92
+ buildTypes {
93
+ release {
94
+ minifyEnabled false
95
+ }
96
+ }
97
+
98
+ lintOptions {
99
+ disable "GradleCompatible"
100
+ }
101
+
102
+ compileOptions {
103
+ sourceCompatibility JavaVersion.VERSION_1_8
104
+ targetCompatibility JavaVersion.VERSION_1_8
105
+ }
106
+
107
+ sourceSets {
108
+ main {
109
+ java.srcDirs += [
110
+ "generated/java",
111
+ "generated/jni"
112
+ ]
113
+ }
114
+ }
115
+ }
116
+
117
+ repositories {
118
+ mavenCentral()
119
+ google()
120
+ }
121
+
122
+ def kotlin_version = getExtOrDefault("kotlinVersion")
123
+
124
+ dependencies {
125
+ implementation "com.facebook.react:react-android"
126
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
127
+ implementation project(":react-native-nitro-modules")
128
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,6 @@
1
+ #include <jni.h>
2
+ #include "segmentsliderOnLoad.hpp"
3
+
4
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
+ return margelo::nitro::segmentslider::initialize(vm);
6
+ }
@@ -0,0 +1,364 @@
1
+ package com.margelo.nitro.segmentslider
2
+
3
+ import android.animation.ValueAnimator
4
+ import android.graphics.Canvas
5
+ import android.graphics.Paint
6
+ import android.graphics.RectF
7
+ import android.view.HapticFeedbackConstants
8
+ import android.view.MotionEvent
9
+ import android.view.View
10
+ import com.facebook.proguard.annotations.DoNotStrip
11
+ import com.facebook.react.uimanager.ThemedReactContext
12
+
13
+ /**
14
+ * Native segmented slider. Draws the track, fill bar, segment marks, thumb and
15
+ * value bubble in [View.onDraw] and handles the drag in [View.onTouchEvent], so
16
+ * a drag never crosses the JS bridge per frame. Mirrors the web variant
17
+ * (`composite/SegmentSlider/index.tsx`) pixel-for-pixel.
18
+ */
19
+ @DoNotStrip
20
+ class HybridSegmentSlider(val context: ThemedReactContext) : HybridSegmentSliderSpec() {
21
+
22
+ private val density = context.resources.displayMetrics.density
23
+
24
+ // Geometry (dp), identical to the web constants.
25
+ private val thumbSizePx = 16f * density
26
+ private val markSizePx = 10f * density
27
+ private val insetPx = thumbSizePx / 2f
28
+ private val haloRingPx = 4f * density // translucent halo width beyond each mark
29
+
30
+ // Paints.
31
+ private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
32
+ private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
33
+ private val thumbFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
34
+ private val thumbStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
35
+ style = Paint.Style.STROKE; strokeWidth = density
36
+ }
37
+ private val markFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
38
+ private val markStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
39
+ style = Paint.Style.STROKE; strokeWidth = density
40
+ }
41
+ // Translucent highlight around the node the thumb is over while dragging.
42
+ private val markHaloPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
43
+ private val bubblePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
44
+ private val bubbleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
45
+ textAlign = Paint.Align.CENTER
46
+ textSize = 12f * density
47
+ isFakeBoldText = true
48
+ }
49
+ private val rect = RectF()
50
+
51
+ // Cached colors.
52
+ private var cFill = 0
53
+ private var cTrack = 0
54
+ private var cThumb = 0
55
+ private var cThumbBorder = 0
56
+ private var cMarkActive = 0
57
+ private var cMarkInactive = 0
58
+ private var cMarkBorder = 0
59
+
60
+ // Interaction state.
61
+ private var currentValue = 0.0
62
+ private var lastEmitted = Double.NaN
63
+ private var isDragging = false
64
+ private var thumbScale = 1f
65
+ private var thumbScaleAnimator: ValueAnimator? = null
66
+ private var isDisposed = false
67
+ // Segment node the thumb currently sits on (-1 = none). Drives the halo +
68
+ // a light haptic tick fired once each time the thumb enters a new node.
69
+ private var hoverMarkIndex = -1
70
+
71
+ override val view: View = object : View(context) {
72
+ override fun onDraw(canvas: Canvas) {
73
+ drawSlider(canvas)
74
+ }
75
+
76
+ override fun onTouchEvent(event: MotionEvent): Boolean {
77
+ if (disabled || isDisposed) return false
78
+ when (event.action) {
79
+ MotionEvent.ACTION_DOWN -> {
80
+ isDragging = true
81
+ onSlideStart?.invoke()
82
+ animateThumbScale(1.15f)
83
+ commitDragValue(valueFromX(event.x))
84
+ parent?.requestDisallowInterceptTouchEvent(true)
85
+ return true
86
+ }
87
+ MotionEvent.ACTION_MOVE -> {
88
+ if (isDragging) commitDragValue(valueFromX(event.x))
89
+ return true
90
+ }
91
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
92
+ if (isDragging) {
93
+ isDragging = false
94
+ hoverMarkIndex = -1
95
+ animateThumbScale(1f)
96
+ view.invalidate()
97
+ onSlideComplete?.invoke()
98
+ }
99
+ parent?.requestDisallowInterceptTouchEvent(false)
100
+ return true
101
+ }
102
+ }
103
+ return super.onTouchEvent(event)
104
+ }
105
+ }.apply {
106
+ isClickable = true
107
+ isFocusable = false
108
+ isHapticFeedbackEnabled = true
109
+ }
110
+
111
+ // MARK: - Props
112
+ override var value: Double = 0.0
113
+ get() = field
114
+ set(v) {
115
+ field = v
116
+ // While dragging, the finger owns the value (mirrors web).
117
+ if (!isDragging && !isDisposed) {
118
+ currentValue = v
119
+ view.invalidate()
120
+ }
121
+ }
122
+
123
+ override var min: Double = 0.0
124
+ get() = field
125
+ set(v) { field = v; invalidateIfAlive() }
126
+
127
+ override var max: Double = 100.0
128
+ get() = field
129
+ set(v) { field = v; invalidateIfAlive() }
130
+
131
+ override var segments: Double = 0.0
132
+ get() = field
133
+ set(v) { field = v; invalidateIfAlive() }
134
+
135
+ override var sliderHeight: Double = 4.0
136
+ get() = field
137
+ set(v) { field = v; invalidateIfAlive() }
138
+
139
+ override var disabled: Boolean = false
140
+ get() = field
141
+ set(v) {
142
+ field = v
143
+ view.alpha = if (v) 0.5f else 1f
144
+ invalidateIfAlive()
145
+ }
146
+
147
+ override var showBubble: Boolean = true
148
+ get() = field
149
+ set(v) { field = v; invalidateIfAlive() }
150
+
151
+ override var centerOrigin: Boolean = false
152
+ get() = field
153
+ set(v) { field = v; invalidateIfAlive() }
154
+
155
+ override var epoch: Double = 0.0
156
+ get() = field
157
+ set(v) { field = v }
158
+
159
+ override var fillColor: String = ""
160
+ get() = field
161
+ set(v) { field = v; cFill = parse(v); fillPaint.color = cFill; invalidateIfAlive() }
162
+
163
+ override var trackColor: String = ""
164
+ get() = field
165
+ set(v) { field = v; cTrack = parse(v); trackPaint.color = cTrack; invalidateIfAlive() }
166
+
167
+ override var thumbColor: String = ""
168
+ get() = field
169
+ set(v) { field = v; cThumb = parse(v); thumbFillPaint.color = cThumb; invalidateIfAlive() }
170
+
171
+ override var thumbBorderColor: String = ""
172
+ get() = field
173
+ set(v) { field = v; cThumbBorder = parse(v); thumbStrokePaint.color = cThumbBorder; invalidateIfAlive() }
174
+
175
+ override var markActiveColor: String = ""
176
+ get() = field
177
+ set(v) { field = v; cMarkActive = parse(v); invalidateIfAlive() }
178
+
179
+ override var markInactiveColor: String = ""
180
+ get() = field
181
+ set(v) { field = v; cMarkInactive = parse(v); invalidateIfAlive() }
182
+
183
+ override var markBorderColor: String = ""
184
+ get() = field
185
+ set(v) { field = v; cMarkBorder = parse(v); invalidateIfAlive() }
186
+
187
+ override var bubbleColor: String = ""
188
+ get() = field
189
+ set(v) { field = v; bubblePaint.color = parse(v); invalidateIfAlive() }
190
+
191
+ override var bubbleTextColor: String = ""
192
+ get() = field
193
+ set(v) { field = v; bubbleTextPaint.color = parse(v); invalidateIfAlive() }
194
+
195
+ // MARK: - Callbacks
196
+ override var onChange: ((value: Double) -> Unit)? = null
197
+ override var onSlideStart: (() -> Unit)? = null
198
+ override var onSlideComplete: (() -> Unit)? = null
199
+
200
+ private fun parse(v: String) = SegmentSliderColorParser.parse(v)
201
+
202
+ private fun invalidateIfAlive() {
203
+ if (!isDisposed) view.invalidate()
204
+ }
205
+
206
+ // MARK: - Geometry helpers
207
+ private val trackWidth: Float get() = (view.width - thumbSizePx).coerceAtLeast(0f)
208
+ private val rangeSafe: Double get() = (max - min).let { if (it == 0.0) 1.0 else it }
209
+ private val markCount: Int get() = if (segments >= 1) segments.toInt() + 1 else 0
210
+
211
+ private fun valueToFrac(v: Double): Float {
212
+ val clamped = v.coerceIn(min, max)
213
+ return ((clamped - min) / rangeSafe).toFloat()
214
+ }
215
+
216
+ private fun valueFromX(x: Float): Double {
217
+ val w = trackWidth
218
+ if (w <= 0f) return currentValue
219
+ val clampedX = x.coerceIn(insetPx, insetPx + w)
220
+ val frac = ((clampedX - insetPx) / w).toDouble()
221
+ return min + frac * (max - min)
222
+ }
223
+
224
+ private fun commitDragValue(raw: Double) {
225
+ val rounded = raw.coerceIn(min, max).let { Math.round(it).toDouble() }
226
+ currentValue = rounded
227
+ updateHover()
228
+ view.invalidate()
229
+ if (rounded != lastEmitted) {
230
+ lastEmitted = rounded
231
+ onChange?.invoke(rounded)
232
+ }
233
+ }
234
+
235
+ /// Highlights the node the thumb is over and fires a light haptic tick once
236
+ /// per node entered. Only while dragging; does NOT snap (parity with web/perp).
237
+ private fun updateHover() {
238
+ val tw = trackWidth
239
+ if (!isDragging || markCount <= 0 || tw <= 0f) {
240
+ hoverMarkIndex = -1
241
+ return
242
+ }
243
+ val thumbX = insetPx + valueToFrac(currentValue) * tw
244
+ val threshold = 12f * density // half of the 24dp mark hit area
245
+ var best = -1
246
+ var bestDist = threshold
247
+ for (i in 0 until markCount) {
248
+ val mf = if (markCount > 1) i.toFloat() / (markCount - 1) else 0f
249
+ val mx = insetPx + mf * tw
250
+ val d = Math.abs(thumbX - mx)
251
+ if (d <= bestDist) { bestDist = d; best = i }
252
+ }
253
+ if (best != hoverMarkIndex) {
254
+ hoverMarkIndex = best
255
+ if (best != -1) {
256
+ view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
257
+ }
258
+ }
259
+ }
260
+
261
+ private fun animateThumbScale(target: Float) {
262
+ thumbScaleAnimator?.cancel()
263
+ thumbScaleAnimator = ValueAnimator.ofFloat(thumbScale, target).apply {
264
+ duration = 120L
265
+ addUpdateListener { a ->
266
+ if (isDisposed) return@addUpdateListener
267
+ thumbScale = a.animatedValue as Float
268
+ view.invalidate()
269
+ }
270
+ start()
271
+ }
272
+ }
273
+
274
+ // MARK: - Drawing
275
+ private fun drawSlider(canvas: Canvas) {
276
+ val w = view.width.toFloat()
277
+ if (w <= 0f) return
278
+ val h = (sliderHeight * density).toFloat()
279
+ val midY = view.height / 2f
280
+ val trackTop = midY - h / 2f
281
+ val tw = trackWidth
282
+ val frac = valueToFrac(currentValue)
283
+ val radius = h / 2f
284
+
285
+ // Track.
286
+ rect.set(insetPx, trackTop, insetPx + tw, trackTop + h)
287
+ canvas.drawRoundRect(rect, radius, radius, trackPaint)
288
+
289
+ // Fill.
290
+ if (centerOrigin) {
291
+ if (currentValue != 0.0) {
292
+ val centerFrac = valueToFrac(0.0)
293
+ val leftFrac = minOf(frac, centerFrac)
294
+ val widthFrac = Math.abs(frac - centerFrac)
295
+ rect.set(insetPx + leftFrac * tw, trackTop, insetPx + (leftFrac + widthFrac) * tw, trackTop + h)
296
+ canvas.drawRoundRect(rect, radius, radius, fillPaint)
297
+ }
298
+ } else if (frac > 0f) {
299
+ rect.set(insetPx, trackTop, insetPx + frac * tw, trackTop + h)
300
+ canvas.drawRoundRect(rect, radius, radius, fillPaint)
301
+ }
302
+
303
+ // Thumb (drawn BELOW the marks so a node under the thumb shows through its
304
+ // centre — the "white ring + dark centre" bullseye, matching web where the
305
+ // mark zIndex 3 sits above the thumb zIndex 2).
306
+ val thumbX = insetPx + frac * tw
307
+ val thumbR = thumbSizePx / 2f * thumbScale
308
+ canvas.drawCircle(thumbX, midY, thumbR, thumbFillPaint)
309
+ canvas.drawCircle(thumbX, midY, thumbR - density / 2f, thumbStrokePaint)
310
+
311
+ // Node halo (drag-time highlight) — drawn under the marks so the node dot
312
+ // stays crisp on top. Translucent markActive color (25% alpha).
313
+ if (isDragging && hoverMarkIndex in 0 until markCount) {
314
+ val mf = if (markCount > 1) hoverMarkIndex.toFloat() / (markCount - 1) else 0f
315
+ val hx = insetPx + mf * tw
316
+ markHaloPaint.color = (cMarkActive and 0x00FFFFFF) or (0x3F shl 24)
317
+ canvas.drawCircle(hx, midY, markSizePx / 2f + haloRingPx, markHaloPaint)
318
+ }
319
+
320
+ // Marks (on top of the thumb).
321
+ val centerFrac = valueToFrac(0.0)
322
+ val markR = markSizePx / 2f
323
+ for (i in 0 until markCount) {
324
+ val markFrac = if (markCount > 1) i.toFloat() / (markCount - 1) else 0f
325
+ val cx = insetPx + markFrac * tw
326
+ val active = when {
327
+ centerOrigin && currentValue == 0.0 -> false
328
+ centerOrigin && currentValue > 0 -> markFrac > centerFrac && markFrac <= frac
329
+ centerOrigin && currentValue < 0 -> markFrac >= frac && markFrac < centerFrac
330
+ else -> markFrac <= frac
331
+ }
332
+ markFillPaint.color = if (active) cMarkActive else cMarkInactive
333
+ markStrokePaint.color = if (active) cMarkActive else cMarkBorder
334
+ canvas.drawCircle(cx, midY, markR - density / 2f, markFillPaint)
335
+ canvas.drawCircle(cx, midY, markR - density / 2f, markStrokePaint)
336
+ }
337
+
338
+ // Bubble.
339
+ if (isDragging && showBubble) {
340
+ val text = "${Math.round(currentValue)}%"
341
+ val textW = bubbleTextPaint.measureText(text)
342
+ val padH = 8f * density
343
+ val bw = textW + padH * 2
344
+ val bh = 20f * density
345
+ val left = thumbX - bw / 2f
346
+ val top = trackTop - 8f * density - bh
347
+ rect.set(left, top, left + bw, top + bh)
348
+ canvas.drawRoundRect(rect, 4f * density, 4f * density, bubblePaint)
349
+ val baseline = top + bh / 2f - (bubbleTextPaint.descent() + bubbleTextPaint.ascent()) / 2f
350
+ canvas.drawText(text, thumbX, baseline, bubbleTextPaint)
351
+ }
352
+ }
353
+
354
+ override fun afterUpdate() {
355
+ super.afterUpdate()
356
+ invalidateIfAlive()
357
+ }
358
+
359
+ override fun dispose() {
360
+ isDisposed = true
361
+ thumbScaleAnimator?.cancel()
362
+ thumbScaleAnimator = null
363
+ }
364
+ }
@@ -0,0 +1,56 @@
1
+ package com.margelo.nitro.segmentslider
2
+
3
+ import android.graphics.Color
4
+
5
+ /**
6
+ * Parses color strings from JS. Supports `#rgb`/`#rrggbb`/`#rrggbbaa` and
7
+ * `rgb(...)`/`rgba(...)` (alpha 0..1). Returns [fallback] on failure.
8
+ *
9
+ * Note: Android's [Color.parseColor] expects `#AARRGGBB`, while web/JS emit
10
+ * `#RRGGBBAA`, so 8-digit hex is reordered here before parsing.
11
+ */
12
+ object SegmentSliderColorParser {
13
+ fun parse(value: String?, fallback: Int = Color.TRANSPARENT): Int {
14
+ val raw = value?.trim().orEmpty()
15
+ if (raw.isEmpty()) return fallback
16
+ return try {
17
+ when {
18
+ raw.startsWith("#") -> parseHex(raw) ?: fallback
19
+ raw.startsWith("rgb", ignoreCase = true) -> parseRgb(raw) ?: fallback
20
+ else -> fallback
21
+ }
22
+ } catch (e: Exception) {
23
+ fallback
24
+ }
25
+ }
26
+
27
+ private fun parseHex(hex: String): Int? {
28
+ var s = hex.substring(1)
29
+ if (s.length == 3) {
30
+ s = s.map { "$it$it" }.joinToString("")
31
+ }
32
+ return when (s.length) {
33
+ 6 -> Color.parseColor("#$s")
34
+ 8 -> {
35
+ // #RRGGBBAA (JS) -> #AARRGGBB (Android)
36
+ val rgb = s.substring(0, 6)
37
+ val a = s.substring(6, 8)
38
+ Color.parseColor("#$a$rgb")
39
+ }
40
+ else -> null
41
+ }
42
+ }
43
+
44
+ private fun parseRgb(value: String): Int? {
45
+ val open = value.indexOf('(')
46
+ val close = value.indexOf(')')
47
+ if (open < 0 || close < 0 || close <= open) return null
48
+ val parts = value.substring(open + 1, close).split(",").map { it.trim() }
49
+ if (parts.size < 3) return null
50
+ val r = parts[0].toFloatOrNull()?.toInt() ?: return null
51
+ val g = parts[1].toFloatOrNull()?.toInt() ?: return null
52
+ val b = parts[2].toFloatOrNull()?.toInt() ?: return null
53
+ val a = if (parts.size >= 4) ((parts[3].toFloatOrNull() ?: 1f) * 255f).toInt() else 255
54
+ return Color.argb(a.coerceIn(0, 255), r.coerceIn(0, 255), g.coerceIn(0, 255), b.coerceIn(0, 255))
55
+ }
56
+ }
@@ -0,0 +1,29 @@
1
+ package com.margelo.nitro.segmentslider
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfoProvider
7
+ import com.facebook.react.uimanager.ViewManager
8
+
9
+ import com.margelo.nitro.segmentslider.views.HybridSegmentSliderManager
10
+
11
+ class SegmentSliderPackage : BaseReactPackage() {
12
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
13
+ return null
14
+ }
15
+
16
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
17
+ return ReactModuleInfoProvider { HashMap() }
18
+ }
19
+
20
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
21
+ return listOf(HybridSegmentSliderManager())
22
+ }
23
+
24
+ companion object {
25
+ init {
26
+ System.loadLibrary("segmentslider")
27
+ }
28
+ }
29
+ }