alope-ui 1.1.5 → 1.1.6

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.
Files changed (32) hide show
  1. package/README.md +3013 -4
  2. package/package.json +3 -3
  3. package/dist/components/Accordion/Accordion.d.ts +0 -18
  4. package/dist/components/Alert/Alert.d.ts +0 -34
  5. package/dist/components/Avatar/Avatar.d.ts +0 -38
  6. package/dist/components/Avatar/AvatarGroup.d.ts +0 -10
  7. package/dist/components/Badge/Badge.d.ts +0 -25
  8. package/dist/components/Breadcrumb/Breadcrumb.d.ts +0 -16
  9. package/dist/components/Button/Button.d.ts +0 -67
  10. package/dist/components/Card/Card.d.ts +0 -19
  11. package/dist/components/CheckboxInput/CheckboxInput.d.ts +0 -10
  12. package/dist/components/CheckboxInput/ListCheckboxInput.d.ts +0 -55
  13. package/dist/components/FileUpload/FIleUpload.d.ts +0 -14
  14. package/dist/components/Modal/Modal.d.ts +0 -13
  15. package/dist/components/Offcanvas/Offcanvas.d.ts +0 -14
  16. package/dist/components/Pagination/Pagination.d.ts +0 -22
  17. package/dist/components/RadioInput/RadioInput.d.ts +0 -55
  18. package/dist/components/SelectInput/SelectInput.d.ts +0 -14
  19. package/dist/components/Skeleton/Skeleton.d.ts +0 -10
  20. package/dist/components/Spinner/Spinner.d.ts +0 -23
  21. package/dist/components/Table/Table.d.ts +0 -34
  22. package/dist/components/Tabs/Tabs.d.ts +0 -57
  23. package/dist/components/TextInput/TextInput.d.ts +0 -6
  24. package/dist/components/Textarea/Textarea.d.ts +0 -8
  25. package/dist/components/Toast/Toast.d.ts +0 -28
  26. package/dist/components/Toast/ToastProvider.d.ts +0 -11
  27. package/dist/components/Toggle/Toggle.d.ts +0 -27
  28. package/dist/components/Tooltip/Tooltip.d.ts +0 -14
  29. package/dist/components/index.d.ts +0 -24
  30. package/dist/index.css +0 -1
  31. package/dist/main.cjs +0 -723
  32. package/dist/main.d.ts +0 -25
