Skip to content

feat(pos-app): add BigAmountInput with per-character animations#400

Merged
ignaciosantise merged 6 commits intomainfrom
feat/amount-input-animation
Mar 2, 2026
Merged

feat(pos-app): add BigAmountInput with per-character animations#400
ignaciosantise merged 6 commits intomainfrom
feat/amount-input-animation

Conversation

@ignaciosantise
Copy link
Copy Markdown
Collaborator

@ignaciosantise ignaciosantise commented Feb 27, 2026

Summary

  • Replace static amount text with animated BigAmountInput component featuring per-character enter/exit animations (fade + slide from bottom)
  • Support symbolPosition prop for currencies like EUR where the symbol goes on the right (10.00€ vs $10.00)
  • Auto-scale font size for large amounts, limit decimal input to 2 places
  • Tune animation duration to 200ms for responsive feel when typing quickly

Test plan

  • Open POS app amount screen, type numbers on the numeric keyboard - characters should animate in individually
  • Delete numbers - characters should animate out with fade + slide
  • Type large amounts (8+ digits) - font should scale down smoothly
  • Type decimal amounts - placeholder zeros should appear in secondary color
  • Switch currency to EUR in settings - symbol should appear on the right side
  • Type quickly - display should feel responsive (200ms animations)

🤖 Generated with Claude Code

ignaciosantise and others added 4 commits January 29, 2026 14:42
- Add animated currency display with enter/exit animations per character
- Support locale-aware formatting and thousand separators
- Include blinking cursor with position tracking
- Auto-scale font size for large amounts
- Limit decimal input to 2 places

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolve conflict in amount.tsx:
- Keep BigAmountInput with dynamic currency symbol
- Preserve decimal limit logic
- Keep platform-specific styling from main

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…mation

# Conflicts:
#	dapps/pos-app/app/amount.tsx
Replace static amount display with animated BigAmountInput component
featuring per-character enter/exit animations, dynamic font scaling
for large amounts, and currency symbol position support (left/right).

- Add AnimatedCharacter with fade+slide enter/exit transitions
- Add AnimatedCursor with blinking and position tracking
- Support locale-aware number formatting with thousand separators
- Handle symbolPosition for currencies like EUR (right-side symbol)
- Limit decimal input to 2 places
- Tune animation duration to 200ms for responsive feel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 27, 2026 18:52
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-native-examples Ready Ready Preview, Comment Feb 27, 2026 7:10pm

Request Review

@claude
Copy link
Copy Markdown

claude bot commented Feb 27, 2026

Claude finished @ignaciosantise's task in 2m 31s —— View job


Code Review

  • Gather context
  • Read changed files
  • Run breaking changes analysis
  • Post review findings

No breaking changes detected (all new props are optional with backward-compatible defaults).

Found 4 issue(s)

Issue 1: transformOrigin is not a supported React Native style prop

ID: animated-character-transform-origin-a3f1
File: dapps/pos-app/components/big-amount-input/components/AnimatedCharacter.tsx:61
Severity: HIGH
Category: code_quality

Context:

  • Pattern: transformOrigin: "left center" used inside useAnimatedStyle
  • Risk: This is a CSS/web property. React Native's layout engine does not support transformOrigin — it will be silently ignored. Transforms always originate from the center of the element, so the scale animation will grow/shrink from center rather than the left edge, causing characters to visually shift position during scale transitions.
  • Impact: Incorrect scale animation origin on iOS and Android; characters drift horizontally during font-scaling animations
  • Trigger: Any time scale < 1 (i.e., when the input exceeds 6 characters)

Recommendation: Remove transformOrigin and compensate for the scale offset using translateX:

// Instead of transformOrigin: "left center", manually shift by half the width delta
transform: [
  { translateX: withTiming(positionX - (characterWidth * (1 - scale)) / 2, TIMING_CONFIG) },
  { translateY: translateY.value },
  { scale: withTiming(scale, TIMING_CONFIG) },
],

Issue 2: Cursor animation not cancelled on unmount (memory leak)

ID: animated-cursor-repeat-cancel-b2e7
File: dapps/pos-app/components/big-amount-input/components/AnimatedCursor.tsx:33
Severity: MEDIUM
Category: performance

Context:

  • Pattern: withRepeat(..., -1, false) starts an infinite animation on opacity shared value but the useEffect has no cleanup function
  • Risk: When the component unmounts (e.g., navigation away from amount screen), the infinite animation continues running on the shared value in the Reanimated worklet runtime. This leaks animation resources and can cause callbacks into unmounted component state.
  • Impact: Wasted animation thread resources; potential crashes in edge cases on navigation
  • Trigger: Component unmount while isFocused=true and blinkEnabled=true

Recommendation:

import { cancelAnimation } from "react-native-reanimated";

useEffect(() => {
  // ... existing animation logic ...
  return () => {
    cancelAnimation(opacity);
  };
}, [isFocused, opacity, blinkEnabled]);

