All files / atoms/Input/mobile/Components/Atoms Label.tsx

100% Statements 19/19
62.96% Branches 17/27
100% Functions 3/3
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                  2x                 16x 16x     16x 12x   12x     12x   12x 8x     4x 1x   3x       16x 8x                     16x                     2x                               2x                       2x                       2x      
import React, { useMemo } from "react";
import { Text, StyleSheet, Platform } from "react-native";
import { cn } from "@sb/libs";
import { useInput } from "../Context/InputContext";
 
interface LabelProps {
  /** The text content to display as the label */
  label: string;
  /** Whether the input is currently focused */
  isFocused?: boolean;
  /** Whether the input has text content */
  hasText?: boolean;
  /** Whether to use floating label behavior */
  floatingLabel?: boolean;
  /** The direction of the input */
  direction?: "rtl" | "ltr";
  /** Whether the input is disabled */
  disabled?: boolean;
  /** The style of the placeholder */
  placeholderStyle?: string;
}
 
/**
 * Label component for input fields with support for floating labels and status styling
 */
const Label: React.FC<LabelProps> = ({
  label,
  isFocused = false,
  hasText = false,
  floatingLabel = false,
  direction = "ltr",
  disabled = false,
  placeholderStyle,
}) => {
  const { status, size } = useInput();
  const isFloating = floatingLabel;
 
  // Memoize styles to prevent unnecessary recalculations
  const labelStyles = useMemo(() => {
    const baseStyle = [styles.labelBase, FONTS[size]];
    const focusedStyle =
      isFocused || hasText
        ? [styles.labelFocused, FOCUSED_FONTS[size]]
        : undefined;
    const combinedStyles = StyleSheet.flatten([baseStyle, focusedStyle]);
 
    if (isFloating) {
      return combinedStyles;
    }
 
    if (!isFocused && !hasText) {
      return StyleSheet.flatten(baseStyle);
    }
    return StyleSheet.flatten([styles.hidden]);
  }, [isFloating, isFocused, hasText, size]);
 
  // Memoize className to prevent unnecessary recalculations
  const labelClassName = useMemo(() => {
    return cn(
      "text-primary mb-1 w-full font-normal",
      status === "error" && "text-status-error",
      status === "success" && "text-status-success",
      status === "warning" && "text-status-warning",
      direction === "rtl" && "text-right",
      disabled && "text-input-disabled-text",
      placeholderStyle
    );
  }, [status, direction, disabled, placeholderStyle]);
 
  return (
    <Text
      style={labelStyles}
      className={labelClassName}
      accessibilityRole="text"
    >
      {label}
    </Text>
  );
};
 
const styles = StyleSheet.create({
  labelBase: {
    marginBottom: 0,
    marginTop: Platform.OS === "android" ? -3 : 0,
    alignItems: "center",
  },
  hidden: {
    display: "none",
  },
  labelFocused: {},
  labelRTL: {
    textAlign: "right",
    paddingRight: 5,
  },
});
 
const FONTS = StyleSheet.create({
  small: {
    fontSize: 10,
  },
  default: {
    fontSize: 14,
  },
  large: {
    fontSize: 18,
  },
});
 
const FOCUSED_FONTS = StyleSheet.create({
  small: {
    fontSize: 8,
  },
  default: {
    fontSize: 12,
  },
  large: {
    fontSize: 16,
  },
});
 
Label.displayName = "Label";
 
export default Label;