Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@

All notable changes to this project will be documented in this file.

## [1.1.36] - 2026-03-10

### Features
- **auto-size-input**: Add `contentCentered` prop to center prefix/input/suffix as one visual group in single-line mode

### Bug Fixes
- **auto-size-input (Android)**: Improve baseline centering and centered-layout width calculations for single-line input
- **auto-size-input (iOS)**: Align auto-width sizing behavior with Android so content width and suffix positioning stay consistent

### Chores
- Bump all native modules and views to 1.1.36

## [1.1.35] - 2026-03-10

### Features
- **pager-view**: Add local `@onekeyfe/react-native-pager-view` package in `native-views` with iOS and Android support
- **example**: Add PagerView test page and route integration, including nested pager demos

### Bug Fixes
- **pager-view**: Fix layout metrics/child-view guards and scope refresh-layout callback to the host instance
- **tab-view**: Guard invalid route keys in tab press/long-press callbacks and safely resync selected tab on Android

### Chores
- Bump all native modules and views to 1.1.35

## [1.1.34] - 2026-03-09

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion native-modules/native-logger/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-native-logger",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-native-logger",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-app-update/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-app-update",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-app-update",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-background-thread/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-background-thread",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-background-thread",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-bundle-update/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-bundle-update",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-bundle-update",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-check-biometric-auth-changed",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-check-biometric-auth-changed",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-cloud-kit-module/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-cloud-kit-module",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-cloud-kit-module",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-device-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-device-utils",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-device-utils",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-get-random-values/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-get-random-values",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-get-random-values",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-keychain-module/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-keychain-module",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-keychain-module",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-lite-card/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-lite-card",
"version": "1.1.35",
"version": "1.1.36",
"description": "lite card",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-perf-memory/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-perf-memory",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-perf-memory",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion native-modules/react-native-splash-screen/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onekeyfe/react-native-splash-screen",
"version": "1.1.35",
"version": "1.1.36",
"description": "react-native-splash-screen",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,14 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
view.requestLayout()
}

override var contentCentered: Boolean? = null
get() = field
set(value) {
if (isDisposed) return
field = value
view.requestLayout()
}


override var onChangeText: ((String) -> Unit)? = null
override var onFocus: (() -> Unit)? = null
Expand Down Expand Up @@ -436,25 +444,37 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
val prefixGap = if (prefixView.visibility == View.VISIBLE) ((prefixMarginRight ?: 0.0) * density).toInt() else 0
val suffixGap = if (suffixView.visibility == View.VISIBLE) ((suffixMarginLeft ?: 0.0) * density).toInt() else 0

val inputX = edgeInset + prefixW + prefixGap
val isContentAutoWidthEnabled = contentAutoWidth == true && multiline != true
val isContentCenteredEnabled = contentCentered == true && multiline != true
val prefixSegment = prefixW + prefixGap
val suffixSegment = if (suffixView.visibility == View.VISIBLE) suffixGap + suffixW else 0
val availableTrackWidth = maxOf(width - (edgeInset * 2), 0)
val maxInputWidth = maxOf(availableTrackWidth - prefixSegment - suffixSegment, 0)
val rawInputText = inputView.text?.toString() ?: ""
val displayInputText = if (rawInputText.isEmpty()) (placeholder ?: "") else rawInputText
val inputW: Int
val suffixX: Int

if (isContentAutoWidthEnabled) {
val typedText = inputView.text?.toString() ?: ""
val minInputWidth = (24f * density).toInt()
val desiredInputWidth = maxOf(measureSingleLineTextWidthPx(typedText), minInputWidth)
val suffixSegment = if (suffixView.visibility == View.VISIBLE) suffixGap + suffixW else 0
val maxInputWidth = maxOf(width - edgeInset - inputX - suffixSegment, 0)
val singleLineWidthPadding = contentAutoWidthPaddingPx()
val desiredInputWidth = maxOf(
measureSingleLineTextWidthPx(displayInputText) + singleLineWidthPadding,
minInputWidth
)
inputW = minOf(desiredInputWidth, maxInputWidth)
val desiredSuffixX = if (suffixView.visibility == View.VISIBLE) inputX + inputW + suffixGap else width - edgeInset
suffixX = minOf(desiredSuffixX, width - edgeInset - suffixW)
} else {
inputW = maxOf(width - edgeInset - inputX - suffixW - suffixGap, 0)
suffixX = width - edgeInset - suffixW
inputW = maxInputWidth
}

val groupWidth = prefixSegment + inputW + suffixSegment
val groupStartX = if (isContentCenteredEnabled) {
edgeInset + maxOf((availableTrackWidth - groupWidth) / 2, 0)
} else {
edgeInset
}
val inputX = groupStartX + prefixSegment
val suffixX = inputX + inputW + if (suffixView.visibility == View.VISIBLE) suffixGap else 0

// Re-measure with the exact final slot size before layout.
if (prefixView.visibility == View.VISIBLE) {
prefixView.measure(
Expand All @@ -469,7 +489,6 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
)
}

// Layout input
val inputHeightSpec = if (multiline == true) {
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
} else {
Expand All @@ -479,19 +498,50 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
View.MeasureSpec.makeMeasureSpec(inputW, View.MeasureSpec.EXACTLY),
inputHeightSpec
)
val inputH = if (multiline == true) height else inputView.measuredHeight.coerceAtMost(height)
val inputTop = if (multiline == true) 0 else ((height - inputH) / 2).coerceAtLeast(0)
val inputH = if (multiline == true) {
height
} else {
inputView.measuredHeight.coerceAtMost(height)
}
val targetBaseline: Int = if (multiline == true) {
0
} else {
centeredBaselineY(height)
}
val inputTop = if (multiline == true) {
0
} else {
topForBaseline(inputView, targetBaseline, height)
}
inputView.layout(inputX, inputTop, inputX + inputW, inputTop + inputH)
resetSingleLineVerticalOffset()

val prefixTop = ((height - prefixView.measuredHeight) / 2).coerceAtLeast(0)
val suffixTop = ((height - suffixView.measuredHeight) / 2).coerceAtLeast(0)
val prefixTop = if (multiline == true) {
((height - prefixView.measuredHeight) / 2).coerceAtLeast(0)
} else {
topForBaseline(prefixView, targetBaseline, height)
}
val suffixTop = if (multiline == true) {
((height - suffixView.measuredHeight) / 2).coerceAtLeast(0)
} else {
topForBaseline(suffixView, targetBaseline, height)
}

// Layout prefix
prefixView.layout(edgeInset, prefixTop, edgeInset + prefixW, prefixTop + prefixView.measuredHeight)
prefixView.layout(
groupStartX,
prefixTop,
groupStartX + prefixW,
prefixTop + prefixView.measuredHeight
)

// Layout suffix
suffixView.layout(suffixX, suffixTop, suffixX + suffixW, suffixTop + suffixView.measuredHeight)
suffixView.layout(
suffixX,
suffixTop,
suffixX + suffixW,
suffixTop + suffixView.measuredHeight
)

}

Expand All @@ -517,28 +567,41 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
if (isContentAutoWidthEnabled) {
val density = context.resources.displayMetrics.density
val edgeInset = (2f * density).toInt()
val singleLineWidthPadding = contentAutoWidthPaddingPx()
val prefixW = if (prefixView.visibility == View.VISIBLE) measureTextViewWidthPx(prefixView) else 0
val suffixW = if (suffixView.visibility == View.VISIBLE) measureTextViewWidthPx(suffixView) else 0
val prefixGap = if (prefixView.visibility == View.VISIBLE) ((prefixMarginRight ?: 0.0) * density).toInt() else 0
val suffixGap = if (suffixView.visibility == View.VISIBLE) ((suffixMarginLeft ?: 0.0) * density).toInt() else 0
val inputX = edgeInset + prefixW + prefixGap
val prefixSegment = prefixW + prefixGap
val suffixSegment = if (suffixView.visibility == View.VISIBLE) suffixGap + suffixW else 0
val maxInputWidth = maxOf(width - edgeInset - inputX - suffixSegment, 0)
val availableTrackWidth = maxOf(width - (edgeInset * 2), 0)
val maxInputWidth = maxOf(availableTrackWidth - prefixSegment - suffixSegment, 0)
val maxTextWidth = maxOf(maxInputWidth - singleLineWidthPadding, 0)
val maxTextHeight = maxOf(height - inputView.paddingTop - inputView.paddingBottom, 0)
val textForSizing = if (inputText.isEmpty()) (placeholder ?: "") else inputText
val probeText = if (textForSizing.isEmpty()) "0" else textForSizing

// Expand width first; once width hits max, shrink font to keep full text visible.
val targetSize = if (maxInputWidth <= 0) {
// Keep both width and line-height within the input slot.
val widthFitSize = if (maxTextWidth <= 0) {
minSize
} else if (textForSizing.isEmpty()) {
maxSize
} else {
findOptimalFontSizeSingleLine(
fullText = textForSizing,
availableWidth = maxInputWidth.toFloat(),
fullText = probeText,
availableWidth = maxTextWidth.toFloat(),
minSize = minSize,
maxSize = maxSize
)
}
val heightFitSize = if (maxTextHeight <= 0) {
minSize
} else {
findOptimalFontSizeForSingleLineHeight(
availableHeight = maxTextHeight.toFloat(),
minSize = minSize,
maxSize = maxSize
)
}
val targetSize = minOf(widthFitSize, heightFitSize).coerceIn(minSize, maxSize)
applyFontSize(targetSize)
return
}
Expand Down Expand Up @@ -628,6 +691,32 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
return low
}

private fun findOptimalFontSizeForSingleLineHeight(
availableHeight: Float,
minSize: Float,
maxSize: Float
): Float {
if (availableHeight <= 0f) return minSize
var low = minSize
var high = maxSize
val paint = TextPaint(Paint.ANTI_ALIAS_FLAG)
paint.typeface = makeTypeface()

while (high - low > 0.5f) {
val mid = (low + high) / 2f
paint.textSize = mid * context.resources.displayMetrics.scaledDensity
val fm = paint.fontMetrics
val lineHeight = kotlin.math.ceil((fm.descent - fm.ascent).toDouble()).toFloat()
if (lineHeight <= availableHeight) {
low = mid
} else {
high = mid
}
}

return low
}

private fun applyFontSize(size: Float) {
currentFontSize = size
val typeface = makeTypeface()
Expand Down Expand Up @@ -739,6 +828,29 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
}
}

private fun contentAutoWidthPaddingPx(): Int {
val density = context.resources.displayMetrics.density
return (8f * density).toInt()
}

private fun centeredBaselineY(containerHeight: Int): Int {
val paint = inputView.paint
return kotlin.math.round(
(containerHeight / 2f) - ((paint.descent() + paint.ascent()) / 2f)
).toInt()
}

private fun topForBaseline(textView: TextView, targetBaseline: Int, containerHeight: Int): Int {
val measuredHeight = textView.measuredHeight
if (measuredHeight <= 0) return 0
val baseline = textView.baseline
val fallback = ((containerHeight - measuredHeight) / 2).coerceAtLeast(0)
if (baseline < 0) return fallback
val rawTop = targetBaseline - baseline
val maxTop = maxOf(containerHeight - measuredHeight, 0)
return rawTop.coerceIn(0, maxTop)
}

override fun afterUpdate() {
super.afterUpdate()
if (!isDisposed) {
Expand Down
Loading