alope-ui 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -96,16 +96,23 @@ export default App;
96
96
 
97
97
  - [🪗 Accordion](#accordion) - Collapsible content panels
98
98
  - [🍞 Breadcrumb](#breadcrumb) - Navigation trail
99
+ - [📑 Tabs](#tabs) - Tabbed navigation interface
100
+ - [📄 Pagination](#pagination) - Page navigation controls
99
101
 
100
102
  ### Feedback Components
101
103
 
102
104
  - [⚠️ Alert](#alert) - Status messages and notifications
103
105
  - [🍞 Toast](#toast) - Temporary notification messages
106
+ - [💡 Tooltip](#tooltip) - Contextual information on hover
107
+ - [⏳ Spinner](#spinner) - Loading indicators
108
+ - [💀 Skeleton](#skeleton) - Content loading placeholders
104
109
 
105
110
  ### Data Display
106
111
 
107
112
  - [🏷️ Badge](#badge) - Status indicators and labels
108
113
  - [🃏 Card](#card) - Content containers
114
+ - [📊 Table](#table) - Tabular data display
115
+ - [👤 Avatar](#avatar) - User profile images
109
116
 
110
117
  ### Form Controls
111
118
 
@@ -113,6 +120,9 @@ export default App;
113
120
  - [🔘 Radio Input](#radio-input) - Single choice selections
114
121
  - [📝 Select Input](#select-input) - Dropdown selections
115
122
  - [⌨️ Text Input](#text-input) - Text input fields
123
+ - [📝 Textarea](#textarea) - Multi-line text input
124
+ - [🔄 Toggle](#toggle) - Switch controls
125
+ - [📁 File Upload](#file-upload) - File selection and upload
116
126
 
117
127
  ### Interactive Elements
118
128
 
@@ -1703,6 +1713,2388 @@ const BasicExample = () => {
1703
1713
  />
1704
1714
  ```
1705
1715
 
1716
+ ---
1717
+
1718
+ ## Pagination
1719
+
1720
+ The Pagination component provides navigation controls for paginated content, supporting both simplified and full pagination modes with customizable appearance.
1721
+
1722
+ ### Import
1723
+
1724
+ ```jsx
1725
+ import { Pagination } from "alope-ui";
1726
+ ```
1727
+
1728
+ ### Props
1729
+
1730
+ | Prop | Type | Default | Description |
1731
+ | --------------- | --------------------------------- | --------- | ----------------------------------------------------- |
1732
+ | `currentPage` | `number` | `required`| Current active page number |
1733
+ | `onPageChange` | `(page: number) => void` | `required`| Callback function when page changes |
1734
+ | `count` | `number` | `undefined` | Total number of items |
1735
+ | `pageSize` | `number` | `undefined` | Number of items per page |
1736
+ | `totalPages` | `number` | `undefined` | Total number of pages (deprecated, use count + pageSize) |
1737
+ | `format` | `'none' \| 'long'` | `'none'` | Format for displaying page info |
1738
+ | `simplified` | `boolean` | `false` | Show simplified pagination (prev/next only) |
1739
+ | `asLink` | `boolean` | `false` | Render pagination buttons as links |
1740
+ | `getPageHref` | `(page: number) => string` | `undefined` | Custom href generator for pagination links |
1741
+ | `siblingCount` | `number` | `1` | Number of page buttons to show on each side |
1742
+ | `prevIcon` | `React.ReactNode` | `'<'` | Custom icon for previous button |
1743
+ | `nextIcon` | `React.ReactNode` | `'>'` | Custom icon for next button |
1744
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Size variant of pagination buttons |
1745
+
1746
+ ### Basic Pagination
1747
+
1748
+ ```jsx
1749
+ import { useState } from "react";
1750
+ import { Pagination } from "alope-ui";
1751
+
1752
+ const BasicExample = () => {
1753
+ const [currentPage, setCurrentPage] = useState(1);
1754
+
1755
+ return (
1756
+ <Pagination
1757
+ currentPage={currentPage}
1758
+ count={100}
1759
+ pageSize={10}
1760
+ onPageChange={setCurrentPage}
1761
+ />
1762
+ );
1763
+ };
1764
+ ```
1765
+
1766
+ ### Simplified Pagination
1767
+
1768
+ ```jsx
1769
+ const SimplifiedExample = () => {
1770
+ const [currentPage, setCurrentPage] = useState(1);
1771
+
1772
+ return (
1773
+ <Pagination
1774
+ currentPage={currentPage}
1775
+ count={100}
1776
+ pageSize={10}
1777
+ onPageChange={setCurrentPage}
1778
+ simplified
1779
+ />
1780
+ );
1781
+ };
1782
+ ```
1783
+
1784
+ ### Pagination with Format
1785
+
1786
+ ```jsx
1787
+ {/* None Format - Shows page numbers */}
1788
+ <Pagination
1789
+ currentPage={currentPage}
1790
+ count={100}
1791
+ pageSize={10}
1792
+ onPageChange={setCurrentPage}
1793
+ format="none"
1794
+ simplified
1795
+ />
1796
+
1797
+ {/* Long Format - Shows item range */}
1798
+ <Pagination
1799
+ currentPage={currentPage}
1800
+ count={100}
1801
+ pageSize={10}
1802
+ onPageChange={setCurrentPage}
1803
+ format="long"
1804
+ simplified
1805
+ />
1806
+ ```
1807
+
1808
+ ### Sizes
1809
+
1810
+ ```jsx
1811
+ {/* Small Size */}
1812
+ <Pagination
1813
+ currentPage={currentPage}
1814
+ count={100}
1815
+ pageSize={10}
1816
+ onPageChange={setCurrentPage}
1817
+ size="sm"
1818
+ />
1819
+
1820
+ {/* Medium Size */}
1821
+ <Pagination
1822
+ currentPage={currentPage}
1823
+ count={100}
1824
+ pageSize={10}
1825
+ onPageChange={setCurrentPage}
1826
+ size="md"
1827
+ />
1828
+
1829
+ {/* Large Size */}
1830
+ <Pagination
1831
+ currentPage={currentPage}
1832
+ count={100}
1833
+ pageSize={10}
1834
+ onPageChange={setCurrentPage}
1835
+ size="lg"
1836
+ />
1837
+ ```
1838
+
1839
+ ### Custom Sibling Count
1840
+
1841
+ ```jsx
1842
+ {/* Show 2 page buttons on each side */}
1843
+ <Pagination
1844
+ currentPage={currentPage}
1845
+ count={100}
1846
+ pageSize={10}
1847
+ onPageChange={setCurrentPage}
1848
+ siblingCount={2}
1849
+ />
1850
+ ```
1851
+
1852
+ ### Custom Icons
1853
+
1854
+ ```jsx
1855
+ import { ChevronLeft, ChevronRight } from "lucide-react";
1856
+
1857
+ <Pagination
1858
+ currentPage={currentPage}
1859
+ count={100}
1860
+ pageSize={10}
1861
+ onPageChange={setCurrentPage}
1862
+ prevIcon={<ChevronLeft size={16} />}
1863
+ nextIcon={<ChevronRight size={16} />}
1864
+ />
1865
+ ```
1866
+
1867
+ ### As Links
1868
+
1869
+ ```jsx
1870
+ <Pagination
1871
+ currentPage={currentPage}
1872
+ count={100}
1873
+ pageSize={10}
1874
+ onPageChange={setCurrentPage}
1875
+ asLink
1876
+ getPageHref={(page) => `/products?page=${page}`}
1877
+ />
1878
+ ```
1879
+
1880
+ ### Using TotalPages (Legacy)
1881
+
1882
+ ```jsx
1883
+ {/* This approach is deprecated, prefer using count + pageSize */}
1884
+ <Pagination
1885
+ currentPage={currentPage}
1886
+ totalPages={10}
1887
+ onPageChange={setCurrentPage}
1888
+ />
1889
+ ```
1890
+
1891
+ ---
1892
+
1893
+ ## Table
1894
+
1895
+ The Table component provides a flexible and feature-rich data table with support for custom rendering, row selection, striping, sticky headers, and responsive design.
1896
+
1897
+ ### Import
1898
+
1899
+ ```jsx
1900
+ import { Table } from "alope-ui";
1901
+ import type { Column } from "alope-ui";
1902
+ ```
1903
+
1904
+ ### Props
1905
+
1906
+ | Prop | Type | Default | Description |
1907
+ | ---------------------- | --------------------------------- | ---------- | ---------------------------------------------- |
1908
+ | `columns` | `Column<T>[]` | `required` | Array of column configurations |
1909
+ | `data` | `T[]` | `required` | Array of data objects to display |
1910
+ | `striped` | `boolean` | `false` | Enable alternating row colors |
1911
+ | `stripeColor` | `string` | `undefined`| Custom color for striped rows |
1912
+ | `headerClassName` | `string` | `""` | CSS class for table header |
1913
+ | `dataColumnClassName` | `string` | `""` | CSS class for data cells |
1914
+ | `dataRowClassName` | `string` | `""` | CSS class for data rows |
1915
+ | `tableClassName` | `string` | `""` | CSS class for table element |
1916
+ | `captionClassName` | `string` | `""` | CSS class for caption |
1917
+ | `containerClassName` | `string` | `""` | CSS class for container |
1918
+ | `customEmptyData` | `React.ReactNode` | `undefined`| Custom message when no data |
1919
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant of table |
1920
+ | `caption` | `string` | `undefined`| Table caption text |
1921
+ | `captionPosition` | `'top' \| 'bottom'` | `'bottom'` | Position of caption |
1922
+ | `stickyHeader` | `boolean` | `false` | Make header sticky on scroll |
1923
+ | `selectable` | `boolean` | `false` | Enable row selection with checkboxes |
1924
+ | `onSelectionChange` | `(selected: T[]) => void` | `undefined`| Callback when selection changes |
1925
+ | `selectedRows` | `T[]` | `[]` | Array of selected rows |
1926
+
1927
+ ### Column Type
1928
+
1929
+ ```typescript
1930
+ type Column<T> = {
1931
+ header: string;
1932
+ accessor: keyof T;
1933
+ render?: (value: T[keyof T], row: T) => React.ReactNode;
1934
+ };
1935
+ ```
1936
+
1937
+ ### Basic Table
1938
+
1939
+ ```jsx
1940
+ import { Table } from "alope-ui";
1941
+ import type { Column } from "alope-ui";
1942
+
1943
+ type User = {
1944
+ id: number;
1945
+ name: string;
1946
+ email: string;
1947
+ role: string;
1948
+ };
1949
+
1950
+ const columns: Column<User>[] = [
1951
+ { header: "Name", accessor: "name" },
1952
+ { header: "Email", accessor: "email" },
1953
+ { header: "Role", accessor: "role" },
1954
+ ];
1955
+
1956
+ const data: User[] = [
1957
+ { id: 1, name: "John Doe", email: "john@example.com", role: "Admin" },
1958
+ { id: 2, name: "Jane Smith", email: "jane@example.com", role: "User" },
1959
+ { id: 3, name: "Bob Johnson", email: "bob@example.com", role: "Manager" },
1960
+ ];
1961
+
1962
+ const BasicExample = () => {
1963
+ return <Table columns={columns} data={data} />;
1964
+ };
1965
+ ```
1966
+
1967
+ ### Striped Table
1968
+
1969
+ ```jsx
1970
+ <Table columns={columns} data={data} striped />
1971
+
1972
+ {/* With custom stripe color */}
1973
+ <Table
1974
+ columns={columns}
1975
+ data={data}
1976
+ striped
1977
+ stripeColor="bg-blue-50 dark:bg-blue-900"
1978
+ />
1979
+ ```
1980
+
1981
+ ### Sizes
1982
+
1983
+ ```jsx
1984
+ {/* Small Size */}
1985
+ <Table columns={columns} data={data} size="sm" />
1986
+
1987
+ {/* Medium Size */}
1988
+ <Table columns={columns} data={data} size="md" />
1989
+
1990
+ {/* Large Size */}
1991
+ <Table columns={columns} data={data} size="lg" />
1992
+ ```
1993
+
1994
+ ### Custom Rendering
1995
+
1996
+ ```jsx
1997
+ const columns: Column<User>[] = [
1998
+ { header: "Name", accessor: "name" },
1999
+ { header: "Email", accessor: "email" },
2000
+ {
2001
+ header: "Role",
2002
+ accessor: "role",
2003
+ render: (value, row) => (
2004
+ <span
2005
+ className={
2006
+ value === "Admin"
2007
+ ? "bg-red-100 text-red-800 px-2 py-1 rounded"
2008
+ : "bg-blue-100 text-blue-800 px-2 py-1 rounded"
2009
+ }
2010
+ >
2011
+ {value}
2012
+ </span>
2013
+ ),
2014
+ },
2015
+ ];
2016
+
2017
+ <Table columns={columns} data={data} />;
2018
+ ```
2019
+
2020
+ ### Selectable Rows
2021
+
2022
+ ```jsx
2023
+ import { useState } from "react";
2024
+
2025
+ const SelectableExample = () => {
2026
+ const [selectedRows, setSelectedRows] = useState<User[]>([]);
2027
+
2028
+ return (
2029
+ <Table
2030
+ columns={columns}
2031
+ data={data}
2032
+ selectable
2033
+ selectedRows={selectedRows}
2034
+ onSelectionChange={setSelectedRows}
2035
+ />
2036
+ );
2037
+ };
2038
+ ```
2039
+
2040
+ ### Sticky Header
2041
+
2042
+ ```jsx
2043
+ <Table columns={columns} data={data} stickyHeader />
2044
+ ```
2045
+
2046
+ ### With Caption
2047
+
2048
+ ```jsx
2049
+ {/* Caption at bottom */}
2050
+ <Table
2051
+ columns={columns}
2052
+ data={data}
2053
+ caption="User Management Table"
2054
+ captionPosition="bottom"
2055
+ />
2056
+
2057
+ {/* Caption at top */}
2058
+ <Table
2059
+ columns={columns}
2060
+ data={data}
2061
+ caption="User Management Table"
2062
+ captionPosition="top"
2063
+ />
2064
+ ```
2065
+
2066
+ ### Custom Empty State
2067
+
2068
+ ```jsx
2069
+ <Table
2070
+ columns={columns}
2071
+ data={[]}
2072
+ customEmptyData={
2073
+ <div className="flex flex-col items-center gap-2">
2074
+ <span className="text-gray-500">No users found</span>
2075
+ <button className="px-4 py-2 bg-blue-500 text-white rounded">
2076
+ Add User
2077
+ </button>
2078
+ </div>
2079
+ }
2080
+ />
2081
+ ```
2082
+
2083
+ ### Custom Styling
2084
+
2085
+ ```jsx
2086
+ <Table
2087
+ columns={columns}
2088
+ data={data}
2089
+ containerClassName="shadow-lg rounded-lg"
2090
+ tableClassName="border-2 border-blue-500"
2091
+ headerClassName="bg-blue-600 text-white"
2092
+ dataRowClassName="hover:bg-blue-50"
2093
+ dataColumnClassName="text-gray-700"
2094
+ />
2095
+ ```
2096
+
2097
+ ### Complete Example
2098
+
2099
+ ```jsx
2100
+ import { useState } from "react";
2101
+ import { Table } from "alope-ui";
2102
+ import type { Column } from "alope-ui";
2103
+
2104
+ type Product = {
2105
+ id: number;
2106
+ name: string;
2107
+ price: number;
2108
+ stock: number;
2109
+ status: string;
2110
+ };
2111
+
2112
+ const ProductTable = () => {
2113
+ const [selectedRows, setSelectedRows] = useState<Product[]>([]);
2114
+
2115
+ const columns: Column<Product>[] = [
2116
+ { header: "Product Name", accessor: "name" },
2117
+ {
2118
+ header: "Price",
2119
+ accessor: "price",
2120
+ render: (value) => `$${value.toFixed(2)}`,
2121
+ },
2122
+ { header: "Stock", accessor: "stock" },
2123
+ {
2124
+ header: "Status",
2125
+ accessor: "status",
2126
+ render: (value) => (
2127
+ <span
2128
+ className={
2129
+ value === "Available"
2130
+ ? "bg-green-100 text-green-800 px-2 py-1 rounded"
2131
+ : "bg-gray-100 text-gray-800 px-2 py-1 rounded"
2132
+ }
2133
+ >
2134
+ {value}
2135
+ </span>
2136
+ ),
2137
+ },
2138
+ ];
2139
+
2140
+ const data: Product[] = [
2141
+ { id: 1, name: "Laptop", price: 999.99, stock: 15, status: "Available" },
2142
+ { id: 2, name: "Mouse", price: 29.99, stock: 0, status: "Out of Stock" },
2143
+ { id: 3, name: "Keyboard", price: 79.99, stock: 23, status: "Available" },
2144
+ ];
2145
+
2146
+ return (
2147
+ <Table
2148
+ columns={columns}
2149
+ data={data}
2150
+ striped
2151
+ selectable
2152
+ selectedRows={selectedRows}
2153
+ onSelectionChange={setSelectedRows}
2154
+ stickyHeader
2155
+ caption="Product Inventory"
2156
+ size="md"
2157
+ />
2158
+ );
2159
+ };
2160
+ ```
2161
+
2162
+ ---
2163
+
2164
+ ## Tooltip
2165
+
2166
+ The Tooltip component displays informative text when hovering over an element, with customizable placement, styling, and animation options.
2167
+
2168
+ ### Import
2169
+
2170
+ ```jsx
2171
+ import { Tooltip } from "alope-ui";
2172
+ ```
2173
+
2174
+ ### Props
2175
+
2176
+ | Prop | Type | Default | Description |
2177
+ | ------------------- | ----------------------------------------- | --------------------------------- | ---------------------------------------- |
2178
+ | `children` | `React.ReactNode` | `required` | Element that triggers the tooltip |
2179
+ | `text` | `string` | `required` | Text content of the tooltip |
2180
+ | `showArrow` | `boolean` | `true` | Show arrow pointing to trigger element |
2181
+ | `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | Position of tooltip relative to trigger |
2182
+ | `offset` | `number` | `8` | Distance from trigger element (in px) |
2183
+ | `bgColor` | `string` | `'bg-gray-800 dark:bg-gray-600'` | Background color class |
2184
+ | `isOpen` | `boolean` | `undefined` | Controlled visibility state |
2185
+ | `onOpenChange` | `(open: boolean) => void` | `undefined` | Callback when visibility changes |
2186
+ | `disabled` | `boolean` | `false` | Disable tooltip functionality |
2187
+ | `animationDuration` | `number` | `300` | Animation duration (in ms) |
2188
+ | `delay` | `number` | `700` | Delay before showing tooltip (in ms) |
2189
+
2190
+ ### Basic Tooltip
2191
+
2192
+ ```jsx
2193
+ import { Tooltip } from "alope-ui";
2194
+
2195
+ const BasicExample = () => {
2196
+ return (
2197
+ <Tooltip text="This is a helpful tooltip">
2198
+ <button className="px-4 py-2 bg-blue-500 text-white rounded">
2199
+ Hover me
2200
+ </button>
2201
+ </Tooltip>
2202
+ );
2203
+ };
2204
+ ```
2205
+
2206
+ ### Placement
2207
+
2208
+ ```jsx
2209
+ {/* Top Placement */}
2210
+ <Tooltip text="Tooltip on top" placement="top">
2211
+ <button>Top</button>
2212
+ </Tooltip>
2213
+
2214
+ {/* Bottom Placement */}
2215
+ <Tooltip text="Tooltip on bottom" placement="bottom">
2216
+ <button>Bottom</button>
2217
+ </Tooltip>
2218
+
2219
+ {/* Left Placement */}
2220
+ <Tooltip text="Tooltip on left" placement="left">
2221
+ <button>Left</button>
2222
+ </Tooltip>
2223
+
2224
+ {/* Right Placement */}
2225
+ <Tooltip text="Tooltip on right" placement="right">
2226
+ <button>Right</button>
2227
+ </Tooltip>
2228
+ ```
2229
+
2230
+ ### Without Arrow
2231
+
2232
+ ```jsx
2233
+ <Tooltip text="Tooltip without arrow" showArrow={false}>
2234
+ <button>No Arrow</button>
2235
+ </Tooltip>
2236
+ ```
2237
+
2238
+ ### Custom Offset
2239
+
2240
+ ```jsx
2241
+ {/* Small offset */}
2242
+ <Tooltip text="Close to element" offset={4}>
2243
+ <button>Small Offset</button>
2244
+ </Tooltip>
2245
+
2246
+ {/* Large offset */}
2247
+ <Tooltip text="Far from element" offset={16}>
2248
+ <button>Large Offset</button>
2249
+ </Tooltip>
2250
+ ```
2251
+
2252
+ ### Custom Background Color
2253
+
2254
+ ```jsx
2255
+ <Tooltip text="Blue tooltip" bgColor="bg-blue-600">
2256
+ <button>Blue Tooltip</button>
2257
+ </Tooltip>
2258
+
2259
+ <Tooltip text="Red tooltip" bgColor="bg-red-600">
2260
+ <button>Red Tooltip</button>
2261
+ </Tooltip>
2262
+
2263
+ <Tooltip text="Green tooltip" bgColor="bg-green-600">
2264
+ <button>Green Tooltip</button>
2265
+ </Tooltip>
2266
+ ```
2267
+
2268
+ ### Controlled Tooltip
2269
+
2270
+ ```jsx
2271
+ import { useState } from "react";
2272
+
2273
+ const ControlledExample = () => {
2274
+ const [isOpen, setIsOpen] = useState(false);
2275
+
2276
+ return (
2277
+ <div>
2278
+ <Tooltip
2279
+ text="Controlled tooltip"
2280
+ isOpen={isOpen}
2281
+ onOpenChange={setIsOpen}
2282
+ >
2283
+ <button onClick={() => setIsOpen(!isOpen)}>
2284
+ Click to toggle
2285
+ </button>
2286
+ </Tooltip>
2287
+ </div>
2288
+ );
2289
+ };
2290
+ ```
2291
+
2292
+ ### Custom Animation
2293
+
2294
+ ```jsx
2295
+ {/* Fast animation */}
2296
+ <Tooltip text="Fast animation" animationDuration={150}>
2297
+ <button>Fast</button>
2298
+ </Tooltip>
2299
+
2300
+ {/* Slow animation */}
2301
+ <Tooltip text="Slow animation" animationDuration={600}>
2302
+ <button>Slow</button>
2303
+ </Tooltip>
2304
+ ```
2305
+
2306
+ ### Custom Delay
2307
+
2308
+ ```jsx
2309
+ {/* No delay */}
2310
+ <Tooltip text="Instant tooltip" delay={0}>
2311
+ <button>No Delay</button>
2312
+ </Tooltip>
2313
+
2314
+ {/* Long delay */}
2315
+ <Tooltip text="Patient tooltip" delay={1500}>
2316
+ <button>Long Delay</button>
2317
+ </Tooltip>
2318
+ ```
2319
+
2320
+ ### Disabled Tooltip
2321
+
2322
+ ```jsx
2323
+ <Tooltip text="This won't show" disabled>
2324
+ <button>Disabled Tooltip</button>
2325
+ </Tooltip>
2326
+ ```
2327
+
2328
+ ### With Icons
2329
+
2330
+ ```jsx
2331
+ import { Info, HelpCircle } from "lucide-react";
2332
+
2333
+ <Tooltip text="Additional information about this feature">
2334
+ <Info className="w-4 h-4 text-gray-500 cursor-help" />
2335
+ </Tooltip>
2336
+
2337
+ <Tooltip text="Click for help documentation" placement="right">
2338
+ <HelpCircle className="w-5 h-5 text-blue-500 cursor-pointer" />
2339
+ </Tooltip>
2340
+ ```
2341
+
2342
+ ### Complete Example
2343
+
2344
+ ```jsx
2345
+ import { Tooltip } from "alope-ui";
2346
+ import { Info } from "lucide-react";
2347
+
2348
+ const UserProfile = () => {
2349
+ return (
2350
+ <div className="flex items-center gap-4">
2351
+ <div className="flex items-center gap-2">
2352
+ <span>Username</span>
2353
+ <Tooltip
2354
+ text="Your username is visible to all users"
2355
+ placement="top"
2356
+ bgColor="bg-blue-600"
2357
+ delay={500}
2358
+ >
2359
+ <Info className="w-4 h-4 text-gray-400 cursor-help" />
2360
+ </Tooltip>
2361
+ </div>
2362
+
2363
+ <div className="flex items-center gap-2">
2364
+ <span>Email</span>
2365
+ <Tooltip
2366
+ text="Your email is kept private and secure"
2367
+ placement="right"
2368
+ showArrow={false}
2369
+ offset={12}
2370
+ >
2371
+ <Info className="w-4 h-4 text-gray-400 cursor-help" />
2372
+ </Tooltip>
2373
+ </div>
2374
+
2375
+ <Tooltip text="Click to edit your profile" placement="bottom">
2376
+ <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
2377
+ Edit Profile
2378
+ </button>
2379
+ </Tooltip>
2380
+ </div>
2381
+ );
2382
+ };
2383
+ ```
2384
+
2385
+ ---
2386
+
2387
+ ## Spinner
2388
+
2389
+ The Spinner component displays an animated loading indicator with customizable size, color, duration, and thickness.
2390
+
2391
+ ### Import
2392
+
2393
+ ```jsx
2394
+ import { Spinner } from "alope-ui";
2395
+ ```
2396
+
2397
+ ### Props
2398
+
2399
+ | Prop | Type | Default | Description |
2400
+ | ----------- | --------------------------------------------------------------- | --------- | ------------------------------------- |
2401
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the spinner |
2402
+ | `color` | `'primary' \| 'error' \| 'warning' \| 'success' \| 'info' \| 'secondary'` | `'primary'` | Color variant of the spinner |
2403
+ | `className` | `string` | `""` | Additional CSS classes |
2404
+ | `duration` | `number` | `1000` | Animation duration (in ms) |
2405
+ | `thickness` | `number` | `undefined` | Custom border thickness (in px) |
2406
+
2407
+ ### Basic Spinner
2408
+
2409
+ ```jsx
2410
+ import { Spinner } from "alope-ui";
2411
+
2412
+ const BasicExample = () => {
2413
+ return <Spinner />;
2414
+ };
2415
+ ```
2416
+
2417
+ ### Sizes
2418
+
2419
+ ```jsx
2420
+ {/* Small Size */}
2421
+ <Spinner size="sm" />
2422
+
2423
+ {/* Medium Size */}
2424
+ <Spinner size="md" />
2425
+
2426
+ {/* Large Size */}
2427
+ <Spinner size="lg" />
2428
+ ```
2429
+
2430
+ ### Colors
2431
+
2432
+ ```jsx
2433
+ {/* Primary Color */}
2434
+ <Spinner color="primary" />
2435
+
2436
+ {/* Error Color */}
2437
+ <Spinner color="error" />
2438
+
2439
+ {/* Warning Color */}
2440
+ <Spinner color="warning" />
2441
+
2442
+ {/* Success Color */}
2443
+ <Spinner color="success" />
2444
+
2445
+ {/* Info Color */}
2446
+ <Spinner color="info" />
2447
+
2448
+ {/* Secondary Color */}
2449
+ <Spinner color="secondary" />
2450
+ ```
2451
+
2452
+ ### Custom Duration
2453
+
2454
+ ```jsx
2455
+ {/* Fast animation */}
2456
+ <Spinner duration={500} />
2457
+
2458
+ {/* Slow animation */}
2459
+ <Spinner duration={2000} />
2460
+ ```
2461
+
2462
+ ### Custom Thickness
2463
+
2464
+ ```jsx
2465
+ {/* Thin spinner */}
2466
+ <Spinner thickness={1} />
2467
+
2468
+ {/* Thick spinner */}
2469
+ <Spinner thickness={6} />
2470
+ ```
2471
+
2472
+ ### Combined Customization
2473
+
2474
+ ```jsx
2475
+ <Spinner size="lg" color="success" duration={1500} thickness={5} />
2476
+ ```
2477
+
2478
+ ### With Custom Styling
2479
+
2480
+ ```jsx
2481
+ <Spinner className="mx-auto my-4" />
2482
+
2483
+ <Spinner className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" />
2484
+ ```
2485
+
2486
+ ### Loading States
2487
+
2488
+ ```jsx
2489
+ import { useState } from "react";
2490
+
2491
+ const LoadingButton = () => {
2492
+ const [isLoading, setIsLoading] = useState(false);
2493
+
2494
+ const handleClick = async () => {
2495
+ setIsLoading(true);
2496
+ // Simulate API call
2497
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2498
+ setIsLoading(false);
2499
+ };
2500
+
2501
+ return (
2502
+ <button
2503
+ onClick={handleClick}
2504
+ disabled={isLoading}
2505
+ className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50 flex items-center gap-2"
2506
+ >
2507
+ {isLoading && <Spinner size="sm" className="border-white" />}
2508
+ {isLoading ? "Loading..." : "Submit"}
2509
+ </button>
2510
+ );
2511
+ };
2512
+ ```
2513
+
2514
+ ### Centered Loading
2515
+
2516
+ ```jsx
2517
+ const CenteredLoading = () => {
2518
+ return (
2519
+ <div className="flex items-center justify-center h-64">
2520
+ <Spinner size="lg" color="primary" />
2521
+ </div>
2522
+ );
2523
+ };
2524
+ ```
2525
+
2526
+ ### With Text
2527
+
2528
+ ```jsx
2529
+ const LoadingWithText = () => {
2530
+ return (
2531
+ <div className="flex flex-col items-center gap-3">
2532
+ <Spinner size="lg" color="info" />
2533
+ <p className="text-gray-600 dark:text-gray-400">Loading data...</p>
2534
+ </div>
2535
+ );
2536
+ };
2537
+ ```
2538
+
2539
+ ### Complete Example
2540
+
2541
+ ```jsx
2542
+ import { useState, useEffect } from "react";
2543
+ import { Spinner } from "alope-ui";
2544
+
2545
+ const DataFetcher = () => {
2546
+ const [isLoading, setIsLoading] = useState(true);
2547
+ const [data, setData] = useState(null);
2548
+
2549
+ useEffect(() => {
2550
+ const fetchData = async () => {
2551
+ setIsLoading(true);
2552
+ try {
2553
+ // Simulate API call
2554
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2555
+ setData({ message: "Data loaded successfully!" });
2556
+ } catch (error) {
2557
+ console.error(error);
2558
+ } finally {
2559
+ setIsLoading(false);
2560
+ }
2561
+ };
2562
+
2563
+ fetchData();
2564
+ }, []);
2565
+
2566
+ if (isLoading) {
2567
+ return (
2568
+ <div className="flex flex-col items-center justify-center h-96 gap-4">
2569
+ <Spinner size="lg" color="primary" duration={800} />
2570
+ <p className="text-gray-600 dark:text-gray-400">
2571
+ Fetching your data...
2572
+ </p>
2573
+ </div>
2574
+ );
2575
+ }
2576
+
2577
+ return (
2578
+ <div className="p-4">
2579
+ <h2 className="text-xl font-bold">{data?.message}</h2>
2580
+ </div>
2581
+ );
2582
+ };
2583
+ ```
2584
+
2585
+ ---
2586
+
2587
+ ## Skeleton
2588
+
2589
+ The Skeleton component displays a placeholder preview of content before the actual data is loaded, improving perceived performance and user experience.
2590
+
2591
+ ### Import
2592
+
2593
+ ```jsx
2594
+ import { Skeleton } from "alope-ui";
2595
+ ```
2596
+
2597
+ ### Props
2598
+
2599
+ | Prop | Type | Default | Description |
2600
+ | ----------- | ------------------------- | ------------ | ---------------------------------------------- |
2601
+ | `width` | `string` | `undefined` | Width of the skeleton (CSS value) |
2602
+ | `height` | `string` | `undefined` | Height of the skeleton (CSS value) |
2603
+ | `rounded` | `boolean` | `false` | Apply rounded shape (full circle/pill) |
2604
+ | `isLoading` | `boolean` | `true` | Show skeleton or actual content |
2605
+ | `children` | `React.ReactNode` | `undefined` | Content to render when not loading |
2606
+ | `animode` | `'shimmer' \| 'flash'` | `'flash'` | Animation mode for the skeleton |
2607
+
2608
+ ### Basic Skeleton
2609
+
2610
+ ```jsx
2611
+ import { Skeleton } from "alope-ui";
2612
+
2613
+ const BasicExample = () => {
2614
+ return <Skeleton width="200px" height="20px" />;
2615
+ };
2616
+ ```
2617
+
2618
+ ### With Children
2619
+
2620
+ ```jsx
2621
+ import { useState, useEffect } from "react";
2622
+
2623
+ const SkeletonWithChildren = () => {
2624
+ const [isLoading, setIsLoading] = useState(true);
2625
+
2626
+ useEffect(() => {
2627
+ setTimeout(() => setIsLoading(false), 2000);
2628
+ }, []);
2629
+
2630
+ return (
2631
+ <Skeleton isLoading={isLoading}>
2632
+ <p>This content will show after loading</p>
2633
+ </Skeleton>
2634
+ );
2635
+ };
2636
+ ```
2637
+
2638
+ ### Animation Modes
2639
+
2640
+ ```jsx
2641
+ {/* Flash animation */}
2642
+ <Skeleton width="200px" height="20px" animode="flash" />
2643
+
2644
+ {/* Shimmer animation */}
2645
+ <Skeleton width="200px" height="20px" animode="shimmer" />
2646
+ ```
2647
+
2648
+ ### Rounded Skeleton
2649
+
2650
+ ```jsx
2651
+ {/* Rounded rectangle */}
2652
+ <Skeleton width="100px" height="100px" rounded />
2653
+
2654
+ {/* Avatar skeleton */}
2655
+ <Skeleton width="48px" height="48px" rounded />
2656
+ ```
2657
+
2658
+ ### Different Sizes
2659
+
2660
+ ```jsx
2661
+ {/* Small */}
2662
+ <Skeleton width="100px" height="16px" />
2663
+
2664
+ {/* Medium */}
2665
+ <Skeleton width="200px" height="20px" />
2666
+
2667
+ {/* Large */}
2668
+ <Skeleton width="300px" height="24px" />
2669
+
2670
+ {/* Full width */}
2671
+ <Skeleton width="100%" height="20px" />
2672
+ ```
2673
+
2674
+ ### Text Skeleton
2675
+
2676
+ ```jsx
2677
+ const TextSkeleton = () => {
2678
+ return (
2679
+ <div className="space-y-2">
2680
+ <Skeleton width="100%" height="20px" />
2681
+ <Skeleton width="90%" height="20px" />
2682
+ <Skeleton width="95%" height="20px" />
2683
+ <Skeleton width="85%" height="20px" />
2684
+ </div>
2685
+ );
2686
+ };
2687
+ ```
2688
+
2689
+ ### Card Skeleton
2690
+
2691
+ ```jsx
2692
+ const CardSkeleton = () => {
2693
+ return (
2694
+ <div className="border rounded-lg p-4 space-y-4">
2695
+ {/* Image skeleton */}
2696
+ <Skeleton width="100%" height="200px" />
2697
+
2698
+ {/* Title skeleton */}
2699
+ <Skeleton width="70%" height="24px" />
2700
+
2701
+ {/* Description skeleton */}
2702
+ <div className="space-y-2">
2703
+ <Skeleton width="100%" height="16px" />
2704
+ <Skeleton width="95%" height="16px" />
2705
+ <Skeleton width="90%" height="16px" />
2706
+ </div>
2707
+
2708
+ {/* Button skeleton */}
2709
+ <Skeleton width="120px" height="40px" />
2710
+ </div>
2711
+ );
2712
+ };
2713
+ ```
2714
+
2715
+ ### Avatar Skeleton
2716
+
2717
+ ```jsx
2718
+ const AvatarSkeleton = () => {
2719
+ return (
2720
+ <div className="flex items-center gap-3">
2721
+ <Skeleton width="48px" height="48px" rounded />
2722
+ <div className="space-y-2 flex-1">
2723
+ <Skeleton width="150px" height="16px" />
2724
+ <Skeleton width="100px" height="14px" />
2725
+ </div>
2726
+ </div>
2727
+ );
2728
+ };
2729
+ ```
2730
+
2731
+ ### List Skeleton
2732
+
2733
+ ```jsx
2734
+ const ListSkeleton = () => {
2735
+ return (
2736
+ <div className="space-y-4">
2737
+ {[...Array(5)].map((_, i) => (
2738
+ <div key={i} className="flex items-center gap-3">
2739
+ <Skeleton width="40px" height="40px" rounded />
2740
+ <div className="space-y-2 flex-1">
2741
+ <Skeleton width="60%" height="16px" />
2742
+ <Skeleton width="40%" height="14px" />
2743
+ </div>
2744
+ </div>
2745
+ ))}
2746
+ </div>
2747
+ );
2748
+ };
2749
+ ```
2750
+
2751
+ ### Table Skeleton
2752
+
2753
+ ```jsx
2754
+ const TableSkeleton = () => {
2755
+ return (
2756
+ <div className="space-y-3">
2757
+ {/* Header */}
2758
+ <div className="grid grid-cols-4 gap-4">
2759
+ <Skeleton width="100%" height="20px" />
2760
+ <Skeleton width="100%" height="20px" />
2761
+ <Skeleton width="100%" height="20px" />
2762
+ <Skeleton width="100%" height="20px" />
2763
+ </div>
2764
+
2765
+ {/* Rows */}
2766
+ {[...Array(5)].map((_, i) => (
2767
+ <div key={i} className="grid grid-cols-4 gap-4">
2768
+ <Skeleton width="100%" height="16px" />
2769
+ <Skeleton width="100%" height="16px" />
2770
+ <Skeleton width="100%" height="16px" />
2771
+ <Skeleton width="100%" height="16px" />
2772
+ </div>
2773
+ ))}
2774
+ </div>
2775
+ );
2776
+ };
2777
+ ```
2778
+
2779
+ ### Complete Example
2780
+
2781
+ ```jsx
2782
+ import { useState, useEffect } from "react";
2783
+ import { Skeleton } from "alope-ui";
2784
+
2785
+ type User = {
2786
+ id: number;
2787
+ name: string;
2788
+ email: string;
2789
+ avatar: string;
2790
+ };
2791
+
2792
+ const UserProfile = () => {
2793
+ const [isLoading, setIsLoading] = useState(true);
2794
+ const [user, setUser] = useState<User | null>(null);
2795
+
2796
+ useEffect(() => {
2797
+ const fetchUser = async () => {
2798
+ setIsLoading(true);
2799
+ // Simulate API call
2800
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2801
+ setUser({
2802
+ id: 1,
2803
+ name: "John Doe",
2804
+ email: "john@example.com",
2805
+ avatar: "/avatar.jpg",
2806
+ });
2807
+ setIsLoading(false);
2808
+ };
2809
+
2810
+ fetchUser();
2811
+ }, []);
2812
+
2813
+ if (isLoading) {
2814
+ return (
2815
+ <div className="max-w-md border rounded-lg p-6 space-y-4">
2816
+ {/* Avatar and name skeleton */}
2817
+ <div className="flex items-center gap-4">
2818
+ <Skeleton width="64px" height="64px" rounded />
2819
+ <div className="space-y-2 flex-1">
2820
+ <Skeleton width="150px" height="20px" />
2821
+ <Skeleton width="200px" height="16px" />
2822
+ </div>
2823
+ </div>
2824
+
2825
+ {/* Bio skeleton */}
2826
+ <div className="space-y-2">
2827
+ <Skeleton width="100%" height="16px" />
2828
+ <Skeleton width="95%" height="16px" />
2829
+ <Skeleton width="90%" height="16px" />
2830
+ </div>
2831
+
2832
+ {/* Stats skeleton */}
2833
+ <div className="flex gap-4">
2834
+ <Skeleton width="80px" height="40px" />
2835
+ <Skeleton width="80px" height="40px" />
2836
+ <Skeleton width="80px" height="40px" />
2837
+ </div>
2838
+
2839
+ {/* Button skeleton */}
2840
+ <Skeleton width="100%" height="44px" />
2841
+ </div>
2842
+ );
2843
+ }
2844
+
2845
+ return (
2846
+ <div className="max-w-md border rounded-lg p-6">
2847
+ <div className="flex items-center gap-4 mb-4">
2848
+ <img
2849
+ src={user?.avatar}
2850
+ alt={user?.name}
2851
+ className="w-16 h-16 rounded-full"
2852
+ />
2853
+ <div>
2854
+ <h2 className="text-xl font-bold">{user?.name}</h2>
2855
+ <p className="text-gray-600">{user?.email}</p>
2856
+ </div>
2857
+ </div>
2858
+ {/* Rest of the content */}
2859
+ </div>
2860
+ );
2861
+ };
2862
+ ```
2863
+
2864
+ ---
2865
+
2866
+ ## Toggle
2867
+
2868
+ The Toggle component provides an animated switch control for toggling between on/off states with various visual styles and sizes.
2869
+
2870
+ ### Import
2871
+
2872
+ ```jsx
2873
+ import { Toggle } from "alope-ui";
2874
+ ```
2875
+
2876
+ ### Props
2877
+
2878
+ | Prop | Type | Default | Description |
2879
+ | ------------------- | -------------------------------------------------------------- | ----------- | ---------------------------------------- |
2880
+ | `variant` | `'primary' \| 'error' \| 'warning' \| 'success' \| 'info' \| 'secondary'` | `'primary'` | Color variant of the toggle |
2881
+ | `customSize` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the toggle switch |
2882
+ | `thumbShape` | `'stadium' \| 'rounded'` | `'stadium'` | Shape of the toggle thumb |
2883
+ | `label` | `string` | `undefined` | Label text displayed next to toggle |
2884
+ | `wrapperClassName` | `string` | `""` | CSS class for wrapper element |
2885
+ | `labelClassName` | `string` | `""` | CSS class for label text |
2886
+ | `...props` | `InputHTMLAttributes<HTMLInputElement>` | - | All standard HTML input attributes |
2887
+
2888
+ ### Basic Toggle
2889
+
2890
+ ```jsx
2891
+ import { useState } from "react";
2892
+ import { Toggle } from "alope-ui";
2893
+
2894
+ const BasicExample = () => {
2895
+ const [isChecked, setIsChecked] = useState(false);
2896
+
2897
+ return (
2898
+ <Toggle
2899
+ checked={isChecked}
2900
+ onChange={(e) => setIsChecked(e.target.checked)}
2901
+ />
2902
+ );
2903
+ };
2904
+ ```
2905
+
2906
+ ### With Label
2907
+
2908
+ ```jsx
2909
+ <Toggle
2910
+ checked={isChecked}
2911
+ onChange={(e) => setIsChecked(e.target.checked)}
2912
+ label="Enable notifications"
2913
+ />
2914
+ ```
2915
+
2916
+ ### Variants
2917
+
2918
+ ```jsx
2919
+ {/* Primary Variant */}
2920
+ <Toggle
2921
+ checked={isChecked}
2922
+ onChange={(e) => setIsChecked(e.target.checked)}
2923
+ variant="primary"
2924
+ label="Primary"
2925
+ />
2926
+
2927
+ {/* Error Variant */}
2928
+ <Toggle
2929
+ checked={isChecked}
2930
+ onChange={(e) => setIsChecked(e.target.checked)}
2931
+ variant="error"
2932
+ label="Error"
2933
+ />
2934
+
2935
+ {/* Warning Variant */}
2936
+ <Toggle
2937
+ checked={isChecked}
2938
+ onChange={(e) => setIsChecked(e.target.checked)}
2939
+ variant="warning"
2940
+ label="Warning"
2941
+ />
2942
+
2943
+ {/* Success Variant */}
2944
+ <Toggle
2945
+ checked={isChecked}
2946
+ onChange={(e) => setIsChecked(e.target.checked)}
2947
+ variant="success"
2948
+ label="Success"
2949
+ />
2950
+
2951
+ {/* Info Variant */}
2952
+ <Toggle
2953
+ checked={isChecked}
2954
+ onChange={(e) => setIsChecked(e.target.checked)}
2955
+ variant="info"
2956
+ label="Info"
2957
+ />
2958
+
2959
+ {/* Secondary Variant */}
2960
+ <Toggle
2961
+ checked={isChecked}
2962
+ onChange={(e) => setIsChecked(e.target.checked)}
2963
+ variant="secondary"
2964
+ label="Secondary"
2965
+ />
2966
+ ```
2967
+
2968
+ ### Sizes
2969
+
2970
+ ```jsx
2971
+ {/* Small Size */}
2972
+ <Toggle
2973
+ checked={isChecked}
2974
+ onChange={(e) => setIsChecked(e.target.checked)}
2975
+ customSize="sm"
2976
+ label="Small"
2977
+ />
2978
+
2979
+ {/* Medium Size */}
2980
+ <Toggle
2981
+ checked={isChecked}
2982
+ onChange={(e) => setIsChecked(e.target.checked)}
2983
+ customSize="md"
2984
+ label="Medium"
2985
+ />
2986
+
2987
+ {/* Large Size */}
2988
+ <Toggle
2989
+ checked={isChecked}
2990
+ onChange={(e) => setIsChecked(e.target.checked)}
2991
+ customSize="lg"
2992
+ label="Large"
2993
+ />
2994
+ ```
2995
+
2996
+ ### Thumb Shapes
2997
+
2998
+ ```jsx
2999
+ {/* Stadium Shape (Fully Rounded) */}
3000
+ <Toggle
3001
+ checked={isChecked}
3002
+ onChange={(e) => setIsChecked(e.target.checked)}
3003
+ thumbShape="stadium"
3004
+ label="Stadium"
3005
+ />
3006
+
3007
+ {/* Rounded Shape (Slightly Rounded) */}
3008
+ <Toggle
3009
+ checked={isChecked}
3010
+ onChange={(e) => setIsChecked(e.target.checked)}
3011
+ thumbShape="rounded"
3012
+ label="Rounded"
3013
+ />
3014
+ ```
3015
+
3016
+ ### Disabled State
3017
+
3018
+ ```jsx
3019
+ <Toggle
3020
+ checked={isChecked}
3021
+ onChange={(e) => setIsChecked(e.target.checked)}
3022
+ disabled
3023
+ label="Disabled toggle"
3024
+ />
3025
+ ```
3026
+
3027
+ ### Custom Styling
3028
+
3029
+ ```jsx
3030
+ <Toggle
3031
+ checked={isChecked}
3032
+ onChange={(e) => setIsChecked(e.target.checked)}
3033
+ label="Custom styled toggle"
3034
+ wrapperClassName="bg-gray-100 p-4 rounded-lg"
3035
+ labelClassName="text-lg font-bold text-blue-600"
3036
+ />
3037
+ ```
3038
+
3039
+ ### Complete Example
3040
+
3041
+ ```jsx
3042
+ import { useState } from "react";
3043
+ import { Toggle } from "alope-ui";
3044
+
3045
+ const CompleteExample = () => {
3046
+ const [settings, setSettings] = useState({
3047
+ notifications: false,
3048
+ darkMode: true,
3049
+ autoSave: false,
3050
+ });
3051
+
3052
+ const handleToggle = (key: string) => (e: React.ChangeEvent<HTMLInputElement>) => {
3053
+ setSettings(prev => ({
3054
+ ...prev,
3055
+ [key]: e.target.checked
3056
+ }));
3057
+ };
3058
+
3059
+ return (
3060
+ <div className="space-y-4">
3061
+ <Toggle
3062
+ checked={settings.notifications}
3063
+ onChange={handleToggle('notifications')}
3064
+ variant="primary"
3065
+ customSize="md"
3066
+ label="Enable notifications"
3067
+ />
3068
+
3069
+ <Toggle
3070
+ checked={settings.darkMode}
3071
+ onChange={handleToggle('darkMode')}
3072
+ variant="secondary"
3073
+ customSize="md"
3074
+ label="Dark mode"
3075
+ />
3076
+
3077
+ <Toggle
3078
+ checked={settings.autoSave}
3079
+ onChange={handleToggle('autoSave')}
3080
+ variant="success"
3081
+ customSize="md"
3082
+ label="Auto-save documents"
3083
+ />
3084
+ </div>
3085
+ );
3086
+ };
3087
+ ```
3088
+
3089
+ ---
3090
+
3091
+ ## FileUpload
3092
+
3093
+ The FileUpload component provides a flexible file upload interface with support for drag-and-drop, file validation, preview, and two display modes (dropzone and text input).
3094
+
3095
+ ### Import
3096
+
3097
+ ```jsx
3098
+ import { FileUpload } from "alope-ui";
3099
+ ```
3100
+
3101
+ ### Props
3102
+
3103
+ | Prop | Type | Default | Description |
3104
+ | ---------------- | ------------------------------ | ------------ | ------------------------------------------------ |
3105
+ | `onFilesChange` | `(files: File[]) => void` | `required` | Callback fired when files change |
3106
+ | `acceptedFiles` | `string` | `'image/*'` | Accepted file types (MIME types or extensions) |
3107
+ | `maxFiles` | `number` | `1` | Maximum number of files allowed |
3108
+ | `maxKiloByte` | `number` | `2048` | Maximum file size in kilobytes |
3109
+ | `mode` | `'dropzone' \| 'textinput'` | `'dropzone'` | Display mode of the file upload |
3110
+ | `clearable` | `boolean` | `true` | Show clear button on file previews |
3111
+ | `inputLabel` | `string` | `undefined` | Label for text input mode button |
3112
+ | `helperText` | `string` | `undefined` | Helper text displayed below the input |
3113
+ | `mediaCapture` | `'user' \| 'environment'` | `undefined` | Camera capture mode (for mobile devices) |
3114
+
3115
+ ### Basic FileUpload (Dropzone Mode)
3116
+
3117
+ ```jsx
3118
+ import { useState } from "react";
3119
+ import { FileUpload } from "alope-ui";
3120
+
3121
+ const BasicExample = () => {
3122
+ const [files, setFiles] = useState<File[]>([]);
3123
+
3124
+ return (
3125
+ <FileUpload
3126
+ onFilesChange={setFiles}
3127
+ />
3128
+ );
3129
+ };
3130
+ ```
3131
+
3132
+ ### Text Input Mode
3133
+
3134
+ ```jsx
3135
+ <FileUpload
3136
+ mode="textinput"
3137
+ onFilesChange={setFiles}
3138
+ inputLabel="Upload File"
3139
+ />
3140
+ ```
3141
+
3142
+ ### Multiple Files
3143
+
3144
+ ```jsx
3145
+ <FileUpload
3146
+ maxFiles={5}
3147
+ onFilesChange={setFiles}
3148
+ helperText="You can upload up to 5 files"
3149
+ />
3150
+ ```
3151
+
3152
+ ### Custom File Types
3153
+
3154
+ ```jsx
3155
+ {/* Accept only images */}
3156
+ <FileUpload
3157
+ acceptedFiles="image/*"
3158
+ onFilesChange={setFiles}
3159
+ />
3160
+
3161
+ {/* Accept specific image types */}
3162
+ <FileUpload
3163
+ acceptedFiles="image/png, image/jpeg, image/jpg"
3164
+ onFilesChange={setFiles}
3165
+ />
3166
+
3167
+ {/* Accept PDFs */}
3168
+ <FileUpload
3169
+ acceptedFiles="application/pdf"
3170
+ onFilesChange={setFiles}
3171
+ />
3172
+
3173
+ {/* Accept by file extension */}
3174
+ <FileUpload
3175
+ acceptedFiles=".pdf, .doc, .docx"
3176
+ onFilesChange={setFiles}
3177
+ />
3178
+
3179
+ {/* Accept multiple types */}
3180
+ <FileUpload
3181
+ acceptedFiles="image/*, .pdf, .doc"
3182
+ onFilesChange={setFiles}
3183
+ />
3184
+ ```
3185
+
3186
+ ### File Size Limit
3187
+
3188
+ ```jsx
3189
+ {/* 5MB limit */}
3190
+ <FileUpload
3191
+ maxKiloByte={5120}
3192
+ onFilesChange={setFiles}
3193
+ helperText="Maximum file size: 5MB"
3194
+ />
3195
+
3196
+ {/* 500KB limit */}
3197
+ <FileUpload
3198
+ maxKiloByte={512}
3199
+ onFilesChange={setFiles}
3200
+ helperText="Maximum file size: 500KB"
3201
+ />
3202
+ ```
3203
+
3204
+ ### With Helper Text
3205
+
3206
+ ```jsx
3207
+ <FileUpload
3208
+ onFilesChange={setFiles}
3209
+ helperText="Accepted formats: PNG, JPG, JPEG. Max size: 2MB"
3210
+ />
3211
+ ```
3212
+
3213
+ ### Camera Capture (Mobile)
3214
+
3215
+ ```jsx
3216
+ {/* Use front camera */}
3217
+ <FileUpload
3218
+ acceptedFiles="image/*"
3219
+ mediaCapture="user"
3220
+ onFilesChange={setFiles}
3221
+ />
3222
+
3223
+ {/* Use back camera */}
3224
+ <FileUpload
3225
+ acceptedFiles="image/*"
3226
+ mediaCapture="environment"
3227
+ onFilesChange={setFiles}
3228
+ />
3229
+ ```
3230
+
3231
+ ### Non-clearable Files
3232
+
3233
+ ```jsx
3234
+ <FileUpload
3235
+ onFilesChange={setFiles}
3236
+ clearable={false}
3237
+ />
3238
+ ```
3239
+
3240
+ ### Complete Example with Dropzone Mode
3241
+
3242
+ ```jsx
3243
+ import { useState } from "react";
3244
+ import { FileUpload } from "alope-ui";
3245
+
3246
+ const DropzoneExample = () => {
3247
+ const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
3248
+
3249
+ const handleFilesChange = (files: File[]) => {
3250
+ setUploadedFiles(files);
3251
+ console.log('Uploaded files:', files);
3252
+ };
3253
+
3254
+ return (
3255
+ <div className="max-w-2xl mx-auto p-6">
3256
+ <h2 className="text-xl font-bold mb-4">Upload Your Images</h2>
3257
+
3258
+ <FileUpload
3259
+ mode="dropzone"
3260
+ acceptedFiles="image/png, image/jpeg, image/jpg"
3261
+ maxFiles={3}
3262
+ maxKiloByte={5120}
3263
+ onFilesChange={handleFilesChange}
3264
+ helperText="Upload up to 3 images. Max 5MB per file."
3265
+ />
3266
+
3267
+ {uploadedFiles.length > 0 && (
3268
+ <div className="mt-4">
3269
+ <p className="font-medium">Selected files:</p>
3270
+ <ul className="list-disc list-inside">
3271
+ {uploadedFiles.map((file, index) => (
3272
+ <li key={index}>{file.name}</li>
3273
+ ))}
3274
+ </ul>
3275
+ </div>
3276
+ )}
3277
+ </div>
3278
+ );
3279
+ };
3280
+ ```
3281
+
3282
+ ### Complete Example with Text Input Mode
3283
+
3284
+ ```jsx
3285
+ import { useState } from "react";
3286
+ import { FileUpload } from "alope-ui";
3287
+
3288
+ const TextInputExample = () => {
3289
+ const [documents, setDocuments] = useState<File[]>([]);
3290
+
3291
+ return (
3292
+ <div className="max-w-2xl mx-auto p-6">
3293
+ <h2 className="text-xl font-bold mb-4">Upload Documents</h2>
3294
+
3295
+ <FileUpload
3296
+ mode="textinput"
3297
+ acceptedFiles=".pdf, .doc, .docx"
3298
+ maxFiles={5}
3299
+ maxKiloByte={10240}
3300
+ inputLabel="Choose Documents"
3301
+ onFilesChange={setDocuments}
3302
+ helperText="Accepted: PDF, DOC, DOCX. Max 10MB per file."
3303
+ />
3304
+ </div>
3305
+ );
3306
+ };
3307
+ ```
3308
+
3309
+ ---
3310
+
3311
+ ## Textarea
3312
+
3313
+ The Textarea component provides a multi-line text input with optional auto-resize functionality, label, error messages, and dark mode support.
3314
+
3315
+ ### Import
3316
+
3317
+ ```jsx
3318
+ import { Textarea } from "alope-ui";
3319
+ import type { TextareaType } from "alope-ui";
3320
+ ```
3321
+
3322
+ ### Props
3323
+
3324
+ | Prop | Type | Default | Description |
3325
+ | ----------------- | --------------------------------------------- | ------- | ------------------------------------------------ |
3326
+ | `label` | `string` | `undefined` | Label text displayed above textarea |
3327
+ | `error` | `string` | `undefined` | Error message displayed below textarea |
3328
+ | `autoResize` | `boolean` | `false` | Enable auto-resize based on content |
3329
+ | `customClassName` | `string` | `""` | Custom CSS class for textarea element |
3330
+ | `...props` | `InputHTMLAttributes<HTMLTextAreaElement>` | - | All standard HTML textarea attributes |
3331
+
3332
+ ### Basic Textarea
3333
+
3334
+ ```jsx
3335
+ import { useState } from "react";
3336
+ import { Textarea } from "alope-ui";
3337
+
3338
+ const BasicExample = () => {
3339
+ const [value, setValue] = useState("");
3340
+
3341
+ return (
3342
+ <Textarea
3343
+ value={value}
3344
+ onChange={(e) => setValue(e.target.value)}
3345
+ placeholder="Enter your text here..."
3346
+ />
3347
+ );
3348
+ };
3349
+ ```
3350
+
3351
+ ### With Label
3352
+
3353
+ ```jsx
3354
+ <Textarea
3355
+ label="Description"
3356
+ name="description"
3357
+ value={value}
3358
+ onChange={(e) => setValue(e.target.value)}
3359
+ placeholder="Enter description..."
3360
+ />
3361
+ ```
3362
+
3363
+ ### With Error Message
3364
+
3365
+ ```jsx
3366
+ const [description, setDescription] = useState("");
3367
+ const [error, setError] = useState("");
3368
+
3369
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
3370
+ setDescription(e.target.value);
3371
+ if (e.target.value.length < 10) {
3372
+ setError("Description must be at least 10 characters");
3373
+ } else {
3374
+ setError("");
3375
+ }
3376
+ };
3377
+
3378
+ <Textarea
3379
+ label="Description"
3380
+ value={description}
3381
+ onChange={handleChange}
3382
+ error={error}
3383
+ placeholder="Enter at least 10 characters..."
3384
+ />
3385
+ ```
3386
+
3387
+ ### Auto-Resize Textarea
3388
+
3389
+ ```jsx
3390
+ <Textarea
3391
+ label="Auto-resizing textarea"
3392
+ autoResize
3393
+ value={value}
3394
+ onChange={(e) => setValue(e.target.value)}
3395
+ placeholder="This textarea grows as you type..."
3396
+ />
3397
+ ```
3398
+
3399
+ ### Disabled State
3400
+
3401
+ ```jsx
3402
+ <Textarea
3403
+ label="Disabled textarea"
3404
+ value="This textarea is disabled"
3405
+ disabled
3406
+ />
3407
+ ```
3408
+
3409
+ ### Custom Styling
3410
+
3411
+ ```jsx
3412
+ <Textarea
3413
+ label="Custom styled textarea"
3414
+ value={value}
3415
+ onChange={(e) => setValue(e.target.value)}
3416
+ customClassName="border-2 border-blue-500 focus:ring-blue-600 rounded-lg"
3417
+ placeholder="Custom styled..."
3418
+ />
3419
+ ```
3420
+
3421
+ ### With Rows
3422
+
3423
+ ```jsx
3424
+ <Textarea
3425
+ label="Fixed height textarea"
3426
+ rows={8}
3427
+ value={value}
3428
+ onChange={(e) => setValue(e.target.value)}
3429
+ placeholder="8 rows high..."
3430
+ />
3431
+ ```
3432
+
3433
+ ### Complete Example
3434
+
3435
+ ```jsx
3436
+ import { useState } from "react";
3437
+ import { Textarea } from "alope-ui";
3438
+
3439
+ const CompleteExample = () => {
3440
+ const [feedback, setFeedback] = useState("");
3441
+ const [error, setError] = useState("");
3442
+
3443
+ const handleSubmit = (e: React.FormEvent) => {
3444
+ e.preventDefault();
3445
+
3446
+ if (feedback.length < 20) {
3447
+ setError("Feedback must be at least 20 characters");
3448
+ return;
3449
+ }
3450
+
3451
+ console.log("Feedback submitted:", feedback);
3452
+ setFeedback("");
3453
+ setError("");
3454
+ };
3455
+
3456
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
3457
+ setFeedback(e.target.value);
3458
+ if (error && e.target.value.length >= 20) {
3459
+ setError("");
3460
+ }
3461
+ };
3462
+
3463
+ return (
3464
+ <form onSubmit={handleSubmit} className="max-w-lg mx-auto p-6">
3465
+ <h2 className="text-xl font-bold mb-4">Share Your Feedback</h2>
3466
+
3467
+ <Textarea
3468
+ label="Feedback"
3469
+ name="feedback"
3470
+ value={feedback}
3471
+ onChange={handleChange}
3472
+ error={error}
3473
+ autoResize
3474
+ placeholder="Tell us what you think... (minimum 20 characters)"
3475
+ customClassName="mb-4"
3476
+ />
3477
+
3478
+ <p className="text-sm text-gray-500 mb-4">
3479
+ Character count: {feedback.length}
3480
+ </p>
3481
+
3482
+ <button
3483
+ type="submit"
3484
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
3485
+ >
3486
+ Submit Feedback
3487
+ </button>
3488
+ </form>
3489
+ );
3490
+ };
3491
+ ```
3492
+
3493
+ ### Form Integration Example
3494
+
3495
+ ```jsx
3496
+ import { useState } from "react";
3497
+ import { Textarea } from "alope-ui";
3498
+
3499
+ const FormExample = () => {
3500
+ const [formData, setFormData] = useState({
3501
+ name: "",
3502
+ message: "",
3503
+ comments: ""
3504
+ });
3505
+
3506
+ const handleChange = (field: string) => (
3507
+ e: React.ChangeEvent<HTMLTextAreaElement>
3508
+ ) => {
3509
+ setFormData(prev => ({
3510
+ ...prev,
3511
+ [field]: e.target.value
3512
+ }));
3513
+ };
3514
+
3515
+ return (
3516
+ <div className="space-y-4 max-w-lg mx-auto p-6">
3517
+ <Textarea
3518
+ label="Message"
3519
+ name="message"
3520
+ value={formData.message}
3521
+ onChange={handleChange('message')}
3522
+ placeholder="Your message..."
3523
+ rows={4}
3524
+ />
3525
+
3526
+ <Textarea
3527
+ label="Additional Comments"
3528
+ name="comments"
3529
+ value={formData.comments}
3530
+ onChange={handleChange('comments')}
3531
+ placeholder="Any additional comments..."
3532
+ autoResize
3533
+ />
3534
+ </div>
3535
+ );
3536
+ };
3537
+ ```
3538
+
3539
+ ---
3540
+
3541
+ ## Tabs
3542
+
3543
+ The Tabs component provides a tabbed navigation interface for organizing and displaying content in separate panels.
3544
+
3545
+ ### Import
3546
+
3547
+ ```jsx
3548
+ import { Tabs } from "alope-ui";
3549
+ import type { Tabs as TabType } from "alope-ui";
3550
+ ```
3551
+
3552
+ ### Props
3553
+
3554
+ | Prop | Type | Default | Description |
3555
+ | -------------------- | --------------------------------------------------- | -------- | ---------------------------------------- |
3556
+ | `tabs` | `TabType[]` | `required` | Array of tab objects |
3557
+ | `variant` | `'line' \| 'solid' \| 'subtle' \| 'inline' \| 'outline' \| 'plain'` | `'line'` | Visual variant style |
3558
+ | `fitted` | `boolean` | `false` | Make tabs fill available width |
3559
+ | `wrapperClassName` | `string` | `""` | Wrapper container CSS class |
3560
+ | `buttonClassName` | `string` | `""` | Tab button CSS class |
3561
+ | `activeClassName` | `string` | `""` | Active tab CSS class |
3562
+ | `inactiveClassName` | `string` | `""` | Inactive tab CSS class |
3563
+ | `activeTab` | `number` | `undefined` | Controlled active tab index |
3564
+ | `onTabChange` | `(index: number) => void` | `undefined` | Callback when tab changes |
3565
+
3566
+ ### Tab Object Type
3567
+
3568
+ ```typescript
3569
+ type Tabs = {
3570
+ label: string;
3571
+ content: React.ReactNode;
3572
+ disabled?: boolean;
3573
+ link?: string;
3574
+ }
3575
+ ```
3576
+
3577
+ ### Basic Tabs
3578
+
3579
+ ```jsx
3580
+ import { Tabs } from "alope-ui";
3581
+
3582
+ const BasicExample = () => {
3583
+ const tabs = [
3584
+ {
3585
+ label: "Profile",
3586
+ content: <div>Profile content here</div>,
3587
+ },
3588
+ {
3589
+ label: "Settings",
3590
+ content: <div>Settings content here</div>,
3591
+ },
3592
+ {
3593
+ label: "Notifications",
3594
+ content: <div>Notifications content here</div>,
3595
+ },
3596
+ ];
3597
+
3598
+ return <Tabs tabs={tabs} />;
3599
+ };
3600
+ ```
3601
+
3602
+ ### Variants
3603
+
3604
+ ```jsx
3605
+ {/* Line Variant */}
3606
+ <Tabs tabs={tabs} variant="line" />
3607
+
3608
+ {/* Solid Variant */}
3609
+ <Tabs tabs={tabs} variant="solid" />
3610
+
3611
+ {/* Subtle Variant */}
3612
+ <Tabs tabs={tabs} variant="subtle" />
3613
+
3614
+ {/* Inline Variant */}
3615
+ <Tabs tabs={tabs} variant="inline" />
3616
+
3617
+ {/* Outline Variant */}
3618
+ <Tabs tabs={tabs} variant="outline" />
3619
+
3620
+ {/* Plain Variant */}
3621
+ <Tabs tabs={tabs} variant="plain" />
3622
+ ```
3623
+
3624
+ ### Fitted Tabs
3625
+
3626
+ ```jsx
3627
+ {/* Tabs will fill the available width equally */}
3628
+ <Tabs tabs={tabs} fitted />
3629
+ ```
3630
+
3631
+ ### Controlled Tabs
3632
+
3633
+ ```jsx
3634
+ import { useState } from "react";
3635
+ import { Tabs } from "alope-ui";
3636
+
3637
+ const ControlledExample = () => {
3638
+ const [activeTab, setActiveTab] = useState(0);
3639
+
3640
+ const tabs = [
3641
+ {
3642
+ label: "Tab 1",
3643
+ content: <div>Content 1</div>,
3644
+ },
3645
+ {
3646
+ label: "Tab 2",
3647
+ content: <div>Content 2</div>,
3648
+ },
3649
+ ];
3650
+
3651
+ return (
3652
+ <Tabs
3653
+ tabs={tabs}
3654
+ activeTab={activeTab}
3655
+ onTabChange={setActiveTab}
3656
+ />
3657
+ );
3658
+ };
3659
+ ```
3660
+
3661
+ ### Disabled Tabs
3662
+
3663
+ ```jsx
3664
+ const tabs = [
3665
+ {
3666
+ label: "Active Tab",
3667
+ content: <div>Active content</div>,
3668
+ },
3669
+ {
3670
+ label: "Disabled Tab",
3671
+ content: <div>This content won't show</div>,
3672
+ disabled: true,
3673
+ },
3674
+ ];
3675
+
3676
+ <Tabs tabs={tabs} />
3677
+ ```
3678
+
3679
+ ### Tabs with Links
3680
+
3681
+ ```jsx
3682
+ const tabs = [
3683
+ {
3684
+ label: "Dashboard",
3685
+ content: <div>Dashboard content</div>,
3686
+ link: "/dashboard",
3687
+ },
3688
+ {
3689
+ label: "Profile",
3690
+ content: <div>Profile content</div>,
3691
+ link: "/profile",
3692
+ },
3693
+ ];
3694
+
3695
+ <Tabs tabs={tabs} />
3696
+ ```
3697
+
3698
+ ### Custom Styling
3699
+
3700
+ ```jsx
3701
+ <Tabs
3702
+ tabs={tabs}
3703
+ variant="solid"
3704
+ wrapperClassName="bg-gray-100 dark:bg-gray-800"
3705
+ buttonClassName="text-base"
3706
+ activeClassName="font-bold"
3707
+ inactiveClassName="opacity-70"
3708
+ />
3709
+ ```
3710
+
3711
+ ### Complete Example
3712
+
3713
+ ```jsx
3714
+ import { useState } from "react";
3715
+ import { Tabs } from "alope-ui";
3716
+
3717
+ const CompleteExample = () => {
3718
+ const [activeTab, setActiveTab] = useState(0);
3719
+
3720
+ const tabs = [
3721
+ {
3722
+ label: "Overview",
3723
+ content: (
3724
+ <div className="p-4">
3725
+ <h2 className="text-xl font-bold mb-2">Overview</h2>
3726
+ <p>Dashboard overview content goes here.</p>
3727
+ </div>
3728
+ ),
3729
+ },
3730
+ {
3731
+ label: "Analytics",
3732
+ content: (
3733
+ <div className="p-4">
3734
+ <h2 className="text-xl font-bold mb-2">Analytics</h2>
3735
+ <p>Analytics and statistics content.</p>
3736
+ </div>
3737
+ ),
3738
+ },
3739
+ {
3740
+ label: "Reports",
3741
+ content: (
3742
+ <div className="p-4">
3743
+ <h2 className="text-xl font-bold mb-2">Reports</h2>
3744
+ <p>Reports and data visualization.</p>
3745
+ </div>
3746
+ ),
3747
+ },
3748
+ {
3749
+ label: "Settings",
3750
+ content: (
3751
+ <div className="p-4">
3752
+ <h2 className="text-xl font-bold mb-2">Settings</h2>
3753
+ <p>Configuration and preferences.</p>
3754
+ </div>
3755
+ ),
3756
+ disabled: true,
3757
+ },
3758
+ ];
3759
+
3760
+ return (
3761
+ <Tabs
3762
+ tabs={tabs}
3763
+ variant="subtle"
3764
+ fitted
3765
+ activeTab={activeTab}
3766
+ onTabChange={setActiveTab}
3767
+ />
3768
+ );
3769
+ };
3770
+ ```
3771
+
3772
+ ---
3773
+
3774
+ ## Avatar
3775
+
3776
+ The Avatar component displays user profile images with fallback initials, status indicators, and various styling options.
3777
+
3778
+ ### Import
3779
+
3780
+ ```jsx
3781
+ import { Avatar } from "alope-ui";
3782
+ ```
3783
+
3784
+ ### Props
3785
+
3786
+ | Prop | Type | Default | Description |
3787
+ | ----------------- | --------------------------------------------------------- | ----------- | ------------------------------------- |
3788
+ | `imageSrc` | `string` | `undefined` | URL of the avatar image |
3789
+ | `fallbackName` | `string` | `undefined` | Name to generate initials from |
3790
+ | `fallbackColor` | `'primary' \| 'error' \| 'warning' \| 'success' \| 'info' \| 'secondary'` | `'primary'` | Background color for fallback |
3791
+ | `shape` | `'sharp' \| 'cornered' \| 'rounded'` | `'rounded'` | Shape of the avatar |
3792
+ | `variant` | `'solid' \| 'outline'` | `'solid'` | Visual variant style |
3793
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the avatar |
3794
+ | `ring` | `boolean` | `false` | Show ring around avatar |
3795
+ | `ringClassName` | `string` | `""` | Custom ring CSS class |
3796
+ | `alt` | `string` | `'avatar'` | Alt text for image |
3797
+ | `status` | `'online' \| 'offline'` | `undefined` | Status indicator |
3798
+ | `className` | `string` | `""` | Additional CSS class |
3799
+
3800
+ ### Basic Avatar
3801
+
3802
+ ```jsx
3803
+ import { Avatar } from "alope-ui";
3804
+
3805
+ const BasicExample = () => {
3806
+ return (
3807
+ <Avatar
3808
+ imageSrc="https://i.pravatar.cc/150?img=1"
3809
+ alt="User Avatar"
3810
+ />
3811
+ );
3812
+ };
3813
+ ```
3814
+
3815
+ ### Fallback with Initials
3816
+
3817
+ ```jsx
3818
+ {/* Avatar with fallback name */}
3819
+ <Avatar fallbackName="John Doe" />
3820
+
3821
+ {/* Avatar with no image (shows initials) */}
3822
+ <Avatar
3823
+ imageSrc="invalid-url.jpg"
3824
+ fallbackName="Jane Smith"
3825
+ />
3826
+ ```
3827
+
3828
+ ### Sizes
3829
+
3830
+ ```jsx
3831
+ {/* Small */}
3832
+ <Avatar
3833
+ imageSrc="https://i.pravatar.cc/150?img=1"
3834
+ size="sm"
3835
+ />
3836
+
3837
+ {/* Medium */}
3838
+ <Avatar
3839
+ imageSrc="https://i.pravatar.cc/150?img=1"
3840
+ size="md"
3841
+ />
3842
+
3843
+ {/* Large */}
3844
+ <Avatar
3845
+ imageSrc="https://i.pravatar.cc/150?img=1"
3846
+ size="lg"
3847
+ />
3848
+ ```
3849
+
3850
+ ### Shapes
3851
+
3852
+ ```jsx
3853
+ {/* Sharp corners */}
3854
+ <Avatar
3855
+ imageSrc="https://i.pravatar.cc/150?img=1"
3856
+ shape="sharp"
3857
+ />
3858
+
3859
+ {/* Cornered (slightly rounded) */}
3860
+ <Avatar
3861
+ imageSrc="https://i.pravatar.cc/150?img=1"
3862
+ shape="cornered"
3863
+ />
3864
+
3865
+ {/* Rounded (circular) */}
3866
+ <Avatar
3867
+ imageSrc="https://i.pravatar.cc/150?img=1"
3868
+ shape="rounded"
3869
+ />
3870
+ ```
3871
+
3872
+ ### Variants
3873
+
3874
+ ```jsx
3875
+ {/* Solid variant */}
3876
+ <Avatar
3877
+ fallbackName="John Doe"
3878
+ variant="solid"
3879
+ />
3880
+
3881
+ {/* Outline variant */}
3882
+ <Avatar
3883
+ fallbackName="John Doe"
3884
+ variant="outline"
3885
+ />
3886
+ ```
3887
+
3888
+ ### Fallback Colors
3889
+
3890
+ ```jsx
3891
+ {/* Primary color */}
3892
+ <Avatar fallbackName="John Doe" fallbackColor="primary" />
3893
+
3894
+ {/* Error color */}
3895
+ <Avatar fallbackName="John Doe" fallbackColor="error" />
3896
+
3897
+ {/* Warning color */}
3898
+ <Avatar fallbackName="John Doe" fallbackColor="warning" />
3899
+
3900
+ {/* Success color */}
3901
+ <Avatar fallbackName="John Doe" fallbackColor="success" />
3902
+
3903
+ {/* Info color */}
3904
+ <Avatar fallbackName="John Doe" fallbackColor="info" />
3905
+
3906
+ {/* Secondary color */}
3907
+ <Avatar fallbackName="John Doe" fallbackColor="secondary" />
3908
+ ```
3909
+
3910
+ ### With Ring
3911
+
3912
+ ```jsx
3913
+ {/* Default ring */}
3914
+ <Avatar
3915
+ imageSrc="https://i.pravatar.cc/150?img=1"
3916
+ ring
3917
+ />
3918
+
3919
+ {/* Custom ring color */}
3920
+ <Avatar
3921
+ imageSrc="https://i.pravatar.cc/150?img=1"
3922
+ ring
3923
+ ringClassName="ring-success dark:ring-success-dark"
3924
+ />
3925
+ ```
3926
+
3927
+ ### Status Indicator
3928
+
3929
+ ```jsx
3930
+ {/* Online status */}
3931
+ <Avatar
3932
+ imageSrc="https://i.pravatar.cc/150?img=1"
3933
+ status="online"
3934
+ />
3935
+
3936
+ {/* Offline status */}
3937
+ <Avatar
3938
+ imageSrc="https://i.pravatar.cc/150?img=1"
3939
+ status="offline"
3940
+ />
3941
+ ```
3942
+
3943
+ ### Complete Example
3944
+
3945
+ ```jsx
3946
+ import { Avatar } from "alope-ui";
3947
+
3948
+ const CompleteExample = () => {
3949
+ return (
3950
+ <div className="flex gap-4">
3951
+ <Avatar
3952
+ imageSrc="https://i.pravatar.cc/150?img=1"
3953
+ fallbackName="John Doe"
3954
+ size="lg"
3955
+ shape="rounded"
3956
+ ring
3957
+ status="online"
3958
+ alt="John Doe Avatar"
3959
+ />
3960
+
3961
+ <Avatar
3962
+ imageSrc="https://i.pravatar.cc/150?img=2"
3963
+ fallbackName="Jane Smith"
3964
+ size="lg"
3965
+ shape="rounded"
3966
+ ring
3967
+ status="offline"
3968
+ alt="Jane Smith Avatar"
3969
+ />
3970
+ </div>
3971
+ );
3972
+ };
3973
+ ```
3974
+
3975
+ ## AvatarGroup
3976
+
3977
+ The AvatarGroup component displays multiple avatars in an overlapping stack with a counter for additional avatars.
3978
+
3979
+ ### Import
3980
+
3981
+ ```jsx
3982
+ import { AvatarGroup } from "alope-ui";
3983
+ import type { AvatarGroupProps } from "alope-ui";
3984
+ ```
3985
+
3986
+ ### Props
3987
+
3988
+ | Prop | Type | Default | Description |
3989
+ | ------------- | ----------------- | -------- | ---------------------------------------- |
3990
+ | `avatars` | `AvatarProps[]` | `required` | Array of avatar configurations |
3991
+ | `maxVisible` | `number` | `3` | Maximum number of visible avatars |
3992
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of all avatars in the group |
3993
+ | `overlap` | `string` | `'-ml-3'` | CSS class for overlap spacing |
3994
+ | `className` | `string` | `""` | Additional CSS class |
3995
+
3996
+ ### Basic AvatarGroup
3997
+
3998
+ ```jsx
3999
+ import { AvatarGroup } from "alope-ui";
4000
+
4001
+ const BasicGroupExample = () => {
4002
+ const avatars = [
4003
+ { imageSrc: "https://i.pravatar.cc/150?img=1", fallbackName: "John Doe" },
4004
+ { imageSrc: "https://i.pravatar.cc/150?img=2", fallbackName: "Jane Smith" },
4005
+ { imageSrc: "https://i.pravatar.cc/150?img=3", fallbackName: "Bob Wilson" },
4006
+ ];
4007
+
4008
+ return <AvatarGroup avatars={avatars} />;
4009
+ };
4010
+ ```
4011
+
4012
+ ### With Max Visible Limit
4013
+
4014
+ ```jsx
4015
+ const avatars = [
4016
+ { imageSrc: "https://i.pravatar.cc/150?img=1", fallbackName: "User 1" },
4017
+ { imageSrc: "https://i.pravatar.cc/150?img=2", fallbackName: "User 2" },
4018
+ { imageSrc: "https://i.pravatar.cc/150?img=3", fallbackName: "User 3" },
4019
+ { imageSrc: "https://i.pravatar.cc/150?img=4", fallbackName: "User 4" },
4020
+ { imageSrc: "https://i.pravatar.cc/150?img=5", fallbackName: "User 5" },
4021
+ ];
4022
+
4023
+ {/* Show only 3 avatars, rest shown as "+2" */}
4024
+ <AvatarGroup avatars={avatars} maxVisible={3} />
4025
+ ```
4026
+
4027
+ ### Different Sizes
4028
+
4029
+ ```jsx
4030
+ {/* Small size */}
4031
+ <AvatarGroup avatars={avatars} size="sm" />
4032
+
4033
+ {/* Medium size */}
4034
+ <AvatarGroup avatars={avatars} size="md" />
4035
+
4036
+ {/* Large size */}
4037
+ <AvatarGroup avatars={avatars} size="lg" />
4038
+ ```
4039
+
4040
+ ### Custom Overlap
4041
+
4042
+ ```jsx
4043
+ {/* More overlap */}
4044
+ <AvatarGroup avatars={avatars} overlap="-ml-4" />
4045
+
4046
+ {/* Less overlap */}
4047
+ <AvatarGroup avatars={avatars} overlap="-ml-2" />
4048
+
4049
+ {/* No overlap */}
4050
+ <AvatarGroup avatars={avatars} overlap="" />
4051
+ ```
4052
+
4053
+ ### Complete AvatarGroup Example
4054
+
4055
+ ```jsx
4056
+ import { AvatarGroup } from "alope-ui";
4057
+
4058
+ const CompleteGroupExample = () => {
4059
+ const teamMembers = [
4060
+ {
4061
+ imageSrc: "https://i.pravatar.cc/150?img=1",
4062
+ fallbackName: "John Doe",
4063
+ status: "online"
4064
+ },
4065
+ {
4066
+ imageSrc: "https://i.pravatar.cc/150?img=2",
4067
+ fallbackName: "Jane Smith",
4068
+ status: "online"
4069
+ },
4070
+ {
4071
+ imageSrc: "https://i.pravatar.cc/150?img=3",
4072
+ fallbackName: "Bob Wilson",
4073
+ status: "offline"
4074
+ },
4075
+ {
4076
+ fallbackName: "Alice Brown",
4077
+ fallbackColor: "success"
4078
+ },
4079
+ {
4080
+ fallbackName: "Charlie Davis",
4081
+ fallbackColor: "info"
4082
+ },
4083
+ ];
4084
+
4085
+ return (
4086
+ <AvatarGroup
4087
+ avatars={teamMembers}
4088
+ maxVisible={3}
4089
+ size="md"
4090
+ overlap="-ml-3"
4091
+ />
4092
+ );
4093
+ };
4094
+ ```
4095
+
4096
+ ---
4097
+
1706
4098
  ## 💡 Contributing
1707
4099
 
1708
4100
  We welcome contributions! If you want to add features, fix bugs, or improve documentation, feel free to fork this project and submit a pull request.
@@ -1717,4 +4109,4 @@ MIT License – Free to use, modify, and distribute.
1717
4109
 
1718
4110
  ## 🙌 Thank You
1719
4111
 
1720
- Thank you for using **ALOPE UI Library**. We hope this helps you build beautiful and functional user interfaces with ease.
4112
+ Thank you for using **ALOPE UI Library**. We hope this helps you build beautiful and functional user interfaces with ease.