@true-armor/ta-atoms2-public
v0.3.6
Published
textarea / input
Maintainers
Keywords
Readme
@true-armor/atoms
A comprehensive React component library built with TypeScript, Tailwind CSS, and Flowbite React. Features 20+ production-ready components with compound component patterns for maximum flexibility.
Whenever any changes are made to the components, please follow the steps below to publish an updated version.
1. Increment the patch version in package.json:
"version": "0.0.7",Create Build
npm run buildPublish the package to the npm registry:
npm publish📦 Installation
npm install @true-armor/atomsRequired Peer Dependencies
npm install react react-dom flowbite flowbite-react lucide-react⚙️ Setup
1. Import Styles
Add this to your main entry point (main.tsx or App.tsx):
import "@true-armor/atoms/styles.css";2. Configure Tailwind CSS
Update your tailwind.config.js or tailwind.config.cjs:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
// ⚠️ IMPORTANT: Include the library components
"./node_modules/@true-armor/atoms/dist/**/*.{js,cjs}",
],
theme: {
extend: {
fontFamily: {
sans: ['Montserrat', 'sans-serif'],
}
}
},
plugins: []
}3. Add Montserrat Font
Add to your index.html:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">🚀 Quick Start
import { ButtonTA, ButtonTALabel } from "@true-armor/atoms";
function App() {
return (
<ButtonTA buttonType="primary">
<ButtonTALabel>Click Me</ButtonTALabel>
</ButtonTA>
);
}📚 Components Reference
ButtonTA
Primary, secondary, and tertiary button variants with icon support.
Import:
import { ButtonTA, ButtonTALabel, ButtonTAIcon, ButtonTAContent } from "@true-armor/atoms";
import { Download } from "lucide-react";Usage:
// Simple button
<ButtonTA buttonType="primary">
<ButtonTALabel>Click Me</ButtonTALabel>
</ButtonTA>
// Button with icon
<ButtonTA buttonType="secondary">
<ButtonTAContent>
<ButtonTAIcon>
<Download size={16} />
</ButtonTAIcon>
<ButtonTALabel>Download</ButtonTALabel>
</ButtonTAContent>
</ButtonTA>
// Tertiary button
<ButtonTA buttonType="tertiary" onClick={() => console.log('clicked')}>
<ButtonTALabel>Tertiary</ButtonTALabel>
</ButtonTA>Props:
buttonType:"primary" | "secondary" | "tertiary"(default:"primary")onClick:() => voiddisabled:boolean
TabsTA
Tabbed interface with multiple variants.
Import:
import { TabsTA, TabsTAList, TabsTAItem, TabsTAPanel } from "@true-armor/atoms";
import { Home, Settings } from "lucide-react";Usage:
<TabsTA variant="tertiary" color="#173B4E">
<TabsTAList>
<TabsTAItem index={0} title="Home" icon={Home} />
<TabsTAItem index={1} title="Settings" icon={Settings} />
<TabsTAItem index={2} title="Profile" />
</TabsTAList>
<TabsTAPanel index={0}>
<div className="p-4">Home content goes here</div>
</TabsTAPanel>
<TabsTAPanel index={1}>
<div className="p-4">Settings content goes here</div>
</TabsTAPanel>
<TabsTAPanel index={2}>
<div className="p-4">Profile content goes here</div>
</TabsTAPanel>
</TabsTA>Props:
variant:"primary" | "secondary" | "tertiary"(default:"tertiary")color:string(default:"#173B4E")TabsTAItem.index:number(required)TabsTAItem.title:string(required)TabsTAItem.icon:React.ComponentType(optional)
ToolTipTA
Dark and light tooltip variants.
Import:
import { ToolTipTA, ToolTipTAContent, ToolTipTAArrow, ToolTipTAWrapper } from "@true-armor/atoms";Usage:
// Dark tooltip
<ToolTipTA type="dark">
<button className="px-4 py-2 bg-gray-900 text-white rounded">
Hover me (Dark)
</button>
<ToolTipTAWrapper>
<ToolTipTAContent>This is a dark tooltip</ToolTipTAContent>
<ToolTipTAArrow />
</ToolTipTAWrapper>
</ToolTipTA>
// Light tooltip
<ToolTipTA type="light">
<button className="px-4 py-2 bg-gray-500 text-white rounded">
Hover me (Light)
</button>
<ToolTipTAWrapper>
<ToolTipTAContent>This is a light tooltip</ToolTipTAContent>
<ToolTipTAArrow />
</ToolTipTAWrapper>
</ToolTipTA>Props:
type:"dark" | "light"(default:"dark")
StepperTA
Multi-step progress indicator.
Import:
import { StepperTA, StepperTAStep, StepperTAConnector } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [currentStep, setCurrentStep] = useState(2);
<StepperTA currentStep={currentStep} color="#173B4E">
<StepperTAStep label="Step 1" />
<StepperTAStep label="Step 2" />
<StepperTAStep label="Step 3" />
<StepperTAStep label="Step 4" />
</StepperTA>Props:
currentStep:number(default:1)color:string(default:"#173B4E")
DropDownTA
Dropdown selector with optional search functionality and avatar support. Uses a compound component pattern for maximum flexibility.
Import:
import {
DropDownTA,
DropDownTATrigger,
DropDownTAContent,
DropDownTASearch,
DropDownTAItem,
DropDownTAEmpty
} from "@true-armor/atoms";
import { useState } from "react";Usage:
const [isOpen, setIsOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
const [selectedUser, setSelectedUser] = useState(null);
const teamMembers = [
{
id: 1,
name: "Leslie Alexander",
avatar: "https://randomuser.me/api/portraits/women/44.jpg",
isYou: true
},
{
id: 2,
name: "Michael Gough",
avatar: "https://randomuser.me/api/portraits/men/32.jpg"
},
{
id: 3,
name: "Lana Byrd",
avatar: "https://randomuser.me/api/portraits/women/68.jpg"
},
];
const filteredItems = teamMembers.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
const handleSelect = (item) => {
setSelectedUser(item);
setIsOpen(false);
setSearchTerm("");
};
<DropDownTA isOpen={isOpen} onToggle={setIsOpen}>
<DropDownTATrigger
selectedItem={selectedUser}
placeholder="Select a team member"
getItemLabel={(item) => item.name}
/>
<DropDownTAContent>
<DropDownTASearch
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
placeholder="Search team members..."
/>
{filteredItems.map((item) => (
<DropDownTAItem
key={item.id}
item={item}
getItemLabel={(item) => item.name}
onSelect={handleSelect}
/>
))}
{filteredItems.length === 0 && (
<DropDownTAEmpty message="No results found" />
)}
</DropDownTAContent>
</DropDownTA>Simple Usage (without search):
const [isOpen, setIsOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
const users = [
{ id: 1, name: "John Doe", email: "[email protected]" },
{ id: 2, name: "Jane Smith", email: "[email protected]" },
];
<DropDownTA isOpen={isOpen} onToggle={setIsOpen}>
<DropDownTATrigger
selectedItem={selectedUser}
placeholder="Select user"
getItemLabel={(item) => item.name}
/>
<DropDownTAContent>
{users.map((user) => (
<DropDownTAItem
key={user.id}
item={user}
getItemLabel={(item) => item.name}
onSelect={(item) => {
setSelectedUser(item);
setIsOpen(false);
}}
/>
))}
</DropDownTAContent>
</DropDownTA>Props:
DropDownTA (Main Component)
isOpen:boolean(default:false) - Controlled state for dropdown visibilityonToggle:(open: boolean) => void(default:() => {}) - Callback when dropdown state changesclassName:string(optional) - Additional CSS classeschildren:React.ReactNode(required) - DropDownTATrigger and DropDownTAContent components
DropDownTATrigger
selectedItem:any(default:null) - Currently selected item objectplaceholder:string(default:"Select an item") - Placeholder text when no item is selectedgetItemLabel:(item: any) => string(default:(item) => item?.name || "") - Function to extract label from itemisOpen:boolean(auto-injected from DropDownTA) - Whether dropdown is openonToggle:(open: boolean) => void(auto-injected from DropDownTA) - Toggle handler
DropDownTAContent
isOpen:boolean(auto-injected from DropDownTA) - Whether dropdown is openchildren:React.ReactNode(required) - DropDownTASearch, DropDownTAItem, and DropDownTAEmpty components
DropDownTASearch
searchTerm:string(default:"") - Current search valueonSearchChange:(term: string) => void(default:() => {}) - Callback when search term changesplaceholder:string(default:"Search...") - Search input placeholder
DropDownTAItem
item:any(required) - Item object to displaygetItemLabel:(item: any) => string(default:(item) => item?.name || "") - Function to extract labelonSelect:(item: any) => void(default:() => {}) - Callback when item is selected
DropDownTAEmpty
message:string(default:"No results found") - Message to display when no items match
Features:
- ✅ Avatar support (displays
item.avatarif available) - ✅ "(You)" indicator (displays when
item.isYou === true) - ✅ Search/filter functionality
- ✅ Customizable item rendering
- ✅ Empty state handling
- ✅ Fully controlled component pattern
AccordionTA
Collapsible content panels.
Import:
import { AccordionTA, AccordionPanel, AccordionTitle, AccordionContent } from "@true-armor/atoms";Usage:
<AccordionTA>
<AccordionPanel>
<AccordionTitle>AI Findings</AccordionTitle>
<AccordionContent>
<p>Your content here...</p>
</AccordionContent>
</AccordionPanel>
<AccordionPanel>
<AccordionTitle>Analysis Results</AccordionTitle>
<AccordionContent>
<p>Detailed analysis results...</p>
</AccordionContent>
</AccordionPanel>
</AccordionTA>AlertTA
Alert messages with multiple types.
Import:
import { AlertTA, Alert, AlertIcon, AlertTitle, AlertDescription } from "@true-armor/atoms";
import { CheckCircle2, AlertCircle } from "lucide-react";Usage:
// Simple alert
<AlertTA
type="success"
title="Success!"
message="Operation completed successfully"
icon={<CheckCircle2 size={20} />}
/>
// Compound alert
<Alert type="error">
<AlertCircle size={20} className="text-red-600" />
<div>
<AlertTitle type="error">Error</AlertTitle>
<AlertDescription>Something went wrong</AlertDescription>
</div>
</Alert>Props:
type:"success" | "error" | "warning" | "info"title:stringmessage:stringicon:React.ReactNode
BadgeTA
Status indicators and labels.
Import:
import { BadgeTA, Badge, BadgeIcon, BadgeLabel } from "@true-armor/atoms";
import { CheckCircle2, Star, AlertTriangle } from "lucide-react";Usage:
// Simple badge
<BadgeTA label="Active" color="success" icon={CheckCircle2} />
<BadgeTA label="New" color="info" icon={Star} />
<BadgeTA label="Warning" color="warning" icon={AlertTriangle} />
// Compound badge
<Badge color="success">
<BadgeIcon icon={CheckCircle2} />
<BadgeLabel>Verified</BadgeLabel>
</Badge>Props:
color:"success" | "info" | "warning" | "failure" | "purple" | "yellow" | "blue" | "green" | "gray" | "indigo" | "pink"label:stringicon:React.ComponentType
BreadCrumbTA
Navigation breadcrumbs.
Import:
import { BreadCrumbTA, BreadCrumbItem, BreadCrumbSeperator } from "@true-armor/atoms";
import { Home, ChevronRight, ShoppingBag, Package } from "lucide-react";Usage:
<BreadCrumbTA>
<BreadCrumbItem label="Home" icon={Home} href="/" />
<BreadCrumbSeperator icon={ChevronRight} />
<BreadCrumbItem label="Products" icon={ShoppingBag} href="/products" />
<BreadCrumbSeperator icon={ChevronRight} />
<BreadCrumbItem label="Current" icon={Package} href="#" active />
</BreadCrumbTA>Props:
BreadCrumbItem.label:string(required)BreadCrumbItem.icon:React.ComponentTypeBreadCrumbItem.href:stringBreadCrumbItem.active:boolean
CheckboxTA
Custom checkbox component.
Import:
import { CheckboxTA, CheckboxTAInput, CheckboxTALabel } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [isChecked, setIsChecked] = useState(false);
<CheckboxTA checked={isChecked} onChange={setIsChecked}>
<CheckboxTAInput />
<CheckboxTALabel>Accept terms and conditions</CheckboxTALabel>
</CheckboxTA>Props:
checked:boolean(required)onChange:(checked: boolean) => void(required)disabled:boolean
ContextMenuTA
Context menu with actions.
Import:
import { ContextMenuTA, ContextMenuTAItem, ContextMenuTAIcon } from "@true-armor/atoms";
import { Edit2Icon, Trash2, Share2 } from "lucide-react";Usage:
<ContextMenuTA onSelect={(item) => console.log("Clicked:", item)}>
<ContextMenuTAItem label="Edit" icon={Edit2Icon} />
<ContextMenuTAItem label="Delete" icon={Trash2} />
<ContextMenuTAItem label="Share" icon={Share2} />
</ContextMenuTA>Props:
onSelect:(label: string) => voidContextMenuTAItem.label:string(required)ContextMenuTAItem.icon:React.ComponentType
CustomRadioTA
Custom radio button.
Import:
import { CustomRadioTA, CustomRadioTAInput, CustomRadioTALabel } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [selected, setSelected] = useState("option1");
<CustomRadioTA
name="demo-radio"
checked={selected === "option1"}
onChange={() => setSelected("option1")}
>
<CustomRadioTAInput />
<CustomRadioTALabel>Option 1</CustomRadioTALabel>
</CustomRadioTA>
<CustomRadioTA
name="demo-radio"
checked={selected === "option2"}
onChange={() => setSelected("option2")}
>
<CustomRadioTAInput />
<CustomRadioTALabel>Option 2</CustomRadioTALabel>
</CustomRadioTA>Props:
name:string(required)checked:boolean(required)onChange:() => void(required)disabled:boolean
DatePickerTA
Date selection component.
Import:
import { DatePickerTA, DatePickerTAInput, DatePickerTACalendar } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
<DatePickerTA
initialDate={selectedDate || new Date()}
onDateChange={(date) => setSelectedDate(date)}
>
<DatePickerTAInput placeholder="Select date" />
<DatePickerTACalendar />
</DatePickerTA>Props:
initialDate:Date(required)onDateChange:(date: Date) => void(required)
FileInputTA
File upload with drag-and-drop.
Import:
import { FileInputTA, FileInputTADropzone, FileInputTAIcon, FileInputTAText, FileInputTAButton } from "@true-armor/atoms";Usage:
<FileInputTA accept="image/*">
<FileInputTADropzone>
<FileInputTAIcon />
<FileInputTAText
mainText="Click to upload"
subText="SVG, PNG, JPG or GIF (MAX. 800x400px)"
/>
<FileInputTAButton label="Browse File" />
</FileInputTADropzone>
</FileInputTA>Props:
accept:string(e.g.,"image/*",".pdf")onChange:(files: FileList | null) => voidmultiple:boolean
InputTA
Text input and textarea.
Import:
import { InputTA, InputTALabel, InputTAField, InputTAError } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
// Text input
<InputTA id="email" multiline={false}>
<InputTALabel>Email Address</InputTALabel>
<InputTAField
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</InputTA>
// Textarea
<InputTA id="message" multiline={true} rows={5}>
<InputTALabel>Message</InputTALabel>
<InputTAField
placeholder="Enter your message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</InputTA>
// With error
<InputTA id="email-error">
<InputTALabel>Email</InputTALabel>
<InputTAField
type="email"
error="Invalid email address"
/>
<InputTAError>Invalid email address</InputTAError>
</InputTA>Props:
id:string(required)multiline:boolean(default:false)rows:number(for textarea)InputTAField.type:stringInputTAField.error:stringInputTAField.showPasswordToggle:boolean
ModalTA
Modal dialog.
Import:
import { ModalTA, ModalTAHeader, ModalTABody, ModalTAFooter } from "@true-armor/atoms";
import { ButtonTA, ButtonTALabel } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [isModalOpen, setIsModalOpen] = useState(false);
<ButtonTA buttonType="primary" onClick={() => setIsModalOpen(true)}>
<ButtonTALabel>Open Modal</ButtonTALabel>
</ButtonTA>
<ModalTA open={isModalOpen} onClose={() => setIsModalOpen(false)}>
<ModalTAHeader>Confirm Action</ModalTAHeader>
<ModalTABody>
<p>Are you sure you want to proceed?</p>
</ModalTABody>
<ModalTAFooter>
<ButtonTA buttonType="secondary" onClick={() => setIsModalOpen(false)}>
<ButtonTALabel>Cancel</ButtonTALabel>
</ButtonTA>
<ButtonTA buttonType="primary" onClick={() => setIsModalOpen(false)}>
<ButtonTALabel>Confirm</ButtonTALabel>
</ButtonTA>
</ModalTAFooter>
</ModalTA>Props:
open:boolean(required)onClose:() => void(required)
PaginationTA
Pagination controls.
Import:
import { PaginationTA, PaginationTAControls } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [currentPage, setCurrentPage] = useState(1);
// Default pagination
<PaginationTA
totalPages={10}
initialPage={currentPage}
onChange={(page) => setCurrentPage(page)}
/>
// Custom controls
<PaginationTA totalPages={10} initialPage={1} onChange={(page) => console.log(page)}>
<PaginationTAControls previousLabel="<" nextLabel=">" />
</PaginationTA>Props:
totalPages:number(required)initialPage:number(default:1)onChange:(page: number) => void(required)
ProgressBarTA
Progress indicator.
Import:
import { ProgressBarTA, ProgressBarTALabel, ProgressBarTABar } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [progress, setProgress] = useState(75);
<ProgressBarTA progress={progress} height="h-2">
<ProgressBarTALabel />
<ProgressBarTABar />
</ProgressBarTA>Props:
progress:number(0-100, required)height:string(Tailwind height class)
RangeSliderTA
Range slider input.
Import:
import { RangeSliderTA, RangeSliderTAInput, RangeSliderTATooltip, RangeSliderTAMarks, RangeSliderTAMark } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [sliderValue, setSliderValue] = useState(50);
// Controlled mode
<RangeSliderTA
min={0}
max={100}
value={sliderValue}
step={1}
onChange={(val) => setSliderValue(val)}
>
<RangeSliderTATooltip />
<RangeSliderTAInput />
<RangeSliderTAMarks />
</RangeSliderTA>
// Uncontrolled mode
<RangeSliderTA min={0} max={100} defaultValue={50} step={1}>
<RangeSliderTATooltip />
<RangeSliderTAInput />
<RangeSliderTAMarks />
</RangeSliderTA>Props:
min:number(default:0)max:number(default:100)value:number(controlled)defaultValue:number(uncontrolled)step:number(default:1)onChange:(value: number) => void
TableTA
Data table component.
Import:
import { TableTA } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [page, setPage] = useState(0);
const rowsPerPage = 10;
const columns = [
{ key: "name", label: "Name", header: "Name" },
{ key: "email", label: "Email", header: "Email" },
{ key: "status", label: "Status", header: "Status" }
];
const tableData = [
{ id: 1, name: "John Doe", email: "[email protected]", status: "Active" },
{ id: 2, name: "Jane Smith", email: "[email protected]", status: "Inactive" },
];
const paginatedData = tableData.slice(
page * rowsPerPage,
(page + 1) * rowsPerPage
);
<TableTA
columns={columns}
tableData={paginatedData}
length={tableData.length}
page={page}
setPage={setPage}
rowsCount={rowsPerPage}
/>Props:
columns:Array<{ key: string; label?: string; header?: string }>(required)tableData:any[](required)length:number(total items)page:numbersetPage:(page: number) => voidrowsCount:numberonRowClick:(row: any) => voidstriped:booleanhoverable:boolean
ToastTA
Toast notifications.
Import:
import { ToastTA, ToastTAItem, ToastTAIcon, ToastTAMessage, ToastTAProgress } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [toasts, setToasts] = useState([]);
function showToast(type) {
setToasts((prev) => [
...prev,
{
id: Date.now(),
type,
message: type === "success"
? "Operation completed successfully"
: "Something went wrong",
},
]);
}
<button onClick={() => showToast("success")}>Show Success Toast</button>
<button onClick={() => showToast("error")}>Show Error Toast</button>
<ToastTA data={toasts} position="top-right" duration={3000}>
{toasts.map((toast) => (
<ToastTAItem toast={toast} key={toast.id}>
<ToastTAIcon />
<ToastTAMessage />
<ToastTAProgress />
</ToastTAItem>
))}
</ToastTA>Props:
data:Array<{ id: number | string; type: "success" | "error" | "warning" | "info"; message: string }>(required)position:"top-right" | "top-left" | "bottom-right" | "bottom-left"duration:number(milliseconds, default:3000)
ToggleSwitchTA
Toggle switch component.
Import:
import { ToggleSwitchTA, ToggleSwitchTAControl, ToggleSwitchTALabel, ToggleSwitchTADescription } from "@true-armor/atoms";
import { useState } from "react";Usage:
const [isSwitchEnabled, setIsSwitchEnabled] = useState(false);
<ToggleSwitchTA checked={isSwitchEnabled} onChange={setIsSwitchEnabled} size="md">
<ToggleSwitchTAControl />
<div>
<ToggleSwitchTALabel>Enable notifications</ToggleSwitchTALabel>
<ToggleSwitchTADescription>Receive email notifications</ToggleSwitchTADescription>
</div>
</ToggleSwitchTA>Props:
checked:boolean(required)onChange:(checked: boolean) => void(required)size:"sm" | "md" | "lg"(default:"md")disabled:boolean
CalendarTA
Calendar component for date selection.
Import:
import { CalendarTA } from "@true-armor/atoms";Usage:
<CalendarTA
initialDate={new Date()}
onOk={(date) => console.log('Selected:', date)}
onCancel={() => console.log('Cancelled')}
/>Props:
initialDate:Date(required)onOk:(date: Date) => void(required)onCancel:() => void(required)
CardTA
Card component with icon, title, and body sections. Perfect for dashboard widgets, statistics cards, and feature highlights. Uses a compound component pattern for flexible composition.
Import:
import {
CardTA,
CardTAIcon,
CardTAContent,
CardTATitle,
CardTABody
} from "@true-armor/atoms";
import { UserCircle2, Users, Shield, TrendingUp } from "lucide-react";Usage:
// Basic card with all components
<CardTA borderColor="#C60000">
<CardTAIcon icon={UserCircle2} bg="#A80C0C" color="white" />
<CardTAContent>
<CardTATitle count={42} title="Total Users" />
<CardTABody instances={12} label="Active Now" />
</CardTAContent>
</CardTA>Multiple Cards Example:
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Users Card */}
<CardTA borderColor="#C60000">
<CardTAIcon icon={UserCircle2} bg="#A80C0C" color="white" />
<CardTAContent>
<CardTATitle count={42} title="Total Users" />
<CardTABody instances={12} label="Active Now" />
</CardTAContent>
</CardTA>
{/* Teams Card */}
<CardTA borderColor="#059669">
<CardTAIcon icon={Users} bg="#047857" color="white" />
<CardTAContent>
<CardTATitle count={8} title="Teams" />
<CardTABody instances={24} label="Members" />
</CardTAContent>
</CardTA>
{/* Security Card */}
<CardTA borderColor="#DC2626">
<CardTAIcon icon={Shield} bg="#B91C1C" color="white" />
<CardTAContent>
<CardTATitle count={156} title="Scans Completed" />
<CardTABody instances={3} label="In Progress" />
</CardTAContent>
</CardTA>
</div>Card with Title Only:
<CardTA borderColor="#173B4E">
<CardTAIcon icon={TrendingUp} bg="#1E40AF" color="white" />
<CardTAContent>
<CardTATitle title="Revenue Growth" />
<CardTABody instances="+24%" label="This Month" />
</CardTAContent>
</CardTA>Card with Custom Content:
<CardTA borderColor="#7C3AED" className="max-w-sm">
<CardTAIcon icon={Shield} bg="#6D28D9" color="white" />
<CardTAContent>
<CardTATitle count={99.9} title="Uptime" color="#7C3AED" />
<div className="mt-3">
<p className="text-sm text-gray-600">
System is running smoothly with no issues detected.
</p>
</div>
</CardTAContent>
</CardTA>Props:
CardTA (Main Component)
borderColor:string(default:"#C60000") - Left border color (6px solid border)className:string(optional) - Additional CSS classeschildren:React.ReactNode(required) - CardTAIcon and CardTAContent components
CardTAIcon
icon:React.ComponentType<any>(default:UserCircle2) - Icon component from lucide-reactbg:string(default:"#A80C0C") - Background color of the icon containercolor:string(default:"white") - Icon color
CardTAContent
children:React.ReactNode(required) - CardTATitle, CardTABody, or custom content
CardTATitle
count:number | string(optional) - Large number displayed at the toptitle:string(optional) - Title text displayed below the countcolor:string(default:"#111827") - Color of the count text
CardTABody
instances:number | string(optional) - Number or text displayed before the labellabel:string(default:"Instances") - Label text displayed after instancescolor:string(default:"#6B7280") - Color of both instances and label text
Features:
- ✅ Customizable left border color
- ✅ Icon support with custom background and color
- ✅ Flexible content layout
- ✅ Optional count and title display
- ✅ Optional instances and label display
- ✅ Fully customizable styling
- ✅ Responsive design ready
NoDataPageTA
Empty state component for displaying "no data" or "empty" states in your application. Perfect for when lists are empty, search results return nothing, or when users need guidance on what to do next. Uses a compound component pattern with context-based prop sharing for flexible composition.
Import:
import {
NoDataPageTA,
NoDataPageTAImg,
NoDataPageTABody,
NoDataPageTATitle,
NoDataPageTAButton
} from "@true-armor/atoms";Usage:
Basic Example (using context props):
<NoDataPageTA
title="No data available"
buttonText="Add New Item"
image="/path/to/empty-state-image.svg"
>
<NoDataPageTAImg />
<NoDataPageTABody>
<NoDataPageTATitle />
<NoDataPageTAButton onClick={() => console.log("Clicked!")} />
</NoDataPageTABody>
</NoDataPageTA>With Direct Props (overriding context):
<NoDataPageTA>
<NoDataPageTAImg src="/custom-empty-image.png" />
<NoDataPageTABody>
<NoDataPageTATitle>No users found</NoDataPageTATitle>
<NoDataPageTAButton onClick={handleAddUser}>
Create First User
</NoDataPageTAButton>
</NoDataPageTABody>
</NoDataPageTA>Minimal Example (only image):
<NoDataPageTA image="/empty-state.svg">
<NoDataPageTAImg />
</NoDataPageTA>Custom Content Example:
<NoDataPageTA title="No results found">
<NoDataPageTAImg src="/search-empty.svg" />
<NoDataPageTABody>
<NoDataPageTATitle>Try adjusting your search filters</NoDataPageTATitle>
<div className="mt-4">
<p className="text-gray-500 text-xs">
We couldn't find any items matching your criteria.
</p>
</div>
<NoDataPageTAButton onClick={handleResetFilters}>
Clear Filters
</NoDataPageTAButton>
</NoDataPageTABody>
</NoDataPageTA>Without Button:
<NoDataPageTA title="No items to display" image="/empty-box.svg">
<NoDataPageTAImg />
<NoDataPageTABody>
<NoDataPageTATitle />
</NoDataPageTABody>
</NoDataPageTA>Not Found / 404 Page Example:
import {
NoDataPageTA,
NoDataPageTAImg,
NoDataPageTABody,
NoDataPageTATitle,
NoDataPageTAButton
} from "@true-armor/atoms";
import { useNavigate } from "react-router-dom"; // or your routing library
function NotFoundPage() {
const navigate = useNavigate();
return (
<NoDataPageTA
title="Page Not Found"
buttonText="Go Back Home"
image="/404-illustration.svg"
>
<NoDataPageTAImg />
<NoDataPageTABody>
<NoDataPageTATitle>Oops! The page you're looking for doesn't exist.</NoDataPageTATitle>
<div className="mt-2">
<p className="text-gray-500 text-xs">
The page might have been moved or deleted.
</p>
</div>
<NoDataPageTAButton onClick={() => navigate("/")}>
Go Back Home
</NoDataPageTAButton>
</NoDataPageTABody>
</NoDataPageTA>
);
}Not Found with Custom Message:
<NoDataPageTA>
<NoDataPageTAImg src="/not-found.svg" />
<NoDataPageTABody>
<NoDataPageTATitle>404 - Page Not Found</NoDataPageTATitle>
<div className="mt-3 space-y-2">
<p className="text-gray-500 text-xs">
Sorry, we couldn't find the page you're looking for.
</p>
<p className="text-gray-400 text-xs">
Check the URL or go back to the homepage.
</p>
</div>
<NoDataPageTAButton onClick={() => window.history.back()}>
Go Back
</NoDataPageTAButton>
</NoDataPageTABody>
</NoDataPageTA>Simple Not Found (Minimal):
<NoDataPageTA
title="404 - Not Found"
image="/404.svg"
>
<NoDataPageTAImg />
<NoDataPageTABody>
<NoDataPageTATitle />
</NoDataPageTABody>
</NoDataPageTA>Props:
NoDataPageTA (Main Component)
title:string(optional) - Title text to display (can be overridden by NoDataPageTATitle children)buttonText:string(optional) - Button text (can be overridden by NoDataPageTAButton children)image:string(optional) - Image URL/path (can be overridden by NoDataPageTAImg src prop)children:React.ReactNode(required) - NoDataPageTAImg, NoDataPageTABody, and their children
NoDataPageTAImg
src:string(optional) - Image URL/path (overrides context image if provided)- Uses context
imageifsrcis not provided - Returns
nullif no image is available
NoDataPageTABody
children:React.ReactNode(required) - NoDataPageTATitle, NoDataPageTAButton, or custom content- Wraps content in a centered, text-aligned container
NoDataPageTATitle
children:React.ReactNode(optional) - Title text (overrides context title if provided)- Uses context
titleif children are not provided - Returns
nullif no title is available - Styled with
text-[#4B5563] text-sm
NoDataPageTAButton
children:React.ReactNode(optional) - Button text (overrides context buttonText if provided)onClick:() => void(optional) - Click handler function- Uses context
buttonTextif children are not provided - Returns
nullif no button text is available - Uses
ButtonTAwithbuttonType="primary"
Features:
- ✅ Context-based prop sharing for flexible composition
- ✅ Direct prop override support (props take precedence over context)
- ✅ Conditional rendering (components return null if no data provided)
- ✅ Centered, responsive layout
- ✅ Custom image support
- ✅ Integrated with ButtonTA component
- ✅ Flexible content structure
- ✅ Perfect for empty states, no results, and onboarding flows
Use Cases:
- Empty lists or tables
- No search results
- Empty cart or checkout
- No notifications
- Empty dashboard sections
- Onboarding flows
- Error recovery states
CircularLoaderTA
Animated circular loading spinner component with customizable colors, size, and animation speed. Perfect for indicating loading states, data fetching, or processing operations.
Import:
import { CircularLoaderTA, CircularLoaderTASpinner } from "@true-armor/atoms";Usage:
Basic Loader:
<CircularLoaderTA size={80}>
<CircularLoaderTASpinner />
</CircularLoaderTA>Custom Size:
<CircularLoaderTA size={120}>
<CircularLoaderTASpinner />
</CircularLoaderTA>Custom Colors:
const customColors = [
'#FF6B6B',
'#4ECDC4',
'#45B7D1',
'#FFA07A',
'#98D8C8'
];
<CircularLoaderTA size={100}>
<CircularLoaderTASpinner colors={customColors} />
</CircularLoaderTA>Custom Animation Speed:
<CircularLoaderTA size={80}>
<CircularLoaderTASpinner
animationDuration={1.5}
sticks={8}
/>
</CircularLoaderTA>Full Page Loader:
<div className="relative min-h-screen">
<CircularLoaderTA size={100}>
<CircularLoaderTASpinner />
</CircularLoaderTA>
</div>Props:
CircularLoaderTA (Main Component)
size:number(default:80) - Size of the loader in pixels (width and height)children:React.ReactNode(required) - CircularLoaderTASpinner component
CircularLoaderTASpinner
colors:string[](optional) - Array of color hex codes for the spinner. Defaults to a blue gradient palettesticks:number(default:11) - Number of spinner sticks/segmentsanimationDuration:number(default:2.2) - Animation duration in seconds
Features:
- ✅ Smooth color rotation animation
- ✅ Customizable size
- ✅ Custom color palette support
- ✅ Adjustable animation speed
- ✅ Configurable number of spinner segments
- ✅ Absolute positioning for overlay loading states
- ✅ Responsive and lightweight
Use Cases:
- Page loading indicators
- Data fetching states
- Form submission loading
- Image upload progress
- API request indicators
- Background processing states
GroupedMultiSelectTA
Hierarchical multi-select component with nested groups and checkboxes. Supports parent-child relationships with automatic selection propagation. Perfect for category selection, permission management, and nested data selection. Uses a compound component pattern with recursive rendering for unlimited nesting depth.
Import:
import { GroupedMultiSelectTA, GroupedMultiSelectTAItem } from "@true-armor/atoms";
import { useState } from "react";Usage:
Basic Example:
const [selected, setSelected] = useState<string[]>([]);
<GroupedMultiSelectTA value={selected} onChange={setSelected}>
<GroupedMultiSelectTAItem value="fruits">
Fruits
<GroupedMultiSelectTAItem value="apple">Apple</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="banana">Banana</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="orange">Orange</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="vegetables">
Vegetables
<GroupedMultiSelectTAItem value="carrot">Carrot</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="broccoli">Broccoli</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>Deeply Nested Groups (Permission Management):
const [permissions, setPermissions] = useState<string[]>([]);
<GroupedMultiSelectTA value={permissions} onChange={setPermissions}>
<GroupedMultiSelectTAItem value="admin">
Admin Access
<GroupedMultiSelectTAItem value="users">
User Management
<GroupedMultiSelectTAItem value="create-user">Create User</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="edit-user">Edit User</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="delete-user">Delete User</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="view-user">View User</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="settings">
Settings
<GroupedMultiSelectTAItem value="general">General Settings</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="security">Security Settings</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="notifications">Notification Settings</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="reports">
Reports
<GroupedMultiSelectTAItem value="view-reports">View Reports</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="export-reports">Export Reports</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>Controlled Component with Initial Selection:
const [selectedItems, setSelectedItems] = useState<string[]>(['apple', 'banana']);
<GroupedMultiSelectTA value={selectedItems} onChange={setSelectedItems}>
<GroupedMultiSelectTAItem value="fruits">
Fruits
<GroupedMultiSelectTAItem value="apple">Apple</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="banana">Banana</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="orange">Orange</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>
// Access selected values
console.log(selectedItems); // ['fruits', 'apple', 'banana']Category Selection with Subcategories:
const [categories, setCategories] = useState<string[]>([]);
<GroupedMultiSelectTA value={categories} onChange={setCategories}>
<GroupedMultiSelectTAItem value="electronics">
Electronics
<GroupedMultiSelectTAItem value="phones">
Phones
<GroupedMultiSelectTAItem value="smartphone">Smartphone</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="feature-phone">Feature Phone</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="computers">
Computers
<GroupedMultiSelectTAItem value="laptop">Laptop</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="desktop">Desktop</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="tablet">Tablet</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="clothing">
Clothing
<GroupedMultiSelectTAItem value="men">Men's</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="women">Women's</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="kids">Kids</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>File/Folder Structure Example:
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
<GroupedMultiSelectTA value={selectedFiles} onChange={setSelectedFiles}>
<GroupedMultiSelectTAItem value="documents">
Documents
<GroupedMultiSelectTAItem value="projects">
Projects
<GroupedMultiSelectTAItem value="project-1">Project 1</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="project-2">Project 2</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="reports">Reports</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="notes">Notes</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="images">
Images
<GroupedMultiSelectTAItem value="photos">Photos</GroupedMultiSelectTAItem>
<GroupedMultiSelectTAItem value="screenshots">Screenshots</GroupedMultiSelectTAItem>
</GroupedMultiSelectTAItem>
</GroupedMultiSelectTA>Props:
GroupedMultiSelectTA (Main Component)
value:string[](required) - Array of selected item values. When a parent is selected, all its children are automatically included.onChange:(value: string[]) => void(required) - Callback function triggered when selection changes. Receives the updated array of selected values.children:React.ReactNode(required) - GroupedMultiSelectTAItem components representing the tree structure
GroupedMultiSelectTAItem
value:string(required) - Unique identifier for the item. Must be unique across all items in the tree.children:React.ReactNode(required) - Label text (first text node) and/or nested GroupedMultiSelectTAItem components for creating sub-items
Features:
- ✅ Unlimited nesting depth (recursive rendering)
- ✅ Hierarchical/nested structure support
- ✅ Parent-child selection propagation
- ✅ Indeterminate state (partial selection) with minus icon
- ✅ Expandable/collapsible groups with chevron icons
- ✅ Fully controlled component pattern
- ✅ Automatic value collection (selecting parent includes all descendants)
- ✅ Visual feedback with checkmarks (✓) and minus (-) icons
- ✅ Hover states and smooth transitions
- ✅ Click to expand/collapse groups
- ✅ Independent checkbox selection
Selection Behavior:
- Selecting a parent: Automatically selects all children and descendants
- Deselecting a parent: Automatically deselects all children and descendants
- Selecting all children: Automatically selects the parent
- Partial selection: Shows indeterminate state (minus icon) when some but not all children are selected
- Leaf nodes: Can be selected independently without affecting siblings
- Value array: Contains all selected values including parents and all their descendants
Visual States:
- Unselected: Empty checkbox with white background
- Fully Selected: Checkmark icon (✓) with dark blue background (#173B4E)
- Partially Selected: Minus icon (-) with dark blue background (#173B4E)
- Expanded Group: ChevronDown icon pointing down
- Collapsed Group: ChevronRight icon pointing right
Use Cases:
- Permission management systems (role-based access control)
- Category selection with subcategories (e-commerce filters)
- File/folder selection (file managers, cloud storage)
- Feature toggles with groups (feature flags)
- Multi-level filtering (advanced search)
- Hierarchical data selection (organizational structures)
- Tag management with categories
- Settings configuration with grouped options
🎨 Styling
All components use:
- Tailwind CSS for utility classes
- Montserrat font family
- Flowbite React for base components
- Lucide React for icons
📝 TypeScript Support
Full TypeScript support with type definitions included. All components are fully typed.
🔗 Icon Library
This library works seamlessly with Lucide React:
npm install lucide-reactCommon icons used:
Home,Settings,UserCheckCircle2,AlertCircle,AlertTriangle,InfoDownload,Upload,Share2,Edit2Icon,Trash2ChevronRight,ChevronLeft,ChevronUp,ChevronDown
📄 License
MIT
🔗 Links
Made with ❤️ by TrueArmor
