Skip to content
Merged
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
2 changes: 1 addition & 1 deletion apps/docs/public/rn-ui/llms.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# @masumdev/rn-ui — AI Developer & Context Guide

> Production-ready, typed, flat-aesthetic React Native & Expo component library.
> Version: 0.4.0 | 61 Components | Monorepo: Masum Dev
> Version: 0.4.2 | 61 Components | Monorepo: Masum Dev

This document is optimized for LLMs and AI coding assistants writing React Native / Expo applications using `@masumdev/rn-ui`.

Expand Down
34 changes: 33 additions & 1 deletion apps/docs/src/content/docs/rn-ui/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,43 @@ description: Release history and version notes for @masumdev/rn-ui.
import ChangelogTimeline from '@/components/ChangelogTimeline.astro';

export const releases = [
{
version: 'v0.4.2',
date: '2026-09-21',
tag: 'Patch Release',
isLatest: true,
changes: [
{
type: 'fix',
text: '<strong>Progress Animation Memory Leak</strong>: Added <code>cancelAnimation</code> cleanup to the <code>Progress</code> component for both indeterminate (<code>withRepeat</code>) and determinate (<code>withTiming</code>) animation branches, preventing orphaned UI-thread worklets on unmount or prop changes.',
},
],
},
{
version: 'v0.4.1',
date: '2026-09-21',
tag: 'Patch Release',
isLatest: false,
changes: [
{
type: 'fix',
text: '<strong>Spinner / Loader Rotation Crash-Proofing</strong>: Implemented safe degree normalization worklet (<code>toSafeDeg</code>) that strictly constrains rotation angles to finite positive values in <code>[0, 360)</code>. Completely eliminated <code>--&lt;num&gt;deg</code> and <code>NaNdeg</code> errors that caused fatal React Native transform parser exceptions.',
},
{
type: 'perf',
text: '<strong>Ultra-Lightweight Spinner Execution</strong>: Extracted <code>DotItem</code> and <code>BarItem</code> as memoized components outside render loops to eliminate React Hook rule violations and memory leaks. Implemented selective animation execution (rotation for circular/ring, pulse for dots/bars) to save UI thread cycles.',
},
{
type: 'fix',
text: '<strong>Defensive SVG & Transform Bounds</strong>: Clamped dimensions and radii to strictly positive numbers to prevent invalid SVG circle rendering, and safeguarded rotation transforms across <code>Toast</code> and <code>DatePicker</code>.',
},
],
},
{
version: 'v0.4.0',
date: '2026-09-19',
tag: 'Breaking Release',
isLatest: true,
isLatest: false,
changes: [
{
type: 'refactor',
Expand Down
24 changes: 24 additions & 0 deletions packages/rn-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to **@masumdev/rn-ui** will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.2] - 2026-09-21

### Fixed
- **Progress Animation Memory Leak**: Added `cancelAnimation` cleanup to the `Progress` component's `useEffect` for both indeterminate (`withRepeat`) and determinate (`withTiming`) animation branches. Previously, unmounting or toggling `indeterminate`/`animated` props could leave orphaned animation worklets on the UI thread.

---

## [0.4.1] - 2026-09-21

### Fixed
- **Spinner / Loader Crash-Proofing & Safe Degrees**:
- Implemented worklet angle normalization (`toSafeDeg`) ensuring rotation angles are strictly finite positive numbers in the range `[0, 360)`.
- Eliminated the double negative crash (`--<num>deg`) in counter-clockwise and reverse rotations which caused fatal native parser exceptions in React Native (`Invalid transform rotate: "--...deg"`).
- Eliminated `NaNdeg` and `-NaNdeg` exceptions caused by uninitialized or non-finite shared values.
- Safeguarded rotation transforms in `<Toast>` and `<DatePicker>` against `NaNdeg`.

### Optimized
- **Ultra-Lightweight Spinner Execution & Hook Compliance**:
- Extracted `DotItem` and `BarItem` outside array render loops as top-level `React.memo` components, eliminating React Hook rule violations, preventing memory leaks, and removing GC stuttering.
- Implemented selective animation execution: `rotation` only runs for `circular` and `ring` variants, while `pulse` only runs for `dots` and `bars`. Idle animations are cancelled and reset to 0 to save UI thread cycles.
- Added defensive clamping for dimensions (`dimension >= 8`, `strokeWidth >= 1`, `radius >= 1`) to guarantee SVG `<Circle>` attributes never receive non-positive or NaN values.

---

## [0.4.0] - 2026-09-19

### Removed
Expand Down
2 changes: 1 addition & 1 deletion packages/rn-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@masumdev/rn-ui",
"version": "0.4.0",
"version": "0.4.2",
"description": "A reusable React Native UI kit with typed theme tokens, light/dark mode, and composable core components.",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
Expand Down
8 changes: 5 additions & 3 deletions packages/rn-ui/src/components/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,14 @@ function AnimatedWheelItem({
"clamp",
);

const rotateX = `${interpolate(
(scrollY.value - itemPosition) / itemHeight,
const rawVal = itemHeight > 0 ? (scrollY.value - itemPosition) / itemHeight : 0;
const interpDeg = interpolate(
Number.isFinite(rawVal) ? rawVal : 0,
[-2, -1, 0, 1, 2],
[36, 18, 0, -18, -36],
"clamp",
)}deg`;
);
const rotateX = `${Number.isFinite(interpDeg) ? interpDeg : 0}deg`;

return {
opacity,
Expand Down
10 changes: 9 additions & 1 deletion packages/rn-ui/src/components/Progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type ViewStyle,
} from "react-native";
import Animated, {
cancelAnimation,
Easing,
useAnimatedStyle,
useSharedValue,
Expand Down Expand Up @@ -77,14 +78,21 @@ export function Progress({
-1,
false,
);
return;
return () => {
cancelAnimation(indeterminateOffset);
};
}

if (!animated) {
cancelAnimation(width);
width.value = progress;
return;
}
width.value = withTiming(progress, { duration: 220, easing: Easing.out(Easing.quad) });

return () => {
cancelAnimation(width);
};
}, [animated, indeterminate, progress, width, indeterminateOffset]);

const indicatorAnimatedStyle = useAnimatedStyle(() => {
Expand Down
Loading
Loading