clox-picker 0.1.1

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 (107) hide show
  1. package/.eslintrc.js +2 -0
  2. package/README.md +35 -0
  3. package/android/build.gradle +42 -0
  4. package/android/src/main/java/expo/modules/cloxpicker/CloxPickerModule.kt +30 -0
  5. package/android/src/main/java/expo/modules/cloxpicker/CloxPickerView.kt +173 -0
  6. package/build/CloxPicker.types.d.ts +35 -0
  7. package/build/CloxPicker.types.d.ts.map +1 -0
  8. package/build/CloxPicker.types.js +2 -0
  9. package/build/CloxPicker.types.js.map +1 -0
  10. package/build/CloxPickerModule.d.ts +9 -0
  11. package/build/CloxPickerModule.d.ts.map +1 -0
  12. package/build/CloxPickerModule.js +3 -0
  13. package/build/CloxPickerModule.js.map +1 -0
  14. package/build/CloxPickerView.d.ts +5 -0
  15. package/build/CloxPickerView.d.ts.map +1 -0
  16. package/build/CloxPickerView.js +8 -0
  17. package/build/CloxPickerView.js.map +1 -0
  18. package/build/index.d.ts +6 -0
  19. package/build/index.d.ts.map +1 -0
  20. package/build/index.js +11 -0
  21. package/build/index.js.map +1 -0
  22. package/example/App.tsx +158 -0
  23. package/example/android/app/build.gradle +182 -0
  24. package/example/android/app/debug.keystore +0 -0
  25. package/example/android/app/proguard-rules.pro +14 -0
  26. package/example/android/app/src/debug/AndroidManifest.xml +7 -0
  27. package/example/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
  28. package/example/android/app/src/main/AndroidManifest.xml +31 -0
  29. package/example/android/app/src/main/java/expo/modules/cloxpicker/example/MainActivity.kt +61 -0
  30. package/example/android/app/src/main/java/expo/modules/cloxpicker/example/MainApplication.kt +56 -0
  31. package/example/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
  32. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  33. package/example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
  34. package/example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
  35. package/example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
  36. package/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
  37. package/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
  38. package/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  39. package/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  40. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  41. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
  42. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  43. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  44. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
  45. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  46. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  47. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
  48. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  49. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  50. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
  51. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  52. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  53. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
  54. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  55. package/example/android/app/src/main/res/values/colors.xml +6 -0
  56. package/example/android/app/src/main/res/values/strings.xml +5 -0
  57. package/example/android/app/src/main/res/values/styles.xml +11 -0
  58. package/example/android/app/src/main/res/values-night/colors.xml +1 -0
  59. package/example/android/build.gradle +24 -0
  60. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  61. package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  62. package/example/android/gradle.properties +65 -0
  63. package/example/android/gradlew +251 -0
  64. package/example/android/gradlew.bat +94 -0
  65. package/example/android/settings.gradle +39 -0
  66. package/example/app.json +30 -0
  67. package/example/assets/adaptive-icon.png +0 -0
  68. package/example/assets/favicon.png +0 -0
  69. package/example/assets/icon.png +0 -0
  70. package/example/assets/splash-icon.png +0 -0
  71. package/example/index.ts +5 -0
  72. package/example/ios/.xcode.env +11 -0
  73. package/example/ios/Podfile +60 -0
  74. package/example/ios/Podfile.lock +2211 -0
  75. package/example/ios/Podfile.properties.json +5 -0
  76. package/example/ios/cloxpickerexample/AppDelegate.swift +70 -0
  77. package/example/ios/cloxpickerexample/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  78. package/example/ios/cloxpickerexample/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
  79. package/example/ios/cloxpickerexample/Images.xcassets/Contents.json +6 -0
  80. package/example/ios/cloxpickerexample/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
  81. package/example/ios/cloxpickerexample/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
  82. package/example/ios/cloxpickerexample/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
  83. package/example/ios/cloxpickerexample/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
  84. package/example/ios/cloxpickerexample/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
  85. package/example/ios/cloxpickerexample/Info.plist +82 -0
  86. package/example/ios/cloxpickerexample/PrivacyInfo.xcprivacy +48 -0
  87. package/example/ios/cloxpickerexample/SplashScreen.storyboard +48 -0
  88. package/example/ios/cloxpickerexample/Supporting/Expo.plist +12 -0
  89. package/example/ios/cloxpickerexample/cloxpickerexample-Bridging-Header.h +3 -0
  90. package/example/ios/cloxpickerexample/cloxpickerexample.entitlements +5 -0
  91. package/example/ios/cloxpickerexample.xcodeproj/project.pbxproj +552 -0
  92. package/example/ios/cloxpickerexample.xcodeproj/xcshareddata/xcschemes/cloxpickerexample.xcscheme +88 -0
  93. package/example/ios/cloxpickerexample.xcworkspace/contents.xcworkspacedata +10 -0
  94. package/example/metro.config.js +30 -0
  95. package/example/package.json +37 -0
  96. package/example/tsconfig.json +11 -0
  97. package/example/yarn.lock +5942 -0
  98. package/expo-module.config.json +9 -0
  99. package/ios/CloxPicker.podspec +29 -0
  100. package/ios/CloxPickerModule.swift +48 -0
  101. package/ios/CloxPickerView.swift +372 -0
  102. package/package.json +56 -0
  103. package/src/CloxPicker.types.ts +33 -0
  104. package/src/CloxPickerModule.ts +10 -0
  105. package/src/CloxPickerView.tsx +12 -0
  106. package/src/index.ts +13 -0
  107. package/tsconfig.json +8 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,2 @@
