@skalfa/skalfa-component 1.0.6 → 1.0.8
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.js +10 -10
- package/package.json +2 -2
- package/src/accordion/Accordion.component.tsx +87 -0
- package/src/breadcrumb/Breadcrumb.component.tsx +79 -0
- package/src/button/Button.component.tsx +89 -0
- package/src/card/AlertCard.component.tsx +69 -0
- package/src/card/Card.component.tsx +25 -0
- package/src/card/DashboardCard.component.tsx +44 -0
- package/src/card/GalleryCard.component.tsx +50 -0
- package/src/card/ProductCard.component.tsx +65 -0
- package/src/card/ProfileCard.component.tsx +71 -0
- package/src/carousel/Carousel.component.tsx +111 -0
- package/src/chip/Chip.component.tsx +39 -0
- package/src/index.ts +70 -0
- package/src/input/Checkbox.component.tsx +102 -0
- package/src/input/Input.component.tsx +334 -0
- package/src/input/InputCheckbox.component.tsx +174 -0
- package/src/input/InputCurrency.component.tsx +165 -0
- package/src/input/InputDate.component.tsx +356 -0
- package/src/input/InputDatetime.component.tsx +267 -0
- package/src/input/InputDocument.component.tsx +360 -0
- package/src/input/InputImage.component.tsx +535 -0
- package/src/input/InputNumber.component.tsx +194 -0
- package/src/input/InputOtp.component.tsx +169 -0
- package/src/input/InputPassword.component.tsx +245 -0
- package/src/input/InputRadio.component.tsx +174 -0
- package/src/input/InputTime.component.tsx +280 -0
- package/src/input/InputValues.component.tsx +71 -0
- package/src/input/Radio.component.tsx +98 -0
- package/src/input/Select.component.tsx +557 -0
- package/src/modal/BottomSheet.component.tsx +246 -0
- package/src/modal/FloatingPage.component.tsx +103 -0
- package/src/modal/Modal.component.tsx +95 -0
- package/src/modal/ModalConfirm.component.tsx +219 -0
- package/src/modal/Toast.component.tsx +125 -0
- package/src/nav/Bottombar.component.tsx +72 -0
- package/src/nav/Footer.component.tsx +177 -0
- package/src/nav/Headbar.component.tsx +33 -0
- package/src/nav/Navbar.component.tsx +138 -0
- package/src/nav/Sidebar.component.tsx +298 -0
- package/src/nav/Tabbar.component.tsx +61 -0
- package/src/nav/Wizard.component.tsx +80 -0
- package/src/supervision/FormSupervision.component.tsx +425 -0
- package/src/supervision/TableSupervision.component.tsx +688 -0
- package/src/table/ControlBar.component.tsx +501 -0
- package/src/table/FilterComponent.tsx +519 -0
- package/src/table/Pagination.component.tsx +152 -0
- package/src/table/Table.component.tsx +436 -0
- package/src/types.d.ts +7 -0
- package/src/typography/TypographyArticle.component.tsx +26 -0
- package/src/typography/TypographyColumn.component.tsx +20 -0
- package/src/typography/TypographyContent.component.tsx +20 -0
- package/src/typography/TypographyTips.component.tsx +20 -0
- package/src/wrap/Draggable.component.tsx +303 -0
- package/src/wrap/Image.component.tsx +10 -0
- package/src/wrap/OutsideClick.component.tsx +48 -0
- package/src/wrap/ScrollContainer.component.tsx +107 -0
- package/src/wrap/ShortcutProvider.tsx +57 -0
- package/src/wrap/Swipe.component.tsx +121 -0
package/dist/index.js
CHANGED
|
@@ -186,15 +186,15 @@ exports.ShortcutProvider = require("./wrap/ShortcutProvider").ShortcutProvider;
|
|
|
186
186
|
exports.SwipeComponent = require("./wrap/Swipe.component").SwipeComponent;
|
|
187
187
|
|
|
188
188
|
const _utils_1 = require("@utils");
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
189
|
+
const Table_component_1 = require("./table/Table.component");
|
|
190
|
+
const Button_component_1 = require("./button/Button.component");
|
|
191
|
+
const Select_component_1 = require("./input/Select.component");
|
|
192
|
+
const Modal_component_1 = require("./modal/Modal.component");
|
|
193
|
+
const FilterComponent_1 = require("./table/FilterComponent");
|
|
194
194
|
const _contexts_1 = require("@contexts");
|
|
195
|
-
_utils_1.registry.register("TableComponent",
|
|
196
|
-
_utils_1.registry.register("ButtonComponent",
|
|
197
|
-
_utils_1.registry.register("SelectComponent",
|
|
198
|
-
_utils_1.registry.register("ModalComponent",
|
|
199
|
-
_utils_1.registry.register("FilterComponent",
|
|
195
|
+
_utils_1.registry.register("TableComponent", Table_component_1.TableComponent);
|
|
196
|
+
_utils_1.registry.register("ButtonComponent", Button_component_1.ButtonComponent);
|
|
197
|
+
_utils_1.registry.register("SelectComponent", Select_component_1.SelectComponent);
|
|
198
|
+
_utils_1.registry.register("ModalComponent", Modal_component_1.ModalComponent);
|
|
199
|
+
_utils_1.registry.register("FilterComponent", FilterComponent_1.FilterComponent);
|
|
200
200
|
_utils_1.registry.register("useToggleContext", _contexts_1.useToggleContext);
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skalfa/skalfa-component",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Reusable UI components for Skalfa App.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": ["skalfa", "skalfa-app", "skalfa-component", "next-framework", "next-template"],
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
-
"files": ["dist"],
|
|
9
|
+
"files": ["dist", "src"],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc -p tsconfig.json",
|
|
12
12
|
"postbuild": "bun fix-exports.js"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ReactNode, useEffect, useState } from "react";
|
|
4
|
+
import { faChevronDown, faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
|
5
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
6
|
+
import { cn, pcn } from "@utils";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
type CT = "container" | "head" | "active" | "base";
|
|
11
|
+
|
|
12
|
+
export interface AccordionProps {
|
|
13
|
+
setActive ?: number | null;
|
|
14
|
+
items : { head: ReactNode; content: ReactNode }[];
|
|
15
|
+
horizontal ?: boolean;
|
|
16
|
+
className ?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export function AccordionComponent({
|
|
22
|
+
items,
|
|
23
|
+
setActive = null,
|
|
24
|
+
horizontal = false,
|
|
25
|
+
|
|
26
|
+
/** Use custom class with: "container::", "head::", "active::". */
|
|
27
|
+
className = "",
|
|
28
|
+
}: AccordionProps) {
|
|
29
|
+
const [isActive, setIsActive] = useState<number | null>(setActive);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
setIsActive(setActive);
|
|
33
|
+
}, [setActive]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
className={cn(
|
|
38
|
+
"accordion",
|
|
39
|
+
horizontal ? "accordion-horizontal" : "accordion-vertical",
|
|
40
|
+
pcn<CT>(className, "container"),
|
|
41
|
+
)}
|
|
42
|
+
>
|
|
43
|
+
{items.map(({ head, content }, key) => (
|
|
44
|
+
<div
|
|
45
|
+
key={key}
|
|
46
|
+
className={cn(
|
|
47
|
+
"accordion-item",
|
|
48
|
+
horizontal ? "accordion-item-horizontal" : "accordion-item-vertical",
|
|
49
|
+
)}
|
|
50
|
+
>
|
|
51
|
+
<div
|
|
52
|
+
className={cn(
|
|
53
|
+
"accordion-head",
|
|
54
|
+
horizontal ? "accordion-head-horizontal" : "accordion-head-vertical",
|
|
55
|
+
pcn<CT>(className, "head"),
|
|
56
|
+
)}
|
|
57
|
+
onClick={() => setIsActive(isActive === key ? null : key)}
|
|
58
|
+
>
|
|
59
|
+
<div>{head}</div>
|
|
60
|
+
<div
|
|
61
|
+
className={cn(
|
|
62
|
+
"w-min transition-transform",
|
|
63
|
+
isActive !== key && "rotate-180",
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
<FontAwesomeIcon icon={horizontal ? faChevronLeft : faChevronDown} />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div
|
|
70
|
+
className={cn(
|
|
71
|
+
"accordion-content",
|
|
72
|
+
horizontal
|
|
73
|
+
? isActive === key
|
|
74
|
+
? "accordion-content-horizontal-active"
|
|
75
|
+
: "accordion-content-horizontal-inactive"
|
|
76
|
+
: isActive === key
|
|
77
|
+
? "accordion-content-vertical-active"
|
|
78
|
+
: "accordion-content-vertical-inactive",
|
|
79
|
+
pcn<CT>(className, "base"),
|
|
80
|
+
isActive === key && pcn<CT>(className, "active"),
|
|
81
|
+
)}
|
|
82
|
+
>{content}</div>
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Fragment, ReactNode } from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { faChevronRight } from "@fortawesome/free-solid-svg-icons";
|
|
6
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
7
|
+
import { cn, pcn } from "@utils";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
type CT = "container" | "active" | "base";
|
|
12
|
+
|
|
13
|
+
export interface BreadcrumbItemProps {
|
|
14
|
+
path : string;
|
|
15
|
+
label : string;
|
|
16
|
+
className ?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface BreadcrumbProps {
|
|
20
|
+
items : BreadcrumbItemProps[];
|
|
21
|
+
square ?: boolean;
|
|
22
|
+
separatorContent ?: string | ReactNode;
|
|
23
|
+
|
|
24
|
+
/** Use custom class with: "container::", "active::". */
|
|
25
|
+
className ?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export function BreadcrumbComponent({
|
|
30
|
+
items,
|
|
31
|
+
className = "",
|
|
32
|
+
square = false,
|
|
33
|
+
separatorContent,
|
|
34
|
+
}: BreadcrumbProps) {
|
|
35
|
+
return (
|
|
36
|
+
<nav
|
|
37
|
+
className={cn(
|
|
38
|
+
"breadcrumb",
|
|
39
|
+
pcn<CT>(className, "container"),
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
42
|
+
<ol className="breadcrumb-list">
|
|
43
|
+
{items.map((item, index) => {
|
|
44
|
+
const isActive = index === items.length - 1;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Fragment key={item.path}>
|
|
48
|
+
<li>
|
|
49
|
+
<Link
|
|
50
|
+
href={item.path}
|
|
51
|
+
className={cn(
|
|
52
|
+
"breadcrumb-link",
|
|
53
|
+
square && "breadcrumb-link-square",
|
|
54
|
+
isActive && !square && "breadcrumb-link-active",
|
|
55
|
+
isActive && square && "breadcrumb-link-square-active",
|
|
56
|
+
pcn<CT>(className, "base"),
|
|
57
|
+
isActive && pcn<CT>(className, "active"),
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{item.label}
|
|
61
|
+
</Link>
|
|
62
|
+
</li>
|
|
63
|
+
|
|
64
|
+
{!isActive && (
|
|
65
|
+
<li className="breadcrumb-separator">
|
|
66
|
+
{separatorContent || (
|
|
67
|
+
<FontAwesomeIcon
|
|
68
|
+
icon={faChevronRight}
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
</li>
|
|
72
|
+
)}
|
|
73
|
+
</Fragment>
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
</ol>
|
|
77
|
+
</nav>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
6
|
+
import { cn, pcn } from "@utils";
|
|
7
|
+
|
|
8
|
+
type CT = "icon" | "loading" | "base";
|
|
9
|
+
|
|
10
|
+
export interface ButtonProps {
|
|
11
|
+
type ?: "submit" | "button";
|
|
12
|
+
label ?: string | ReactNode;
|
|
13
|
+
variant ?: "solid" | "outline" | "light" | "simple";
|
|
14
|
+
paint ?: "primary" | "secondary" | "success" | "danger" | "warning";
|
|
15
|
+
rounded ?: boolean | string;
|
|
16
|
+
block ?: boolean;
|
|
17
|
+
disabled ?: boolean;
|
|
18
|
+
size ?: "xs" | "sm" | "md" | "lg";
|
|
19
|
+
onClick ?: any;
|
|
20
|
+
href ?: string;
|
|
21
|
+
icon ?: any;
|
|
22
|
+
loading ?: boolean;
|
|
23
|
+
hover ?: boolean;
|
|
24
|
+
tips ?: string; // Tooltip / title attribute
|
|
25
|
+
title ?: string;
|
|
26
|
+
|
|
27
|
+
/** Use custom class with: "icon::", "loading::". */
|
|
28
|
+
className?: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function ButtonComponent({
|
|
32
|
+
type = "button",
|
|
33
|
+
label,
|
|
34
|
+
variant = "solid",
|
|
35
|
+
paint = "primary",
|
|
36
|
+
rounded,
|
|
37
|
+
block,
|
|
38
|
+
disabled,
|
|
39
|
+
size = "md",
|
|
40
|
+
onClick,
|
|
41
|
+
href,
|
|
42
|
+
icon,
|
|
43
|
+
loading,
|
|
44
|
+
tips,
|
|
45
|
+
title,
|
|
46
|
+
className = "",
|
|
47
|
+
}: ButtonProps) {
|
|
48
|
+
const isIconButton = icon && !label;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ButtonComponentWrapper
|
|
52
|
+
type={type}
|
|
53
|
+
className={cn(
|
|
54
|
+
"button",
|
|
55
|
+
`button-${variant}-${paint}`,
|
|
56
|
+
isIconButton ? `icon-button-${size}` : `button-${size}`,
|
|
57
|
+
rounded && "rounded-full",
|
|
58
|
+
!isIconButton && block && "w-full justify-center",
|
|
59
|
+
pcn<CT>(className, "base"),
|
|
60
|
+
)}
|
|
61
|
+
disabled={disabled || loading}
|
|
62
|
+
onClick={onClick}
|
|
63
|
+
href={href}
|
|
64
|
+
title={tips || title}
|
|
65
|
+
>
|
|
66
|
+
{loading ? (
|
|
67
|
+
<div className={cn("button-loading", pcn<CT>(className, "loading"))}></div>
|
|
68
|
+
) : (
|
|
69
|
+
icon && (
|
|
70
|
+
<FontAwesomeIcon
|
|
71
|
+
icon={icon}
|
|
72
|
+
className={cn("button-icon", pcn<CT>(className, "icon"))}
|
|
73
|
+
/>
|
|
74
|
+
)
|
|
75
|
+
)}
|
|
76
|
+
{!isIconButton && label}
|
|
77
|
+
</ButtonComponentWrapper>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ButtonComponentWrapper = ({ children, href, ...props }: ButtonProps & { children?: ReactNode }) => {
|
|
82
|
+
return !href ? (
|
|
83
|
+
<button {...(props as any)}>{children}</button>
|
|
84
|
+
) : (
|
|
85
|
+
<Link href={href} {...(props as any)}>
|
|
86
|
+
{children}
|
|
87
|
+
</Link>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
import { cn, pcn } from "@utils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type CT = "badge" | "title" | "base";
|
|
9
|
+
|
|
10
|
+
export interface AlertCardProps {
|
|
11
|
+
title ?: string | ReactNode;
|
|
12
|
+
leftContent ?: string | ReactNode;
|
|
13
|
+
badge ?: string | ReactNode;
|
|
14
|
+
content ?: string | ReactNode;
|
|
15
|
+
footer ?: string | ReactNode;
|
|
16
|
+
|
|
17
|
+
/** Use custom class with: "badge::", "title::". */
|
|
18
|
+
className ?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
export function AlertCardComponent({
|
|
24
|
+
title,
|
|
25
|
+
leftContent,
|
|
26
|
+
badge,
|
|
27
|
+
content,
|
|
28
|
+
footer,
|
|
29
|
+
className = "",
|
|
30
|
+
}: AlertCardProps) {
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<section
|
|
34
|
+
className={cn(
|
|
35
|
+
"alert-card",
|
|
36
|
+
pcn<CT>(className, "base"),
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
<div className="flex items-start sm:gap-8">
|
|
40
|
+
{leftContent}
|
|
41
|
+
|
|
42
|
+
<div>
|
|
43
|
+
<strong
|
|
44
|
+
className={cn(
|
|
45
|
+
"alert-card-badge",
|
|
46
|
+
pcn<CT>(className, "badge"),
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
{badge}
|
|
50
|
+
</strong>
|
|
51
|
+
|
|
52
|
+
<h3
|
|
53
|
+
className={cn(
|
|
54
|
+
"alert-card-title",
|
|
55
|
+
pcn<CT>(className, "title"),
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{title}
|
|
59
|
+
</h3>
|
|
60
|
+
|
|
61
|
+
{content}
|
|
62
|
+
|
|
63
|
+
{footer && <div className="pt-4">{footer}</div>}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</section>
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@utils";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export interface CardProps {
|
|
8
|
+
children : React.ReactNode;
|
|
9
|
+
className ?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export function CardComponent({
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
}: CardProps) {
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<div className={cn("card", className)}>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { ReactNode } from "react";
|
|
5
|
+
import { cn } from "@utils";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export interface DashboardCardProps {
|
|
10
|
+
content ?: string | ReactNode;
|
|
11
|
+
title ?: string | ReactNode;
|
|
12
|
+
rightContent ?: string | ReactNode;
|
|
13
|
+
path ?: string;
|
|
14
|
+
className ?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export function DashboardCardComponent({
|
|
20
|
+
content,
|
|
21
|
+
title,
|
|
22
|
+
rightContent,
|
|
23
|
+
path,
|
|
24
|
+
className,
|
|
25
|
+
} : DashboardCardProps) {
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<Link href={path || ""}>
|
|
29
|
+
<div
|
|
30
|
+
className={cn(
|
|
31
|
+
"dashboard-card",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
<div>
|
|
36
|
+
<div className="flex gap-4 items-center">{content}</div>
|
|
37
|
+
{title}
|
|
38
|
+
</div>
|
|
39
|
+
<div>{rightContent}</div>
|
|
40
|
+
</div>
|
|
41
|
+
</Link>
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import { cn, pcn } from "@utils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type CT = "label" | "image" | "base";
|
|
9
|
+
|
|
10
|
+
export interface GalleryCardProps {
|
|
11
|
+
src : string;
|
|
12
|
+
alt ?: string;
|
|
13
|
+
|
|
14
|
+
/** Use custom class with: "label::", "image::". */
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export function GalleryCardComponent({
|
|
21
|
+
src,
|
|
22
|
+
alt = "",
|
|
23
|
+
className = "",
|
|
24
|
+
} : GalleryCardProps) {
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<div className={pcn<CT>(className, "base")}>
|
|
28
|
+
<Image
|
|
29
|
+
src={src}
|
|
30
|
+
alt={src}
|
|
31
|
+
width={400}
|
|
32
|
+
height={300}
|
|
33
|
+
className={cn(
|
|
34
|
+
"gallery-card-image",
|
|
35
|
+
pcn<CT>(className, "image"),
|
|
36
|
+
)}
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
className={cn(
|
|
41
|
+
"gallery-card-label",
|
|
42
|
+
pcn<CT>(className, "label"),
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
{alt}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import { cn, pcn } from "@utils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type CT = "image" | "base" | "content";
|
|
9
|
+
|
|
10
|
+
export interface ProductCardProps {
|
|
11
|
+
name : string;
|
|
12
|
+
price ?: string;
|
|
13
|
+
image ?: string;
|
|
14
|
+
description ?: string | React.ReactNode;
|
|
15
|
+
|
|
16
|
+
/** Use custom class with: "image::", "content::". */
|
|
17
|
+
className ?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export function ProductCardComponent({
|
|
23
|
+
image = "",
|
|
24
|
+
name,
|
|
25
|
+
price,
|
|
26
|
+
description,
|
|
27
|
+
className = "",
|
|
28
|
+
} : ProductCardProps) {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<div
|
|
32
|
+
className={cn(
|
|
33
|
+
"product-card",
|
|
34
|
+
pcn<CT>(className, "base"),
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
<Image
|
|
38
|
+
src={image}
|
|
39
|
+
alt={image}
|
|
40
|
+
width={400}
|
|
41
|
+
height={300}
|
|
42
|
+
className={cn(
|
|
43
|
+
"product-card-image",
|
|
44
|
+
pcn<CT>(className, "image"),
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
<div className={cn("p-2", pcn<CT>(className, "content"))}>
|
|
49
|
+
<dl>
|
|
50
|
+
<div>
|
|
51
|
+
<dt className="sr-only">Name</dt>
|
|
52
|
+
<dd className="text-sm">{name}</dd>
|
|
53
|
+
</div>
|
|
54
|
+
<div>
|
|
55
|
+
<dt className="sr-only">Price</dt>
|
|
56
|
+
<dd className="font-semibold">{price}</dd>
|
|
57
|
+
</div>
|
|
58
|
+
</dl>
|
|
59
|
+
|
|
60
|
+
{description && <div>{description}</div>}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import { cn, pcn } from "@utils";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type CT = "image" | "base" | "content";
|
|
9
|
+
|
|
10
|
+
export interface ProfileCardProps {
|
|
11
|
+
name : string;
|
|
12
|
+
short ?: string;
|
|
13
|
+
image ?: string;
|
|
14
|
+
description ?: string | React.ReactNode;
|
|
15
|
+
footer ?: string | React.ReactNode;
|
|
16
|
+
|
|
17
|
+
/** Use custom class with: "image::", "content::". */
|
|
18
|
+
className ?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
export function ProfileCardComponent({
|
|
24
|
+
image = "",
|
|
25
|
+
name,
|
|
26
|
+
short,
|
|
27
|
+
description,
|
|
28
|
+
footer,
|
|
29
|
+
className = "",
|
|
30
|
+
} : ProfileCardProps) {
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<div
|
|
34
|
+
className={cn(
|
|
35
|
+
"profile-card",
|
|
36
|
+
pcn<CT>(className, "base"),
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
<span className="absolute inset-x-0 bottom-0 h-1 bg-primary"></span>
|
|
40
|
+
|
|
41
|
+
<div className="sm:flex sm:gap-4 sm:items-center">
|
|
42
|
+
{image && (
|
|
43
|
+
<div className="hidden sm:block sm:shrink-0">
|
|
44
|
+
<Image
|
|
45
|
+
src={image}
|
|
46
|
+
alt={image}
|
|
47
|
+
width={400}
|
|
48
|
+
height={400}
|
|
49
|
+
className={cn(
|
|
50
|
+
"profile-card-image",
|
|
51
|
+
pcn<CT>(className, "image"),
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
<div className={pcn<CT>(className, "content")}>
|
|
57
|
+
<h3 className="text-lg font-bold sm:text-xl">{name}</h3>
|
|
58
|
+
|
|
59
|
+
<p className="mt-1 text-xs font-medium text-light-foreground">
|
|
60
|
+
{short}
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{description}
|
|
66
|
+
|
|
67
|
+
{footer && <dl className="mt-4">{footer}</dl>}
|
|
68
|
+
</div>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|