package/README.md CHANGED
@@ -98,6 +98,9 @@ export default App;
98
98
  - [🍞 Breadcrumb](#breadcrumb) - Navigation trail
99
99
  - [📑 Tabs](#tabs) - Tabbed navigation interface
100
100
  - [📄 Pagination](#pagination) - Page navigation controls
101
+ - [📱 Bottom Navbar](#bottom-navbar) - Mobile bottom navigation bar
102
+
103
+ ---
101
104
 
102
105
  ### Feedback Components
103
106
 
@@ -106,6 +109,9 @@ export default App;
106
109
  - [💡 Tooltip](#tooltip) - Contextual information on hover
107
110
  - [⏳ Spinner](#spinner) - Loading indicators
108
111
  - [💀 Skeleton](#skeleton) - Content loading placeholders
112
+ - [📅 Date Picker](#datepicker) - Date selection calendar
113
+
114
+ ---
109
115
 
110
116
  ### Data Display
111
117
 
@@ -113,6 +119,12 @@ export default App;
113
119
  - [🃏 Card](#card) - Content containers
114
120
  - [📊 Table](#table) - Tabular data display
115
121
  - [👤 Avatar](#avatar) - User profile images
122
+ - [🧱 Grid](#grid) - Structured layout with columns and rows
123
+ - [🧩 Masonry](#masonry) - Pinterest-style staggered grid layout
124
+ - [🕒 Timeline](#timeline) - Chronological event display
125
+ - [📚 Collection](#collection) - Grouped content or list display
126
+
127
+ ---
116
128
 
117
129
  ### Form Controls
118
130
 
@@ -123,12 +135,17 @@ export default App;
123
135
  - [📝 Textarea](#textarea) - Multi-line text input
124
136
  - [🔄 Toggle](#toggle) - Switch controls
125
137
  - [📁 File Upload](#file-upload) - File selection and upload
138
+ - [🔢 Code Input](#codeinput) - Multi-field input for codes or OTPs
139
+ - [🎚️ Slider](#slider) - Range or value selector
140
+
141
+ ---
126
142
 
127
143
  ### Interactive Elements
128
144
 
129
145
  - [🔘 Button](#button) - Clickable actions
130
146
  - [🪟 Modal](#modal) - Dialog boxes and overlays
131
147
  - [📱 Offcanvas](#offcanvas) - Side panels and drawers
148
+ - [➕ FAB (Floating Action Button)](#fab-floating-action-button) - Prominent floating action button
132
149
 
133
150
  ---
134
151
 
@@ -553,7 +570,7 @@ import { Button } from "alope-ui";
553
570
  | `variant` | `'solid' \| 'outline' \| 'ghost' \| 'plain'` | `'solid'` | Button style variant |
554
571
  | `variantType` | `'primary' \| 'secondary' \| 'success' \| 'info' \| 'warning' \| 'error'` | `'primary'` | Button color theme |
555
572
  | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
556
- | `radius` | `'regular' \| 'stadium'` | `'regular'` | Border radius style |
573
+ | `borderType` | `'regular' \| 'stadium'` | `'regular'` | Border radius style |
557
574
  | `fullWidth` | `boolean` | `false` | Full width button |
558
575
  | `disabled` | `boolean` | `false` | Disabled state |
559
576
  | `to` | `string` | `undefined` | Link destination |
@@ -600,8 +617,8 @@ import { Button } from "alope-ui";
600
617
 
601
618
  ```jsx
602
619
  <div className="flex flex-wrap gap-2">
603
- <Button radius="regular">Regular Radius</Button>
604
- <Button radius="stadium">Stadium Radius</Button>
620
+ <Button borderType="regular">Regular Radius</Button>
621
+ <Button borderType="stadium">Stadium Radius</Button>
605
622
  </div>
606
623
  ```
607
624
 
@@ -3197,7 +3214,7 @@ const CompleteExample = () => {
3197
3214
 
3198
3215
  ---
3199
3216
 
3200
- ## FileUpload
3217
+ ## File Upload
3201
3218
 
3202
3219
  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).
3203
3220
 
@@ -4227,6 +4244,2998 @@ const CompleteGroupExample = () => {
4227
4244
  };
4228
4245
  ```
4229
4246
 
4247
+ ## Collection
4248
+
4249
+ The Collection component displays a list of items with optional active state management and customizable rendering.
4250
+
4251
+ ### Import
4252
+
4253
+ ```jsx
4254
+ import { Collection } from "alope-ui";
4255
+ import type { CollectionProps } from "alope-ui";
4256
+ ```
4257
+
4258
+ ### Props
4259
+
4260
+ | Prop | Type | Default | Description |
4261
+ | ------------------ | ---------------------------------------------------------------------------------------- | ----------- | --------------------------------------- |
4262
+ | `data` | `T[]` | `required` | Array of items to display |
4263
+ | `title` | `string` | `undefined` | Optional title for the collection |
4264
+ | `children` | `(item: T, index: number, isActive: boolean) => React.ReactNode` | `undefined` | Custom render function for items |
4265
+ | `className` | `string` | `""` | Container CSS class |
4266
+ | `wrapperClassName` | `string` | `""` | List wrapper CSS class |
4267
+ | `suffixIcon` | `React.ReactNode \| ((item?: T, index?: number, isActive?: boolean) => React.ReactNode)` | `undefined` | Icon or render function for item suffix |
4268
+ | `activeItem` | `any` | `undefined` | ID of the currently active item |
4269
+ | `onChangeActive` | `(item: T, index: number) => void` | `undefined` | Callback when item is clicked |
4270
+
4271
+ ### Type Constraint
4272
+
4273
+ The generic type `T` must extend `{ id: any, name?: string }`:
4274
+
4275
+ ```typescript
4276
+ type ItemType = {
4277
+ id: string | number;
4278
+ name?: string;
4279
+ // ... other properties
4280
+ };
4281
+ ```
4282
+
4283
+ ### Basic Collection
4284
+
4285
+ ```jsx
4286
+ import { Collection } from "alope-ui";
4287
+
4288
+ const BasicExample = () => {
4289
+ const items = [
4290
+ { id: 1, name: "Item 1" },
4291
+ { id: 2, name: "Item 2" },
4292
+ { id: 3, name: "Item 3" },
4293
+ ];
4294
+
4295
+ return <Collection data={items} />;
4296
+ };
4297
+ ```
4298
+
4299
+ ### With Title
4300
+
4301
+ ```jsx
4302
+ <Collection data={items} title="My Collection" />
4303
+ ```
4304
+
4305
+ ### With Active State
4306
+
4307
+ ```jsx
4308
+ import { useState } from "react";
4309
+ import { Collection } from "alope-ui";
4310
+
4311
+ const ActiveStateExample = () => {
4312
+ const [activeId, setActiveId] = useState(1);
4313
+
4314
+ const items = [
4315
+ { id: 1, name: "Item 1" },
4316
+ { id: 2, name: "Item 2" },
4317
+ { id: 3, name: "Item 3" },
4318
+ ];
4319
+
4320
+ return (
4321
+ <Collection
4322
+ data={items}
4323
+ activeItem={activeId}
4324
+ onChangeActive={(item) => setActiveId(item.id)}
4325
+ />
4326
+ );
4327
+ };
4328
+ ```
4329
+
4330
+ ### With Suffix Icon
4331
+
4332
+ ```jsx
4333
+ import { ChevronRight } from "lucide-react";
4334
+
4335
+ {
4336
+ /* Static icon */
4337
+ }
4338
+ <Collection data={items} suffixIcon={<ChevronRight size={20} />} />;
4339
+
4340
+ {
4341
+ /* Dynamic icon based on state */
4342
+ }
4343
+ <Collection
4344
+ data={items}
4345
+ activeItem={activeId}
4346
+ onChangeActive={(item) => setActiveId(item.id)}
4347
+ suffixIcon={(item, index, isActive) =>
4348
+ isActive ? <Check size={20} /> : <ChevronRight size={20} />
4349
+ }
4350
+ />;
4351
+ ```
4352
+
4353
+ ### Custom Rendering
4354
+
4355
+ ```jsx
4356
+ import { Collection } from "alope-ui";
4357
+
4358
+ const CustomRenderExample = () => {
4359
+ const [activeId, setActiveId] = useState(1);
4360
+
4361
+ const items = [
4362
+ { id: 1, name: "Task 1", completed: false },
4363
+ { id: 2, name: "Task 2", completed: true },
4364
+ { id: 3, name: "Task 3", completed: false },
4365
+ ];
4366
+
4367
+ return (
4368
+ <Collection
4369
+ data={items}
4370
+ activeItem={activeId}
4371
+ onChangeActive={(item) => setActiveId(item.id)}
4372
+ >
4373
+ {(item, index, isActive) => (
4374
+ <div
4375
+ className={`
4376
+ p-4 border rounded-lg cursor-pointer
4377
+ ${isActive ? "bg-blue-100 border-blue-500" : "border-gray-300"}
4378
+ ${item.completed ? "opacity-50" : ""}
4379
+ `}
4380
+ >
4381
+ <div className="flex items-center justify-between">
4382
+ <span className={item.completed ? "line-through" : ""}>
4383
+ {item.name}
4384
+ </span>
4385
+ {item.completed && <span className="text-green-600">✓</span>}
4386
+ </div>
4387
+ </div>
4388
+ )}
4389
+ </Collection>
4390
+ );
4391
+ };
4392
+ ```
4393
+
4394
+ ### Custom Styling
4395
+
4396
+ ```jsx
4397
+ <Collection
4398
+ data={items}
4399
+ title="Styled Collection"
4400
+ className="bg-gray-50 dark:bg-gray-900 p-6 rounded-xl border-2"
4401
+ wrapperClassName="space-y-4"
4402
+ activeItem={activeId}
4403
+ onChangeActive={(item) => setActiveId(item.id)}
4404
+ />
4405
+ ```
4406
+
4407
+ ### Empty State
4408
+
4409
+ ```jsx
4410
+ {
4411
+ /* When data array is empty */
4412
+ }
4413
+ <Collection data={[]} title="Empty Collection" />;
4414
+ {
4415
+ /* Displays: "No items to display." */
4416
+ }
4417
+ ```
4418
+
4419
+ ### Complete Example
4420
+
4421
+ ```jsx
4422
+ import { useState } from "react";
4423
+ import { Collection } from "alope-ui";
4424
+ import { CheckCircle, Circle, Trash2 } from "lucide-react";
4425
+
4426
+ const CompleteExample = () => {
4427
+ const [activeId, setActiveId] = useState(null);
4428
+ const [todos, setTodos] = useState([
4429
+ { id: 1, name: "Complete project documentation", completed: false },
4430
+ { id: 2, name: "Review pull requests", completed: true },
4431
+ { id: 3, name: "Update dependencies", completed: false },
4432
+ ]);
4433
+
4434
+ const toggleComplete = (id) => {
4435
+ setTodos(
4436
+ todos.map((todo) =>
4437
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
4438
+ )
4439
+ );
4440
+ };
4441
+
4442
+ return (
4443
+ <Collection
4444
+ data={todos}
4445
+ title="Todo List"
4446
+ activeItem={activeId}
4447
+ onChangeActive={(item) => setActiveId(item.id)}
4448
+ className="max-w-md"
4449
+ >
4450
+ {(item, index, isActive) => (
4451
+ <div
4452
+ className={`
4453
+ p-4 border rounded-lg cursor-pointer transition-all
4454
+ ${isActive ? "bg-blue-50 border-blue-500" : "border-gray-300"}
4455
+ `}
4456
+ >
4457
+ <div className="flex items-center justify-between">
4458
+ <div className="flex items-center gap-3">
4459
+ <button
4460
+ onClick={(e) => {
4461
+ e.stopPropagation();
4462
+ toggleComplete(item.id);
4463
+ }}
4464
+ className="text-gray-600 hover:text-blue-600"
4465
+ >
4466
+ {item.completed ? (
4467
+ <CheckCircle size={20} className="text-green-600" />
4468
+ ) : (
4469
+ <Circle size={20} />
4470
+ )}
4471
+ </button>
4472
+ <span
4473
+ className={item.completed ? "line-through text-gray-500" : ""}
4474
+ >
4475
+ {item.name}
4476
+ </span>
4477
+ </div>
4478
+ <button
4479
+ onClick={(e) => {
4480
+ e.stopPropagation();
4481
+ setTodos(todos.filter((t) => t.id !== item.id));
4482
+ }}
4483
+ className="text-red-500 hover:text-red-700"
4484
+ >
4485
+ <Trash2 size={18} />
4486
+ </button>
4487
+ </div>
4488
+ </div>
4489
+ )}
4490
+ </Collection>
4491
+ );
4492
+ };
4493
+ ```
4494
+
4495
+ ## FAB (Floating Action Button)
4496
+
4497
+ The FAB component provides a floating action button with expandable child actions, typically positioned at the bottom-right corner of the screen.
4498
+
4499
+ ### Import
4500
+
4501
+ ```jsx
4502
+ import { FAB } from "alope-ui";
4503
+ ```
4504
+
4505
+ ### Props
4506
+
4507
+ | Prop | Type | Default | Description |
4508
+ | ------------- | ------------------------------------------------------------------------- | ------------ | --------------------------------------- |
4509
+ | `children` | `React.ReactNode` | `required` | Action buttons to display when expanded |
4510
+ | `isOpen` | `boolean` | `required` | Controls the expanded state |
4511
+ | `onOpen` | `() => void` | `required` | Callback to toggle open/close state |
4512
+ | `direction` | `'horizontal' \| 'vertical'` | `'vertical'` | Direction of child actions expansion |
4513
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the FAB button |
4514
+ | `variant` | `'solid' \| 'outline' \| 'ghost' \| 'soft'` | `'solid'` | Visual variant style |
4515
+ | `variantType` | `'primary' \| 'secondary' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'primary'` | Color theme |
4516
+ | `icon` | `React.ReactNode` | `'+'` | Icon displayed on the main FAB button |
4517
+
4518
+ ### Basic FAB
4519
+
4520
+ ```jsx
4521
+ import { useState } from "react";
4522
+ import { FAB } from "alope-ui";
4523
+ import { Button } from "alope-ui";
4524
+
4525
+ const BasicExample = () => {
4526
+ const [isOpen, setIsOpen] = useState(false);
4527
+
4528
+ return (
4529
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)}>
4530
+ <Button variant="solid" variantType="primary">
4531
+ Action 1
4532
+ </Button>
4533
+ <Button variant="solid" variantType="secondary">
4534
+ Action 2
4535
+ </Button>
4536
+ <Button variant="solid" variantType="success">
4537
+ Action 3
4538
+ </Button>
4539
+ </FAB>
4540
+ );
4541
+ };
4542
+ ```
4543
+
4544
+ ### Directions
4545
+
4546
+ ```jsx
4547
+ {
4548
+ /* Vertical direction (default) */
4549
+ }
4550
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} direction="vertical">
4551
+ {/* Actions */}
4552
+ </FAB>;
4553
+
4554
+ {
4555
+ /* Horizontal direction */
4556
+ }
4557
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} direction="horizontal">
4558
+ {/* Actions */}
4559
+ </FAB>;
4560
+ ```
4561
+
4562
+ ### Sizes
4563
+
4564
+ ```jsx
4565
+ {
4566
+ /* Small size */
4567
+ }
4568
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} size="sm">
4569
+ {/* Actions */}
4570
+ </FAB>;
4571
+
4572
+ {
4573
+ /* Medium size */
4574
+ }
4575
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} size="md">
4576
+ {/* Actions */}
4577
+ </FAB>;
4578
+
4579
+ {
4580
+ /* Large size */
4581
+ }
4582
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} size="lg">
4583
+ {/* Actions */}
4584
+ </FAB>;
4585
+ ```
4586
+
4587
+ ### Variants
4588
+
4589
+ ```jsx
4590
+ {
4591
+ /* Solid variant */
4592
+ }
4593
+ <FAB
4594
+ isOpen={isOpen}
4595
+ onOpen={() => setIsOpen(!isOpen)}
4596
+ variant="solid"
4597
+ variantType="primary"
4598
+ >
4599
+ {/* Actions */}
4600
+ </FAB>;
4601
+
4602
+ {
4603
+ /* Outline variant */
4604
+ }
4605
+ <FAB
4606
+ isOpen={isOpen}
4607
+ onOpen={() => setIsOpen(!isOpen)}
4608
+ variant="outline"
4609
+ variantType="secondary"
4610
+ >
4611
+ {/* Actions */}
4612
+ </FAB>;
4613
+
4614
+ {
4615
+ /* Ghost variant */
4616
+ }
4617
+ <FAB
4618
+ isOpen={isOpen}
4619
+ onOpen={() => setIsOpen(!isOpen)}
4620
+ variant="ghost"
4621
+ variantType="success"
4622
+ >
4623
+ {/* Actions */}
4624
+ </FAB>;
4625
+
4626
+ {
4627
+ /* Soft variant */
4628
+ }
4629
+ <FAB
4630
+ isOpen={isOpen}
4631
+ onOpen={() => setIsOpen(!isOpen)}
4632
+ variant="soft"
4633
+ variantType="warning"
4634
+ >
4635
+ {/* Actions */}
4636
+ </FAB>;
4637
+ ```
4638
+
4639
+ ### Variant Types (Colors)
4640
+
4641
+ ```jsx
4642
+ {
4643
+ /* Primary */
4644
+ }
4645
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="primary">
4646
+ {/* Actions */}
4647
+ </FAB>;
4648
+
4649
+ {
4650
+ /* Secondary */
4651
+ }
4652
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="secondary">
4653
+ {/* Actions */}
4654
+ </FAB>;
4655
+
4656
+ {
4657
+ /* Success */
4658
+ }
4659
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="success">
4660
+ {/* Actions */}
4661
+ </FAB>;
4662
+
4663
+ {
4664
+ /* Error */
4665
+ }
4666
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="error">
4667
+ {/* Actions */}
4668
+ </FAB>;
4669
+
4670
+ {
4671
+ /* Warning */
4672
+ }
4673
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="warning">
4674
+ {/* Actions */}
4675
+ </FAB>;
4676
+
4677
+ {
4678
+ /* Info */
4679
+ }
4680
+ <FAB isOpen={isOpen} onOpen={() => setIsOpen(!isOpen)} variantType="info">
4681
+ {/* Actions */}
4682
+ </FAB>;
4683
+ ```
4684
+
4685
+ ### Custom Icon
4686
+
4687
+ ```jsx
4688
+ import { Plus, Menu } from "lucide-react";
4689
+
4690
+ {
4691
+ /* Custom icon */
4692
+ }
4693
+ <FAB
4694
+ isOpen={isOpen}
4695
+ onOpen={() => setIsOpen(!isOpen)}
4696
+ icon={<Plus size={24} />}
4697
+ >
4698
+ {/* Actions */}
4699
+ </FAB>;
4700
+
4701
+ {
4702
+ /* Menu icon */
4703
+ }
4704
+ <FAB
4705
+ isOpen={isOpen}
4706
+ onOpen={() => setIsOpen(!isOpen)}
4707
+ icon={<Menu size={24} />}
4708
+ >
4709
+ {/* Actions */}
4710
+ </FAB>;
4711
+ ```
4712
+
4713
+ ### With Icon Actions
4714
+
4715
+ ```jsx
4716
+ import { useState } from "react";
4717
+ import { FAB } from "alope-ui";
4718
+ import { Button } from "alope-ui";
4719
+ import { Edit, Trash, Share, Plus } from "lucide-react";
4720
+
4721
+ const IconActionsExample = () => {
4722
+ const [isOpen, setIsOpen] = useState(false);
4723
+
4724
+ return (
4725
+ <FAB
4726
+ isOpen={isOpen}
4727
+ onOpen={() => setIsOpen(!isOpen)}
4728
+ icon={<Plus size={24} />}
4729
+ >
4730
+ <Button variant="solid" variantType="primary" size="sm">
4731
+ <Edit size={18} />
4732
+ </Button>
4733
+ <Button variant="solid" variantType="error" size="sm">
4734
+ <Trash size={18} />
4735
+ </Button>
4736
+ <Button variant="solid" variantType="success" size="sm">
4737
+ <Share size={18} />
4738
+ </Button>
4739
+ </FAB>
4740
+ );
4741
+ };
4742
+ ```
4743
+
4744
+ ### Complete Example
4745
+
4746
+ ```jsx
4747
+ import { useState } from "react";
4748
+ import { FAB } from "alope-ui";
4749
+ import { Button } from "alope-ui";
4750
+ import { Plus, MessageCircle, Phone, Video, Mail } from "lucide-react";
4751
+
4752
+ const CompleteExample = () => {
4753
+ const [isOpen, setIsOpen] = useState(false);
4754
+
4755
+ const handleAction = (action) => {
4756
+ console.log(`${action} clicked`);
4757
+ setIsOpen(false);
4758
+ };
4759
+
4760
+ return (
4761
+ <FAB
4762
+ isOpen={isOpen}
4763
+ onOpen={() => setIsOpen(!isOpen)}
4764
+ direction="vertical"
4765
+ size="md"
4766
+ variant="solid"
4767
+ variantType="primary"
4768
+ icon={<Plus size={24} />}
4769
+ >
4770
+ <Button
4771
+ variant="solid"
4772
+ variantType="primary"
4773
+ size="sm"
4774
+ onClick={() => handleAction("Message")}
4775
+ >
4776
+ <MessageCircle size={20} />
4777
+ <span className="ml-2">Message</span>
4778
+ </Button>
4779
+
4780
+ <Button
4781
+ variant="solid"
4782
+ variantType="success"
4783
+ size="sm"
4784
+ onClick={() => handleAction("Call")}
4785
+ >
4786
+ <Phone size={20} />
4787
+ <span className="ml-2">Call</span>
4788
+ </Button>
4789
+
4790
+ <Button
4791
+ variant="solid"
4792
+ variantType="info"
4793
+ size="sm"
4794
+ onClick={() => handleAction("Video")}
4795
+ >
4796
+ <Video size={20} />
4797
+ <span className="ml-2">Video</span>
4798
+ </Button>
4799
+
4800
+ <Button
4801
+ variant="solid"
4802
+ variantType="secondary"
4803
+ size="sm"
4804
+ onClick={() => handleAction("Email")}
4805
+ >
4806
+ <Mail size={20} />
4807
+ <span className="ml-2">Email</span>
4808
+ </Button>
4809
+ </FAB>
4810
+ );
4811
+ };
4812
+ ```
4813
+
4814
+ ### Horizontal Layout Example
4815
+
4816
+ ```jsx
4817
+ import { useState } from "react";
4818
+ import { FAB } from "alope-ui";
4819
+ import { Button } from "alope-ui";
4820
+ import { Plus, Home, User, Settings } from "lucide-react";
4821
+
4822
+ const HorizontalExample = () => {
4823
+ const [isOpen, setIsOpen] = useState(false);
4824
+
4825
+ return (
4826
+ <FAB
4827
+ isOpen={isOpen}
4828
+ onOpen={() => setIsOpen(!isOpen)}
4829
+ direction="horizontal"
4830
+ size="md"
4831
+ variant="solid"
4832
+ variantType="primary"
4833
+ icon={<Plus size={24} />}
4834
+ >
4835
+ <Button variant="solid" variantType="primary" size="sm">
4836
+ <Home size={20} />
4837
+ </Button>
4838
+ <Button variant="solid" variantType="secondary" size="sm">
4839
+ <User size={20} />
4840
+ </Button>
4841
+ <Button variant="solid" variantType="info" size="sm">
4842
+ <Settings size={20} />
4843
+ </Button>
4844
+ </FAB>
4845
+ );
4846
+ };
4847
+ ```
4848
+
4849
+ ## BottomNavbar
4850
+
4851
+ The BottomNavbar component provides a mobile-friendly navigation bar fixed at the bottom of the screen, composed of BottomNavbarAction items.
4852
+
4853
+ ### Import
4854
+
4855
+ ```jsx
4856
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
4857
+ ```
4858
+
4859
+ ### BottomNavbar Props
4860
+
4861
+ | Prop | Type | Default | Description |
4862
+ | --------------- | ------------------------- | ---------- | -------------------------------- |
4863
+ | `children` | `React.ReactNode` | `required` | BottomNavbarAction components |
4864
+ | `value` | `string` | `required` | Currently selected action value |
4865
+ | `onChangeValue` | `(value: string) => void` | `required` | Callback when action is selected |
4866
+ | `className` | `string` | `""` | Additional CSS class |
4867
+
4868
+ ### BottomNavbarAction Props
4869
+
4870
+ | Prop | Type | Default | Description |
4871
+ | ---------------- | ------------------------- | ----------- | ---------------------------------------- |
4872
+ | `value` | `string` | `required` | Unique identifier for the action |
4873
+ | `label` | `React.ReactNode` | `undefined` | Label text or component |
4874
+ | `icon` | `React.ReactNode` | `undefined` | Icon component |
4875
+ | `selectedValue` | `string` | `undefined` | Currently selected value (auto-injected) |
4876
+ | `onChangeValue` | `(value: string) => void` | `undefined` | Change handler (auto-injected) |
4877
+ | `labelClassName` | `string` | `""` | Label CSS class |
4878
+ | `className` | `string` | `""` | Additional CSS class |
4879
+
4880
+ ### Basic BottomNavbar
4881
+
4882
+ ```jsx
4883
+ import { useState } from "react";
4884
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
4885
+ import { Home, Search, Bell, User } from "lucide-react";
4886
+
4887
+ const BasicExample = () => {
4888
+ const [activeTab, setActiveTab] = useState("home");
4889
+
4890
+ return (
4891
+ <BottomNavbar value={activeTab} onChangeValue={setActiveTab}>
4892
+ <BottomNavbarAction value="home" icon={<Home size={24} />} label="Home" />
4893
+ <BottomNavbarAction
4894
+ value="search"
4895
+ icon={<Search size={24} />}
4896
+ label="Search"
4897
+ />
4898
+ <BottomNavbarAction
4899
+ value="notifications"
4900
+ icon={<Bell size={24} />}
4901
+ label="Notifications"
4902
+ />
4903
+ <BottomNavbarAction
4904
+ value="profile"
4905
+ icon={<User size={24} />}
4906
+ label="Profile"
4907
+ />
4908
+ </BottomNavbar>
4909
+ );
4910
+ };
4911
+ ```
4912
+
4913
+ ### Without Labels
4914
+
4915
+ ```jsx
4916
+ import { useState } from "react";
4917
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
4918
+ import { Home, Search, Bell, User } from "lucide-react";
4919
+
4920
+ const IconOnlyExample = () => {
4921
+ const [activeTab, setActiveTab] = useState("home");
4922
+
4923
+ return (
4924
+ <BottomNavbar value={activeTab} onChangeValue={setActiveTab}>
4925
+ <BottomNavbarAction value="home" icon={<Home size={24} />} />
4926
+ <BottomNavbarAction value="search" icon={<Search size={24} />} />
4927
+ <BottomNavbarAction value="notifications" icon={<Bell size={24} />} />
4928
+ <BottomNavbarAction value="profile" icon={<User size={24} />} />
4929
+ </BottomNavbar>
4930
+ );
4931
+ };
4932
+ ```
4933
+
4934
+ ### With Badge Indicators
4935
+
4936
+ ```jsx
4937
+ import { useState } from "react";
4938
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
4939
+ import { Home, ShoppingCart, Bell, User } from "lucide-react";
4940
+
4941
+ const BadgeExample = () => {
4942
+ const [activeTab, setActiveTab] = useState("home");
4943
+
4944
+ return (
4945
+ <BottomNavbar value={activeTab} onChangeValue={setActiveTab}>
4946
+ <BottomNavbarAction value="home" icon={<Home size={24} />} label="Home" />
4947
+ <BottomNavbarAction
4948
+ value="cart"
4949
+ icon={
4950
+ <div className="relative">
4951
+ <ShoppingCart size={24} />
4952
+ <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
4953
+ 3
4954
+ </span>
4955
+ </div>
4956
+ }
4957
+ label="Cart"
4958
+ />
4959
+ <BottomNavbarAction
4960
+ value="notifications"
4961
+ icon={
4962
+ <div className="relative">
4963
+ <Bell size={24} />
4964
+ <span className="absolute -top-1 -right-1 bg-red-500 w-2 h-2 rounded-full"></span>
4965
+ </div>
4966
+ }
4967
+ label="Notifications"
4968
+ />
4969
+ <BottomNavbarAction
4970
+ value="profile"
4971
+ icon={<User size={24} />}
4972
+ label="Profile"
4973
+ />
4974
+ </BottomNavbar>
4975
+ );
4976
+ };
4977
+ ```
4978
+
4979
+ ### Custom Styling
4980
+
4981
+ ```jsx
4982
+ import { useState } from "react";
4983
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
4984
+ import { Home, Search, Heart, User } from "lucide-react";
4985
+
4986
+ const CustomStyledExample = () => {
4987
+ const [activeTab, setActiveTab] = useState("home");
4988
+
4989
+ return (
4990
+ <BottomNavbar
4991
+ value={activeTab}
4992
+ onChangeValue={setActiveTab}
4993
+ className="border-t-2 border-primary"
4994
+ >
4995
+ <BottomNavbarAction
4996
+ value="home"
4997
+ icon={<Home size={24} />}
4998
+ label="Home"
4999
+ labelClassName="font-semibold"
5000
+ />
5001
+ <BottomNavbarAction
5002
+ value="search"
5003
+ icon={<Search size={24} />}
5004
+ label="Search"
5005
+ labelClassName="font-semibold"
5006
+ />
5007
+ <BottomNavbarAction
5008
+ value="favorites"
5009
+ icon={<Heart size={24} />}
5010
+ label="Favorites"
5011
+ labelClassName="font-semibold"
5012
+ />
5013
+ <BottomNavbarAction
5014
+ value="profile"
5015
+ icon={<User size={24} />}
5016
+ label="Profile"
5017
+ labelClassName="font-semibold"
5018
+ />
5019
+ </BottomNavbar>
5020
+ );
5021
+ };
5022
+ ```
5023
+
5024
+ ### With Routing Integration
5025
+
5026
+ ```jsx
5027
+ import { useState, useEffect } from "react";
5028
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
5029
+ import { Home, Compass, PlusSquare, Heart, User } from "lucide-react";
5030
+ import { useNavigate, useLocation } from "react-router-dom";
5031
+
5032
+ const RoutingExample = () => {
5033
+ const navigate = useNavigate();
5034
+ const location = useLocation();
5035
+ const [activeTab, setActiveTab] = useState("home");
5036
+
5037
+ useEffect(() => {
5038
+ // Update active tab based on current route
5039
+ const path = location.pathname.substring(1) || "home";
5040
+ setActiveTab(path);
5041
+ }, [location]);
5042
+
5043
+ const handleTabChange = (value) => {
5044
+ setActiveTab(value);
5045
+ navigate(`/${value}`);
5046
+ };
5047
+
5048
+ return (
5049
+ <BottomNavbar value={activeTab} onChangeValue={handleTabChange}>
5050
+ <BottomNavbarAction value="home" icon={<Home size={24} />} label="Home" />
5051
+ <BottomNavbarAction
5052
+ value="explore"
5053
+ icon={<Compass size={24} />}
5054
+ label="Explore"
5055
+ />
5056
+ <BottomNavbarAction
5057
+ value="create"
5058
+ icon={<PlusSquare size={24} />}
5059
+ label="Create"
5060
+ />
5061
+ <BottomNavbarAction
5062
+ value="likes"
5063
+ icon={<Heart size={24} />}
5064
+ label="Likes"
5065
+ />
5066
+ <BottomNavbarAction
5067
+ value="profile"
5068
+ icon={<User size={24} />}
5069
+ label="Profile"
5070
+ />
5071
+ </BottomNavbar>
5072
+ );
5073
+ };
5074
+ ```
5075
+
5076
+ ### Complete Example
5077
+
5078
+ ```jsx
5079
+ import { useState } from "react";
5080
+ import { BottomNavbar, BottomNavbarAction } from "alope-ui";
5081
+ import { Home, Search, PlusCircle, MessageCircle, User } from "lucide-react";
5082
+
5083
+ const CompleteExample = () => {
5084
+ const [activeTab, setActiveTab] = useState("home");
5085
+ const [notifications, setNotifications] = useState({
5086
+ messages: 5,
5087
+ hasNewNotifications: true,
5088
+ });
5089
+
5090
+ const handleTabChange = (value) => {
5091
+ setActiveTab(value);
5092
+
5093
+ // Clear notifications when viewing messages
5094
+ if (value === "messages") {
5095
+ setNotifications({ ...notifications, messages: 0 });
5096
+ }
5097
+ };
5098
+
5099
+ return (
5100
+ <div className="relative min-h-screen pb-20">
5101
+ {/* Your page content here */}
5102
+ <div className="p-4">
5103
+ <h1 className="text-2xl font-bold">
5104
+ {activeTab.charAt(0).toUpperCase() + activeTab.slice(1)} Page
5105
+ </h1>
5106
+ <p className="text-gray-600 mt-2">Current active tab: {activeTab}</p>
5107
+ </div>
5108
+
5109
+ {/* Bottom Navigation */}
5110
+ <BottomNavbar value={activeTab} onChangeValue={handleTabChange}>
5111
+ <BottomNavbarAction
5112
+ value="home"
5113
+ icon={<Home size={24} />}
5114
+ label="Home"
5115
+ />
5116
+
5117
+ <BottomNavbarAction
5118
+ value="search"
5119
+ icon={<Search size={24} />}
5120
+ label="Search"
5121
+ />
5122
+
5123
+ <BottomNavbarAction
5124
+ value="create"
5125
+ icon={<PlusCircle size={28} />}
5126
+ label="Create"
5127
+ className="relative -top-2"
5128
+ />
5129
+
5130
+ <BottomNavbarAction
5131
+ value="messages"
5132
+ icon={
5133
+ <div className="relative">
5134
+ <MessageCircle size={24} />
5135
+ {notifications.messages > 0 && (
5136
+ <span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
5137
+ {notifications.messages}
5138
+ </span>
5139
+ )}
5140
+ </div>
5141
+ }
5142
+ label="Messages"
5143
+ />
5144
+
5145
+ <BottomNavbarAction
5146
+ value="profile"
5147
+ icon={
5148
+ <div className="relative">
5149
+ <User size={24} />
5150
+ {notifications.hasNewNotifications && (
5151
+ <span className="absolute -top-1 -right-1 bg-red-500 w-2 h-2 rounded-full"></span>
5152
+ )}
5153
+ </div>
5154
+ }
5155
+ label="Profile"
5156
+ />
5157
+ </BottomNavbar>
5158
+ </div>
5159
+ );
5160
+ };
5161
+ ```
5162
+
5163
+ ## Grid
5164
+
5165
+ The Grid component provides a flexible and responsive CSS Grid layout system with support for responsive columns and gaps.
5166
+
5167
+ ### Import
5168
+
5169
+ ```jsx
5170
+ import { Grid, GridItem } from "alope-ui";
5171
+ ```
5172
+
5173
+ ### Grid Props
5174
+
5175
+ | Prop | Type | Default | Description |
5176
+ | ----------- | ----------------- | ----------- | -------------------------------------- |
5177
+ | `children` | `React.ReactNode` | `required` | Grid items to display |
5178
+ | `cols` | `ResponsiveCols` | `undefined` | Number of columns (1-12) or responsive |
5179
+ | `gap` | `ResponsiveGap` | `undefined` | Gap between items (1-6) or responsive |
5180
+ | `className` | `string` | `""` | Additional CSS class |
5181
+
5182
+ ### GridItem Props
5183
+
5184
+ | Prop | Type | Default | Description |
5185
+ | ----------- | ------------------- | ----------- | ------------------------------------------ |
5186
+ | `children` | `React.ReactNode` | `required` | Content to display |
5187
+ | `colSpan` | `ResponsiveColSpan` | `undefined` | Number of columns to span (1-12 or 'full') |
5188
+ | `className` | `string` | `""` | Additional CSS class |
5189
+
5190
+ ### Responsive Types
5191
+
5192
+ ```typescript
5193
+ type ResponsiveValue<T> = T | Partial<Record<Breakpoint, T>>
5194
+ type Breakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
5195
+
5196
+ // Examples:
5197
+ cols={3} // Same for all breakpoints
5198
+ cols={{ base: 1, md: 2, lg: 3 }} // Responsive columns
5199
+ gap={4} // Same for all breakpoints
5200
+ gap={{ base: 2, md: 4, lg: 6 }} // Responsive gaps
5201
+ ```
5202
+
5203
+ ### Basic Grid
5204
+
5205
+ ```jsx
5206
+ import { Grid, GridItem } from "alope-ui";
5207
+
5208
+ const BasicExample = () => {
5209
+ return (
5210
+ <Grid cols={3} gap={4}>
5211
+ <GridItem>Item 1</GridItem>
5212
+ <GridItem>Item 2</GridItem>
5213
+ <GridItem>Item 3</GridItem>
5214
+ <GridItem>Item 4</GridItem>
5215
+ <GridItem>Item 5</GridItem>
5216
+ <GridItem>Item 6</GridItem>
5217
+ </Grid>
5218
+ );
5219
+ };
5220
+ ```
5221
+
5222
+ ### Responsive Columns
5223
+
5224
+ ```jsx
5225
+ {
5226
+ /* 1 column on mobile, 2 on tablet, 3 on desktop */
5227
+ }
5228
+ <Grid cols={{ base: 1, md: 2, lg: 3 }} gap={4}>
5229
+ <GridItem>Item 1</GridItem>
5230
+ <GridItem>Item 2</GridItem>
5231
+ <GridItem>Item 3</GridItem>
5232
+ <GridItem>Item 4</GridItem>
5233
+ <GridItem>Item 5</GridItem>
5234
+ <GridItem>Item 6</GridItem>
5235
+ </Grid>;
5236
+ ```
5237
+
5238
+ ### Responsive Gaps
5239
+
5240
+ ```jsx
5241
+ {
5242
+ /* Small gap on mobile, larger on desktop */
5243
+ }
5244
+ <Grid cols={3} gap={{ base: 2, md: 4, lg: 6 }}>
5245
+ <GridItem>Item 1</GridItem>
5246
+ <GridItem>Item 2</GridItem>
5247
+ <GridItem>Item 3</GridItem>
5248
+ </Grid>;
5249
+ ```
5250
+
5251
+ ### Column Spanning
5252
+
5253
+ ```jsx
5254
+ {
5255
+ /* Items spanning multiple columns */
5256
+ }
5257
+ <Grid cols={4} gap={4}>
5258
+ <GridItem colSpan={2}>Spans 2 columns</GridItem>
5259
+ <GridItem colSpan={2}>Spans 2 columns</GridItem>
5260
+ <GridItem colSpan={1}>1 column</GridItem>
5261
+ <GridItem colSpan={3}>Spans 3 columns</GridItem>
5262
+ <GridItem colSpan={4}>Spans full width</GridItem>
5263
+ </Grid>;
5264
+ ```
5265
+
5266
+ ### Responsive Column Spanning
5267
+
5268
+ ```jsx
5269
+ {
5270
+ /* Different spans at different breakpoints */
5271
+ }
5272
+ <Grid cols={{ base: 2, md: 4, lg: 6 }} gap={4}>
5273
+ <GridItem colSpan={{ base: 2, md: 2, lg: 3 }}>
5274
+ Full width on mobile, half on tablet, half on desktop
5275
+ </GridItem>
5276
+ <GridItem colSpan={{ base: 2, md: 2, lg: 3 }}>
5277
+ Full width on mobile, half on tablet, half on desktop
5278
+ </GridItem>
5279
+ <GridItem colSpan={{ base: 1, md: 2, lg: 2 }}>Responsive span</GridItem>
5280
+ <GridItem colSpan={{ base: 1, md: 2, lg: 2 }}>Responsive span</GridItem>
5281
+ </Grid>;
5282
+ ```
5283
+
5284
+ ### Card Layout
5285
+
5286
+ ```jsx
5287
+ import { Grid, GridItem } from "alope-ui";
5288
+
5289
+ const CardLayoutExample = () => {
5290
+ const cards = [
5291
+ { id: 1, title: "Card 1", description: "Description 1" },
5292
+ { id: 2, title: "Card 2", description: "Description 2" },
5293
+ { id: 3, title: "Card 3", description: "Description 3" },
5294
+ { id: 4, title: "Card 4", description: "Description 4" },
5295
+ ];
5296
+
5297
+ return (
5298
+ <Grid cols={{ base: 1, md: 2, lg: 4 }} gap={4}>
5299
+ {cards.map((card) => (
5300
+ <GridItem key={card.id}>
5301
+ <div className="p-4 border rounded-lg shadow-sm">
5302
+ <h3 className="font-bold text-lg">{card.title}</h3>
5303
+ <p className="text-gray-600">{card.description}</p>
5304
+ </div>
5305
+ </GridItem>
5306
+ ))}
5307
+ </Grid>
5308
+ );
5309
+ };
5310
+ ```
5311
+
5312
+ ### Dashboard Layout
5313
+
5314
+ ```jsx
5315
+ import { Grid, GridItem } from "alope-ui";
5316
+
5317
+ const DashboardExample = () => {
5318
+ return (
5319
+ <Grid cols={{ base: 1, lg: 12 }} gap={4}>
5320
+ {/* Header - Full width */}
5321
+ <GridItem colSpan={{ base: 1, lg: 12 }}>
5322
+ <div className="p-6 bg-blue-500 text-white rounded-lg">
5323
+ <h1 className="text-2xl font-bold">Dashboard Header</h1>
5324
+ </div>
5325
+ </GridItem>
5326
+
5327
+ {/* Sidebar - Full width on mobile, 3 cols on desktop */}
5328
+ <GridItem colSpan={{ base: 1, lg: 3 }}>
5329
+ <div className="p-6 bg-gray-100 rounded-lg h-full">
5330
+ <h2 className="font-bold mb-4">Sidebar</h2>
5331
+ <ul className="space-y-2">
5332
+ <li>Menu Item 1</li>
5333
+ <li>Menu Item 2</li>
5334
+ <li>Menu Item 3</li>
5335
+ </ul>
5336
+ </div>
5337
+ </GridItem>
5338
+
5339
+ {/* Main content - Full width on mobile, 9 cols on desktop */}
5340
+ <GridItem colSpan={{ base: 1, lg: 9 }}>
5341
+ <div className="p-6 bg-white border rounded-lg">
5342
+ <h2 className="font-bold text-xl mb-4">Main Content</h2>
5343
+ <p>Your main content goes here...</p>
5344
+ </div>
5345
+ </GridItem>
5346
+ </Grid>
5347
+ );
5348
+ };
5349
+ ```
5350
+
5351
+ ### Image Gallery
5352
+
5353
+ ```jsx
5354
+ import { Grid, GridItem } from "alope-ui";
5355
+
5356
+ const GalleryExample = () => {
5357
+ const images = [
5358
+ { id: 1, src: "image1.jpg", featured: true },
5359
+ { id: 2, src: "image2.jpg", featured: false },
5360
+ { id: 3, src: "image3.jpg", featured: false },
5361
+ { id: 4, src: "image4.jpg", featured: false },
5362
+ { id: 5, src: "image5.jpg", featured: true },
5363
+ { id: 6, src: "image6.jpg", featured: false },
5364
+ ];
5365
+
5366
+ return (
5367
+ <Grid cols={{ base: 2, md: 4, lg: 6 }} gap={2}>
5368
+ {images.map((image) => (
5369
+ <GridItem
5370
+ key={image.id}
5371
+ colSpan={
5372
+ image.featured
5373
+ ? { base: 2, md: 2, lg: 3 }
5374
+ : { base: 1, md: 1, lg: 1 }
5375
+ }
5376
+ >
5377
+ <div className="aspect-square bg-gray-200 rounded-lg overflow-hidden">
5378
+ <img
5379
+ src={image.src}
5380
+ alt={`Image ${image.id}`}
5381
+ className="w-full h-full object-cover"
5382
+ />
5383
+ </div>
5384
+ </GridItem>
5385
+ ))}
5386
+ </Grid>
5387
+ );
5388
+ };
5389
+ ```
5390
+
5391
+ ### Complete Example
5392
+
5393
+ ```jsx
5394
+ import { Grid, GridItem } from "alope-ui";
5395
+
5396
+ const CompleteExample = () => {
5397
+ return (
5398
+ <div className="p-8">
5399
+ <h1 className="text-3xl font-bold mb-8">Responsive Grid Layout</h1>
5400
+
5401
+ <Grid
5402
+ cols={{ base: 1, sm: 2, md: 3, lg: 4, xl: 6 }}
5403
+ gap={{ base: 2, md: 4, lg: 6 }}
5404
+ >
5405
+ {/* Hero section - Full width */}
5406
+ <GridItem colSpan={{ base: 1, sm: 2, md: 3, lg: 4, xl: 6 }}>
5407
+ <div className="p-8 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-lg">
5408
+ <h2 className="text-2xl font-bold mb-2">Hero Section</h2>
5409
+ <p>Full width across all breakpoints</p>
5410
+ </div>
5411
+ </GridItem>
5412
+
5413
+ {/* Feature cards */}
5414
+ <GridItem colSpan={{ base: 1, sm: 1, md: 1, lg: 2, xl: 2 }}>
5415
+ <div className="p-6 bg-blue-100 rounded-lg h-full">
5416
+ <h3 className="font-bold mb-2">Feature 1</h3>
5417
+ <p className="text-sm">Description of feature 1</p>
5418
+ </div>
5419
+ </GridItem>
5420
+
5421
+ <GridItem colSpan={{ base: 1, sm: 1, md: 1, lg: 2, xl: 2 }}>
5422
+ <div className="p-6 bg-green-100 rounded-lg h-full">
5423
+ <h3 className="font-bold mb-2">Feature 2</h3>
5424
+ <p className="text-sm">Description of feature 2</p>
5425
+ </div>
5426
+ </GridItem>
5427
+
5428
+ <GridItem colSpan={{ base: 1, sm: 2, md: 1, lg: 4, xl: 2 }}>
5429
+ <div className="p-6 bg-purple-100 rounded-lg h-full">
5430
+ <h3 className="font-bold mb-2">Feature 3</h3>
5431
+ <p className="text-sm">Description of feature 3</p>
5432
+ </div>
5433
+ </GridItem>
5434
+
5435
+ {/* Grid items */}
5436
+ {[1, 2, 3, 4, 5, 6].map((item) => (
5437
+ <GridItem key={item}>
5438
+ <div className="p-4 border rounded-lg text-center">
5439
+ <p className="font-semibold">Item {item}</p>
5440
+ </div>
5441
+ </GridItem>
5442
+ ))}
5443
+
5444
+ {/* Footer - Full width */}
5445
+ <GridItem colSpan={{ base: 1, sm: 2, md: 3, lg: 4, xl: 6 }}>
5446
+ <div className="p-6 bg-gray-800 text-white rounded-lg text-center">
5447
+ <p>Footer - Full width</p>
5448
+ </div>
5449
+ </GridItem>
5450
+ </Grid>
5451
+ </div>
5452
+ );
5453
+ };
5454
+ ```
5455
+
5456
+ ## Masonry
5457
+
5458
+ The Masonry component provides a Pinterest-style masonry layout using CSS columns, perfect for displaying items of varying heights in a visually appealing grid.
5459
+
5460
+ ### Import
5461
+
5462
+ ```jsx
5463
+ import { Masonry } from "alope-ui";
5464
+ ```
5465
+
5466
+ ### Props
5467
+
5468
+ | Prop | Type | Default | Description |
5469
+ | ----------- | ----------------- | ----------- | -------------------------------------- |
5470
+ | `children` | `React.ReactNode` | `required` | Items to display in masonry layout |
5471
+ | `cols` | `ResponsiveCols` | `undefined` | Number of columns (1-12) or responsive |
5472
+ | `gap` | `ResponsiveGap` | `4` | Gap between items (1-6) or responsive |
5473
+ | `className` | `string` | `""` | Additional CSS class |
5474
+
5475
+ ### Responsive Types
5476
+
5477
+ ```typescript
5478
+ type ResponsiveValue<T> = T | Partial<Record<Breakpoint, T>>
5479
+ type Breakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
5480
+
5481
+ // Examples:
5482
+ cols={3} // Same for all breakpoints
5483
+ cols={{ base: 1, md: 2, lg: 3 }} // Responsive columns
5484
+ gap={4} // Same for all breakpoints
5485
+ gap={{ base: 2, md: 4, lg: 6 }} // Responsive gaps
5486
+ ```
5487
+
5488
+ ### Basic Masonry
5489
+
5490
+ ```jsx
5491
+ import { Masonry } from "alope-ui";
5492
+
5493
+ const BasicExample = () => {
5494
+ return (
5495
+ <Masonry cols={3} gap={4}>
5496
+ <div className="p-4 bg-blue-100 rounded">Item 1</div>
5497
+ <div className="p-4 bg-green-100 rounded">Item 2</div>
5498
+ <div className="p-4 bg-purple-100 rounded">Item 3</div>
5499
+ <div className="p-4 bg-yellow-100 rounded">Item 4</div>
5500
+ <div className="p-4 bg-pink-100 rounded">Item 5</div>
5501
+ <div className="p-4 bg-indigo-100 rounded">Item 6</div>
5502
+ </Masonry>
5503
+ );
5504
+ };
5505
+ ```
5506
+
5507
+ ### Responsive Columns
5508
+
5509
+ ```jsx
5510
+ {
5511
+ /* 1 column on mobile, 2 on tablet, 3 on desktop */
5512
+ }
5513
+ <Masonry cols={{ base: 1, md: 2, lg: 3 }} gap={4}>
5514
+ <div className="p-4 bg-blue-100 rounded">Item 1</div>
5515
+ <div className="p-4 bg-green-100 rounded">Item 2</div>
5516
+ <div className="p-4 bg-purple-100 rounded">Item 3</div>
5517
+ <div className="p-4 bg-yellow-100 rounded">Item 4</div>
5518
+ <div className="p-4 bg-pink-100 rounded">Item 5</div>
5519
+ <div className="p-4 bg-indigo-100 rounded">Item 6</div>
5520
+ </Masonry>;
5521
+ ```
5522
+
5523
+ ### Responsive Gaps
5524
+
5525
+ ```jsx
5526
+ {
5527
+ /* Small gap on mobile, larger on desktop */
5528
+ }
5529
+ <Masonry cols={3} gap={{ base: 2, md: 4, lg: 6 }}>
5530
+ <div className="p-4 bg-blue-100 rounded">Item 1</div>
5531
+ <div className="p-4 bg-green-100 rounded">Item 2</div>
5532
+ <div className="p-4 bg-purple-100 rounded">Item 3</div>
5533
+ </Masonry>;
5534
+ ```
5535
+
5536
+ ### Image Gallery
5537
+
5538
+ ```jsx
5539
+ import { Masonry } from "alope-ui";
5540
+
5541
+ const ImageGalleryExample = () => {
5542
+ const images = [
5543
+ { id: 1, src: "image1.jpg", height: "h-64" },
5544
+ { id: 2, src: "image2.jpg", height: "h-48" },
5545
+ { id: 3, src: "image3.jpg", height: "h-80" },
5546
+ { id: 4, src: "image4.jpg", height: "h-56" },
5547
+ { id: 5, src: "image5.jpg", height: "h-72" },
5548
+ { id: 6, src: "image6.jpg", height: "h-40" },
5549
+ ];
5550
+
5551
+ return (
5552
+ <Masonry cols={{ base: 2, md: 3, lg: 4 }} gap={3}>
5553
+ {images.map((image) => (
5554
+ <div key={image.id} className="break-inside-avoid mb-3">
5555
+ <img
5556
+ src={image.src}
5557
+ alt={`Image ${image.id}`}
5558
+ className={`w-full ${image.height} object-cover rounded-lg`}
5559
+ />
5560
+ </div>
5561
+ ))}
5562
+ </Masonry>
5563
+ );
5564
+ };
5565
+ ```
5566
+
5567
+ ### Card Masonry
5568
+
5569
+ ```jsx
5570
+ import { Masonry } from "alope-ui";
5571
+
5572
+ const CardMasonryExample = () => {
5573
+ const cards = [
5574
+ { id: 1, title: "Card 1", content: "Short content" },
5575
+ {
5576
+ id: 2,
5577
+ title: "Card 2",
5578
+ content:
5579
+ "This is a longer content that will make the card taller than others in the masonry layout.",
5580
+ },
5581
+ { id: 3, title: "Card 3", content: "Medium length content here." },
5582
+ { id: 4, title: "Card 4", content: "Another short one." },
5583
+ {
5584
+ id: 5,
5585
+ title: "Card 5",
5586
+ content:
5587
+ "This card has quite a bit more content to display, making it one of the taller cards in the layout.",
5588
+ },
5589
+ ];
5590
+
5591
+ return (
5592
+ <Masonry cols={{ base: 1, sm: 2, lg: 3 }} gap={4}>
5593
+ {cards.map((card) => (
5594
+ <div
5595
+ key={card.id}
5596
+ className="break-inside-avoid mb-4 p-6 bg-white border rounded-lg shadow-sm"
5597
+ >
5598
+ <h3 className="font-bold text-lg mb-2">{card.title}</h3>
5599
+ <p className="text-gray-600">{card.content}</p>
5600
+ </div>
5601
+ ))}
5602
+ </Masonry>
5603
+ );
5604
+ };
5605
+ ```
5606
+
5607
+ ### Pinterest-Style Layout
5608
+
5609
+ ```jsx
5610
+ import { Masonry } from "alope-ui";
5611
+
5612
+ const PinterestExample = () => {
5613
+ const pins = [
5614
+ { id: 1, image: "pin1.jpg", title: "Design Inspiration", height: "h-64" },
5615
+ { id: 2, image: "pin2.jpg", title: "Photography", height: "h-80" },
5616
+ { id: 3, image: "pin3.jpg", title: "Architecture", height: "h-56" },
5617
+ { id: 4, image: "pin4.jpg", title: "Nature", height: "h-72" },
5618
+ { id: 5, image: "pin5.jpg", title: "Food", height: "h-48" },
5619
+ { id: 6, image: "pin6.jpg", title: "Travel", height: "h-96" },
5620
+ ];
5621
+
5622
+ return (
5623
+ <Masonry cols={{ base: 2, md: 3, lg: 4, xl: 5 }} gap={{ base: 2, md: 3 }}>
5624
+ {pins.map((pin) => (
5625
+ <div
5626
+ key={pin.id}
5627
+ className="break-inside-avoid mb-3 group cursor-pointer"
5628
+ >
5629
+ <div className="relative overflow-hidden rounded-lg">
5630
+ <img
5631
+ src={pin.image}
5632
+ alt={pin.title}
5633
+ className={`w-full ${pin.height} object-cover transition-transform group-hover:scale-105`}
5634
+ />
5635
+ <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition-opacity" />
5636
+ <div className="absolute bottom-0 left-0 right-0 p-4 text-white opacity-0 group-hover:opacity-100 transition-opacity">
5637
+ <h3 className="font-semibold">{pin.title}</h3>
5638
+ </div>
5639
+ </div>
5640
+ </div>
5641
+ ))}
5642
+ </Masonry>
5643
+ );
5644
+ };
5645
+ ```
5646
+
5647
+ ### Blog Post Grid
5648
+
5649
+ ```jsx
5650
+ import { Masonry } from "alope-ui";
5651
+
5652
+ const BlogGridExample = () => {
5653
+ const posts = [
5654
+ {
5655
+ id: 1,
5656
+ title: "Getting Started with React",
5657
+ excerpt: "Learn the basics of React in this comprehensive guide.",
5658
+ image: "post1.jpg",
5659
+ date: "Jan 15, 2024",
5660
+ },
5661
+ {
5662
+ id: 2,
5663
+ title: "Advanced TypeScript Patterns",
5664
+ excerpt:
5665
+ "Dive deep into advanced TypeScript patterns and best practices for building scalable applications.",
5666
+ image: "post2.jpg",
5667
+ date: "Jan 20, 2024",
5668
+ },
5669
+ {
5670
+ id: 3,
5671
+ title: "CSS Grid vs Flexbox",
5672
+ excerpt: "Understanding when to use Grid and when to use Flexbox.",
5673
+ image: "post3.jpg",
5674
+ date: "Jan 25, 2024",
5675
+ },
5676
+ ];
5677
+
5678
+ return (
5679
+ <Masonry cols={{ base: 1, md: 2, lg: 3 }} gap={5}>
5680
+ {posts.map((post) => (
5681
+ <article
5682
+ key={post.id}
5683
+ className="break-inside-avoid mb-5 bg-white rounded-xl overflow-hidden shadow-lg hover:shadow-xl transition-shadow"
5684
+ >
5685
+ <img
5686
+ src={post.image}
5687
+ alt={post.title}
5688
+ className="w-full h-48 object-cover"
5689
+ />
5690
+ <div className="p-6">
5691
+ <time className="text-sm text-gray-500">{post.date}</time>
5692
+ <h2 className="text-xl font-bold mt-2 mb-3">{post.title}</h2>
5693
+ <p className="text-gray-600">{post.excerpt}</p>
5694
+ <button className="mt-4 text-blue-600 hover:text-blue-800 font-medium">
5695
+ Read More →
5696
+ </button>
5697
+ </div>
5698
+ </article>
5699
+ ))}
5700
+ </Masonry>
5701
+ );
5702
+ };
5703
+ ```
5704
+
5705
+ ### Complete Example
5706
+
5707
+ ```jsx
5708
+ import { Masonry } from "alope-ui";
5709
+ import { Heart, MessageCircle, Share2 } from "lucide-react";
5710
+
5711
+ const CompleteExample = () => {
5712
+ const items = [
5713
+ {
5714
+ id: 1,
5715
+ type: "image",
5716
+ src: "nature1.jpg",
5717
+ title: "Mountain Landscape",
5718
+ likes: 234,
5719
+ comments: 12,
5720
+ height: "h-64",
5721
+ },
5722
+ {
5723
+ id: 2,
5724
+ type: "quote",
5725
+ text: "Design is not just what it looks like and feels like. Design is how it works.",
5726
+ author: "Steve Jobs",
5727
+ height: "h-48",
5728
+ },
5729
+ {
5730
+ id: 3,
5731
+ type: "image",
5732
+ src: "architecture.jpg",
5733
+ title: "Modern Architecture",
5734
+ likes: 567,
5735
+ comments: 23,
5736
+ height: "h-80",
5737
+ },
5738
+ {
5739
+ id: 4,
5740
+ type: "article",
5741
+ title: "The Future of Web Development",
5742
+ excerpt:
5743
+ "Exploring upcoming trends and technologies that will shape the future of web development.",
5744
+ readTime: "5 min read",
5745
+ height: "auto",
5746
+ },
5747
+ {
5748
+ id: 5,
5749
+ type: "image",
5750
+ src: "food.jpg",
5751
+ title: "Culinary Art",
5752
+ likes: 189,
5753
+ comments: 8,
5754
+ height: "h-56",
5755
+ },
5756
+ ];
5757
+
5758
+ return (
5759
+ <div className="p-8 bg-gray-50 min-h-screen">
5760
+ <h1 className="text-3xl font-bold mb-8">Masonry Gallery</h1>
5761
+
5762
+ <Masonry cols={{ base: 1, sm: 2, md: 3, lg: 4 }} gap={{ base: 3, md: 4 }}>
5763
+ {items.map((item) => (
5764
+ <div key={item.id} className="break-inside-avoid mb-4">
5765
+ {item.type === "image" && (
5766
+ <div className="bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition-shadow">
5767
+ <img
5768
+ src={item.src}
5769
+ alt={item.title}
5770
+ className={`w-full ${item.height} object-cover`}
5771
+ />
5772
+ <div className="p-4">
5773
+ <h3 className="font-semibold mb-3">{item.title}</h3>
5774
+ <div className="flex items-center gap-4 text-gray-600">
5775
+ <button className="flex items-center gap-1 hover:text-red-500">
5776
+ <Heart size={18} />
5777
+ <span className="text-sm">{item.likes}</span>
5778
+ </button>
5779
+ <button className="flex items-center gap-1 hover:text-blue-500">
5780
+ <MessageCircle size={18} />
5781
+ <span className="text-sm">{item.comments}</span>
5782
+ </button>
5783
+ <button className="flex items-center gap-1 hover:text-green-500">
5784
+ <Share2 size={18} />
5785
+ </button>
5786
+ </div>
5787
+ </div>
5788
+ </div>
5789
+ )}
5790
+
5791
+ {item.type === "quote" && (
5792
+ <div
5793
+ className={`bg-gradient-to-br from-purple-500 to-pink-500 ${item.height} rounded-lg p-6 text-white flex flex-col justify-center shadow-md`}
5794
+ >
5795
+ <p className="text-xl italic mb-4">"{item.text}"</p>
5796
+ <p className="text-sm opacity-90">— {item.author}</p>
5797
+ </div>
5798
+ )}
5799
+
5800
+ {item.type === "article" && (
5801
+ <div className="bg-white rounded-lg p-6 shadow-md hover:shadow-xl transition-shadow">
5802
+ <h3 className="font-bold text-lg mb-2">{item.title}</h3>
5803
+ <p className="text-gray-600 mb-4">{item.excerpt}</p>
5804
+ <div className="flex items-center justify-between text-sm">
5805
+ <span className="text-gray-500">{item.readTime}</span>
5806
+ <button className="text-blue-600 hover:text-blue-800 font-medium">
5807
+ Read More →
5808
+ </button>
5809
+ </div>
5810
+ </div>
5811
+ )}
5812
+ </div>
5813
+ ))}
5814
+ </Masonry>
5815
+ </div>
5816
+ );
5817
+ };
5818
+ ```
5819
+
5820
+ ## Slider
5821
+
5822
+ The Slider component provides an interactive range input with tooltips, marks, and customizable styling options.
5823
+
5824
+ ### Import
5825
+
5826
+ ```jsx
5827
+ import { Slider } from "alope-ui";
5828
+ ```
5829
+
5830
+ ### Props
5831
+
5832
+ | Prop | Type | Default | Description |
5833
+ | ------------- | ------------------------------------------------------------------------- | ----------- | --------------------------- |
5834
+ | `label` | `string` | `undefined` | Label text for the slider |
5835
+ | `value` | `number` | `required` | Current slider value |
5836
+ | `onChange` | `(e: ChangeEvent<HTMLInputElement>) => void` | `required` | Change handler function |
5837
+ | `min` | `number` | `0` | Minimum value |
5838
+ | `max` | `number` | `100` | Maximum value |
5839
+ | `step` | `number` | `1` | Step increment |
5840
+ | `color` | `'primary' \| 'error' \| 'warning' \| 'success' \| 'info' \| 'secondary'` | `'primary'` | Color theme |
5841
+ | `sliderSize` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Size of the slider |
5842
+ | `showTooltip` | `boolean` | `true` | Show value tooltip on hover |
5843
+ | `showMarks` | `boolean` | `true` | Show step marks |
5844
+ | `prefixIcon` | `React.ReactNode` | `undefined` | Icon before slider |
5845
+ | `suffixIcon` | `React.ReactNode` | `undefined` | Icon after slider |
5846
+ | `disabled` | `boolean` | `false` | Disable slider interaction |
5847
+ | `id` | `string` | `undefined` | HTML id attribute |
5848
+ | `name` | `string` | `undefined` | HTML name attribute |
5849
+
5850
+ ### Basic Slider
5851
+
5852
+ ```jsx
5853
+ import { useState } from "react";
5854
+ import { Slider } from "alope-ui";
5855
+
5856
+ const BasicExample = () => {
5857
+ const [value, setValue] = useState(50);
5858
+
5859
+ return (
5860
+ <Slider
5861
+ label="Volume"
5862
+ value={value}
5863
+ onChange={(e) => setValue(Number(e.target.value))}
5864
+ />
5865
+ );
5866
+ };
5867
+ ```
5868
+
5869
+ ### Range Configuration
5870
+
5871
+ ```jsx
5872
+ import { useState } from "react";
5873
+ import { Slider } from "alope-ui";
5874
+
5875
+ const RangeExample = () => {
5876
+ const [value, setValue] = useState(25);
5877
+
5878
+ return (
5879
+ <Slider
5880
+ label="Select a value"
5881
+ value={value}
5882
+ min={0}
5883
+ max={100}
5884
+ step={5}
5885
+ onChange={(e) => setValue(Number(e.target.value))}
5886
+ />
5887
+ );
5888
+ };
5889
+ ```
5890
+
5891
+ ### Colors
5892
+
5893
+ ```jsx
5894
+ import { useState } from "react";
5895
+ import { Slider } from "alope-ui";
5896
+
5897
+ const ColorsExample = () => {
5898
+ const [value, setValue] = useState(50);
5899
+
5900
+ return (
5901
+ <div className="space-y-6">
5902
+ {/* Primary */}
5903
+ <Slider
5904
+ label="Primary"
5905
+ value={value}
5906
+ color="primary"
5907
+ onChange={(e) => setValue(Number(e.target.value))}
5908
+ />
5909
+
5910
+ {/* Success */}
5911
+ <Slider
5912
+ label="Success"
5913
+ value={value}
5914
+ color="success"
5915
+ onChange={(e) => setValue(Number(e.target.value))}
5916
+ />
5917
+
5918
+ {/* Error */}
5919
+ <Slider
5920
+ label="Error"
5921
+ value={value}
5922
+ color="error"
5923
+ onChange={(e) => setValue(Number(e.target.value))}
5924
+ />
5925
+
5926
+ {/* Warning */}
5927
+ <Slider
5928
+ label="Warning"
5929
+ value={value}
5930
+ color="warning"
5931
+ onChange={(e) => setValue(Number(e.target.value))}
5932
+ />
5933
+
5934
+ {/* Info */}
5935
+ <Slider
5936
+ label="Info"
5937
+ value={value}
5938
+ color="info"
5939
+ onChange={(e) => setValue(Number(e.target.value))}
5940
+ />
5941
+
5942
+ {/* Secondary */}
5943
+ <Slider
5944
+ label="Secondary"
5945
+ value={value}
5946
+ color="secondary"
5947
+ onChange={(e) => setValue(Number(e.target.value))}
5948
+ />
5949
+ </div>
5950
+ );
5951
+ };
5952
+ ```
5953
+
5954
+ ### Sizes
5955
+
5956
+ ```jsx
5957
+ import { useState } from "react";
5958
+ import { Slider } from "alope-ui";
5959
+
5960
+ const SizesExample = () => {
5961
+ const [value, setValue] = useState(50);
5962
+
5963
+ return (
5964
+ <div className="space-y-6">
5965
+ {/* Small */}
5966
+ <Slider
5967
+ label="Small"
5968
+ value={value}
5969
+ sliderSize="sm"
5970
+ onChange={(e) => setValue(Number(e.target.value))}
5971
+ />
5972
+
5973
+ {/* Medium */}
5974
+ <Slider
5975
+ label="Medium"
5976
+ value={value}
5977
+ sliderSize="md"
5978
+ onChange={(e) => setValue(Number(e.target.value))}
5979
+ />
5980
+
5981
+ {/* Large */}
5982
+ <Slider
5983
+ label="Large"
5984
+ value={value}
5985
+ sliderSize="lg"
5986
+ onChange={(e) => setValue(Number(e.target.value))}
5987
+ />
5988
+
5989
+ {/* Extra Large */}
5990
+ <Slider
5991
+ label="Extra Large"
5992
+ value={value}
5993
+ sliderSize="xl"
5994
+ onChange={(e) => setValue(Number(e.target.value))}
5995
+ />
5996
+ </div>
5997
+ );
5998
+ };
5999
+ ```
6000
+
6001
+ ### With Icons
6002
+
6003
+ ```jsx
6004
+ import { useState } from "react";
6005
+ import { Slider } from "alope-ui";
6006
+ import { Volume, Volume2, VolumeX } from "lucide-react";
6007
+
6008
+ const IconExample = () => {
6009
+ const [volume, setVolume] = useState(50);
6010
+ const [brightness, setBrightness] = useState(75);
6011
+
6012
+ return (
6013
+ <div className="space-y-6">
6014
+ {/* Volume slider with icons */}
6015
+ <Slider
6016
+ label="Volume Control"
6017
+ value={volume}
6018
+ prefixIcon={<VolumeX size={20} className="text-gray-500" />}
6019
+ suffixIcon={<Volume2 size={20} className="text-gray-500" />}
6020
+ onChange={(e) => setVolume(Number(e.target.value))}
6021
+ />
6022
+
6023
+ {/* Brightness slider */}
6024
+ <Slider
6025
+ label="Brightness"
6026
+ value={brightness}
6027
+ prefixIcon={<Volume size={20} className="text-gray-500" />}
6028
+ suffixIcon={<Volume2 size={20} className="text-gray-500" />}
6029
+ color="warning"
6030
+ onChange={(e) => setBrightness(Number(e.target.value))}
6031
+ />
6032
+ </div>
6033
+ );
6034
+ };
6035
+ ```
6036
+
6037
+ ### Without Tooltip
6038
+
6039
+ ```jsx
6040
+ import { useState } from "react";
6041
+ import { Slider } from "alope-ui";
6042
+
6043
+ const NoTooltipExample = () => {
6044
+ const [value, setValue] = useState(50);
6045
+
6046
+ return (
6047
+ <Slider
6048
+ label="No Tooltip"
6049
+ value={value}
6050
+ showTooltip={false}
6051
+ onChange={(e) => setValue(Number(e.target.value))}
6052
+ />
6053
+ );
6054
+ };
6055
+ ```
6056
+
6057
+ ### Without Marks
6058
+
6059
+ ```jsx
6060
+ import { useState } from "react";
6061
+ import { Slider } from "alope-ui";
6062
+
6063
+ const NoMarksExample = () => {
6064
+ const [value, setValue] = useState(50);
6065
+
6066
+ return (
6067
+ <Slider
6068
+ label="No Marks"
6069
+ value={value}
6070
+ step={10}
6071
+ showMarks={false}
6072
+ onChange={(e) => setValue(Number(e.target.value))}
6073
+ />
6074
+ );
6075
+ };
6076
+ ```
6077
+
6078
+ ### Disabled State
6079
+
6080
+ ```jsx
6081
+ import { useState } from "react";
6082
+ import { Slider } from "alope-ui";
6083
+
6084
+ const DisabledExample = () => {
6085
+ const [value, setValue] = useState(50);
6086
+
6087
+ return (
6088
+ <Slider
6089
+ label="Disabled Slider"
6090
+ value={value}
6091
+ disabled
6092
+ onChange={(e) => setValue(Number(e.target.value))}
6093
+ />
6094
+ );
6095
+ };
6096
+ ```
6097
+
6098
+ ### Price Range Slider
6099
+
6100
+ ```jsx
6101
+ import { useState } from "react";
6102
+ import { Slider } from "alope-ui";
6103
+ import { DollarSign } from "lucide-react";
6104
+
6105
+ const PriceRangeExample = () => {
6106
+ const [price, setPrice] = useState(500);
6107
+
6108
+ return (
6109
+ <Slider
6110
+ label="Price Range"
6111
+ value={price}
6112
+ min={0}
6113
+ max={1000}
6114
+ step={50}
6115
+ color="success"
6116
+ prefixIcon={<DollarSign size={20} className="text-green-600" />}
6117
+ onChange={(e) => setPrice(Number(e.target.value))}
6118
+ />
6119
+ );
6120
+ };
6121
+ ```
6122
+
6123
+ ### Temperature Control
6124
+
6125
+ ```jsx
6126
+ import { useState } from "react";
6127
+ import { Slider } from "alope-ui";
6128
+ import { Thermometer } from "lucide-react";
6129
+
6130
+ const TemperatureExample = () => {
6131
+ const [temperature, setTemperature] = useState(22);
6132
+
6133
+ const getColor = () => {
6134
+ if (temperature < 18) return "info";
6135
+ if (temperature > 26) return "error";
6136
+ return "success";
6137
+ };
6138
+
6139
+ return (
6140
+ <Slider
6141
+ label={`Temperature: ${temperature}°C`}
6142
+ value={temperature}
6143
+ min={16}
6144
+ max={30}
6145
+ step={1}
6146
+ color={getColor()}
6147
+ prefixIcon={<Thermometer size={20} />}
6148
+ onChange={(e) => setTemperature(Number(e.target.value))}
6149
+ />
6150
+ );
6151
+ };
6152
+ ```
6153
+
6154
+ ### Complete Example
6155
+
6156
+ ```jsx
6157
+ import { useState } from "react";
6158
+ import { Slider } from "alope-ui";
6159
+ import { Volume2, Sun, Gauge, Settings } from "lucide-react";
6160
+
6161
+ const CompleteExample = () => {
6162
+ const [settings, setSettings] = useState({
6163
+ volume: 75,
6164
+ brightness: 60,
6165
+ speed: 50,
6166
+ quality: 80,
6167
+ });
6168
+
6169
+ const updateSetting = (key, value) => {
6170
+ setSettings((prev) => ({ ...prev, [key]: value }));
6171
+ };
6172
+
6173
+ return (
6174
+ <div className="max-w-2xl mx-auto p-8 space-y-8">
6175
+ <div className="flex items-center gap-3 mb-8">
6176
+ <Settings size={32} className="text-primary" />
6177
+ <h1 className="text-2xl font-bold">System Settings</h1>
6178
+ </div>
6179
+
6180
+ {/* Volume Control */}
6181
+ <Slider
6182
+ label="Volume"
6183
+ value={settings.volume}
6184
+ min={0}
6185
+ max={100}
6186
+ step={5}
6187
+ color="primary"
6188
+ sliderSize="md"
6189
+ prefixIcon={<Volume2 size={20} className="text-gray-500" />}
6190
+ onChange={(e) => updateSetting("volume", Number(e.target.value))}
6191
+ />
6192
+
6193
+ {/* Brightness Control */}
6194
+ <Slider
6195
+ label="Brightness"
6196
+ value={settings.brightness}
6197
+ min={0}
6198
+ max={100}
6199
+ step={10}
6200
+ color="warning"
6201
+ sliderSize="md"
6202
+ prefixIcon={<Sun size={20} className="text-yellow-500" />}
6203
+ onChange={(e) => updateSetting("brightness", Number(e.target.value))}
6204
+ />
6205
+
6206
+ {/* Speed Control */}
6207
+ <Slider
6208
+ label="Playback Speed"
6209
+ value={settings.speed}
6210
+ min={25}
6211
+ max={200}
6212
+ step={25}
6213
+ color="info"
6214
+ sliderSize="md"
6215
+ prefixIcon={<Gauge size={20} className="text-blue-500" />}
6216
+ onChange={(e) => updateSetting("speed", Number(e.target.value))}
6217
+ />
6218
+
6219
+ {/* Quality Control */}
6220
+ <Slider
6221
+ label="Video Quality"
6222
+ value={settings.quality}
6223
+ min={0}
6224
+ max={100}
6225
+ step={20}
6226
+ color="success"
6227
+ sliderSize="lg"
6228
+ onChange={(e) => updateSetting("quality", Number(e.target.value))}
6229
+ />
6230
+
6231
+ {/* Settings Summary */}
6232
+ <div className="mt-8 p-4 bg-gray-100 dark:bg-gray-800 rounded-lg">
6233
+ <h3 className="font-semibold mb-2">Current Settings:</h3>
6234
+ <ul className="space-y-1 text-sm">
6235
+ <li>Volume: {settings.volume}%</li>
6236
+ <li>Brightness: {settings.brightness}%</li>
6237
+ <li>Speed: {settings.speed}%</li>
6238
+ <li>Quality: {settings.quality}%</li>
6239
+ </ul>
6240
+ </div>
6241
+ </div>
6242
+ );
6243
+ };
6244
+ ```
6245
+
6246
+ ## DatePicker
6247
+
6248
+ The DatePicker component provides an interactive calendar interface for selecting dates with month and year navigation.
6249
+
6250
+ ### Import
6251
+
6252
+ ```jsx
6253
+ import { DatePicker } from "alope-ui";
6254
+ ```
6255
+
6256
+ ### Props
6257
+
6258
+ | Prop | Type | Default | Description |
6259
+ | ---------------- | ---------------------------------------------------------- | ----------- | ------------------------------ |
6260
+ | `selectedDate` | `Date` | `undefined` | Currently selected date |
6261
+ | `onDateChange` | `(date: Date) => void` | `undefined` | Callback when date is selected |
6262
+ | `label` | `string` | `undefined` | Label text for the input |
6263
+ | `color` | `'success' \| 'info' \| 'error' \| 'warning' \| 'default'` | `'default'` | Color theme |
6264
+ | `labelClassName` | `string` | `""` | Label CSS class |
6265
+ | `id` | `string` | `undefined` | HTML id attribute |
6266
+ | `name` | `string` | `undefined` | HTML name attribute |
6267
+
6268
+ ### Basic DatePicker
6269
+
6270
+ ```jsx
6271
+ import { useState } from "react";
6272
+ import { DatePicker } from "alope-ui";
6273
+
6274
+ const BasicExample = () => {
6275
+ const [selectedDate, setSelectedDate] = useState(new Date());
6276
+
6277
+ return (
6278
+ <DatePicker
6279
+ label="Select Date"
6280
+ selectedDate={selectedDate}
6281
+ onDateChange={setSelectedDate}
6282
+ />
6283
+ );
6284
+ };
6285
+ ```
6286
+
6287
+ ### Without Initial Date
6288
+
6289
+ ```jsx
6290
+ import { useState } from "react";
6291
+ import { DatePicker } from "alope-ui";
6292
+
6293
+ const NoInitialDateExample = () => {
6294
+ const [selectedDate, setSelectedDate] = useState(undefined);
6295
+
6296
+ return (
6297
+ <DatePicker
6298
+ label="Choose a date"
6299
+ selectedDate={selectedDate}
6300
+ onDateChange={setSelectedDate}
6301
+ />
6302
+ );
6303
+ };
6304
+ ```
6305
+
6306
+ ### Custom Label Styling
6307
+
6308
+ ```jsx
6309
+ import { useState } from "react";
6310
+ import { DatePicker } from "alope-ui";
6311
+
6312
+ const CustomLabelExample = () => {
6313
+ const [selectedDate, setSelectedDate] = useState(new Date());
6314
+
6315
+ return (
6316
+ <DatePicker
6317
+ label="Appointment Date"
6318
+ labelClassName="text-lg font-bold text-blue-600"
6319
+ selectedDate={selectedDate}
6320
+ onDateChange={setSelectedDate}
6321
+ />
6322
+ );
6323
+ };
6324
+ ```
6325
+
6326
+ ### Colors
6327
+
6328
+ ```jsx
6329
+ import { useState } from "react";
6330
+ import { DatePicker } from "alope-ui";
6331
+
6332
+ const ColorsExample = () => {
6333
+ const [selectedDate, setSelectedDate] = useState(new Date());
6334
+
6335
+ return (
6336
+ <div className="space-y-4">
6337
+ {/* Default */}
6338
+ <DatePicker
6339
+ label="Default"
6340
+ color="default"
6341
+ selectedDate={selectedDate}
6342
+ onDateChange={setSelectedDate}
6343
+ />
6344
+
6345
+ {/* Success */}
6346
+ <DatePicker
6347
+ label="Success"
6348
+ color="success"
6349
+ selectedDate={selectedDate}
6350
+ onDateChange={setSelectedDate}
6351
+ />
6352
+
6353
+ {/* Info */}
6354
+ <DatePicker
6355
+ label="Info"
6356
+ color="info"
6357
+ selectedDate={selectedDate}
6358
+ onDateChange={setSelectedDate}
6359
+ />
6360
+
6361
+ {/* Warning */}
6362
+ <DatePicker
6363
+ label="Warning"
6364
+ color="warning"
6365
+ selectedDate={selectedDate}
6366
+ onDateChange={setSelectedDate}
6367
+ />
6368
+
6369
+ {/* Error */}
6370
+ <DatePicker
6371
+ label="Error"
6372
+ color="error"
6373
+ selectedDate={selectedDate}
6374
+ onDateChange={setSelectedDate}
6375
+ />
6376
+ </div>
6377
+ );
6378
+ };
6379
+ ```
6380
+
6381
+ ### Form Integration
6382
+
6383
+ ```jsx
6384
+ import { useState } from "react";
6385
+ import { DatePicker } from "alope-ui";
6386
+ import { Button } from "alope-ui";
6387
+
6388
+ const FormExample = () => {
6389
+ const [birthDate, setBirthDate] = useState(undefined);
6390
+
6391
+ const handleSubmit = (e) => {
6392
+ e.preventDefault();
6393
+ console.log("Selected birth date:", birthDate);
6394
+ };
6395
+
6396
+ return (
6397
+ <form onSubmit={handleSubmit} className="space-y-4">
6398
+ <DatePicker
6399
+ label="Birth Date"
6400
+ id="birth-date"
6401
+ name="birthDate"
6402
+ selectedDate={birthDate}
6403
+ onDateChange={setBirthDate}
6404
+ />
6405
+
6406
+ <Button type="submit" variant="solid" variantType="primary">
6407
+ Submit
6408
+ </Button>
6409
+ </form>
6410
+ );
6411
+ };
6412
+ ```
6413
+
6414
+ ### Multiple DatePickers
6415
+
6416
+ ```jsx
6417
+ import { useState } from "react";
6418
+ import { DatePicker } from "alope-ui";
6419
+
6420
+ const MultipleDatePickersExample = () => {
6421
+ const [startDate, setStartDate] = useState(undefined);
6422
+ const [endDate, setEndDate] = useState(undefined);
6423
+
6424
+ return (
6425
+ <div className="space-y-4">
6426
+ <DatePicker
6427
+ label="Start Date"
6428
+ selectedDate={startDate}
6429
+ onDateChange={setStartDate}
6430
+ />
6431
+
6432
+ <DatePicker
6433
+ label="End Date"
6434
+ selectedDate={endDate}
6435
+ onDateChange={setEndDate}
6436
+ />
6437
+
6438
+ {startDate && endDate && (
6439
+ <div className="p-4 bg-blue-50 rounded-lg">
6440
+ <p className="text-sm">
6441
+ Duration: {Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24))}{" "}
6442
+ days
6443
+ </p>
6444
+ </div>
6445
+ )}
6446
+ </div>
6447
+ );
6448
+ };
6449
+ ```
6450
+
6451
+ ### Event Booking
6452
+
6453
+ ```jsx
6454
+ import { useState } from "react";
6455
+ import { DatePicker } from "alope-ui";
6456
+ import { Button } from "alope-ui";
6457
+ import { Calendar, Clock } from "lucide-react";
6458
+
6459
+ const EventBookingExample = () => {
6460
+ const [eventDate, setEventDate] = useState(undefined);
6461
+
6462
+ const handleBooking = () => {
6463
+ if (eventDate) {
6464
+ alert(`Event booked for ${eventDate.toLocaleDateString()}`);
6465
+ }
6466
+ };
6467
+
6468
+ return (
6469
+ <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
6470
+ <div className="flex items-center gap-2 mb-6">
6471
+ <Calendar size={24} className="text-primary" />
6472
+ <h2 className="text-xl font-bold">Book an Event</h2>
6473
+ </div>
6474
+
6475
+ <DatePicker
6476
+ label="Event Date"
6477
+ selectedDate={eventDate}
6478
+ onDateChange={setEventDate}
6479
+ />
6480
+
6481
+ {eventDate && (
6482
+ <div className="mt-4 p-4 bg-green-50 rounded-lg">
6483
+ <div className="flex items-center gap-2 text-green-700">
6484
+ <Clock size={16} />
6485
+ <p className="text-sm font-medium">
6486
+ Selected:{" "}
6487
+ {eventDate.toLocaleDateString("en-US", {
6488
+ weekday: "long",
6489
+ year: "numeric",
6490
+ month: "long",
6491
+ day: "numeric",
6492
+ })}
6493
+ </p>
6494
+ </div>
6495
+ </div>
6496
+ )}
6497
+
6498
+ <Button
6499
+ variant="solid"
6500
+ variantType="primary"
6501
+ className="w-full mt-6"
6502
+ onClick={handleBooking}
6503
+ disabled={!eventDate}
6504
+ >
6505
+ Confirm Booking
6506
+ </Button>
6507
+ </div>
6508
+ );
6509
+ };
6510
+ ```
6511
+
6512
+ ### Travel Booking
6513
+
6514
+ ```jsx
6515
+ import { useState } from "react";
6516
+ import { DatePicker } from "alope-ui";
6517
+ import { Button } from "alope-ui";
6518
+ import { Plane, MapPin } from "lucide-react";
6519
+
6520
+ const TravelBookingExample = () => {
6521
+ const [departureDate, setDepartureDate] = useState(undefined);
6522
+ const [returnDate, setReturnDate] = useState(undefined);
6523
+
6524
+ const calculateNights = () => {
6525
+ if (departureDate && returnDate) {
6526
+ const nights = Math.ceil(
6527
+ (returnDate - departureDate) / (1000 * 60 * 60 * 24)
6528
+ );
6529
+ return nights > 0 ? nights : 0;
6530
+ }
6531
+ return 0;
6532
+ };
6533
+
6534
+ return (
6535
+ <div className="max-w-2xl mx-auto p-8 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl shadow-lg">
6536
+ <div className="flex items-center gap-3 mb-8">
6537
+ <Plane size={32} className="text-indigo-600" />
6538
+ <h2 className="text-2xl font-bold text-gray-800">Flight Booking</h2>
6539
+ </div>
6540
+
6541
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
6542
+ <DatePicker
6543
+ label="Departure Date"
6544
+ selectedDate={departureDate}
6545
+ onDateChange={setDepartureDate}
6546
+ />
6547
+
6548
+ <DatePicker
6549
+ label="Return Date"
6550
+ selectedDate={returnDate}
6551
+ onDateChange={setReturnDate}
6552
+ />
6553
+ </div>
6554
+
6555
+ {departureDate && returnDate && calculateNights() > 0 && (
6556
+ <div className="mt-6 p-4 bg-white rounded-lg shadow-sm">
6557
+ <div className="flex items-center justify-between">
6558
+ <div className="flex items-center gap-2">
6559
+ <MapPin size={20} className="text-indigo-600" />
6560
+ <span className="font-semibold text-gray-700">Trip Duration</span>
6561
+ </div>
6562
+ <span className="text-2xl font-bold text-indigo-600">
6563
+ {calculateNights()} {calculateNights() === 1 ? "night" : "nights"}
6564
+ </span>
6565
+ </div>
6566
+ </div>
6567
+ )}
6568
+
6569
+ <Button
6570
+ variant="solid"
6571
+ variantType="primary"
6572
+ className="w-full mt-8"
6573
+ disabled={!departureDate || !returnDate || calculateNights() <= 0}
6574
+ >
6575
+ Search Flights
6576
+ </Button>
6577
+ </div>
6578
+ );
6579
+ };
6580
+ ```
6581
+
6582
+ ### Complete Example
6583
+
6584
+ ```jsx
6585
+ import { useState } from "react";
6586
+ import { DatePicker } from "alope-ui";
6587
+ import { Button } from "alope-ui";
6588
+ import { Calendar, User, Mail, Phone } from "lucide-react";
6589
+ import { TextInput } from "alope-ui";
6590
+
6591
+ const CompleteExample = () => {
6592
+ const [formData, setFormData] = useState({
6593
+ name: "",
6594
+ email: "",
6595
+ phone: "",
6596
+ appointmentDate: undefined,
6597
+ });
6598
+
6599
+ const handleSubmit = (e) => {
6600
+ e.preventDefault();
6601
+ console.log("Form submitted:", formData);
6602
+ alert("Appointment scheduled successfully!");
6603
+ };
6604
+
6605
+ const handleInputChange = (field, value) => {
6606
+ setFormData((prev) => ({ ...prev, [field]: value }));
6607
+ };
6608
+
6609
+ const isFormValid = () => {
6610
+ return (
6611
+ formData.name &&
6612
+ formData.email &&
6613
+ formData.phone &&
6614
+ formData.appointmentDate
6615
+ );
6616
+ };
6617
+
6618
+ return (
6619
+ <div className="max-w-2xl mx-auto p-8">
6620
+ <div className="bg-white rounded-xl shadow-lg p-8">
6621
+ <div className="flex items-center gap-3 mb-8">
6622
+ <Calendar size={32} className="text-primary" />
6623
+ <div>
6624
+ <h1 className="text-2xl font-bold text-gray-800">
6625
+ Schedule Appointment
6626
+ </h1>
6627
+ <p className="text-gray-600 text-sm">
6628
+ Fill in your details to book an appointment
6629
+ </p>
6630
+ </div>
6631
+ </div>
6632
+
6633
+ <form onSubmit={handleSubmit} className="space-y-6">
6634
+ {/* Name Input */}
6635
+ <div>
6636
+ <label className="block text-sm font-medium mb-2 text-gray-700">
6637
+ <div className="flex items-center gap-2">
6638
+ <User size={16} />
6639
+ Full Name
6640
+ </div>
6641
+ </label>
6642
+ <TextInput
6643
+ type="text"
6644
+ value={formData.name}
6645
+ onChange={(e) => handleInputChange("name", e.target.value)}
6646
+ placeholder="John Doe"
6647
+ required
6648
+ />
6649
+ </div>
6650
+
6651
+ {/* Email Input */}
6652
+ <div>
6653
+ <label className="block text-sm font-medium mb-2 text-gray-700">
6654
+ <div className="flex items-center gap-2">
6655
+ <Mail size={16} />
6656
+ Email Address
6657
+ </div>
6658
+ </label>
6659
+ <TextInput
6660
+ type="email"
6661
+ value={formData.email}
6662
+ onChange={(e) => handleInputChange("email", e.target.value)}
6663
+ placeholder="john@example.com"
6664
+ required
6665
+ />
6666
+ </div>
6667
+
6668
+ {/* Phone Input */}
6669
+ <div>
6670
+ <label className="block text-sm font-medium mb-2 text-gray-700">
6671
+ <div className="flex items-center gap-2">
6672
+ <Phone size={16} />
6673
+ Phone Number
6674
+ </div>
6675
+ </label>
6676
+ <TextInput
6677
+ type="tel"
6678
+ value={formData.phone}
6679
+ onChange={(e) => handleInputChange("phone", e.target.value)}
6680
+ placeholder="+1 (555) 000-0000"
6681
+ required
6682
+ />
6683
+ </div>
6684
+
6685
+ {/* Date Picker */}
6686
+ <DatePicker
6687
+ label="Appointment Date"
6688
+ selectedDate={formData.appointmentDate}
6689
+ onDateChange={(date) => handleInputChange("appointmentDate", date)}
6690
+ />
6691
+
6692
+ {/* Selected Date Summary */}
6693
+ {formData.appointmentDate && (
6694
+ <div className="p-4 bg-blue-50 border border-blue-200 rounded-lg">
6695
+ <p className="text-sm font-medium text-blue-900">
6696
+ Your appointment is scheduled for:
6697
+ </p>
6698
+ <p className="text-lg font-bold text-blue-700 mt-1">
6699
+ {formData.appointmentDate.toLocaleDateString("en-US", {
6700
+ weekday: "long",
6701
+ year: "numeric",
6702
+ month: "long",
6703
+ day: "numeric",
6704
+ })}
6705
+ </p>
6706
+ </div>
6707
+ )}
6708
+
6709
+ {/* Submit Button */}
6710
+ <Button
6711
+ type="submit"
6712
+ variant="solid"
6713
+ variantType="primary"
6714
+ className="w-full"
6715
+ disabled={!isFormValid()}
6716
+ >
6717
+ Schedule Appointment
6718
+ </Button>
6719
+ </form>
6720
+ </div>
6721
+ </div>
6722
+ );
6723
+ };
6724
+ ```
6725
+
6726
+ ## CodeInput
6727
+
6728
+ The CodeInput component provides an input interface for entering verification codes, OTPs, or PINs with individual character boxes and automatic focus management.
6729
+
6730
+ ### Import
6731
+
6732
+ ```jsx
6733
+ import { CodeInput } from "alope-ui";
6734
+ ```
6735
+
6736
+ ### Props
6737
+
6738
+ | Prop | Type | Default | Description |
6739
+ | ---------------- | ------------------------------ | ----------- | -------------------------------- |
6740
+ | `value` | `string` | `required` | Current code value |
6741
+ | `length` | `number` | `required` | Number of code digits/characters |
6742
+ | `onCodeChange` | `(code: string) => void` | `required` | Callback when code changes |
6743
+ | `label` | `string` | `undefined` | Label text for the input |
6744
+ | `inputSize` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Size of input boxes |
6745
+ | `labelClassName` | `string` | `""` | Label CSS class |
6746
+ | `disabled` | `boolean` | `false` | Disable all inputs |
6747
+ | `id` | `string` | `undefined` | HTML id attribute |
6748
+ | `name` | `string` | `undefined` | HTML name attribute |
6749
+
6750
+ ### Basic CodeInput
6751
+
6752
+ ```jsx
6753
+ import { useState } from "react";
6754
+ import { CodeInput } from "alope-ui";
6755
+
6756
+ const BasicExample = () => {
6757
+ const [code, setCode] = useState("");
6758
+
6759
+ return (
6760
+ <CodeInput
6761
+ label="Enter Verification Code"
6762
+ value={code}
6763
+ length={6}
6764
+ onCodeChange={setCode}
6765
+ />
6766
+ );
6767
+ };
6768
+ ```
6769
+
6770
+ ### Sizes
6771
+
6772
+ ```jsx
6773
+ import { useState } from "react";
6774
+ import { CodeInput } from "alope-ui";
6775
+
6776
+ const SizesExample = () => {
6777
+ const [code, setCode] = useState("");
6778
+
6779
+ return (
6780
+ <div className="space-y-6">
6781
+ {/* Small */}
6782
+ <CodeInput
6783
+ label="Small"
6784
+ value={code}
6785
+ length={4}
6786
+ inputSize="sm"
6787
+ onCodeChange={setCode}
6788
+ />
6789
+
6790
+ {/* Medium */}
6791
+ <CodeInput
6792
+ label="Medium"
6793
+ value={code}
6794
+ length={4}
6795
+ inputSize="md"
6796
+ onCodeChange={setCode}
6797
+ />
6798
+
6799
+ {/* Large */}
6800
+ <CodeInput
6801
+ label="Large"
6802
+ value={code}
6803
+ length={4}
6804
+ inputSize="lg"
6805
+ onCodeChange={setCode}
6806
+ />
6807
+
6808
+ {/* Extra Large */}
6809
+ <CodeInput
6810
+ label="Extra Large"
6811
+ value={code}
6812
+ length={4}
6813
+ inputSize="xl"
6814
+ onCodeChange={setCode}
6815
+ />
6816
+ </div>
6817
+ );
6818
+ };
6819
+ ```
6820
+
6821
+ ### Different Lengths
6822
+
6823
+ ```jsx
6824
+ import { useState } from "react";
6825
+ import { CodeInput } from "alope-ui";
6826
+
6827
+ const LengthsExample = () => {
6828
+ const [code4, setCode4] = useState("");
6829
+ const [code6, setCode6] = useState("");
6830
+ const [code8, setCode8] = useState("");
6831
+
6832
+ return (
6833
+ <div className="space-y-6">
6834
+ {/* 4-digit code */}
6835
+ <CodeInput
6836
+ label="4-Digit PIN"
6837
+ value={code4}
6838
+ length={4}
6839
+ onCodeChange={setCode4}
6840
+ />
6841
+
6842
+ {/* 6-digit code */}
6843
+ <CodeInput
6844
+ label="6-Digit OTP"
6845
+ value={code6}
6846
+ length={6}
6847
+ onCodeChange={setCode6}
6848
+ />
6849
+
6850
+ {/* 8-digit code */}
6851
+ <CodeInput
6852
+ label="8-Digit Code"
6853
+ value={code8}
6854
+ length={8}
6855
+ onCodeChange={setCode8}
6856
+ />
6857
+ </div>
6858
+ );
6859
+ };
6860
+ ```
6861
+
6862
+ ### Disabled State
6863
+
6864
+ ```jsx
6865
+ import { useState } from "react";
6866
+ import { CodeInput } from "alope-ui";
6867
+
6868
+ const DisabledExample = () => {
6869
+ const [code, setCode] = useState("123456");
6870
+
6871
+ return (
6872
+ <CodeInput
6873
+ label="Disabled Code Input"
6874
+ value={code}
6875
+ length={6}
6876
+ disabled
6877
+ onCodeChange={setCode}
6878
+ />
6879
+ );
6880
+ };
6881
+ ```
6882
+
6883
+ ### OTP Verification
6884
+
6885
+ ```jsx
6886
+ import { useState } from "react";
6887
+ import { CodeInput } from "alope-ui";
6888
+ import { Button } from "alope-ui";
6889
+
6890
+ const OTPExample = () => {
6891
+ const [otp, setOtp] = useState("");
6892
+ const [isVerifying, setIsVerifying] = useState(false);
6893
+ const [isVerified, setIsVerified] = useState(false);
6894
+
6895
+ const handleVerify = () => {
6896
+ setIsVerifying(true);
6897
+ // Simulate API call
6898
+ setTimeout(() => {
6899
+ setIsVerifying(false);
6900
+ if (otp === "123456") {
6901
+ setIsVerified(true);
6902
+ } else {
6903
+ alert("Invalid OTP. Please try again.");
6904
+ setOtp("");
6905
+ }
6906
+ }, 1500);
6907
+ };
6908
+
6909
+ return (
6910
+ <div className="max-w-md mx-auto p-8 bg-white rounded-lg shadow-lg">
6911
+ <h2 className="text-2xl font-bold mb-2">Verify Your Account</h2>
6912
+ <p className="text-gray-600 mb-6">
6913
+ Enter the 6-digit code sent to your email
6914
+ </p>
6915
+
6916
+ <CodeInput
6917
+ label="Verification Code"
6918
+ value={otp}
6919
+ length={6}
6920
+ onCodeChange={setOtp}
6921
+ />
6922
+
6923
+ {isVerified && (
6924
+ <div className="mt-4 p-3 bg-green-100 text-green-700 rounded-lg">
6925
+ ✓ Verification successful!
6926
+ </div>
6927
+ )}
6928
+
6929
+ <Button
6930
+ variant="solid"
6931
+ variantType="primary"
6932
+ className="w-full mt-6"
6933
+ onClick={handleVerify}
6934
+ disabled={otp.length !== 6 || isVerifying || isVerified}
6935
+ >
6936
+ {isVerifying ? "Verifying..." : isVerified ? "Verified" : "Verify"}
6937
+ </Button>
6938
+
6939
+ <button className="w-full mt-4 text-sm text-blue-600 hover:text-blue-800">
6940
+ Resend Code
6941
+ </button>
6942
+ </div>
6943
+ );
6944
+ };
6945
+ ```
6946
+
6947
+ ### Two-Factor Authentication
6948
+
6949
+ ```jsx
6950
+ import { useState } from "react";
6951
+ import { CodeInput } from "alope-ui";
6952
+ import { Button } from "alope-ui";
6953
+ import { Shield, Clock } from "lucide-react";
6954
+
6955
+ const TwoFactorExample = () => {
6956
+ const [code, setCode] = useState("");
6957
+ const [timeLeft, setTimeLeft] = useState(60);
6958
+
6959
+ // Timer logic (simplified)
6960
+ React.useEffect(() => {
6961
+ if (timeLeft > 0) {
6962
+ const timer = setTimeout(() => setTimeLeft(timeLeft - 1), 1000);
6963
+ return () => clearTimeout(timer);
6964
+ }
6965
+ }, [timeLeft]);
6966
+
6967
+ return (
6968
+ <div className="max-w-md mx-auto p-8 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl shadow-lg">
6969
+ <div className="flex items-center justify-center mb-6">
6970
+ <div className="p-4 bg-blue-600 rounded-full">
6971
+ <Shield size={32} className="text-white" />
6972
+ </div>
6973
+ </div>
6974
+
6975
+ <h2 className="text-2xl font-bold text-center mb-2">
6976
+ Two-Factor Authentication
6977
+ </h2>
6978
+ <p className="text-gray-600 text-center mb-6">
6979
+ Enter the 6-digit code from your authenticator app
6980
+ </p>
6981
+
6982
+ <CodeInput
6983
+ label="Authentication Code"
6984
+ value={code}
6985
+ length={6}
6986
+ inputSize="lg"
6987
+ onCodeChange={setCode}
6988
+ />
6989
+
6990
+ <div className="flex items-center justify-center gap-2 mt-4 text-sm text-gray-600">
6991
+ <Clock size={16} />
6992
+ <span>Code expires in {timeLeft}s</span>
6993
+ </div>
6994
+
6995
+ <Button
6996
+ variant="solid"
6997
+ variantType="primary"
6998
+ className="w-full mt-6"
6999
+ disabled={code.length !== 6}
7000
+ >
7001
+ Verify & Continue
7002
+ </Button>
7003
+ </div>
7004
+ );
7005
+ };
7006
+ ```
7007
+
7008
+ ### PIN Entry
7009
+
7010
+ ```jsx
7011
+ import { useState } from "react";
7012
+ import { CodeInput } from "alope-ui";
7013
+ import { Button } from "alope-ui";
7014
+ import { Lock, Eye, EyeOff } from "lucide-react";
7015
+
7016
+ const PINExample = () => {
7017
+ const [pin, setPin] = useState("");
7018
+ const [showPin, setShowPin] = useState(false);
7019
+
7020
+ const handleSubmit = () => {
7021
+ console.log("PIN entered:", pin);
7022
+ alert("PIN submitted successfully!");
7023
+ setPin("");
7024
+ };
7025
+
7026
+ return (
7027
+ <div className="max-w-md mx-auto p-8 bg-white rounded-lg shadow-lg">
7028
+ <div className="flex items-center gap-3 mb-6">
7029
+ <Lock size={24} className="text-gray-700" />
7030
+ <h2 className="text-xl font-bold">Enter Your PIN</h2>
7031
+ </div>
7032
+
7033
+ <CodeInput
7034
+ label="4-Digit PIN"
7035
+ value={pin}
7036
+ length={4}
7037
+ inputSize="xl"
7038
+ onCodeChange={setPin}
7039
+ type={showPin ? "text" : "password"}
7040
+ />
7041
+
7042
+ <button
7043
+ onClick={() => setShowPin(!showPin)}
7044
+ className="flex items-center gap-2 mt-4 text-sm text-gray-600 hover:text-gray-800"
7045
+ >
7046
+ {showPin ? <EyeOff size={16} /> : <Eye size={16} />}
7047
+ {showPin ? "Hide" : "Show"} PIN
7048
+ </button>
7049
+
7050
+ <Button
7051
+ variant="solid"
7052
+ variantType="primary"
7053
+ className="w-full mt-6"
7054
+ onClick={handleSubmit}
7055
+ disabled={pin.length !== 4}
7056
+ >
7057
+ Submit PIN
7058
+ </Button>
7059
+ </div>
7060
+ );
7061
+ };
7062
+ ```
7063
+
7064
+ ### Complete Example
7065
+
7066
+ ```jsx
7067
+ import { useState } from "react";
7068
+ import { CodeInput } from "alope-ui";
7069
+ import { Button } from "alope-ui";
7070
+ import { Mail, CheckCircle, AlertCircle } from "lucide-react";
7071
+
7072
+ const CompleteExample = () => {
7073
+ const [step, setStep] = useState(1);
7074
+ const [email, setEmail] = useState("");
7075
+ const [verificationCode, setVerificationCode] = useState("");
7076
+ const [isLoading, setIsLoading] = useState(false);
7077
+ const [error, setError] = useState("");
7078
+
7079
+ const handleSendCode = (e) => {
7080
+ e.preventDefault();
7081
+ setIsLoading(true);
7082
+ setError("");
7083
+
7084
+ // Simulate API call
7085
+ setTimeout(() => {
7086
+ setIsLoading(false);
7087
+ setStep(2);
7088
+ }, 1500);
7089
+ };
7090
+
7091
+ const handleVerify = () => {
7092
+ setIsLoading(true);
7093
+ setError("");
7094
+
7095
+ // Simulate verification
7096
+ setTimeout(() => {
7097
+ setIsLoading(false);
7098
+ if (verificationCode === "123456") {
7099
+ setStep(3);
7100
+ } else {
7101
+ setError("Invalid verification code. Please try again.");
7102
+ setVerificationCode("");
7103
+ }
7104
+ }, 1500);
7105
+ };
7106
+
7107
+ const handleResend = () => {
7108
+ setVerificationCode("");
7109
+ setError("");
7110
+ alert("Verification code resent!");
7111
+ };
7112
+
7113
+ return (
7114
+ <div className="max-w-md mx-auto p-8">
7115
+ <div className="bg-white rounded-xl shadow-lg p-8">
7116
+ {/* Step 1: Enter Email */}
7117
+ {step === 1 && (
7118
+ <div>
7119
+ <div className="flex items-center gap-3 mb-6">
7120
+ <Mail size={32} className="text-blue-600" />
7121
+ <div>
7122
+ <h2 className="text-2xl font-bold">Email Verification</h2>
7123
+ <p className="text-gray-600 text-sm">
7124
+ We'll send you a verification code
7125
+ </p>
7126
+ </div>
7127
+ </div>
7128
+
7129
+ <form onSubmit={handleSendCode} className="space-y-4">
7130
+ <div>
7131
+ <label className="block text-sm font-medium mb-2">
7132
+ Email Address
7133
+ </label>
7134
+ <input
7135
+ type="email"
7136
+ value={email}
7137
+ onChange={(e) => setEmail(e.target.value)}
7138
+ placeholder="your@email.com"
7139
+ className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
7140
+ required
7141
+ />
7142
+ </div>
7143
+
7144
+ <Button
7145
+ type="submit"
7146
+ variant="solid"
7147
+ variantType="primary"
7148
+ className="w-full"
7149
+ disabled={isLoading}
7150
+ >
7151
+ {isLoading ? "Sending..." : "Send Verification Code"}
7152
+ </Button>
7153
+ </form>
7154
+ </div>
7155
+ )}
7156
+
7157
+ {/* Step 2: Enter Code */}
7158
+ {step === 2 && (
7159
+ <div>
7160
+ <div className="flex items-center gap-3 mb-6">
7161
+ <Mail size={32} className="text-blue-600" />
7162
+ <div>
7163
+ <h2 className="text-2xl font-bold">Enter Code</h2>
7164
+ <p className="text-gray-600 text-sm">Sent to {email}</p>
7165
+ </div>
7166
+ </div>
7167
+
7168
+ <CodeInput
7169
+ label="6-Digit Verification Code"
7170
+ value={verificationCode}
7171
+ length={6}
7172
+ inputSize="lg"
7173
+ onCodeChange={setVerificationCode}
7174
+ />
7175
+
7176
+ {error && (
7177
+ <div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg flex items-start gap-2">
7178
+ <AlertCircle
7179
+ size={20}
7180
+ className="text-red-600 flex-shrink-0 mt-0.5"
7181
+ />
7182
+ <p className="text-sm text-red-600">{error}</p>
7183
+ </div>
7184
+ )}
7185
+
7186
+ <Button
7187
+ variant="solid"
7188
+ variantType="primary"
7189
+ className="w-full mt-6"
7190
+ onClick={handleVerify}
7191
+ disabled={verificationCode.length !== 6 || isLoading}
7192
+ >
7193
+ {isLoading ? "Verifying..." : "Verify Code"}
7194
+ </Button>
7195
+
7196
+ <div className="mt-4 text-center">
7197
+ <button
7198
+ onClick={handleResend}
7199
+ className="text-sm text-blue-600 hover:text-blue-800"
7200
+ >
7201
+ Didn't receive code? Resend
7202
+ </button>
7203
+ </div>
7204
+ </div>
7205
+ )}
7206
+
7207
+ {/* Step 3: Success */}
7208
+ {step === 3 && (
7209
+ <div className="text-center">
7210
+ <div className="flex items-center justify-center mb-6">
7211
+ <div className="p-4 bg-green-100 rounded-full">
7212
+ <CheckCircle size={48} className="text-green-600" />
7213
+ </div>
7214
+ </div>
7215
+ <h2 className="text-2xl font-bold mb-2">Verified!</h2>
7216
+ <p className="text-gray-600 mb-6">
7217
+ Your email has been successfully verified
7218
+ </p>
7219
+ <Button
7220
+ variant="solid"
7221
+ variantType="success"
7222
+ className="w-full"
7223
+ onClick={() => {
7224
+ setStep(1);
7225
+ setEmail("");
7226
+ setVerificationCode("");
7227
+ }}
7228
+ >
7229
+ Continue
7230
+ </Button>
7231
+ </div>
7232
+ )}
7233
+ </div>
7234
+ </div>
7235
+ );
7236
+ };
7237
+ ```
7238
+
4230
7239
  ---
4231
7240
 
4232
7241
  ## 💡 Contributing