1
+ // @generated by expo-module-scripts
2
+ module.exports = require('expo-module-scripts/eslintrc.base.js');
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # clox-picker
2
+
3
+ Clox Picker native module
4
+
5
+ # API documentation
6
+
7
+ - [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/clox-picker/)
8
+ - [Documentation for the main branch](https://docs.expo.dev/versions/unversioned/sdk/clox-picker/)
9
+
10
+ # Installation in managed Expo projects
11
+
12
+ For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.
13
+
14
+ # Installation in bare React Native projects
15
+
16
+ For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
17
+
18
+ ### Add the package to your npm dependencies
19
+
20
+ ```
21
+ npm install clox-picker
22
+ ```
23
+
24
+ ### Configure for Android
25
+
26
+
27
+
28
+
29
+ ### Configure for iOS
30
+
31
+ Run `npx pod-install` after installing the npm package.
32
+
33
+ # Contributing
34
+
35
+ Contributions are very welcome! Please refer to guidelines described in the [contributing guide]( https://github.com/expo/expo#contributing).
@@ -0,0 +1,42 @@
1
+ apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+
4
+ group = 'expo.modules.cloxpicker'
5
+ version = '1.0.0'
6
+
7
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
8
+ apply from: expoModulesCorePlugin
9
+ applyKotlinExpoModulesCorePlugin()
10
+ useExpoPublishing()
11
+
12
+ android {
13
+ namespace "expo.modules.cloxpicker"
14
+ compileSdkVersion 36
15
+
16
+ defaultConfig {
17
+ minSdkVersion 24
18
+ targetSdkVersion 36
19
+ versionCode 1
20
+ versionName "1.0.0"
21
+ }
22
+
23
+ buildTypes {
24
+ release {
25
+ minifyEnabled false
26
+ }
27
+ }
28
+
29
+ compileOptions {
30
+ sourceCompatibility JavaVersion.VERSION_17
31
+ targetCompatibility JavaVersion.VERSION_17
32
+ }
33
+
34
+ kotlinOptions {
35
+ jvmTarget = "17"
36
+ }
37
+ }
38
+
39
+ dependencies {
40
+ implementation project(':expo-modules-core')
41
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23"
42
+ }
@@ -0,0 +1,30 @@
1
+ package expo.modules.cloxpicker
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+
6
+ class CloxPickerModule : Module() {
7
+ override fun definition() = ModuleDefinition {
8
+ Name("CloxPicker")
9
+
10
+ Function("getHello") {
11
+ "Hello world"
12
+ }
13
+
14
+ View(CloxPickerView::class) {
15
+ Prop("tabs") { view: CloxPickerView, tabs: List<Map<String, Any>> ->
16
+ view.setTabs(tabs)
17
+ }
18
+ Prop("height") { view: CloxPickerView, height: Double ->
19
+ view.setHeight(height)
20
+ }
21
+ Prop("value") { view: CloxPickerView, value: Int ->
22
+ view.setValue(value)
23
+ }
24
+ Prop("useLiquidGlass") { view: CloxPickerView, _: Boolean ->
25
+ // Android: no-op (always non–liquid-glass look)
26
+ }
27
+ Events("onTabChange")
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,173 @@
1
+ package expo.modules.cloxpicker
2
+
3
+ import android.animation.ValueAnimator
4
+ import android.content.Context
5
+ import android.graphics.drawable.GradientDrawable
6
+ import android.util.TypedValue
7
+ import android.view.Gravity
8
+ import android.view.View
9
+ import android.widget.FrameLayout
10
+ import android.widget.LinearLayout
11
+ import android.widget.TextView
12
+ import expo.modules.kotlin.AppContext
13
+ import expo.modules.kotlin.viewevent.EventDispatcher
14
+ import expo.modules.kotlin.views.ExpoView
15
+
16
+ data class TabItem(val id: Int, val name: String, val iconUri: String?)
17
+
18
+ class CloxPickerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
19
+
20
+ private val onTabChange by EventDispatcher()
21
+
22
+ private var tabs: List<TabItem> = listOf(TabItem(0, "Tab", null))
23
+ private var pickerHeight: Int = dp(44)
24
+ private var selectedIndex: Int = 0
25
+ private var thumbWidth: Int = 0
26
+ private var thumbHeight: Int = 0
27
+ private var thumbTop: Int = 0
28
+
29
+ private val trackBackground: View
30
+ private val thumb: View
31
+ private val segmentsContainer: LinearLayout
32
+
33
+ init {
34
+ setBackgroundColor(0)
35
+
36
+ trackBackground = View(context).apply {
37
+ setBackgroundColor(0xFFEBEBEB.toInt())
38
+ }
39
+ addView(trackBackground)
40
+
41
+ thumb = View(context).apply {
42
+ setBackgroundColor(0xFFFFFFFF.toInt() and 0x00FFFFFF or (0.65 * 255).toInt().shl(24))
43
+ }
44
+ addView(thumb)
45
+
46
+ segmentsContainer = LinearLayout(context).apply {
47
+ orientation = LinearLayout.HORIZONTAL
48
+ gravity = Gravity.CENTER_VERTICAL
49
+ }
50
+ addView(segmentsContainer)
51
+ }
52
+
53
+ private fun dp(value: Int): Int {
54
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value.toFloat(), resources.displayMetrics).toInt()
55
+ }
56
+
57
+ fun setTabs(tabsList: List<Map<String, Any>>) {
58
+ tabs = tabsList.mapNotNull { dict ->
59
+ val name = dict["name"] as? String ?: return@mapNotNull null
60
+ val id = (dict["id"] as? Number)?.toInt() ?: return@mapNotNull null
61
+ val icon = (dict["icon"] as? Map<*, *>)?.get("uri") as? String
62
+ TabItem(id, name, icon)
63
+ }
64
+ if (tabs.isEmpty()) tabs = listOf(TabItem(0, "Tab", null))
65
+ buildSegments()
66
+ }
67
+
68
+ fun setHeight(height: Double) {
69
+ pickerHeight = dp(height.toInt().coerceIn(24, 96))
70
+ requestLayout()
71
+ }
72
+
73
+ fun setValue(value: Int) {
74
+ val newIndex = value.coerceIn(0, (tabs.size - 1).coerceAtLeast(0))
75
+ if (newIndex != selectedIndex) {
76
+ selectedIndex = newIndex
77
+ animateThumbTo(selectedIndex)
78
+ buildSegments()
79
+ }
80
+ }
81
+
82
+ private fun buildSegments() {
83
+ segmentsContainer.removeAllViews()
84
+ val count = tabs.size.coerceAtLeast(1)
85
+ for ((index, tab) in tabs.withIndex()) {
86
+ val textView = TextView(context).apply {
87
+ text = tab.name
88
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
89
+ setTextColor(if (index == selectedIndex) 0xFF000000.toInt() else 0xFF8E8E93.toInt())
90
+ setTypeface(null, if (index == selectedIndex) android.graphics.Typeface.BOLD else android.graphics.Typeface.NORMAL)
91
+ gravity = Gravity.CENTER
92
+ layoutParams = LinearLayout.LayoutParams(0, pickerHeight, 1f)
93
+ setOnClickListener {
94
+ if (index != selectedIndex) {
95
+ selectedIndex = index
96
+ animateThumbTo(selectedIndex)
97
+ onTabChange(mapOf("tabIndex" to index))
98
+ buildSegments()
99
+ }
100
+ }
101
+ }
102
+ segmentsContainer.addView(textView)
103
+ }
104
+ }
105
+
106
+ private var thumbAnimator: ValueAnimator? = null
107
+
108
+ private fun animateThumbTo(index: Int) {
109
+ thumbAnimator?.cancel()
110
+ if (width == 0 || thumbWidth == 0) return
111
+ val count = tabs.size.coerceAtLeast(1)
112
+ val segmentWidth = width / count
113
+ val padding = dp(4)
114
+ val tw = segmentWidth - padding * 2
115
+ val targetLeft = padding + index * segmentWidth + (segmentWidth - tw) / 2
116
+
117
+ val startLeft = thumb.left.toFloat()
118
+ thumbAnimator = ValueAnimator.ofFloat(startLeft, targetLeft.toFloat()).apply {
119
+ duration = 250
120
+ addUpdateListener { anim ->
121
+ val x = (anim.animatedValue as Float).toInt()
122
+ thumb.layout(x, thumbTop, x + thumbWidth, thumbTop + thumbHeight)
123
+ }
124
+ start()
125
+ }
126
+ }
127
+
128
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
129
+ super.onLayout(changed, left, top, right, bottom)
130
+ val w = right - left
131
+ val h = bottom - top
132
+ if (w == 0 || h == 0) return
133
+
134
+ val count = tabs.size.coerceAtLeast(1)
135
+ val segmentWidth = w / count
136
+ val padding = dp(4)
137
+ val thumbWidth = segmentWidth - padding * 2
138
+ val thumbHeight = pickerHeight - padding * 2
139
+ val topOffset = (h - pickerHeight) / 2
140
+
141
+ // Track (pill background)
142
+ (trackBackground.background as? GradientDrawable)?.cornerRadius = (pickerHeight / 2).toFloat()
143
+ if (trackBackground.background !is GradientDrawable) {
144
+ trackBackground.background = GradientDrawable().apply {
145
+ setColor(0xFFEBEBEB.toInt())
146
+ cornerRadius = (pickerHeight / 2).toFloat()
147
+ }
148
+ }
149
+ trackBackground.layout(0, topOffset, w, topOffset + pickerHeight)
150
+
151
+ // Thumb (rounded rect)
152
+ this.thumbWidth = thumbWidth
153
+ this.thumbHeight = thumbHeight
154
+ this.thumbTop = topOffset + padding
155
+ val thumbLeft = padding + selectedIndex * segmentWidth + (segmentWidth - thumbWidth) / 2
156
+ if (thumb.background !is GradientDrawable) {
157
+ thumb.background = GradientDrawable().apply {
158
+ setColor(0xFFFFFFFF.toInt() and 0x00FFFFFF or (0.65 * 255).toInt().shl(24))
159
+ cornerRadius = (thumbHeight / 2).toFloat()
160
+ }
161
+ }
162
+ thumb.layout(thumbLeft, thumbTop, thumbLeft + thumbWidth, thumbTop + thumbHeight)
163
+
164
+ // Segments container
165
+ segmentsContainer.layout(0, topOffset, w, topOffset + pickerHeight)
166
+ }
167
+
168
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
169
+ val h = View.resolveSize(pickerHeight, heightMeasureSpec)
170
+ val w = View.getDefaultSize(suggestedMinimumWidth, widthMeasureSpec)
171
+ setMeasuredDimension(w, h)
172
+ }
173
+ }
@@ -0,0 +1,35 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ export type CloxPickerModuleEvents = {};
3
+ /** Single tab item for the segmented picker */
4
+ export interface CloxPickerTab {
5
+ /** Optional icon (local file or remote URL) */
6
+ icon?: {
7
+ uri: string;
8
+ };
9
+ /** Display label */
10
+ name: string;
11
+ /** Stable id (used as key; can match index) */
12
+ id: number;
13
+ }
14
+ export interface CloxPickerViewProps {
15
+ /** Tab items (icon optional, name, id) */
16
+ tabs: CloxPickerTab[];
17
+ /** Height of the picker in points/dp */
18
+ height: number;
19
+ /** Current selected tab index */
20
+ value: number;
21
+ /** Called when user selects a different tab (passes tab index) */
22
+ onTabChange?: (event: {
23
+ tabIndex: number;
24
+ }) => void;
25
+ /**
26
+ * iOS only: use Liquid Glass effect when available (iOS 26+).
27
+ * When false, uses the same non–liquid-glass look on all iOS versions.
28
+ * Default: true.
29
+ */
30
+ useLiquidGlass?: boolean;
31
+ /** Color for selected tab (hex string, e.g., "#007AFF" or "blue") */
32
+ selectedColor?: string;
33
+ style?: StyleProp<ViewStyle>;
34
+ }
35
+ //# sourceMappingURL=CloxPicker.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPicker.types.d.ts","sourceRoot":"","sources":["../src/CloxPicker.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAExC,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CloxPicker.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPicker.types.js","sourceRoot":"","sources":["../src/CloxPicker.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { StyleProp, ViewStyle } from 'react-native';\n\nexport type CloxPickerModuleEvents = {};\n\n/** Single tab item for the segmented picker */\nexport interface CloxPickerTab {\n /** Optional icon (local file or remote URL) */\n icon?: { uri: string };\n /** Display label */\n name: string;\n /** Stable id (used as key; can match index) */\n id: number;\n}\n\nexport interface CloxPickerViewProps {\n /** Tab items (icon optional, name, id) */\n tabs: CloxPickerTab[];\n /** Height of the picker in points/dp */\n height: number;\n /** Current selected tab index */\n value: number;\n /** Called when user selects a different tab (passes tab index) */\n onTabChange?: (event: { tabIndex: number }) => void;\n /**\n * iOS only: use Liquid Glass effect when available (iOS 26+).\n * When false, uses the same non–liquid-glass look on all iOS versions.\n * Default: true.\n */\n useLiquidGlass?: boolean;\n /** Color for selected tab (hex string, e.g., \"#007AFF\" or \"blue\") */\n selectedColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { NativeModule } from 'expo';
2
+ import { CloxPickerModuleEvents } from './CloxPicker.types';
3
+ declare class CloxPickerModule extends NativeModule<CloxPickerModuleEvents> {
4
+ getHello(): string;
5
+ setColorScheme(scheme: 'light' | 'dark' | 'auto'): void;
6
+ }
7
+ declare const _default: CloxPickerModule;
8
+ export default _default;
9
+ //# sourceMappingURL=CloxPickerModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPickerModule.d.ts","sourceRoot":"","sources":["../src/CloxPickerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE,QAAQ,IAAI,MAAM;IAClB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;CACxD;;AAED,wBAAmE"}
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo';
2
+ export default requireNativeModule('CloxPicker');
3
+ //# sourceMappingURL=CloxPickerModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPickerModule.js","sourceRoot":"","sources":["../src/CloxPickerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AASzD,eAAe,mBAAmB,CAAmB,YAAY,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport { CloxPickerModuleEvents } from './CloxPicker.types';\n\ndeclare class CloxPickerModule extends NativeModule<CloxPickerModuleEvents> {\n getHello(): string;\n setColorScheme(scheme: 'light' | 'dark' | 'auto'): void;\n}\n\nexport default requireNativeModule<CloxPickerModule>('CloxPicker');\n"]}
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ import type { CloxPickerViewProps } from './CloxPicker.types';
3
+ export declare function CloxPickerView(props: CloxPickerViewProps): React.JSX.Element;
4
+ export default CloxPickerView;
5
+ //# sourceMappingURL=CloxPickerView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPickerView.d.ts","sourceRoot":"","sources":["../src/CloxPickerView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,qBAExD;AAED,eAAe,cAAc,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+ const NativeCloxPickerView = requireNativeView('CloxPicker');
4
+ export function CloxPickerView(props) {
5
+ return <NativeCloxPickerView {...props}/>;
6
+ }
7
+ export default CloxPickerView;
8
+ //# sourceMappingURL=CloxPickerView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloxPickerView.js","sourceRoot":"","sources":["../src/CloxPickerView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,oBAAoB,GAAG,iBAAiB,CAAsB,YAAY,CAAC,CAAC;AAElF,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,OAAO,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AAC7C,CAAC;AAED,eAAe,cAAc,CAAC","sourcesContent":["import { requireNativeView } from 'expo';\nimport * as React from 'react';\n\nimport type { CloxPickerViewProps } from './CloxPicker.types';\n\nconst NativeCloxPickerView = requireNativeView<CloxPickerViewProps>('CloxPicker');\n\nexport function CloxPickerView(props: CloxPickerViewProps) {\n return <NativeCloxPickerView {...props} />;\n}\n\nexport default CloxPickerView;\n"]}
@@ -0,0 +1,6 @@
1
+ export declare function getHello(): string;
2
+ export declare function setColorScheme(scheme: 'light' | 'dark' | 'auto'): void;
3
+ export { default as CloxPickerModule } from './CloxPickerModule';
4
+ export { CloxPickerView, default as CloxPickerViewDefault } from './CloxPickerView';
5
+ export * from './CloxPicker.types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACpF,cAAc,oBAAoB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import CloxPickerModule from './CloxPickerModule';
2
+ export function getHello() {
3
+ return CloxPickerModule.getHello();
4
+ }
5
+ export function setColorScheme(scheme) {
6
+ CloxPickerModule.setColorScheme(scheme);
7
+ }
8
+ export { default as CloxPickerModule } from './CloxPickerModule';
9
+ export { CloxPickerView, default as CloxPickerViewDefault } from './CloxPickerView';
10
+ export * from './CloxPicker.types';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,QAAQ;IACtB,OAAO,gBAAgB,CAAC,QAAQ,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAiC;IAC9D,gBAAgB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACpF,cAAc,oBAAoB,CAAC","sourcesContent":["import CloxPickerModule from './CloxPickerModule';\n\nexport function getHello(): string {\n return CloxPickerModule.getHello();\n}\n\nexport function setColorScheme(scheme: 'light' | 'dark' | 'auto'): void {\n CloxPickerModule.setColorScheme(scheme);\n}\n\nexport { default as CloxPickerModule } from './CloxPickerModule';\nexport { CloxPickerView, default as CloxPickerViewDefault } from './CloxPickerView';\nexport * from './CloxPicker.types';\n"]}
@@ -0,0 +1,158 @@
1
+ import {
2
+ CloxPickerView,
3
+ setColorScheme,
4
+ type CloxPickerTab,
5
+ } from 'clox-picker';
6
+ import React, { useState, useEffect } from 'react';
7
+ import {
8
+ Appearance,
9
+ Button,
10
+ Image,
11
+ Platform,
12
+ ScrollView,
13
+ StyleSheet,
14
+ Text,
15
+ useColorScheme,
16
+ useWindowDimensions,
17
+ View,
18
+ } from 'react-native';
19
+
20
+ const DEFAULT_TABS: CloxPickerTab[] = [
21
+ { id: 0, name: 'All', icon: { uri: 'https://cronoz-assets.s3.us-west-2.amazonaws.com/icons/play.png' } },
22
+ { id: 1, name: 'Calendar', icon: { uri: 'https://cronoz-assets.s3.us-west-2.amazonaws.com/icons/upload.png' } },
23
+ { id: 2, name: 'Planning', icon: { uri: 'https://cronoz-assets.s3.us-west-2.amazonaws.com/icons/male.png' } },
24
+ { id: 3, name: 'Explore', icon: { uri: 'https://cronoz-assets.s3.us-west-2.amazonaws.com/icons/female.png' } },
25
+ ];
26
+
27
+ const PICKER_HEIGHT = 90;
28
+
29
+ export default function App() {
30
+ const {width, height} = useWindowDimensions();
31
+ const colorScheme = useColorScheme();
32
+ const [liquidIndex, setLiquidIndex] = useState(0);
33
+ const [standardIndex, setStandardIndex] = useState(0);
34
+ const [androidIndex, setAndroidIndex] = useState(0);
35
+ const [currentScheme, setCurrentScheme] = useState<'light' | 'dark' | null>(null);
36
+
37
+ useEffect(() => {
38
+ setCurrentScheme(colorScheme || 'light');
39
+ }, [colorScheme]);
40
+
41
+ const toggleColorScheme = () => {
42
+ const newScheme = currentScheme === 'light' ? 'dark' : 'light';
43
+ setCurrentScheme(newScheme);
44
+
45
+ if (Platform.OS === 'ios') {
46
+ // Use native module to change color scheme
47
+ setColorScheme(newScheme);
48
+ } else {
49
+ // On Android, use Appearance API
50
+ Appearance.setColorScheme(newScheme);
51
+ }
52
+ };
53
+
54
+ return (
55
+ <View style={styles.container}>
56
+ <Image
57
+ source={{ uri: 'https://picsum.photos/800/1600?random=' + Date.now() }}
58
+ style={{
59
+ position: 'absolute',
60
+ top: 0,
61
+ left: 0,
62
+ width: width,
63
+ height: height,
64
+ }}
65
+ resizeMode="cover"
66
+ />
67
+ <View style={styles.toggleContainer}>
68
+ <Button
69
+ title={`Toggle Theme (Current: ${currentScheme === 'dark' ? 'Dark' : 'Light'})`}
70
+ onPress={toggleColorScheme}
71
+ />
72
+ </View>
73
+ <ScrollView
74
+ contentContainerStyle={styles.container}
75
+ style={styles.scroll}
76
+ >
77
+ {Platform.OS === 'ios' && (
78
+ <View style={{ width: 400 }}>
79
+ <Text style={styles.title}>Liquid Glass (iOS)</Text>
80
+ <View style={styles.pickerWrap}>
81
+ <CloxPickerView
82
+ tabs={DEFAULT_TABS}
83
+ height={PICKER_HEIGHT}
84
+ value={liquidIndex}
85
+ useLiquidGlass={true}
86
+ selectedColor="#FF6B6B"
87
+ onTabChange={({ tabIndex }) => setLiquidIndex(tabIndex)}
88
+ style={styles.picker}
89
+ />
90
+ </View>
91
+
92
+ <Text style={styles.title}>Standard (no liquid glass)</Text>
93
+ <View style={styles.pickerWrap}>
94
+ <CloxPickerView
95
+ tabs={DEFAULT_TABS}
96
+ height={PICKER_HEIGHT}
97
+ value={standardIndex}
98
+ useLiquidGlass={false}
99
+ selectedColor="#4ECDC4"
100
+ onTabChange={({ tabIndex }) => setStandardIndex(tabIndex)}
101
+ style={styles.picker}
102
+ />
103
+ </View>
104
+ </View>
105
+ )}
106
+
107
+ {Platform.OS === 'android' && (
108
+ <>
109
+ <Text style={styles.title}>Segmented Picker</Text>
110
+ <View style={styles.pickerWrap}>
111
+ <CloxPickerView
112
+ tabs={DEFAULT_TABS}
113
+ height={PICKER_HEIGHT}
114
+ value={androidIndex}
115
+ onTabChange={({ tabIndex }) => setAndroidIndex(tabIndex)}
116
+ style={styles.picker}
117
+ />
118
+ </View>
119
+ </>
120
+ )}
121
+ </ScrollView>
122
+
123
+ </View>
124
+
125
+ );
126
+ }
127
+
128
+ const styles = StyleSheet.create({
129
+ scroll: { flex: 1 },
130
+ container: {
131
+ flexGrow: 1,
132
+ paddingVertical: 48,
133
+ alignItems: 'center',
134
+ },
135
+ toggleContainer: {
136
+ position: 'absolute',
137
+ top: 60,
138
+ right: 20,
139
+ zIndex: 1000,
140
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
141
+ borderRadius: 8,
142
+ padding: 8,
143
+ },
144
+ title: {
145
+ fontSize: 16,
146
+ fontWeight: '600',
147
+ marginBottom: 12,
148
+ marginTop: 24,
149
+ },
150
+ pickerWrap: {
151
+ width: '100%',
152
+ height: PICKER_HEIGHT, // Give native view explicit height so `height` prop is respected
153
+ },
154
+ picker: {
155
+ width: '100%',
156
+ height: PICKER_HEIGHT,
157
+ },
158
+ });