@tunjiadeyemi/ui 1.2.0 → 1.4.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/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;
@@ -24,14 +24,16 @@ interface TextInputProps {
24
24
  minLength?: number;
25
25
  maxLength?: number;
26
26
  errorMessage?: string;
27
- color?: string;
27
+ borderColor?: string;
28
28
  textColor?: string;
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, borderColor, textColor, borderRadius, backgroundColor, eyeIcon, eyeClosedIcon }: TextInputProps) => react_jsx_runtime.JSX.Element;
35
37
 
36
38
  interface SkeletonProps {
37
39
  className?: string;
@@ -43,4 +45,26 @@ interface SkeletonProps {
43
45
 
44
46
  declare const Skeleton: ({ className, variant, width, height, animation, }: SkeletonProps) => react_jsx_runtime.JSX.Element;
45
47
 
46
- export { Input, Modal, Skeleton };
48
+ declare const ScrollToTop: () => null;
49
+
50
+ interface AccordionItem {
51
+ question: string;
52
+ answer: React.ReactNode;
53
+ icon?: React.ReactNode;
54
+ }
55
+ interface AccordionProps {
56
+ icon?: React.ReactNode;
57
+ items: AccordionItem[];
58
+ className?: string;
59
+ containerClassName?: string;
60
+ onIconClick?: (idx: number, item: AccordionItem) => void;
61
+ }
62
+ declare const Accordion: ({ icon, items, containerClassName, className, onIconClick }: AccordionProps) => react_jsx_runtime.JSX.Element;
63
+
64
+ declare const Rating: ({ rating, total, icon }: {
65
+ rating: number;
66
+ total: number;
67
+ icon?: string;
68
+ }) => react_jsx_runtime.JSX.Element;
69
+
70
+ export { Accordion, Input, Modal, Rating, ScrollToTop, 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;
@@ -24,14 +24,16 @@ interface TextInputProps {
24
24
  minLength?: number;
25
25
  maxLength?: number;
26
26
  errorMessage?: string;
27
- color?: string;
27
+ borderColor?: string;
28
28
  textColor?: string;
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, borderColor, textColor, borderRadius, backgroundColor, eyeIcon, eyeClosedIcon }: TextInputProps) => react_jsx_runtime.JSX.Element;
35
37
 
36
38
  interface SkeletonProps {
37
39
  className?: string;
@@ -43,4 +45,26 @@ interface SkeletonProps {
43
45
 
44
46
  declare const Skeleton: ({ className, variant, width, height, animation, }: SkeletonProps) => react_jsx_runtime.JSX.Element;
45
47
 
46
- export { Input, Modal, Skeleton };
48
+ declare const ScrollToTop: () => null;
49
+
50
+ interface AccordionItem {
51
+ question: string;
52
+ answer: React.ReactNode;
53
+ icon?: React.ReactNode;
54
+ }
55
+ interface AccordionProps {
56
+ icon?: React.ReactNode;
57
+ items: AccordionItem[];
58
+ className?: string;
59
+ containerClassName?: string;
60
+ onIconClick?: (idx: number, item: AccordionItem) => void;
61
+ }
62
+ declare const Accordion: ({ icon, items, containerClassName, className, onIconClick }: AccordionProps) => react_jsx_runtime.JSX.Element;
63
+
64
+ declare const Rating: ({ rating, total, icon }: {
65
+ rating: number;
66
+ total: number;
67
+ icon?: string;
68
+ }) => react_jsx_runtime.JSX.Element;
69
+
70
+ export { Accordion, Input, Modal, Rating, ScrollToTop, Skeleton };
package/dist/index.js CHANGED
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Accordion: () => Accordion_default,
23
24
  Input: () => Input_default,
24
25
  Modal: () => Modal_default,
26
+ Rating: () => Rating_default,
27
+ ScrollToTop: () => ScrollToTop_default,
25
28
  Skeleton: () => Skeleton_default
26
29
  });
27
30
  module.exports = __toCommonJS(index_exports);
@@ -135,10 +138,12 @@ var Input = ({
135
138
  errorMessage,
136
139
  width = "100%",
137
140
  height = "40px",
138
- color = "#6B2CE9",
141
+ borderColor = "#6B2CE9",
139
142
  textColor = "white",
140
143
  borderRadius = "10px",
141
- backgroundColor = "#1F1F23"
144
+ backgroundColor = "#1F1F23",
145
+ eyeIcon = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Eye, { className: "cursor-pointer", size: 18 }),
146
+ eyeClosedIcon = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.EyeClosed, { className: "cursor-pointer", size: 18 })
142
147
  }) => {
143
148
  const [showPassword, setShowPassword] = (0, import_react.useState)(false);
144
149
  const [error, setError] = (0, import_react.useState)(null);
@@ -215,7 +220,7 @@ var Input = ({
215
220
  width: "100%"
216
221
  },
217
222
  className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-12 placeholder:opacity-30 focus:outline-none transition ${className}`,
218
- onFocus: (e) => !error && (e.target.style.borderColor = color),
223
+ onFocus: (e) => !error && (e.target.style.borderColor = borderColor),
219
224
  onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
220
225
  }
221
226
  ),
@@ -226,7 +231,7 @@ var Input = ({
226
231
  onClick: togglePasswordVisibility,
227
232
  className: "absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white transition-colors",
228
233
  "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 })
234
+ children: showPassword ? eyeIcon : eyeClosedIcon
230
235
  }
231
236
  )
232
237
  ] }),
@@ -234,44 +239,37 @@ var Input = ({
234
239
  ] });
235
240
  } else if (type === "otp") {
236
241
  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
- ),
242
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative flex items-center gap-4", style: { width: "100%" }, 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 = borderColor),
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
+ ] }),
275
273
  error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-red-500 text-xs mt-1 px-1", children: error })
276
274
  ] });
277
275
  } else {
@@ -291,7 +289,7 @@ var Input = ({
291
289
  width: "100%"
292
290
  },
293
291
  className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 placeholder:opacity-30 focus:outline-none transition ${className}`,
294
- onFocus: (e) => !error && (e.target.style.borderColor = color),
292
+ onFocus: (e) => !error && (e.target.style.borderColor = borderColor),
295
293
  onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
296
294
  }
297
295
  ),