Issue 3: animated-amount-input.tsx is unused dead code

ID: animated-amount-input-dead-code-c4d9
File: dapps/pos-app/components/animated-amount-input.tsx
Severity: MEDIUM
Category: code_quality

Context:

  • Pattern: New file added but not imported anywhere — amount.tsx imports BigAmountInput from big-amount-input/, not this file
  • Risk: Dead code adds confusion about which component is the canonical animated input implementation. It also duplicates cursor animation logic without the memory-leak fix needed above.
  • Impact: Maintainability burden; developers may be confused about which component to use or extend
  • Trigger: N/A — file is never executed

Recommendation: Delete animated-amount-input.tsx or, if it's intended as an alternative simpler implementation, add it to the index export and document it. Fix this →


Issue 4: getDeviceLocale is a hardcoded stub, not actual device locale detection

ID: format-amount-get-device-locale-stub-d5c2
File: dapps/pos-app/components/big-amount-input/utils/formatAmount.ts:21
Severity: MEDIUM
Category: code_quality

Context:

  • Pattern: getDeviceLocale() unconditionally returns "en-US" regardless of the device's actual locale setting
  • Risk: The function name and the SupportedLocale type ("en-US" | "fr-FR" | "de-DE" | "nl-NL") imply real locale detection. Users on French or German devices will see US-style number formatting (dot as decimal separator, comma as thousands separator) even if their locale uses the opposite convention. The locale prop on BigAmountInput works correctly, but the fallback does not.
  • Impact: Incorrect number formatting for non-US users who rely on auto-detection
  • Trigger: Any user not explicitly passing the locale prop to BigAmountInput

Recommendation: Either implement real detection or rename to getDefaultLocale to be honest about behavior:

// Option A: rename to signal it's not dynamic
export function getDefaultLocale(): SupportedLocale {
  return DEFAULT_LOCALE;
}

// Option B: real detection (requires expo-localization or RN APIs)
import * as Localization from 'expo-localization';
export function getDeviceLocale(): SupportedLocale {
  const locale = Localization.locale; // e.g., "fr-FR"
  const supported: SupportedLocale[] = ["en-US", "fr-FR", "de-DE", "nl-NL"];
  return supported.find(l => locale.startsWith(l.split('-')[0])) ?? "en-US";
}

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new BigAmountInput component that replaces the static amount text display with an animated component featuring per-character enter/exit animations. The component supports currency symbol positioning (left/right), auto-scaling for large amounts, decimal input limiting, and customizable animation timing.

Changes:

  • Added new BigAmountInput component with per-character fade + slide animations
  • Implemented support for symbolPosition prop to handle currencies like EUR where the symbol appears on the right
  • Added utility functions for amount formatting, character parsing, and layout calculations
  • Integrated the component into the amount screen, replacing the previous static text display

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
dapps/pos-app/components/big-amount-input/utils/getCharactersArray.ts Parses formatted amounts into character arrays with stable keys for animations
dapps/pos-app/components/big-amount-input/utils/formatAmount.ts Handles amount formatting, decimal separator detection, and input cleaning
dapps/pos-app/components/big-amount-input/index.ts Main export file for the BigAmountInput component and types
dapps/pos-app/components/big-amount-input/hooks/useAnimatedNumberValue.ts Hook for managing formatted values and character data for animations
dapps/pos-app/components/big-amount-input/hooks/useAnimatedNumberLayout.ts Hook for calculating character positions, widths, and scaling for layout
dapps/pos-app/components/big-amount-input/components/AnimatedNumberCharacters.tsx Renders the collection of animated characters with separators
dapps/pos-app/components/big-amount-input/components/AnimatedNumber.tsx Main animated number display component that orchestrates character and cursor rendering
dapps/pos-app/components/big-amount-input/components/AnimatedCursor.tsx Animated blinking cursor component with position tracking
dapps/pos-app/components/big-amount-input/components/AnimatedCharacter.tsx Individual character component with enter/exit animations
dapps/pos-app/components/big-amount-input/BigAmountInput.types.ts TypeScript type definitions for component props
dapps/pos-app/components/big-amount-input/BigAmountInput.tsx Main component wrapper that integrates AnimatedNumber
dapps/pos-app/components/animated-amount-input.tsx Appears to be an older version of animated input (potentially unused)
dapps/pos-app/app/amount.tsx Integration point replacing static text with BigAmountInput component

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Remove unsupported transformOrigin, compensate with translateX offset
- Add cancelAnimation cleanup in cursor useEffect to prevent memory leak
- Delete unused animated-amount-input.tsx
- Rename getDeviceLocale to getDefaultLocale (hardcoded stub)
- Remove unused normalizeRawValue export
- Import SymbolPosition from @/utils/currency instead of redefining

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ignaciosantise ignaciosantise merged commit 4956b4c into main Mar 2, 2026
8 checks passed
@ignaciosantise ignaciosantise deleted the feat/amount-input-animation branch March 2, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants