@catalystsoftware/ui 1.0.0
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 +7 -0
- package/components/catalyst-ui/buttons/burger.tsx +207 -0
- package/components/catalyst-ui/core/data-display/timeline.tsx +210 -0
- package/components/catalyst-ui/core/feedback/alert.tsx +491 -0
- package/components/catalyst-ui/core/feedback/spinner-1.tsx +65 -0
- package/components/catalyst-ui/core/feedback/toast.tsx +1857 -0
- package/components/catalyst-ui/core/navigation/menu.tsx +164 -0
- package/components/catalyst-ui/forms/toggle-class.tsx +176 -0
- package/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +419 -0
- package/components/catalyst-ui/hooks/use-counter.tsx +13 -0
- package/components/catalyst-ui/hooks/use-event-listener.tsx +23 -0
- package/components/catalyst-ui/hooks/use-export-markdown.tsx +47 -0
- package/components/catalyst-ui/hooks/use-focus.tsx +17 -0
- package/components/catalyst-ui/hooks/use-interval.tsx +23 -0
- package/components/catalyst-ui/hooks/use-is-client.tsx +16 -0
- package/components/catalyst-ui/hooks/use-media-query.tsx +19 -0
- package/components/catalyst-ui/hooks/use-mobile.tsx +19 -0
- package/components/catalyst-ui/hooks/use-resize-observer.tsx +81 -0
- package/components/catalyst-ui/hooks/use-timeout.tsx +21 -0
- package/components/catalyst-ui/hooks/use-timer.tsx +209 -0
- package/components/catalyst-ui/hooks/use-toggle.tsx +12 -0
- package/components/catalyst-ui/media/image.tsx +13 -0
- package/components/catalyst-ui/overlays/dual-sidebar.tsx +4142 -0
- package/components/catalyst-ui/overlays/sidebar-original.tsx +726 -0
- package/components/catalyst-ui/primitives/accordion.tsx +250 -0
- package/components/catalyst-ui/primitives/alert-dialog.tsx +126 -0
- package/components/catalyst-ui/primitives/aspect-ratio.tsx +9 -0
- package/components/catalyst-ui/primitives/avatar.tsx +296 -0
- package/components/catalyst-ui/primitives/badge.tsx +57 -0
- package/components/catalyst-ui/primitives/breadcrumb.tsx +101 -0
- package/components/catalyst-ui/primitives/button.tsx +265 -0
- package/components/catalyst-ui/primitives/calendar-v4.tsx +208 -0
- package/components/catalyst-ui/primitives/calendar.tsx +295 -0
- package/components/catalyst-ui/primitives/card.tsx +618 -0
- package/components/catalyst-ui/primitives/carousel.tsx +238 -0
- package/components/catalyst-ui/primitives/chart.tsx +347 -0
- package/components/catalyst-ui/primitives/checkbox.tsx +225 -0
- package/components/catalyst-ui/primitives/collapsible.tsx +212 -0
- package/components/catalyst-ui/primitives/command.tsx +393 -0
- package/components/catalyst-ui/primitives/context-menu.tsx +236 -0
- package/components/catalyst-ui/primitives/dialog.tsx +471 -0
- package/components/catalyst-ui/primitives/drawer.tsx +761 -0
- package/components/catalyst-ui/primitives/dropdown-menu.tsx +290 -0
- package/components/catalyst-ui/primitives/empty.tsx +104 -0
- package/components/catalyst-ui/primitives/field.tsx +244 -0
- package/components/catalyst-ui/primitives/hover-card.tsx +124 -0
- package/components/catalyst-ui/primitives/input-otp.tsx +76 -0
- package/components/catalyst-ui/primitives/input.tsx +64 -0
- package/components/catalyst-ui/primitives/item.tsx +196 -0
- package/components/catalyst-ui/primitives/kbd.tsx +75 -0
- package/components/catalyst-ui/primitives/label.tsx +24 -0
- package/components/catalyst-ui/primitives/navigation-menu.tsx +150 -0
- package/components/catalyst-ui/primitives/pagination.tsx +198 -0
- package/components/catalyst-ui/primitives/popover.tsx +232 -0
- package/components/catalyst-ui/primitives/progress.tsx +34 -0
- package/components/catalyst-ui/primitives/radio-group.tsx +43 -0
- package/components/catalyst-ui/primitives/resizable.tsx +56 -0
- package/components/catalyst-ui/primitives/select.tsx +155 -0
- package/components/catalyst-ui/primitives/separator.tsx +74 -0
- package/components/catalyst-ui/primitives/sheet.tsx +126 -0
- package/components/catalyst-ui/primitives/skeleton.tsx +15 -0
- package/components/catalyst-ui/primitives/slider.tsx +27 -0
- package/components/catalyst-ui/primitives/switch.tsx +187 -0
- package/components/catalyst-ui/primitives/tabs.tsx +335 -0
- package/components/catalyst-ui/primitives/textarea.tsx +24 -0
- package/components/catalyst-ui/primitives/toggle-group.tsx +55 -0
- package/components/catalyst-ui/primitives/toggle.tsx +42 -0
- package/components/catalyst-ui/primitives/tooltip.tsx +116 -0
- package/components/catalyst-ui/utils/basic-auth.tsx +40 -0
- package/components/catalyst-ui/utils/context-storage.tsx +19 -0
- package/components/catalyst-ui/utils/cors-middleware.tsx +71 -0
- package/components/catalyst-ui/utils/deferred-content.tsx +595 -0
- package/components/catalyst-ui/utils/honeypot-middleware.tsx +38 -0
- package/components/catalyst-ui/utils/incId.tsx +75 -0
- package/components/catalyst-ui/utils/jwk-auth.tsx +36 -0
- package/components/catalyst-ui/utils/request-id.tsx +14 -0
- package/components/catalyst-ui/utils/secure-headers.tsx +37 -0
- package/components/catalyst-ui/utils/server-timing.tsx +23 -0
- package/components/catalyst-ui/utils/utils.ts +43 -0
- package/components/catalyst-ui/utils/with-cookie.tsx +43 -0
- package/components/catalyst-ui/x/accordian-x.tsx +428 -0
- package/components/catalyst-ui/x/alert-x.tsx +413 -0
- package/components/catalyst-ui/x/animated-text-x.tsx +2242 -0
- package/components/catalyst-ui/x/avatar-x.tsx +515 -0
- package/components/catalyst-ui/x/badge-x.tsx +670 -0
- package/components/catalyst-ui/x/button-X.tsx +2857 -0
- package/components/catalyst-ui/x/button-group-x.tsx +847 -0
- package/components/catalyst-ui/x/calendar-x.tsx +1910 -0
- package/components/catalyst-ui/x/card-x.tsx +2597 -0
- package/components/catalyst-ui/x/checkbox-x.tsx +656 -0
- package/components/catalyst-ui/x/collapsible-x.tsx +1360 -0
- package/components/catalyst-ui/x/combobox-x.tsx +911 -0
- package/components/catalyst-ui/x/data-table-x.tsx +1753 -0
- package/components/catalyst-ui/x/date-picker-x.tsx +648 -0
- package/components/catalyst-ui/x/dialog-x.tsx +659 -0
- package/components/catalyst-ui/x/dropdown-menu-x.tsx +612 -0
- package/components/catalyst-ui/x/hover-card-x.tsx +375 -0
- package/components/catalyst-ui/x/icon-x.tsx +840 -0
- package/components/catalyst-ui/x/input-mask-x.tsx +981 -0
- package/components/catalyst-ui/x/input-otp-x.tsx +659 -0
- package/components/catalyst-ui/x/loader-x.tsx +1757 -0
- package/components/catalyst-ui/x/pagination-x.tsx +622 -0
- package/components/catalyst-ui/x/popover-x.tsx +744 -0
- package/components/catalyst-ui/x/radio-group-x.tsx +499 -0
- package/components/catalyst-ui/x/select-x.tsx +1127 -0
- package/components/catalyst-ui/x/sheet-x.tsx +668 -0
- package/components/catalyst-ui/x/switch-x.tsx +681 -0
- package/components/catalyst-ui/x/table-x.tsx +574 -0
- package/components/catalyst-ui/x/tabs-x.tsx +839 -0
- package/components/catalyst-ui/x/textarea-x.tsx +1263 -0
- package/components/catalyst-ui/x/tooltip-x.tsx +396 -0
- package/components/catalyst-ui/x/tracker-x.tsx +560 -0
- package/data/bg-data.tsx +901 -0
- package/data/buttons-data.tsx +2327 -0
- package/data/charts-data.tsx +102 -0
- package/data/chat-data.tsx +83 -0
- package/data/code-data.tsx +1040 -0
- package/data/comboboxes-data.tsx +1843 -0
- package/data/command-data.tsx +1381 -0
- package/data/core-data.tsx +15953 -0
- package/data/crm-data.tsx +47 -0
- package/data/data.tsx +159 -0
- package/data/date-and-time-data.tsx +554 -0
- package/data/dependencies.tsx +7 -0
- package/data/ecommerce-data.tsx +1387 -0
- package/data/forms-data.tsx +7890 -0
- package/data/hooks-data.tsx +5487 -0
- package/data/index.ts +34 -0
- package/data/inputs-data.tsx +557 -0
- package/data/interactive-data.tsx +5394 -0
- package/data/lofi-data.tsx +18295 -0
- package/data/marketing-data.tsx +2546 -0
- package/data/media-data.tsx +1510 -0
- package/data/motion-data.tsx +5801 -0
- package/data/overlay-data.tsx +4136 -0
- package/data/pdf-data.tsx +124 -0
- package/data/pos-data.tsx +213 -0
- package/data/postcss.config.js +6 -0
- package/data/primitive-data.tsx +5170 -0
- package/data/prompt-data.tsx +1226 -0
- package/data/requiredLibs.ts +4 -0
- package/data/sandbox-data.tsx +1 -0
- package/data/sidebars-data.tsx +5421 -0
- package/data/stacks-data.tsx +32 -0
- package/data/table-data.tsx +706 -0
- package/data/tailwind.config.js +3830 -0
- package/data/tailwind.config.ngin.js +3830 -0
- package/data/tailwind.css +431 -0
- package/data/tools-data.tsx +6910 -0
- package/data/typography-data.tsx +2050 -0
- package/data/utils-data.tsx +6500 -0
- package/data/x-data.tsx +1171 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30245 -0
- package/dist/index.js.map +362 -0
- package/package.json +50 -0
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { motion } from 'motion/react';
|
|
4
|
+
import { cn } from '~/components/catalyst-ui';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* # IconX/MotionIcons Component Props Documentation
|
|
8
|
+
|
|
9
|
+
### ============================ ICONX (Main Selector) ============================
|
|
10
|
+
- **icon**: "Burger" | "Close" | "Plus" | "Chevron" | "Arrow" | "Check" | "Dots" | "Play" | "Heart" (default: "Burger")
|
|
11
|
+
- **opened**: boolean (default: false) - controls the animated state of the icon
|
|
12
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
13
|
+
- **color**: string (default: "currentColor") - CSS color value
|
|
14
|
+
- **className**: string - additional CSS classes
|
|
15
|
+
- **...props**: additional HTML div props
|
|
16
|
+
|
|
17
|
+
**Size Reference:**
|
|
18
|
+
- `sm`: 16px (w-4 h-4)
|
|
19
|
+
- `md`: 24px (w-6 h-6)
|
|
20
|
+
- `lg`: 32px (w-8 h-8)
|
|
21
|
+
- `xl`: 40px (w-10 h-10)
|
|
22
|
+
|
|
23
|
+
**Line Height by Size:**
|
|
24
|
+
- `sm`: 2px
|
|
25
|
+
- `md`: 2px
|
|
26
|
+
- `lg`: 3px
|
|
27
|
+
- `xl`: 3px
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### ============================ Burger =============================================
|
|
32
|
+
- **opened**: boolean (default: false) - when true, transforms to X shape
|
|
33
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
34
|
+
- **color**: string (default: "currentColor")
|
|
35
|
+
- **className**: string
|
|
36
|
+
|
|
37
|
+
**Animation:** Three horizontal lines transform into an X (cross) shape
|
|
38
|
+
- Top line: rotates 45° and moves to center
|
|
39
|
+
- Middle line: fades out (opacity 0)
|
|
40
|
+
- Bottom line: rotates -45° and moves to center
|
|
41
|
+
|
|
42
|
+
**Transition:** 0.3s with easeInOut
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### ============================ Burger2 =================================================
|
|
47
|
+
- **opened**: boolean (default: false) - when true, transforms to X shape
|
|
48
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
49
|
+
- **color**: string (default: "currentColor")
|
|
50
|
+
- **className**: string
|
|
51
|
+
- **onToggle**: function(opened: boolean) - callback when state changes
|
|
52
|
+
- **aria-label**: string - accessibility label
|
|
53
|
+
- **disabled**: boolean - disables interaction
|
|
54
|
+
|
|
55
|
+
**Animation:** CSS-only version (no Framer Motion) - same visual effect as Burger
|
|
56
|
+
**Transition:** 0.3s with ease-in-out
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## ============================ Close ===================================================================
|
|
61
|
+
- **opened**: boolean (default: false) - when true, rotates the X shape
|
|
62
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
63
|
+
- **color**: string (default: "currentColor")
|
|
64
|
+
- **className**: string
|
|
65
|
+
|
|
66
|
+
**Animation:** Two intersecting lines that rotate
|
|
67
|
+
- Line 1: rotates from 0° to 45° when opened
|
|
68
|
+
- Line 2: rotates from 0° to -45° when opened
|
|
69
|
+
- Creates an X shape that can spin
|
|
70
|
+
|
|
71
|
+
**Transition:** 0.3s with easeInOut
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## ============================ Plus ====================================================================
|
|
76
|
+
- **opened**: boolean (default: false) - when true, rotates 90°
|
|
77
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
78
|
+
- **color**: string (default: "currentColor")
|
|
79
|
+
- **className**: string
|
|
80
|
+
|
|
81
|
+
**Animation:** Plus sign that rotates
|
|
82
|
+
- Horizontal line: rotates 90° (becomes vertical)
|
|
83
|
+
- Vertical line: rotates 90° (becomes horizontal)
|
|
84
|
+
- Creates a rotation effect
|
|
85
|
+
|
|
86
|
+
**Transition:** 0.3s with easeInOut
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## ============================ Chevron =================================================================
|
|
91
|
+
- **opened**: boolean (default: false) - when true, changes direction from down to up
|
|
92
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
93
|
+
- **color**: string (default: "currentColor")
|
|
94
|
+
- **className**: string
|
|
95
|
+
|
|
96
|
+
**Animation:** V-shaped chevron that flips direction
|
|
97
|
+
- Left line: rotates from -45° to 45° when opened
|
|
98
|
+
- Right line: rotates from 45° to -45° when opened
|
|
99
|
+
- Chevron down (∨) becomes chevron up (∧)
|
|
100
|
+
|
|
101
|
+
- **Transition:** 0.3s with easeInOut
|
|
102
|
+
- **Transform Origin:** Right center for left line, left center for right line
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## ============================ Arrow ===================================================================
|
|
107
|
+
- **opened**: boolean (default: false) - when true, arrow points backward
|
|
108
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
109
|
+
- **color**: string (default: "currentColor")
|
|
110
|
+
- **className**: string
|
|
111
|
+
|
|
112
|
+
**Animation:** Right-pointing arrow that flips to left
|
|
113
|
+
- Shaft: moves 30% to the right when opened
|
|
114
|
+
- Upper arrowhead: rotates from -45° to -135° when opened
|
|
115
|
+
- Lower arrowhead: rotates from 45° to 135° when opened
|
|
116
|
+
- Arrow right (→) becomes arrow left (←)
|
|
117
|
+
|
|
118
|
+
- **Transition:** 0.3s with easeInOut
|
|
119
|
+
- **Transform Origin:** Right center for arrowhead lines
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## ============================ Check ===================================================================
|
|
124
|
+
- **opened**: boolean (default: false) - when true, draws checkmark
|
|
125
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
126
|
+
- **color**: string (default: "currentColor")
|
|
127
|
+
- **className**: string
|
|
128
|
+
|
|
129
|
+
**Animation:** Checkmark that draws in sequentially
|
|
130
|
+
- Short line: scales from 0 to 1 (draws in)
|
|
131
|
+
- Long line: scales from 0 to 1 with 0.1s delay (draws in after short line)
|
|
132
|
+
- Creates classic checkmark (✓) drawing effect
|
|
133
|
+
|
|
134
|
+
- **Transition:** 0.3s with easeOut, second line has 0.1s delay
|
|
135
|
+
- **Transform Origin:** Left center for both lines
|
|
136
|
+
- **Initial State:** scaleX: 0 (invisible)
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## ============================ Dots ====================================================================
|
|
141
|
+
- **opened**: boolean (default: false) - when true, dots animate
|
|
142
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
143
|
+
- **color**: string (default: "currentColor")
|
|
144
|
+
- **className**: string
|
|
145
|
+
|
|
146
|
+
**Animation:** Three dots that bounce in sequence (loading animation)
|
|
147
|
+
- Each dot: scales to 1.5x and moves up 4px, then returns
|
|
148
|
+
- Sequential animation with 0.1s delay between each dot
|
|
149
|
+
- Creates wave/bounce loading effect
|
|
150
|
+
|
|
151
|
+
- **Transition:** 0.5s with easeInOut
|
|
152
|
+
- **Delays:** Dot 1: 0s, Dot 2: 0.1s, Dot 3: 0.2s
|
|
153
|
+
|
|
154
|
+
**Dot Sizes by Size:**
|
|
155
|
+
- `sm`: 3px
|
|
156
|
+
- `md`: 4px
|
|
157
|
+
- `lg`: 5px
|
|
158
|
+
- `xl`: 6px
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## ============================ Play ====================================================================
|
|
163
|
+
- **opened**: boolean (default: false) - when true, changes to pause
|
|
164
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
165
|
+
- **color**: string (default: "currentColor")
|
|
166
|
+
- **className**: string
|
|
167
|
+
|
|
168
|
+
**Animation:** Play triangle morphs into pause bars
|
|
169
|
+
- Play state: Single triangle path (▶)
|
|
170
|
+
- Pause state: Two vertical bars (⏸)
|
|
171
|
+
- Smooth SVG path morphing
|
|
172
|
+
|
|
173
|
+
- **Transition:** 0.3s with easeInOut
|
|
174
|
+
- **Implementation:** Uses SVG with animated path data
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## ============================ Heart ===================================================================
|
|
179
|
+
- **opened**: boolean (default: false) - when true, fills the heart
|
|
180
|
+
- **size**: "sm" | "md" | "lg" | "xl" (default: "md")
|
|
181
|
+
- **color**: string (default: "currentColor")
|
|
182
|
+
- **className**: string
|
|
183
|
+
|
|
184
|
+
**Animation:** Heart outline that fills and scales
|
|
185
|
+
- Closed state: Outline only (stroke)
|
|
186
|
+
- Opened state: Filled heart with scale animation [1, 1.2, 1]
|
|
187
|
+
- Creates "like" animation effect
|
|
188
|
+
|
|
189
|
+
- **Transition:** 0.3s with easeInOut
|
|
190
|
+
- **Implementation:** Uses SVG with stroke and fill animation
|
|
191
|
+
- **Scale Effect:** Brief 20% increase when filling
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Usage Examples
|
|
196
|
+
|
|
197
|
+
### Basic Usage
|
|
198
|
+
```jsx
|
|
199
|
+
import { IconX } from './icon-x';
|
|
200
|
+
|
|
201
|
+
// Using IconX selector
|
|
202
|
+
<IconX icon="Burger" opened={isOpen} size="md" />
|
|
203
|
+
<IconX icon="Heart" opened={isLiked} color="#ff0000" />
|
|
204
|
+
<IconX icon="Play" opened={isPlaying} size="lg" />
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### With State Management
|
|
208
|
+
```jsx
|
|
209
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
210
|
+
|
|
211
|
+
<button onClick={() => setIsOpen(!isOpen)}>
|
|
212
|
+
<IconX icon="Burger" opened={isOpen} size="md" />
|
|
213
|
+
</button>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Direct Component Import
|
|
217
|
+
```jsx
|
|
218
|
+
import { Burger, Heart, Play, Check } from './icon-x';
|
|
219
|
+
|
|
220
|
+
<Burger opened={isMenuOpen} size="lg" color="blue" />
|
|
221
|
+
<Heart opened={isLiked} color="red" />
|
|
222
|
+
<Play opened={isPlaying} />
|
|
223
|
+
<Check opened={isComplete} size="sm" />
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### With Custom Styling
|
|
227
|
+
```jsx
|
|
228
|
+
<IconX
|
|
229
|
+
icon="Chevron"
|
|
230
|
+
opened={isExpanded}
|
|
231
|
+
size="md"
|
|
232
|
+
color="currentColor"
|
|
233
|
+
className="text-blue-500 hover:text-blue-700"
|
|
234
|
+
/>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### All Sizes Demo
|
|
238
|
+
```jsx
|
|
239
|
+
<div className="flex gap-4">
|
|
240
|
+
<IconX icon="Burger" opened={isOpen} size="sm" />
|
|
241
|
+
<IconX icon="Burger" opened={isOpen} size="md" />
|
|
242
|
+
<IconX icon="Burger" opened={isOpen} size="lg" />
|
|
243
|
+
<IconX icon="Burger" opened={isOpen} size="xl" />
|
|
244
|
+
</div>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Toggle Button Pattern
|
|
248
|
+
```jsx
|
|
249
|
+
const [states, setStates] = useState({
|
|
250
|
+
menu: false,
|
|
251
|
+
liked: false,
|
|
252
|
+
playing: false,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
<button onClick={() => setStates(s => ({ ...s, menu: !s.menu }))}>
|
|
256
|
+
<IconX icon="Burger" opened={states.menu} />
|
|
257
|
+
</button>
|
|
258
|
+
|
|
259
|
+
<button onClick={() => setStates(s => ({ ...s, liked: !s.liked }))}>
|
|
260
|
+
<IconX icon="Heart" opened={states.liked} color="red" />
|
|
261
|
+
</button>
|
|
262
|
+
|
|
263
|
+
<button onClick={() => setStates(s => ({ ...s, playing: !s.playing }))}>
|
|
264
|
+
<IconX icon="Play" opened={states.playing} />
|
|
265
|
+
</button>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Animation States Reference
|
|
271
|
+
|
|
272
|
+
| Icon | Closed State | Opened State | Use Case |
|
|
273
|
+
|------|--------------|--------------|----------|
|
|
274
|
+
| Burger | ≡ (three lines) | ✕ (X shape) | Menu toggle |
|
|
275
|
+
| Burger2 | ≡ (three lines) | ✕ (X shape) | Menu toggle (CSS only) |
|
|
276
|
+
| Close | ✕ (upright X) | ✕ (rotated X) | Close/dismiss with rotation |
|
|
277
|
+
| Plus | + (plus) | + (rotated 90°) | Add/expand toggle |
|
|
278
|
+
| Chevron | ∨ (down) | ∧ (up) | Dropdown/accordion |
|
|
279
|
+
| Arrow | → (right) | ← (left) | Navigation/back |
|
|
280
|
+
| Check | (empty) | ✓ (checkmark) | Task completion |
|
|
281
|
+
| Dots | ··· (static) | ··· (bouncing) | Loading indicator |
|
|
282
|
+
| Play | ▶ (play) | ⏸ (pause) | Media control |
|
|
283
|
+
| Heart | ♡ (outline) | ♥ (filled) | Like/favorite |
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Technical Notes
|
|
288
|
+
|
|
289
|
+
1. **Dependencies:**
|
|
290
|
+
- Framer Motion (`motion/react`) for all components except Burger2
|
|
291
|
+
- Burger2 uses pure CSS transitions (no motion dependency)
|
|
292
|
+
|
|
293
|
+
2. **Performance:**
|
|
294
|
+
- All animations use transform/opacity for GPU acceleration
|
|
295
|
+
- 60fps smooth animations
|
|
296
|
+
- Lightweight (~2-3 KB per icon)
|
|
297
|
+
|
|
298
|
+
3. **Accessibility:**
|
|
299
|
+
- Icons inherit currentColor by default
|
|
300
|
+
- Works with focus states
|
|
301
|
+
- Burger2 supports aria-label and disabled props
|
|
302
|
+
|
|
303
|
+
4. **Customization:**
|
|
304
|
+
- All icons respect CSS color inheritance
|
|
305
|
+
- Sizes are responsive (rem-based)
|
|
306
|
+
- Can override with className prop
|
|
307
|
+
- Supports dark mode via color prop
|
|
308
|
+
|
|
309
|
+
5. **Browser Support:**
|
|
310
|
+
- Modern browsers (Chrome, Firefox, Safari, Edge)
|
|
311
|
+
- Requires CSS transforms and CSS transitions support
|
|
312
|
+
- SVG support required for Play and Heart icons
|
|
313
|
+
*/
|
|
314
|
+
|
|
315
|
+
export function IconX({ icon, ...props }) {
|
|
316
|
+
switch (icon) {
|
|
317
|
+
case 'Burger':
|
|
318
|
+
return <Burger {...props} />;
|
|
319
|
+
case 'Close':
|
|
320
|
+
return <Close {...props} />;
|
|
321
|
+
case 'Plus':
|
|
322
|
+
return <Plus {...props} />;
|
|
323
|
+
case 'Chevron':
|
|
324
|
+
return <Chevron {...props} />;
|
|
325
|
+
case 'Arrow':
|
|
326
|
+
return <Arrow {...props} />;
|
|
327
|
+
case 'Check':
|
|
328
|
+
return <Check {...props} />;
|
|
329
|
+
case 'Dots':
|
|
330
|
+
return <Dots {...props} />;
|
|
331
|
+
case 'Play':
|
|
332
|
+
return <Play {...props} />;
|
|
333
|
+
case 'Heart':
|
|
334
|
+
return <Heart {...props} />;
|
|
335
|
+
default:
|
|
336
|
+
return <Burger2 {...props} />;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Shared props interface
|
|
341
|
+
interface IconProps {
|
|
342
|
+
opened?: boolean;
|
|
343
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
344
|
+
color?: string;
|
|
345
|
+
className?: string;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const sizeClasses = {
|
|
349
|
+
sm: 'w-4 h-4',
|
|
350
|
+
md: 'w-6 h-6',
|
|
351
|
+
lg: 'w-8 h-8',
|
|
352
|
+
xl: 'w-10 h-10'
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const lineHeight = {
|
|
356
|
+
sm: '2px',
|
|
357
|
+
md: '2px',
|
|
358
|
+
lg: '3px',
|
|
359
|
+
xl: '3px'
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
interface BurgerMenuProps {
|
|
363
|
+
opened?: boolean;
|
|
364
|
+
onToggle?: (opened: boolean) => void;
|
|
365
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
366
|
+
color?: string;
|
|
367
|
+
className?: string;
|
|
368
|
+
'aria-label'?: string;
|
|
369
|
+
disabled?: boolean;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function Burger2({ opened = false, size = 'md', color = 'currentColor', }: BurgerMenuProps) {
|
|
373
|
+
const isOpened = opened
|
|
374
|
+
|
|
375
|
+
const sizeClasses = {
|
|
376
|
+
sm: 'w-4 h-4',
|
|
377
|
+
md: 'w-6 h-6',
|
|
378
|
+
lg: 'w-8 h-8',
|
|
379
|
+
xl: 'w-10 h-10'
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const lineHeight = {
|
|
383
|
+
sm: '2px',
|
|
384
|
+
md: '2px',
|
|
385
|
+
lg: '3px',
|
|
386
|
+
xl: '3px'
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return (
|
|
390
|
+
<div className={cn("relative", sizeClasses[size])}>
|
|
391
|
+
<span
|
|
392
|
+
className={cn("absolute left-0 block transition-all duration-300 ease-in-out rounded-full", "bg-current")}
|
|
393
|
+
style={{
|
|
394
|
+
width: '100%',
|
|
395
|
+
height: lineHeight[size],
|
|
396
|
+
color: color,
|
|
397
|
+
top: isOpened ? '50%' : '20%',
|
|
398
|
+
transform: isOpened ? 'translateY(-50%) rotate(45deg)' : 'translateY(-50%) rotate(0deg)'
|
|
399
|
+
}}
|
|
400
|
+
/>
|
|
401
|
+
<span
|
|
402
|
+
className={cn(
|
|
403
|
+
"absolute left-0 block transition-all duration-300 ease-in-out rounded-full",
|
|
404
|
+
"bg-current"
|
|
405
|
+
)}
|
|
406
|
+
style={{
|
|
407
|
+
width: '100%',
|
|
408
|
+
height: lineHeight[size],
|
|
409
|
+
color: color,
|
|
410
|
+
top: '50%',
|
|
411
|
+
opacity: isOpened ? 0 : 1,
|
|
412
|
+
transform: 'translateY(-50%)'
|
|
413
|
+
}}
|
|
414
|
+
/>
|
|
415
|
+
<span
|
|
416
|
+
className={cn(
|
|
417
|
+
"absolute left-0 block transition-all duration-300 ease-in-out rounded-full",
|
|
418
|
+
"bg-current"
|
|
419
|
+
)}
|
|
420
|
+
style={{
|
|
421
|
+
width: '100%',
|
|
422
|
+
height: lineHeight[size],
|
|
423
|
+
color: color,
|
|
424
|
+
bottom: isOpened ? '50%' : '20%',
|
|
425
|
+
transform: isOpened ? 'translateY(50%) rotate(-45deg)' : 'translateY(50%) rotate(0deg)'
|
|
426
|
+
}}
|
|
427
|
+
/>
|
|
428
|
+
</div>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
function Burger({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
432
|
+
return (
|
|
433
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
434
|
+
<motion.span
|
|
435
|
+
className="absolute left-0 block rounded-full bg-current"
|
|
436
|
+
style={{ width: '100%', height: lineHeight[size], color }}
|
|
437
|
+
animate={{
|
|
438
|
+
top: opened ? '50%' : '20%',
|
|
439
|
+
rotate: opened ? 45 : 0,
|
|
440
|
+
y: '-50%'
|
|
441
|
+
}}
|
|
442
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
443
|
+
/>
|
|
444
|
+
<motion.span
|
|
445
|
+
className="absolute left-0 top-1/2 block rounded-full bg-current"
|
|
446
|
+
style={{ width: '100%', height: lineHeight[size], color, y: '-50%' }}
|
|
447
|
+
animate={{ opacity: opened ? 0 : 1 }}
|
|
448
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
449
|
+
/>
|
|
450
|
+
<motion.span
|
|
451
|
+
className="absolute left-0 block rounded-full bg-current"
|
|
452
|
+
style={{ width: '100%', height: lineHeight[size], color }}
|
|
453
|
+
animate={{
|
|
454
|
+
bottom: opened ? '50%' : '20%',
|
|
455
|
+
rotate: opened ? -45 : 0,
|
|
456
|
+
y: '50%'
|
|
457
|
+
}}
|
|
458
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function Close({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
465
|
+
return (
|
|
466
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
467
|
+
<motion.span
|
|
468
|
+
className="absolute left-0 top-1/2 block rounded-full bg-current"
|
|
469
|
+
style={{ width: '100%', height: lineHeight[size], color }}
|
|
470
|
+
animate={{ rotate: opened ? 45 : 0, y: '-50%' }}
|
|
471
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
472
|
+
/>
|
|
473
|
+
<motion.span
|
|
474
|
+
className="absolute left-0 top-1/2 block rounded-full bg-current"
|
|
475
|
+
style={{ width: '100%', height: lineHeight[size], color }}
|
|
476
|
+
animate={{ rotate: opened ? -45 : 0, y: '-50%' }}
|
|
477
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
478
|
+
/>
|
|
479
|
+
</div>
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function Plus({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
484
|
+
return (
|
|
485
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
486
|
+
<motion.span
|
|
487
|
+
className="absolute left-0 top-1/2 block rounded-full bg-current"
|
|
488
|
+
style={{ width: '100%', height: lineHeight[size], color }}
|
|
489
|
+
animate={{ rotate: opened ? 90 : 0, y: '-50%' }}
|
|
490
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
491
|
+
/>
|
|
492
|
+
<motion.span
|
|
493
|
+
className="absolute left-1/2 top-0 block rounded-full bg-current"
|
|
494
|
+
style={{ width: lineHeight[size], height: '100%', color }}
|
|
495
|
+
animate={{ rotate: opened ? 90 : 0, x: '-50%' }}
|
|
496
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
497
|
+
/>
|
|
498
|
+
</div>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function Chevron({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
503
|
+
return (
|
|
504
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
505
|
+
<motion.span
|
|
506
|
+
className="absolute block rounded-full bg-current"
|
|
507
|
+
style={{
|
|
508
|
+
width: '50%',
|
|
509
|
+
height: lineHeight[size],
|
|
510
|
+
color,
|
|
511
|
+
left: '10%',
|
|
512
|
+
top: '50%',
|
|
513
|
+
transformOrigin: 'right center'
|
|
514
|
+
}}
|
|
515
|
+
animate={{
|
|
516
|
+
rotate: opened ? 45 : -45,
|
|
517
|
+
y: '-50%'
|
|
518
|
+
}}
|
|
519
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
520
|
+
/>
|
|
521
|
+
<motion.span
|
|
522
|
+
className="absolute block rounded-full bg-current"
|
|
523
|
+
style={{
|
|
524
|
+
width: '50%',
|
|
525
|
+
height: lineHeight[size],
|
|
526
|
+
color,
|
|
527
|
+
right: '10%',
|
|
528
|
+
top: '50%',
|
|
529
|
+
transformOrigin: 'left center'
|
|
530
|
+
}}
|
|
531
|
+
animate={{
|
|
532
|
+
rotate: opened ? -45 : 45,
|
|
533
|
+
y: '-50%'
|
|
534
|
+
}}
|
|
535
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
536
|
+
/>
|
|
537
|
+
</div>
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function Arrow({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
542
|
+
return (
|
|
543
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
544
|
+
<motion.span
|
|
545
|
+
className="absolute left-0 top-1/2 block rounded-full bg-current"
|
|
546
|
+
style={{ width: '70%', height: lineHeight[size], color }}
|
|
547
|
+
animate={{
|
|
548
|
+
x: opened ? '30%' : '0%',
|
|
549
|
+
y: '-50%'
|
|
550
|
+
}}
|
|
551
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
552
|
+
/>
|
|
553
|
+
<motion.span
|
|
554
|
+
className="absolute block rounded-full bg-current"
|
|
555
|
+
style={{
|
|
556
|
+
width: '40%',
|
|
557
|
+
height: lineHeight[size],
|
|
558
|
+
color,
|
|
559
|
+
right: '5%',
|
|
560
|
+
top: '50%',
|
|
561
|
+
transformOrigin: 'right center'
|
|
562
|
+
}}
|
|
563
|
+
animate={{
|
|
564
|
+
rotate: opened ? -135 : -45,
|
|
565
|
+
y: '-50%'
|
|
566
|
+
}}
|
|
567
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
568
|
+
/>
|
|
569
|
+
<motion.span
|
|
570
|
+
className="absolute block rounded-full bg-current"
|
|
571
|
+
style={{
|
|
572
|
+
width: '40%',
|
|
573
|
+
height: lineHeight[size],
|
|
574
|
+
color,
|
|
575
|
+
right: '5%',
|
|
576
|
+
top: '50%',
|
|
577
|
+
transformOrigin: 'right center'
|
|
578
|
+
}}
|
|
579
|
+
animate={{
|
|
580
|
+
rotate: opened ? 135 : 45,
|
|
581
|
+
y: '-50%'
|
|
582
|
+
}}
|
|
583
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
584
|
+
/>
|
|
585
|
+
</div>
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function Check({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
590
|
+
return (
|
|
591
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
592
|
+
<motion.span
|
|
593
|
+
className="absolute block rounded-full bg-current"
|
|
594
|
+
style={{
|
|
595
|
+
width: '40%',
|
|
596
|
+
height: lineHeight[size],
|
|
597
|
+
color,
|
|
598
|
+
left: '10%',
|
|
599
|
+
top: '60%',
|
|
600
|
+
transformOrigin: 'left center'
|
|
601
|
+
}}
|
|
602
|
+
initial={{ rotate: -45, scaleX: 0 }}
|
|
603
|
+
animate={{
|
|
604
|
+
rotate: -45,
|
|
605
|
+
scaleX: opened ? 1 : 0,
|
|
606
|
+
y: '-50%'
|
|
607
|
+
}}
|
|
608
|
+
transition={{ duration: 0.3, ease: 'easeOut' }}
|
|
609
|
+
/>
|
|
610
|
+
<motion.span
|
|
611
|
+
className="absolute block rounded-full bg-current"
|
|
612
|
+
style={{
|
|
613
|
+
width: '60%',
|
|
614
|
+
height: lineHeight[size],
|
|
615
|
+
color,
|
|
616
|
+
left: '27%',
|
|
617
|
+
top: '50%',
|
|
618
|
+
transformOrigin: 'left center'
|
|
619
|
+
}}
|
|
620
|
+
initial={{ rotate: 45, scaleX: 0 }}
|
|
621
|
+
animate={{
|
|
622
|
+
rotate: 45,
|
|
623
|
+
scaleX: opened ? 1 : 0,
|
|
624
|
+
y: '-50%'
|
|
625
|
+
}}
|
|
626
|
+
transition={{ duration: 0.3, ease: 'easeOut', delay: 0.1 }}
|
|
627
|
+
/>
|
|
628
|
+
</div>
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function Dots({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
633
|
+
const dotSize = {
|
|
634
|
+
sm: '3px',
|
|
635
|
+
md: '4px',
|
|
636
|
+
lg: '5px',
|
|
637
|
+
xl: '6px'
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
<div className={cn("relative flex items-center justify-center gap-1", sizeClasses[size], className)}>
|
|
642
|
+
<motion.span
|
|
643
|
+
className="block rounded-full bg-current"
|
|
644
|
+
style={{
|
|
645
|
+
width: dotSize[size],
|
|
646
|
+
height: dotSize[size],
|
|
647
|
+
color
|
|
648
|
+
}}
|
|
649
|
+
animate={{
|
|
650
|
+
scale: opened ? [1, 1.5, 1] : 1,
|
|
651
|
+
y: opened ? [0, -4, 0] : 0
|
|
652
|
+
}}
|
|
653
|
+
transition={{ duration: 0.5, ease: 'easeInOut' }}
|
|
654
|
+
/>
|
|
655
|
+
<motion.span
|
|
656
|
+
className="block rounded-full bg-current"
|
|
657
|
+
style={{
|
|
658
|
+
width: dotSize[size],
|
|
659
|
+
height: dotSize[size],
|
|
660
|
+
color
|
|
661
|
+
}}
|
|
662
|
+
animate={{
|
|
663
|
+
scale: opened ? [1, 1.5, 1] : 1,
|
|
664
|
+
y: opened ? [0, -4, 0] : 0
|
|
665
|
+
}}
|
|
666
|
+
transition={{ duration: 0.5, ease: 'easeInOut', delay: 0.1 }}
|
|
667
|
+
/>
|
|
668
|
+
<motion.span
|
|
669
|
+
className="block rounded-full bg-current"
|
|
670
|
+
style={{
|
|
671
|
+
width: dotSize[size],
|
|
672
|
+
height: dotSize[size],
|
|
673
|
+
color
|
|
674
|
+
}}
|
|
675
|
+
animate={{
|
|
676
|
+
scale: opened ? [1, 1.5, 1] : 1,
|
|
677
|
+
y: opened ? [0, -4, 0] : 0
|
|
678
|
+
}}
|
|
679
|
+
transition={{ duration: 0.5, ease: 'easeInOut', delay: 0.2 }}
|
|
680
|
+
/>
|
|
681
|
+
</div>
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function Play({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
686
|
+
return (
|
|
687
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
688
|
+
<svg viewBox="0 0 24 24" className="w-full h-full" style={{ color }}>
|
|
689
|
+
<motion.path
|
|
690
|
+
d={opened ? "M6 4h4v16H6V4zm8 0h4v16h-4V4z" : "M8 5v14l11-7z"}
|
|
691
|
+
fill="currentColor"
|
|
692
|
+
animate={{ d: opened ? "M6 4h4v16H6V4zm8 0h4v16h-4V4z" : "M8 5v14l11-7z" }}
|
|
693
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
694
|
+
/>
|
|
695
|
+
</svg>
|
|
696
|
+
</div>
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function Heart({ opened = false, size = 'md', color = 'currentColor', className }: IconProps) {
|
|
701
|
+
return (
|
|
702
|
+
<div className={cn("relative", sizeClasses[size], className)}>
|
|
703
|
+
<svg viewBox="0 0 24 24" className="w-full h-full" style={{ color }}>
|
|
704
|
+
<motion.path
|
|
705
|
+
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
|
|
706
|
+
fill={opened ? 'currentColor' : 'none'}
|
|
707
|
+
stroke="currentColor"
|
|
708
|
+
strokeWidth="2"
|
|
709
|
+
animate={{
|
|
710
|
+
scale: opened ? [1, 1.2, 1] : 1,
|
|
711
|
+
fill: opened ? 'currentColor' : 'none'
|
|
712
|
+
}}
|
|
713
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
714
|
+
/>
|
|
715
|
+
</svg>
|
|
716
|
+
</div>
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
export function MotionIconsDemo() {
|
|
722
|
+
const [states, setStates] = useState({
|
|
723
|
+
burger: false,
|
|
724
|
+
close: false,
|
|
725
|
+
plus: false,
|
|
726
|
+
chevron: false,
|
|
727
|
+
arrow: false,
|
|
728
|
+
check: false,
|
|
729
|
+
dots: false,
|
|
730
|
+
play: false,
|
|
731
|
+
heart: false
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
const toggleIcon = (icon) => {
|
|
735
|
+
setStates(prev => ({ ...prev, [icon]: !prev[icon] }));
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
const icons = [
|
|
739
|
+
{ name: 'burger', label: 'Burger Menu' },
|
|
740
|
+
{ name: 'close', label: 'Close (X)' },
|
|
741
|
+
{ name: 'plus', label: 'Plus' },
|
|
742
|
+
{ name: 'chevron', label: 'Chevron' },
|
|
743
|
+
{ name: 'arrow', label: 'Arrow' },
|
|
744
|
+
{ name: 'check', label: 'Check' },
|
|
745
|
+
{ name: 'dots', label: 'Loading Dots' },
|
|
746
|
+
{ name: 'play', label: 'Play/Pause' },
|
|
747
|
+
{ name: 'heart', label: 'Heart' }
|
|
748
|
+
];
|
|
749
|
+
|
|
750
|
+
return (
|
|
751
|
+
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-8">
|
|
752
|
+
<div className="max-w-6xl mx-auto space-y-8">
|
|
753
|
+
<div className="text-center space-y-2">
|
|
754
|
+
<h1 className="text-4xl font-bold text-slate-900 dark:text-white">
|
|
755
|
+
MotionIcons
|
|
756
|
+
</h1>
|
|
757
|
+
<p className="text-lg text-slate-600 dark:text-slate-400">
|
|
758
|
+
Animated icon components built with Motion/React
|
|
759
|
+
</p>
|
|
760
|
+
</div>
|
|
761
|
+
|
|
762
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
763
|
+
{icons.map(({ name, label }) => (
|
|
764
|
+
<div
|
|
765
|
+
key={name}
|
|
766
|
+
className="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 space-y-4 border border-slate-200 dark:border-slate-700"
|
|
767
|
+
>
|
|
768
|
+
<div className="flex items-center justify-between">
|
|
769
|
+
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">
|
|
770
|
+
{label}
|
|
771
|
+
</h3>
|
|
772
|
+
<code className="text-xs bg-slate-100 dark:bg-slate-700 px-2 py-1 rounded text-slate-600 dark:text-slate-300">
|
|
773
|
+
{name}
|
|
774
|
+
</code>
|
|
775
|
+
</div>
|
|
776
|
+
|
|
777
|
+
<div className="flex items-center justify-center gap-8 py-8">
|
|
778
|
+
{['sm', 'md', 'lg', 'xl'].map(size => (
|
|
779
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
780
|
+
<button
|
|
781
|
+
onClick={() => toggleIcon(name)}
|
|
782
|
+
className="p-4 rounded-lg bg-slate-100 dark:bg-slate-700 hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors"
|
|
783
|
+
aria-label={`Toggle ${label}`}
|
|
784
|
+
>
|
|
785
|
+
<MotionIcons
|
|
786
|
+
icon={name}
|
|
787
|
+
opened={states[name]}
|
|
788
|
+
size={size}
|
|
789
|
+
color="currentColor"
|
|
790
|
+
className="text-slate-900 dark:text-white"
|
|
791
|
+
/>
|
|
792
|
+
</button>
|
|
793
|
+
<span className="text-xs text-slate-500 dark:text-slate-400">
|
|
794
|
+
{size}
|
|
795
|
+
</span>
|
|
796
|
+
</div>
|
|
797
|
+
))}
|
|
798
|
+
</div>
|
|
799
|
+
|
|
800
|
+
<div className="text-center">
|
|
801
|
+
<button
|
|
802
|
+
onClick={() => toggleIcon(name)}
|
|
803
|
+
className="text-sm text-blue-600 dark:text-blue-400 hover:underline"
|
|
804
|
+
>
|
|
805
|
+
Click icons to toggle
|
|
806
|
+
</button>
|
|
807
|
+
</div>
|
|
808
|
+
</div>
|
|
809
|
+
))}
|
|
810
|
+
</div>
|
|
811
|
+
|
|
812
|
+
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6 border border-slate-200 dark:border-slate-700">
|
|
813
|
+
<h2 className="text-xl font-bold text-slate-900 dark:text-white mb-4">
|
|
814
|
+
Usage Example
|
|
815
|
+
</h2>
|
|
816
|
+
<pre className="bg-slate-100 dark:bg-slate-900 p-4 rounded-lg overflow-x-auto text-sm">
|
|
817
|
+
<code className="text-slate-900 dark:text-slate-100">{`import { MotionIcons } from './motion-icons';
|
|
818
|
+
|
|
819
|
+
// Use any icon with the icon prop
|
|
820
|
+
<MotionIcons
|
|
821
|
+
icon="burger"
|
|
822
|
+
opened={isOpen}
|
|
823
|
+
size="md"
|
|
824
|
+
color="currentColor"
|
|
825
|
+
/>
|
|
826
|
+
|
|
827
|
+
// Or import specific icons
|
|
828
|
+
import { Burger, Close, Plus, Heart } from './motion-icons';
|
|
829
|
+
|
|
830
|
+
<Burger opened={isOpen} size="lg" />
|
|
831
|
+
<Close opened={isOpen} />
|
|
832
|
+
<Plus opened={isOpen} size="sm" />
|
|
833
|
+
<Heart opened={isLiked} color="red" />`}</code>
|
|
834
|
+
</pre>
|
|
835
|
+
</div>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|