@@ -345,9 +343,119 @@ var Skeleton = ({
345
343
  );
346
344
  };
347
345
  var Skeleton_default = Skeleton;
346
+
347
+ // src/components/ScrollToTop/ScrollToTop.tsx
348
+ var import_react2 = require("react");
349
+ var ScrollToTop = () => {
350
+ (0, import_react2.useEffect)(() => {
351
+ window.scrollTo(0, 0);
352
+ const handleNavigation = () => {
353
+ window.scrollTo(0, 0);
354
+ };
355
+ window.addEventListener("popstate", handleNavigation);
356
+ const originalPushState = history.pushState;
357
+ const originalReplaceState = history.replaceState;
358
+ history.pushState = function(...args) {
359
+ originalPushState.apply(this, args);
360
+ handleNavigation();
361
+ };
362
+ history.replaceState = function(...args) {
363
+ originalReplaceState.apply(this, args);
364
+ handleNavigation();
365
+ };
366
+ return () => {
367
+ window.removeEventListener("popstate", handleNavigation);
368
+ history.pushState = originalPushState;
369
+ history.replaceState = originalReplaceState;
370
+ };
371
+ }, []);
372
+ return null;
373
+ };
374
+ var ScrollToTop_default = ScrollToTop;
375
+
376
+ // src/components/Accordion/Accordion.tsx
377
+ var import_react3 = require("react");
378
+ var import_jsx_runtime4 = require("react/jsx-runtime");
379
+ var Accordion = ({ icon, items, containerClassName, className, onIconClick }) => {
380
+ const [openIndexes, setOpenIndexes] = (0, import_react3.useState)([]);
381
+ const toggleIndex = (idx) => {
382
+ setOpenIndexes((prev) => prev.includes(idx) ? prev.filter((i) => i !== idx) : [...prev, idx]);
383
+ };
384
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `space-y-2 ${containerClassName || ""}`, children: items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `${className || ""}`, children: [
385
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
386
+ "button",
387
+ {
388
+ className: "w-full flex justify-between items-center cursor-pointer py-3 text-left font-medium focus:outline-none",
389
+ onClick: () => toggleIndex(idx),
390
+ children: [
391
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: item.question }),
392
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
393
+ "span",
394
+ {
395
+ onClick: (e) => {
396
+ e.stopPropagation();
397
+ onIconClick && onIconClick(idx, item);
398
+ },
399
+ className: "ml-2 flex items-center",
400
+ style: {
401
+ cursor: onIconClick ? "pointer" : "default",
402
+ transform: openIndexes.includes(idx) ? "rotate(180deg)" : "rotate(0deg)",
403
+ transition: "transform 0.2s"
404
+ },
405
+ children: icon
406
+ }
407
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: openIndexes.includes(idx) ? "\u2212" : "+" })
408
+ ]
409
+ }
410
+ ),
411
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
412
+ "div",
413
+ {
414
+ className: `grid transition-all duration-300 ease-in-out ${openIndexes.includes(idx) ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`,
415
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pb-3", children: item.answer }) })
416
+ }
417
+ )
418
+ ] }, idx)) });
419
+ };
420
+ var Accordion_default = Accordion;
421
+
422
+ // src/components/Rating/Rating.tsx
423
+ var import_jsx_runtime5 = require("react/jsx-runtime");
424
+ var Rating = ({ rating, total, icon }) => {
425
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center gap-1", children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: icon ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
426
+ "img",
427
+ {
428
+ src: icon,
429
+ alt: "rating icon",
430
+ className: `w-4 h-4 ${i < rating ? "" : "opacity-30"}`
431
+ }
432
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
433
+ "svg",
434
+ {
435
+ xmlns: "http://www.w3.org/2000/svg",
436
+ fill: i < rating ? "currentColor" : "none",
437
+ viewBox: "0 0 24 24",
438
+ stroke: "currentColor",
439
+ className: `w-4 h-4 ${i < rating ? "text-yellow-400" : "text-gray-300"}`,
440
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
441
+ "path",
442
+ {
443
+ strokeLinecap: "round",
444
+ strokeLinejoin: "round",
445
+ strokeWidth: 2,
446
+ d: "M11.049 2.927c.3-.921 1.603-.921 1.902 0l2.036 6.29a1 1 0 00.95.69h6.6c.969 0 1.371 1.24.588 1.81l-5.347 3.89a1 1 0 00-.364 1.118l2.036 6.29c.3.921-.755 1.688-1.54 1.118l-5.347-3.89a1 1 0 00-1.176 0l-5.347 3.89c-.784.57-1.838-.197-1.54-1.118l2.036-6.29a1 1 0 00-.364-1.118l-5.347-3.89c-.783-.57-.38-1.81.588-1.81h6.6a1 1 0 00.95-.69l2.036-6.29z"
447
+ }
448
+ )
449
+ }
450
+ ) }, i)) });
451
+ };
452
+ var Rating_default = Rating;
348
453
  // Annotate the CommonJS export names for ESM import in node:
349
454
  0 && (module.exports = {
455
+ Accordion,
350
456
  Input,
351
457
  Modal,
458
+ Rating,
459
+ ScrollToTop,
352
460
  Skeleton
353
461
  });
package/dist/index.mjs CHANGED
@@ -107,10 +107,12 @@ var Input = ({
107
107
  errorMessage,
108
108
  width = "100%",
109
109
  height = "40px",
110
- color = "#6B2CE9",
110
+ borderColor = "#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);
@@ -187,7 +189,7 @@ var Input = ({
187
189
  width: "100%"
188
190
  },
189
191
  className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 pr-12 placeholder:opacity-30 focus:outline-none transition ${className}`,
190
- onFocus: (e) => !error && (e.target.style.borderColor = color),
192
+ onFocus: (e) => !error && (e.target.style.borderColor = borderColor),
191
193
  onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
192
194
  }
193
195
  ),
@@ -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 = borderColor),
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 {
@@ -263,7 +258,7 @@ var Input = ({
263
258
  width: "100%"
264
259
  },
265
260
  className: `border ${error ? "border-red-500" : "border-transparent"} placeholder:text-sm text-sm px-4 placeholder:opacity-30 focus:outline-none transition ${className}`,
266
- onFocus: (e) => !error && (e.target.style.borderColor = color),
261
+ onFocus: (e) => !error && (e.target.style.borderColor = borderColor),
267
262
  onBlur: (e) => e.target.style.borderColor = error ? "#ef4444" : "transparent"
268
263
  }
269
264
  ),
@@ -317,8 +312,118 @@ var Skeleton = ({
317
312
  );
318
313
  };
319
314
  var Skeleton_default = Skeleton;
315
+
316
+ // src/components/ScrollToTop/ScrollToTop.tsx
317
+ import { useEffect as useEffect2 } from "react";
318
+ var ScrollToTop = () => {
319
+ useEffect2(() => {
320
+ window.scrollTo(0, 0);
321
+ const handleNavigation = () => {
322
+ window.scrollTo(0, 0);
323
+ };
324
+ window.addEventListener("popstate", handleNavigation);
325
+ const originalPushState = history.pushState;
326
+ const originalReplaceState = history.replaceState;
327
+ history.pushState = function(...args) {
328
+ originalPushState.apply(this, args);
329
+ handleNavigation();
330
+ };
331
+ history.replaceState = function(...args) {
332
+ originalReplaceState.apply(this, args);
333
+ handleNavigation();
334
+ };
335
+ return () => {
336
+ window.removeEventListener("popstate", handleNavigation);
337
+ history.pushState = originalPushState;
338
+ history.replaceState = originalReplaceState;
339
+ };
340
+ }, []);
341
+ return null;
342
+ };
343
+ var ScrollToTop_default = ScrollToTop;
344
+
345
+ // src/components/Accordion/Accordion.tsx
346
+ import { useState as useState2 } from "react";
347
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
348
+ var Accordion = ({ icon, items, containerClassName, className, onIconClick }) => {
349
+ const [openIndexes, setOpenIndexes] = useState2([]);
350
+ const toggleIndex = (idx) => {
351
+ setOpenIndexes((prev) => prev.includes(idx) ? prev.filter((i) => i !== idx) : [...prev, idx]);
352
+ };
353
+ return /* @__PURE__ */ jsx4("div", { className: `space-y-2 ${containerClassName || ""}`, children: items.map((item, idx) => /* @__PURE__ */ jsxs3("div", { className: `${className || ""}`, children: [
354
+ /* @__PURE__ */ jsxs3(
355
+ "button",
356
+ {
357
+ className: "w-full flex justify-between items-center cursor-pointer py-3 text-left font-medium focus:outline-none",
358
+ onClick: () => toggleIndex(idx),
359
+ children: [
360
+ /* @__PURE__ */ jsx4("span", { children: item.question }),
361
+ icon ? /* @__PURE__ */ jsx4(
362
+ "span",
363
+ {
364
+ onClick: (e) => {
365
+ e.stopPropagation();
366
+ onIconClick && onIconClick(idx, item);
367
+ },
368
+ className: "ml-2 flex items-center",
369
+ style: {
370
+ cursor: onIconClick ? "pointer" : "default",
371
+ transform: openIndexes.includes(idx) ? "rotate(180deg)" : "rotate(0deg)",
372
+ transition: "transform 0.2s"
373
+ },
374
+ children: icon
375
+ }
376
+ ) : /* @__PURE__ */ jsx4("span", { children: openIndexes.includes(idx) ? "\u2212" : "+" })
377
+ ]
378
+ }
379
+ ),
380
+ /* @__PURE__ */ jsx4(
381
+ "div",
382
+ {
383
+ className: `grid transition-all duration-300 ease-in-out ${openIndexes.includes(idx) ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`,
384
+ children: /* @__PURE__ */ jsx4("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx4("div", { className: "pb-3", children: item.answer }) })
385
+ }
386
+ )
387
+ ] }, idx)) });
388
+ };
389
+ var Accordion_default = Accordion;
390
+
391
+ // src/components/Rating/Rating.tsx
392
+ import { jsx as jsx5 } from "react/jsx-runtime";
393
+ var Rating = ({ rating, total, icon }) => {
394
+ return /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-1", children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx5("div", { children: icon ? /* @__PURE__ */ jsx5(
395
+ "img",
396
+ {
397
+ src: icon,
398
+ alt: "rating icon",
399
+ className: `w-4 h-4 ${i < rating ? "" : "opacity-30"}`
400
+ }
401
+ ) : /* @__PURE__ */ jsx5(
402
+ "svg",
403
+ {
404
+ xmlns: "http://www.w3.org/2000/svg",
405
+ fill: i < rating ? "currentColor" : "none",
406
+ viewBox: "0 0 24 24",
407
+ stroke: "currentColor",
408
+ className: `w-4 h-4 ${i < rating ? "text-yellow-400" : "text-gray-300"}`,
409
+ children: /* @__PURE__ */ jsx5(
410
+ "path",
411
+ {
412
+ strokeLinecap: "round",
413
+ strokeLinejoin: "round",
414
+ strokeWidth: 2,
415
+ d: "M11.049 2.927c.3-.921 1.603-.921 1.902 0l2.036 6.29a1 1 0 00.95.69h6.6c.969 0 1.371 1.24.588 1.81l-5.347 3.89a1 1 0 00-.364 1.118l2.036 6.29c.3.921-.755 1.688-1.54 1.118l-5.347-3.89a1 1 0 00-1.176 0l-5.347 3.89c-.784.57-1.838-.197-1.54-1.118l2.036-6.29a1 1 0 00-.364-1.118l-5.347-3.89c-.783-.57-.38-1.81.588-1.81h6.6a1 1 0 00.95-.69l2.036-6.29z"
416
+ }
417
+ )
418
+ }
419
+ ) }, i)) });
420
+ };
421
+ var Rating_default = Rating;
320
422
  export {
423
+ Accordion_default as Accordion,
321
424
  Input_default as Input,
322
425
  Modal_default as Modal,
426
+ Rating_default as Rating,
427
+ ScrollToTop_default as ScrollToTop,
323
428
  Skeleton_default as Skeleton
324
429
  };
package/dist/styles.css CHANGED
@@ -8,6 +8,8 @@
8
8
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
9
9
  "Liberation Mono", "Courier New", monospace;
10
10
  --color-red-500: oklch(0.637 0.237 25.331);
11
+ --color-yellow-400: oklch(0.852 0.199 91.936);
12
+ --color-gray-300: oklch(0.872 0.01 258.338);
11
13
  --color-gray-400: oklch(0.707 0.022 261.325);
12
14
  --color-black: #000;
13
15
  --color-white: #fff;
@@ -22,6 +24,7 @@
22
24
  --font-weight-medium: 500;
23
25
  --font-weight-bold: 700;
24
26
  --radius-lg: 0.5rem;
27
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
25
28
  --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
26
29
  --default-transition-duration: 150ms;
27
30
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -222,12 +225,21 @@
222
225
  .mb-4 {
223
226
  margin-bottom: calc(var(--spacing) * 4);
224
227
  }
228
+ .ml-2 {
229
+ margin-left: calc(var(--spacing) * 2);
230
+ }
225
231
  .flex {
226
232
  display: flex;
227
233
  }
234
+ .grid {
235
+ display: grid;
236
+ }
228
237
  .h-4 {
229
238
  height: calc(var(--spacing) * 4);
230
239
  }
240
+ .w-4 {
241
+ width: calc(var(--spacing) * 4);
242
+ }
231
243
  .w-fit {
232
244
  width: -moz-fit-content;
233
245
  width: fit-content;
@@ -242,24 +254,49 @@
242
254
  --tw-translate-y: calc(calc(1/2 * 100%) * -1);
243
255
  translate: var(--tw-translate-x) var(--tw-translate-y);
244
256
  }
257
+ .transform {
258
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
259
+ }
245
260
  .animate-pulse {
246
261
  animation: var(--animate-pulse);
247
262
  }
248
263
  .cursor-pointer {
249
264
  cursor: pointer;
250
265
  }
266
+ .grid-rows-\[0fr\] {
267
+ grid-template-rows: 0fr;
268
+ }
269
+ .grid-rows-\[1fr\] {
270
+ grid-template-rows: 1fr;
271
+ }
251
272
  .items-center {
252
273
  align-items: center;
253
274
  }
275
+ .justify-between {
276
+ justify-content: space-between;
277
+ }
254
278
  .justify-center {
255
279
  justify-content: center;
256
280
  }
257
281
  .justify-end {
258
282
  justify-content: flex-end;
259
283
  }
284
+ .gap-1 {
285
+ gap: calc(var(--spacing) * 1);
286
+ }
260
287
  .gap-4 {
261
288
  gap: calc(var(--spacing) * 4);
262
289
  }
290
+ .space-y-2 {
291
+ :where(& > :not(:last-child)) {
292
+ --tw-space-y-reverse: 0;
293
+ margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
294
+ margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
295
+ }
296
+ }
297
+ .overflow-hidden {
298
+ overflow: hidden;
299
+ }
263
300
  .rounded-\[4px\] {
264
301
  border-radius: 4px;
265
302
  }
@@ -326,6 +363,9 @@
326
363
  .px-4 {
327
364
  padding-inline: calc(var(--spacing) * 4);
328
365
  }
366
+ .py-3 {
367
+ padding-block: calc(var(--spacing) * 3);
368
+ }
329
369
  .pt-2 {
330
370
  padding-top: calc(var(--spacing) * 2);
331
371
  }
@@ -335,6 +375,12 @@
335
375
  .pr-28 {
336
376
  padding-right: calc(var(--spacing) * 28);
337
377
  }
378
+ .pb-3 {
379
+ padding-bottom: calc(var(--spacing) * 3);
380
+ }
381
+ .text-left {
382
+ text-align: left;
383
+ }
338
384
  .text-2xl {
339
385
  font-size: var(--text-2xl);
340
386
  line-height: var(--tw-leading, var(--text-2xl--line-height));
@@ -358,22 +404,50 @@
358
404
  .text-\[\#A77BFF\] {
359
405
  color: #A77BFF;
360
406
  }
407
+ .text-gray-300 {
408
+ color: var(--color-gray-300);
409
+ }
361
410
  .text-gray-400 {
362
411
  color: var(--color-gray-400);
363
412
  }
364
413
  .text-red-500 {
365
414
  color: var(--color-red-500);
366
415
  }
416
+ .text-yellow-400 {
417
+ color: var(--color-yellow-400);
418
+ }
419
+ .opacity-0 {
420
+ opacity: 0%;
421
+ }
422
+ .opacity-30 {
423
+ opacity: 30%;
424
+ }
425
+ .opacity-100 {
426
+ opacity: 100%;
427
+ }
367
428
  .transition {
368
429
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
369
430
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
370
431
  transition-duration: var(--tw-duration, var(--default-transition-duration));
371
432
  }
433
+ .transition-all {
434
+ transition-property: all;
435
+ transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
436
+ transition-duration: var(--tw-duration, var(--default-transition-duration));
437
+ }
372
438
  .transition-colors {
373
439
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
374
440
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
375
441
  transition-duration: var(--tw-duration, var(--default-transition-duration));
376
442
  }
443
+ .duration-300 {
444
+ --tw-duration: 300ms;
445
+ transition-duration: 300ms;
446
+ }
447
+ .ease-in-out {
448
+ --tw-ease: var(--ease-in-out);
449
+ transition-timing-function: var(--ease-in-out);
450
+ }
377
451
  .placeholder\:text-sm {
378
452
  &::-moz-placeholder {
379
453
  font-size: var(--text-sm);
@@ -426,6 +500,31 @@
426
500
  inherits: false;
427
501
  initial-value: 0;
428
502
  }
503
+ @property --tw-rotate-x {
504
+ syntax: "*";
505
+ inherits: false;
506
+ }
507
+ @property --tw-rotate-y {
508
+ syntax: "*";
509
+ inherits: false;
510
+ }
511
+ @property --tw-rotate-z {
512
+ syntax: "*";
513
+ inherits: false;
514
+ }
515
+ @property --tw-skew-x {
516
+ syntax: "*";
517
+ inherits: false;
518
+ }
519
+ @property --tw-skew-y {
520
+ syntax: "*";
521
+ inherits: false;
522
+ }
523
+ @property --tw-space-y-reverse {
524
+ syntax: "*";
525
+ inherits: false;
526
+ initial-value: 0;
527
+ }
429
528
  @property --tw-border-style {
430
529
  syntax: "*";
431
530
  inherits: false;
@@ -477,6 +576,14 @@
477
576
  syntax: "*";
478
577
  inherits: false;
479
578
  }
579
+ @property --tw-duration {
580
+ syntax: "*";
581
+ inherits: false;
582
+ }
583
+ @property --tw-ease {
584
+ syntax: "*";
585
+ inherits: false;
586
+ }
480
587
  @keyframes pulse {
481
588
  50% {
482
589
  opacity: 0.5;
@@ -488,6 +595,12 @@
488
595
  --tw-translate-x: 0;
489
596
  --tw-translate-y: 0;
490
597
  --tw-translate-z: 0;
598
+ --tw-rotate-x: initial;
599
+ --tw-rotate-y: initial;
600
+ --tw-rotate-z: initial;
601
+ --tw-skew-x: initial;
602
+ --tw-skew-y: initial;
603
+ --tw-space-y-reverse: 0;
491
604
  --tw-border-style: solid;
492
605
  --tw-gradient-position: initial;
493
606
  --tw-gradient-from: #0000;
@@ -499,6 +612,8 @@
499
612
  --tw-gradient-via-position: 50%;
500
613
  --tw-gradient-to-position: 100%;
501
614
  --tw-font-weight: initial;
615
+ --tw-duration: initial;
616
+ --tw-ease: initial;
502
617
  }
503
618
  }
504
619
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tunjiadeyemi/ui",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "A collection of reusable UI components",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",