@tunjiadeyemi/ui 1.1.0 → 1.3.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 CHANGED
@@ -19,24 +19,33 @@ yarn add @tunjiadeyemi/ui framer-motion lucide-react
19
19
  First, import the styles in your app's entry point (e.g., `App.tsx` or `main.tsx`):
20
20
 
21
21
  ```tsx
22
- import '@tunjiadeyemi/ui/styles.css';
22
+ import "@tunjiadeyemi/ui/styles.css";
23
23
  ```
24
24
 
25
25
  Then use the components:
26
26
 
27
27
  ```tsx
28
- import { Modal } from '@tunjiadeyemi/ui';
29
- import { useState } from 'react';
28
+ import { Modal, Input, Skeleton } from "@tunjiadeyemi/ui";
29
+ import { useState } from "react";
30
30
 
31
31
  function App() {
32
32
  const [showModal, setShowModal] = useState(false);
33
+ const [email, setEmail] = useState("");
33
34
 
34
35
  return (
35
36
  <>
37
+ <Input
38
+ type="email"
39
+ value={email}
40
+ onChange={(e) => setEmail(e.target.value)}
41
+ placeholder="Enter your email"
42
+ validate={true}
43
+ />
44
+
36
45
  <button onClick={() => setShowModal(true)}>Open Modal</button>
37
-
38
- <Modal
39
- showModal={showModal}
46
+
47
+ <Modal
48
+ showModal={showModal}
40
49
  onClose={() => setShowModal(false)}
41
50
  revealMode="fade"
42
51
  className="bg-white p-8 rounded-lg max-w-md"
@@ -44,24 +53,114 @@ function App() {
44
53
  <h2 className="text-2xl font-bold mb-4">Modal Title</h2>
45
54
  <p>Modal content goes here</p>
46
55
  </Modal>
56
+
57
+ <Skeleton width="200px" height="20px" animation="pulse" />
47
58
  </>
48
59
  );
49
60
  }
50
61
  ```
51
62
 
52
- ### Available Components
63
+ ## Available Components
53
64
 
54
- #### Modal
65
+ ### Modal
55
66
 
56
- A flexible modal component with multiple reveal animations.
67
+ A flexible modal component with multiple reveal animations and optional drag-to-dismiss.
57
68
 
58
69
  **Props:**
70
+
59
71
  - `showModal` (boolean): Controls modal visibility
60
72
  - `onClose` (function): Callback when modal is closed
61
- - `revealMode` ('fade' | 'slide-right' | 'slide-bottom'): Animation style
73
+ - `revealMode` ('fade' | 'slide-right' | 'slide-bottom'): Animation style - Default: `'fade'`
74
+ - `isDrag` (boolean): Enable drag to dismiss (for slide-bottom mode) - Default: `false`
62
75
  - `className` (string): Custom classes for the modal content
63
76
  - `children` (ReactNode): Modal content
64
77
 
78
+ **Example:**
79
+
80
+ ```tsx
81
+ <Modal
82
+ showModal={true}
83
+ onClose={() => console.log("Modal closed")}
84
+ revealMode="slide-bottom"
85
+ isDrag={true}
86
+ className="bg-white p-6 rounded-lg"
87
+ >
88
+ <h2>Your Content</h2>
89
+ </Modal>
90
+ ```
91
+
92
+ ### Input
93
+
94
+ A customizable input component with built-in validation, password visibility toggle, and OTP support.
95
+
96
+ **Props:**
97
+
98
+ - `type` ('text' | 'email' | 'password' | 'otp' | 'number'): Input type - Default: `'text'`
99
+ - `value` (string): Input value
100
+ - `onChange` (function): Change handler
101
+ - `placeholder` (string): Placeholder text
102
+ - `validate` (boolean): Enable validation - Default: `false`
103
+ - `minLength` (number): Minimum character length
104
+ - `maxLength` (number): Maximum character length
105
+ - `errorMessage` (string): Custom error message
106
+ - `onOtpClick` (function): Callback for OTP button click (when type is 'otp')
107
+ - `className` (string): Additional CSS classes
108
+ - `width` (string): Input width - Default: `'100%'`
109
+ - `height` (string): Input height - Default: `'40px'`
110
+ - `color` (string): Accent color - Default: `'#6B2CE9'`
111
+ - `textColor` (string): Text color - Default: `'white'`
112
+ - `backgroundColor` (string): Background color - Default: `'#1F1F23'`
113
+ - `borderRadius` (string): Border radius - Default: `'10px'`
114
+
115
+ **Example:**
116
+
117
+ ```tsx
118
+ <Input
119
+ type="email"
120
+ value={email}
121
+ onChange={(e) => setEmail(e.target.value)}
122
+ placeholder="Enter your email"
123
+ validate={true}
124
+ errorMessage="Please enter a valid email"
125
+ />
126
+
127
+ <Input
128
+ type="password"
129
+ value={password}
130
+ onChange={(e) => setPassword(e.target.value)}
131
+ minLength={8}
132
+ validate={true}
133
+ />
134
+
135
+ <Input
136
+ type="otp"
137
+ value={otp}
138
+ onChange={(e) => setOtp(e.target.value)}
139
+ onOtpClick={() => console.log('Send OTP')}
140
+ placeholder="Enter OTP"
141
+ />
142
+ ```
143
+
144
+ ### Skeleton
145
+
146
+ A loading placeholder component with multiple variants and animations.
147
+
148
+ **Props:**
149
+
150
+ - `variant` ('text' | 'circular' | 'rectangular'): Skeleton shape - Default: `'rectangular'`
151
+ - `width` (string | number): Width of skeleton - Default: `'100%'`
152
+ - `height` (string | number): Height of skeleton - Default: `'100%'`
153
+ - `animation` ('pulse' | 'wave' | 'none'): Animation type - Default: `'pulse'`
154
+ - `className` (string): Additional CSS classes
155
+
156
+ **Example:**
157
+
158
+ ```tsx
159
+ <Skeleton variant="text" width="200px" height="20px" />
160
+ <Skeleton variant="circular" width="50px" height="50px" />
161
+ <Skeleton variant="rectangular" width="100%" height="200px" animation="wave" />
162
+ ```
163
+
65
164
  ## Development
66
165
 
67
166
  ```bash
package/dist/index.d.mts CHANGED
@@ -13,7 +13,7 @@ interface ModalProps {
13
13
  declare const Modal: ({ isDrag, showModal, onClose, children, className, revealMode, }: ModalProps) => react_jsx_runtime.JSX.Element;
14
14
 
15
15
  interface TextInputProps {
16
- type?: "password" | "otp" | "text" | "email" | "number";
16
+ type?: 'password' | 'otp' | 'text' | 'email' | 'number';
17
17
  value?: string;
18
18
  onOtpClick?: () => void;
19
19
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
@@ -29,9 +29,11 @@ interface TextInputProps {
29
29
  borderRadius?: string;
30
30
  height?: string;
31
31
  width?: string;
32
+ eyeIcon?: React.ReactNode;
33
+ eyeClosedIcon?: React.ReactNode;
32
34
  }
33
35
 
34
- declare const Input: ({ onChange, onOtpClick, validate, type, value, minLength, maxLength, className, placeholder, errorMessage, width, height, color, textColor, borderRadius, backgroundColor, }: TextInputProps) => react_jsx_runtime.JSX.Element;
36
+ declare const Input: ({ onChange, onOtpClick, validate, type, value, minLength, maxLength, className, placeholder, errorMessage, width, height, color, textColor, borderRadius, backgroundColor, eyeIcon, eyeClosedIcon }: TextInputProps) => react_jsx_runtime.JSX.Element;
35
37
 
36
38
  interface SkeletonProps {
37
39
  className?: string;
@@ -43,4 +45,4 @@ interface SkeletonProps {
43
45
 
44
46
  declare const Skeleton: ({ className, variant, width, height, animation, }: SkeletonProps) => react_jsx_runtime.JSX.Element;
45
47
 
46
- export { Modal, Skeleton, Input as TextInput };
48
+ export { Input, Modal, Skeleton };
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ interface ModalProps {
13
13
  declare const Modal: ({ isDrag, showModal, onClose, children, className, revealMode, }: ModalProps) => react_jsx_runtime.JSX.Element;
14
14
 
15
15
  interface TextInputProps {
16
- type?: "password" | "otp" | "text" | "email" | "number";
16
+ type?: 'password' | 'otp' | 'text' | 'email' | 'number';
17
17
  value?: string;
18
18
  onOtpClick?: () => void;
19
19
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
@@ -29,9 +29,11 @@ interface TextInputProps {
29
29
  borderRadius?: string;
30
30
  height?: string;
31
31
  width?: string;
32
+ eyeIcon?: React.ReactNode;
33
+ eyeClosedIcon?: React.ReactNode;
32
34
  }
33
35
 
34
- declare const Input: ({ onChange, onOtpClick, validate, type, value, minLength, maxLength, className, placeholder, errorMessage, width, height, color, textColor, borderRadius, backgroundColor, }: TextInputProps) => react_jsx_runtime.JSX.Element;
36
+ declare const Input: ({ onChange, onOtpClick, validate, type, value, minLength, maxLength, className, placeholder, errorMessage, width, height, color, textColor, borderRadius, backgroundColor, eyeIcon, eyeClosedIcon }: TextInputProps) => react_jsx_runtime.JSX.Element;
35
37
 
36
38
  interface SkeletonProps {
37
39
  className?: string;
@@ -43,4 +45,4 @@ interface SkeletonProps {
43
45
 
44
46
  declare const Skeleton: ({ className, variant, width, height, animation, }: SkeletonProps) => react_jsx_runtime.JSX.Element;
45
47
 
46
- export { Modal, Skeleton, Input as TextInput };
48
+ export { Input, Modal, Skeleton };
package/dist/index.js CHANGED
@@ -20,9 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Input: () => Input_default,
23
24
  Modal: () => Modal_default,
24
- Skeleton: () => Skeleton_default,
25
- TextInput: () => Input_default
25
+ Skeleton: () => Skeleton_default
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
@@ -138,7 +138,9 @@ var Input = ({
138
138
  color = "#6B2CE9",
139
139
  textColor = "white",
140
140
  borderRadius = "10px",
141
- backgroundColor = "#1F1F23"
141
+ backgroundColor = "#1F1F23",
142
+ eyeIcon = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Eye, { className: "cursor-pointer", size: 18 }),
143
+ eyeClosedIcon = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.EyeClosed, { className: "cursor-pointer", size: 18 })
142
144
  }) => {
143
145
  const [showPassword, setShowPassword] = (0, import_react.useState)(false);
144
146
  const [error, setError] = (0, import_react.useState)(null);
@@ -226,7 +228,7 @@ var Input = ({
226
228
  onClick: togglePasswordVisibility,
227
229
  className: "absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white transition-colors",
228
230
  "aria-label": showPassword ? "Hide password" : "Show password",
229
- children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Eye, { className: "cursor-pointer", size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.EyeClosed, { className: "cursor-pointer", size: 18 })
231
+ children: showPassword ? eyeIcon : eyeClosedIcon
230
232
  }
231
233
  )
232
234
  ] }),
@@ -234,44 +236,37 @@ var Input = ({
234
236
  ] });
235
237
  } else if (type === "otp") {
236
238
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { width }, children: [
237
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
238
- "div",
239
- {
240
- className: "relative flex items-center gap-4",
241
- style: { width: "100%" },
242
- children: [
243
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
244
- "input",
245
- {
246
- type: "number",
247
- value,
248
- onChange,
249
- placeholder,
250
- style: {
251
- backgroundColor,
252
- color: textColor,
253
- borderRadius,
254
- height,
255
- width: "100%"
256
- },
257
- className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-28 placeholder:opacity-30 focus:outline-none transition ${className}`,
258
- onFocus: (e) => !error && (e.target.style.borderColor = color),
259
- onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
260
- }
261
- ),
262
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
263
- "button",
264
- {
265
- type: "button",
266
- onClick: onOtpClick,
267
- className: "text-[#A77BFF] cursor-pointer font-medium text-sm w-fit absolute right-4 top-1/2 -translate-y-1/2",
268
- "aria-label": "Resend code",
269
- children: "Resend code"
270
- }
271
- )
272
- ]
273
- }
274
- ),
239
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative flex items-center gap-4", style: { width: "100%" }, children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
241
+ "input",
242
+ {
243
+ type: "number",
244
+ value,
245
+ onChange,
246
+ placeholder,
247
+ style: {
248
+ backgroundColor,
249
+ color: textColor,
250
+ borderRadius,
251
+ height,
252
+ width: "100%"
253
+ },
254
+ className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-28 placeholder:opacity-30 focus:outline-none transition ${className}`,
255
+ onFocus: (e) => !error && (e.target.style.borderColor = color),
256
+ onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
257
+ }
258
+ ),
259
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
260
+ "button",
261
+ {
262
+ type: "button",
263
+ onClick: onOtpClick,
264
+ className: "text-[#A77BFF] cursor-pointer font-medium text-sm w-fit absolute right-4 top-1/2 -translate-y-1/2",
265
+ "aria-label": "Resend code",
266
+ children: "Resend code"
267
+ }
268
+ )
269
+ ] }),
275
270
  error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-red-500 text-xs mt-1 px-1", children: error })
276
271
  ] });
277
272
  } else {
@@ -347,7 +342,7 @@ var Skeleton = ({
347
342
  var Skeleton_default = Skeleton;
348
343
  // Annotate the CommonJS export names for ESM import in node:
349
344
  0 && (module.exports = {
345
+ Input,
350
346
  Modal,
351
- Skeleton,
352
- TextInput
347
+ Skeleton
353
348
  });
package/dist/index.mjs CHANGED
@@ -110,7 +110,9 @@ var Input = ({
110
110
  color = "#6B2CE9",
111
111
  textColor = "white",
112
112
  borderRadius = "10px",
113
- backgroundColor = "#1F1F23"
113
+ backgroundColor = "#1F1F23",
114
+ eyeIcon = /* @__PURE__ */ jsx2(Eye, { className: "cursor-pointer", size: 18 }),
115
+ eyeClosedIcon = /* @__PURE__ */ jsx2(EyeClosed, { className: "cursor-pointer", size: 18 })
114
116
  }) => {
115
117
  const [showPassword, setShowPassword] = useState(false);
116
118
  const [error, setError] = useState(null);
@@ -198,7 +200,7 @@ var Input = ({
198
200
  onClick: togglePasswordVisibility,
199
201
  className: "absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white transition-colors",
200
202
  "aria-label": showPassword ? "Hide password" : "Show password",
201
- children: showPassword ? /* @__PURE__ */ jsx2(Eye, { className: "cursor-pointer", size: 18 }) : /* @__PURE__ */ jsx2(EyeClosed, { className: "cursor-pointer", size: 18 })
203
+ children: showPassword ? eyeIcon : eyeClosedIcon
202
204
  }
203
205
  )
204
206
  ] }),
@@ -206,44 +208,37 @@ var Input = ({
206
208
  ] });
207
209
  } else if (type === "otp") {
208
210
  return /* @__PURE__ */ jsxs2("div", { style: { width }, children: [
209
- /* @__PURE__ */ jsxs2(
210
- "div",
211
- {
212
- className: "relative flex items-center gap-4",
213
- style: { width: "100%" },
214
- children: [
215
- /* @__PURE__ */ jsx2(
216
- "input",
217
- {
218
- type: "number",
219
- value,
220
- onChange,
221
- placeholder,
222
- style: {
223
- backgroundColor,
224
- color: textColor,
225
- borderRadius,
226
- height,
227
- width: "100%"
228
- },
229
- className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-28 placeholder:opacity-30 focus:outline-none transition ${className}`,
230
- onFocus: (e) => !error && (e.target.style.borderColor = color),
231
- onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
232
- }
233
- ),
234
- /* @__PURE__ */ jsx2(
235
- "button",
236
- {
237
- type: "button",
238
- onClick: onOtpClick,
239
- className: "text-[#A77BFF] cursor-pointer font-medium text-sm w-fit absolute right-4 top-1/2 -translate-y-1/2",
240
- "aria-label": "Resend code",
241
- children: "Resend code"
242
- }
243
- )
244
- ]
245
- }
246
- ),
211
+ /* @__PURE__ */ jsxs2("div", { className: "relative flex items-center gap-4", style: { width: "100%" }, children: [
212
+ /* @__PURE__ */ jsx2(
213
+ "input",
214
+ {
215
+ type: "number",
216
+ value,
217
+ onChange,
218
+ placeholder,
219
+ style: {
220
+ backgroundColor,
221
+ color: textColor,
222
+ borderRadius,
223
+ height,
224
+ width: "100%"
225
+ },
226
+ className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-28 placeholder:opacity-30 focus:outline-none transition ${className}`,
227
+ onFocus: (e) => !error && (e.target.style.borderColor = color),
228
+ onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
229
+ }
230
+ ),
231
+ /* @__PURE__ */ jsx2(
232
+ "button",
233
+ {
234
+ type: "button",
235
+ onClick: onOtpClick,
236
+ className: "text-[#A77BFF] cursor-pointer font-medium text-sm w-fit absolute right-4 top-1/2 -translate-y-1/2",
237
+ "aria-label": "Resend code",
238
+ children: "Resend code"
239
+ }
240
+ )
241
+ ] }),
247
242
  error && /* @__PURE__ */ jsx2("p", { className: "text-red-500 text-xs mt-1 px-1", children: error })
248
243
  ] });
249
244
  } else {
@@ -318,7 +313,7 @@ var Skeleton = ({
318
313
  };
319
314
  var Skeleton_default = Skeleton;
320
315
  export {
316
+ Input_default as Input,
321
317
  Modal_default as Modal,
322
- Skeleton_default as Skeleton,
323
- Input_default as TextInput
318
+ Skeleton_default as Skeleton
324
319
  };
package/dist/styles.css CHANGED
@@ -314,6 +314,9 @@
314
314
  .bg-\[length\:200\%_100\%\] {
315
315
  background-size: 200% 100%;
316
316
  }
317
+ .p-6 {
318
+ padding: calc(var(--spacing) * 6);
319
+ }
317
320
  .p-8 {
318
321
  padding: calc(var(--spacing) * 8);
319
322
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tunjiadeyemi/ui",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A collection of reusable UI components",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",