@schandlergarcia/sf-web-components 1.3.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/.a4drules/features/command-center-dashboard-rule.md +1 -1
- package/.a4drules/skills/command-center-project/SKILL.md +4 -4
- package/.a4drules/skills/component-library/SKILL.md +4 -1
- package/dist/components/library/ui/Avatar.d.ts +22 -10
- package/dist/components/library/ui/Avatar.js +40 -17
- package/dist/components/library/ui/Avatar.js.map +1 -1
- package/dist/components/library/ui/Card.d.ts +23 -37
- package/dist/components/library/ui/EmptyState.d.ts +34 -7
- package/dist/components/library/ui/EmptyState.js +5 -6
- package/dist/components/library/ui/EmptyState.js.map +1 -1
- package/dist/components/library/ui/Spinner.d.ts +21 -5
- package/dist/components/library/ui/UIButton.d.ts +22 -11
- package/dist/components/library/ui/UIButton.js +23 -28
- package/dist/components/library/ui/UIButton.js.map +1 -1
- package/dist/components/library/ui/UIInput.d.ts +4 -5
- package/dist/components/library/ui/UIInput.js +5 -6
- package/dist/components/library/ui/UIInput.js.map +1 -1
- package/dist/components/library/ui/alert.d.ts +23 -21
- package/dist/components/library/ui/alert.js +13 -14
- package/dist/components/library/ui/alert.js.map +1 -1
- package/dist/components/library/ui/card.js +24 -26
- package/dist/components/library/ui/card.js.map +1 -1
- package/dist/components/library/ui/checkbox.d.ts +4 -4
- package/dist/components/library/ui/checkbox.js +4 -5
- package/dist/components/library/ui/checkbox.js.map +1 -1
- package/dist/components/library/ui/dialog.d.ts +1 -1
- package/dist/components/library/ui/label.d.ts +5 -7
- package/dist/components/library/ui/label.js +9 -10
- package/dist/components/library/ui/label.js.map +1 -1
- package/dist/components/library/ui/popover.d.ts +1 -1
- package/dist/components/library/ui/spinner.js +16 -17
- package/dist/components/library/ui/spinner.js.map +1 -1
- package/package.json +7 -3
- package/scripts/convert-to-typescript.sh +52 -0
- package/src/components/library/ui/{Alert.jsx → Alert.tsx} +17 -7
- package/src/components/library/ui/Avatar.tsx +68 -0
- package/src/components/library/ui/{Card.jsx → Card.tsx} +24 -10
- package/src/components/library/ui/{Checkbox.jsx → Checkbox.tsx} +4 -2
- package/src/components/library/ui/Dialog.tsx +1 -1
- package/src/components/library/ui/{EmptyState.jsx → EmptyState.tsx} +13 -10
- package/src/components/library/ui/{Label.jsx → Label.tsx} +9 -8
- package/src/components/library/ui/Popover.tsx +1 -1
- package/src/components/library/ui/{Spinner.jsx → Spinner.tsx} +12 -10
- package/src/components/library/ui/{UIButton.jsx → UIButton.tsx} +15 -14
- package/src/components/library/ui/{UIInput.jsx → UIInput.tsx} +5 -5
- package/src/components/library/ui/Avatar.jsx +0 -44
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface UICardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
padding?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function UICard({ children, padding = "p-5", className = "", ...rest }: UICardProps) {
|
|
4
8
|
return (
|
|
5
9
|
<div
|
|
6
|
-
style={style}
|
|
7
10
|
className={[
|
|
8
11
|
"rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900",
|
|
9
12
|
padding,
|
|
@@ -19,7 +22,9 @@ export default function UICard({ children, padding = "p-5", style, className = "
|
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
// shadcn-compatible Card components
|
|
22
|
-
export
|
|
25
|
+
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
26
|
+
|
|
27
|
+
export function Card({ className = "", children, ...rest }: CardProps) {
|
|
23
28
|
return (
|
|
24
29
|
<div
|
|
25
30
|
className={[
|
|
@@ -35,7 +40,9 @@ export function Card({ className = "", children, ...rest }) {
|
|
|
35
40
|
);
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
export
|
|
43
|
+
export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
44
|
+
|
|
45
|
+
export function CardHeader({ className = "", children, ...rest }: CardHeaderProps) {
|
|
39
46
|
return (
|
|
40
47
|
<div
|
|
41
48
|
className={[
|
|
@@ -51,7 +58,9 @@ export function CardHeader({ className = "", children, ...rest }) {
|
|
|
51
58
|
);
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
export
|
|
61
|
+
export interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}
|
|
62
|
+
|
|
63
|
+
export function CardTitle({ className = "", children, ...rest }: CardTitleProps) {
|
|
55
64
|
return (
|
|
56
65
|
<h3
|
|
57
66
|
className={[
|
|
@@ -67,7 +76,9 @@ export function CardTitle({ className = "", children, ...rest }) {
|
|
|
67
76
|
);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
export
|
|
79
|
+
export interface CardDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
80
|
+
|
|
81
|
+
export function CardDescription({ className = "", children, ...rest }: CardDescriptionProps) {
|
|
71
82
|
return (
|
|
72
83
|
<p
|
|
73
84
|
className={[
|
|
@@ -83,7 +94,9 @@ export function CardDescription({ className = "", children, ...rest }) {
|
|
|
83
94
|
);
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
export
|
|
97
|
+
export interface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
98
|
+
|
|
99
|
+
export function CardContent({ className = "", children, ...rest }: CardContentProps) {
|
|
87
100
|
return (
|
|
88
101
|
<div
|
|
89
102
|
className={[
|
|
@@ -99,7 +112,9 @@ export function CardContent({ className = "", children, ...rest }) {
|
|
|
99
112
|
);
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
export
|
|
115
|
+
export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
116
|
+
|
|
117
|
+
export function CardFooter({ className = "", children, ...rest }: CardFooterProps) {
|
|
103
118
|
return (
|
|
104
119
|
<div
|
|
105
120
|
className={[
|
|
@@ -114,4 +129,3 @@ export function CardFooter({ className = "", children, ...rest }) {
|
|
|
114
129
|
</div>
|
|
115
130
|
);
|
|
116
131
|
}
|
|
117
|
-
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
4
|
+
|
|
5
|
+
export default function Checkbox({ className = "", ...rest }: CheckboxProps) {
|
|
4
6
|
return (
|
|
5
7
|
<input
|
|
6
8
|
type="checkbox"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
3
3
|
|
|
4
4
|
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
5
5
|
return <DialogPrimitive.Root {...props} />;
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
const SIZES = {
|
|
4
4
|
sm: { icon: "h-8 w-8", heading: "text-sm", body: "text-xs", gap: "gap-2", pad: "py-6" },
|
|
5
5
|
md: { icon: "h-10 w-10", heading: "text-base", body: "text-sm", gap: "gap-3", pad: "py-10" },
|
|
6
6
|
lg: { icon: "h-14 w-14", heading: "text-lg", body: "text-sm", gap: "gap-4", pad: "py-16" },
|
|
7
|
-
};
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export interface EmptyStateProps {
|
|
10
|
+
icon?: React.ReactNode;
|
|
11
|
+
heading?: string;
|
|
12
|
+
body?: string;
|
|
13
|
+
action?: React.ReactNode;
|
|
14
|
+
size?: keyof typeof SIZES;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
8
17
|
|
|
9
18
|
/**
|
|
10
19
|
* Empty state placeholder — shows when a list, table, or section has no content.
|
|
11
|
-
*
|
|
12
|
-
* @param {ReactNode} icon Optional icon element
|
|
13
|
-
* @param {string} heading Primary message
|
|
14
|
-
* @param {string} body Secondary description
|
|
15
|
-
* @param {ReactNode} action Optional CTA (button, link, etc.)
|
|
16
|
-
* @param {"sm"|"md"|"lg"} size Visual density
|
|
17
20
|
*/
|
|
18
21
|
export default function EmptyState({
|
|
19
22
|
icon,
|
|
@@ -22,8 +25,8 @@ export default function EmptyState({
|
|
|
22
25
|
action,
|
|
23
26
|
size = "md",
|
|
24
27
|
className = "",
|
|
25
|
-
}) {
|
|
26
|
-
const s = SIZES[size]
|
|
28
|
+
}: EmptyStateProps) {
|
|
29
|
+
const s = SIZES[size];
|
|
27
30
|
|
|
28
31
|
return (
|
|
29
32
|
<div className={`flex flex-col items-center justify-center text-center ${s.pad} ${s.gap} ${className}`}>
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const props = htmlFor ? { htmlFor, ...rest } : rest;
|
|
3
|
+
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
|
4
|
+
required?: boolean;
|
|
5
|
+
}
|
|
7
6
|
|
|
7
|
+
export default function Label({ children, htmlFor, required, className = "", ...rest }: LabelProps) {
|
|
8
8
|
return (
|
|
9
|
-
<
|
|
10
|
-
{
|
|
9
|
+
<label
|
|
10
|
+
htmlFor={htmlFor}
|
|
11
11
|
className={[
|
|
12
12
|
"text-sm font-medium text-slate-700 dark:text-slate-200",
|
|
13
13
|
className
|
|
14
14
|
]
|
|
15
15
|
.filter(Boolean)
|
|
16
16
|
.join(" ")}
|
|
17
|
+
{...rest}
|
|
17
18
|
>
|
|
18
19
|
{children}
|
|
19
20
|
{required && <span className="ml-0.5 text-red-500">*</span>}
|
|
20
|
-
</
|
|
21
|
+
</label>
|
|
21
22
|
);
|
|
22
23
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
3
3
|
|
|
4
4
|
function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
|
5
5
|
return <PopoverPrimitive.Root {...props} />;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
const SIZE_CLASSES = {
|
|
4
4
|
xs: "h-3 w-3",
|
|
@@ -6,21 +6,23 @@ const SIZE_CLASSES = {
|
|
|
6
6
|
md: "h-5 w-5",
|
|
7
7
|
lg: "h-6 w-6",
|
|
8
8
|
xl: "h-8 w-8",
|
|
9
|
-
};
|
|
9
|
+
} as const;
|
|
10
10
|
|
|
11
11
|
const TONE_CLASSES = {
|
|
12
12
|
brand: "text-brand-500",
|
|
13
13
|
white: "text-white",
|
|
14
14
|
muted: "text-slate-400 dark:text-slate-500",
|
|
15
15
|
current: "text-current",
|
|
16
|
-
};
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export interface SpinnerProps extends React.SVGAttributes<SVGSVGElement> {
|
|
19
|
+
size?: keyof typeof SIZE_CLASSES;
|
|
20
|
+
tone?: keyof typeof TONE_CLASSES;
|
|
21
|
+
label?: string;
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Animated spinner.
|
|
20
|
-
*
|
|
21
|
-
* @param {"xs"|"sm"|"md"|"lg"|"xl"} size
|
|
22
|
-
* @param {"brand"|"white"|"muted"|"current"} tone
|
|
23
|
-
* @param {string} label — screen-reader label
|
|
24
26
|
*/
|
|
25
27
|
export default function Spinner({
|
|
26
28
|
size = "md",
|
|
@@ -28,14 +30,14 @@ export default function Spinner({
|
|
|
28
30
|
label = "Loading",
|
|
29
31
|
className = "",
|
|
30
32
|
...rest
|
|
31
|
-
}) {
|
|
33
|
+
}: SpinnerProps) {
|
|
32
34
|
return (
|
|
33
35
|
<svg
|
|
34
36
|
{...rest}
|
|
35
37
|
className={[
|
|
36
38
|
"animate-spin",
|
|
37
|
-
SIZE_CLASSES[size]
|
|
38
|
-
TONE_CLASSES[tone]
|
|
39
|
+
SIZE_CLASSES[size],
|
|
40
|
+
TONE_CLASSES[tone],
|
|
39
41
|
className,
|
|
40
42
|
]
|
|
41
43
|
.filter(Boolean)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
const VARIANT_CLASSES = {
|
|
4
4
|
primary:
|
|
@@ -10,36 +10,39 @@ const VARIANT_CLASSES = {
|
|
|
10
10
|
outline:
|
|
11
11
|
"bg-transparent text-slate-900 hover:bg-slate-50 dark:text-slate-50 dark:hover:bg-slate-900 border-slate-200 dark:border-slate-800",
|
|
12
12
|
ghost:
|
|
13
|
-
"bg-transparent text-slate-900 hover:bg-slate-100 dark:text-slate-50 dark:hover:bg-slate-900 border-transparent"
|
|
14
|
-
|
|
13
|
+
"bg-transparent text-slate-900 hover:bg-slate-100 dark:text-slate-50 dark:hover:bg-slate-900 border-transparent",
|
|
14
|
+
link: "text-brand-600 underline-offset-4 hover:underline dark:text-brand-400 border-transparent"
|
|
15
|
+
} as const;
|
|
15
16
|
|
|
16
17
|
const SIZE_CLASSES = {
|
|
17
18
|
sm: "h-8 px-3 text-sm",
|
|
18
19
|
md: "h-10 px-4 text-sm",
|
|
19
20
|
lg: "h-12 px-5 text-base",
|
|
20
21
|
icon: "h-10 w-10 p-0"
|
|
21
|
-
};
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export interface UIButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
25
|
+
variant?: keyof typeof VARIANT_CLASSES;
|
|
26
|
+
size?: keyof typeof SIZE_CLASSES;
|
|
27
|
+
fullWidth?: boolean;
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
export default function UIButton({
|
|
24
31
|
variant = "primary",
|
|
25
32
|
size = "md",
|
|
26
33
|
fullWidth = false,
|
|
27
34
|
disabled = false,
|
|
28
|
-
onClick = () => {},
|
|
29
|
-
children,
|
|
30
|
-
style = undefined,
|
|
31
35
|
className = "",
|
|
36
|
+
children,
|
|
32
37
|
...rest
|
|
33
|
-
}) {
|
|
34
|
-
const variantClass = VARIANT_CLASSES[variant]
|
|
35
|
-
const sizeClass = SIZE_CLASSES[size]
|
|
38
|
+
}: UIButtonProps) {
|
|
39
|
+
const variantClass = VARIANT_CLASSES[variant];
|
|
40
|
+
const sizeClass = SIZE_CLASSES[size];
|
|
36
41
|
|
|
37
42
|
return (
|
|
38
43
|
<button
|
|
39
44
|
type="button"
|
|
40
|
-
onClick={onClick}
|
|
41
45
|
disabled={disabled}
|
|
42
|
-
style={style}
|
|
43
46
|
className={[
|
|
44
47
|
"inline-flex items-center justify-center gap-2 rounded-lg border font-medium shadow-sm transition",
|
|
45
48
|
"focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950",
|
|
@@ -57,5 +60,3 @@ export default function UIButton({
|
|
|
57
60
|
</button>
|
|
58
61
|
);
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface UIInputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
4
|
+
|
|
5
|
+
export default function UIInput({ className = "", ...rest }: UIInputProps) {
|
|
4
6
|
return (
|
|
5
7
|
<input
|
|
6
|
-
style={style}
|
|
7
8
|
className={[
|
|
8
9
|
"h-10 w-full rounded-lg border border-slate-200 bg-white px-3 text-sm text-slate-900 shadow-sm",
|
|
9
10
|
"placeholder:text-slate-400",
|
|
10
11
|
"focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-slate-950",
|
|
11
12
|
"dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 dark:placeholder:text-slate-500",
|
|
13
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
12
14
|
className
|
|
13
15
|
]
|
|
14
16
|
.filter(Boolean)
|
|
@@ -17,5 +19,3 @@ export default function UIInput({ style = undefined, className = "", ...rest })
|
|
|
17
19
|
/>
|
|
18
20
|
);
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
const SIZE_MAP = {
|
|
4
|
-
xs: "h-6 w-6 text-[9px]",
|
|
5
|
-
sm: "h-8 w-8 text-[10px]",
|
|
6
|
-
md: "h-9 w-9 text-xs",
|
|
7
|
-
lg: "h-11 w-11 text-sm",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const TONE_MAP = {
|
|
11
|
-
slate: "bg-slate-800 text-white",
|
|
12
|
-
brand: "bg-brand-500 text-white",
|
|
13
|
-
neutral: "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default function Avatar({ src, name, initials, icon, size = "sm", tone = "slate", className = "", ...rest }) {
|
|
17
|
-
const sizeClass = SIZE_MAP[size] ?? SIZE_MAP.sm;
|
|
18
|
-
|
|
19
|
-
if (src) {
|
|
20
|
-
return (
|
|
21
|
-
<img
|
|
22
|
-
src={src}
|
|
23
|
-
alt={name ?? ""}
|
|
24
|
-
className={`${sizeClass} shrink-0 rounded-full border border-slate-200 object-cover dark:border-slate-800 ${className}`}
|
|
25
|
-
{...rest}
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (React.isValidElement(icon)) {
|
|
31
|
-
return (
|
|
32
|
-
<div className={`${sizeClass} ${TONE_MAP[tone] ?? TONE_MAP.slate} flex shrink-0 items-center justify-center rounded-full ${className}`} {...rest}>
|
|
33
|
-
{icon}
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const label = initials ?? (name ? name.split(" ").map(w => w[0]).join("").slice(0, 2).toUpperCase() : "?");
|
|
39
|
-
return (
|
|
40
|
-
<div className={`${sizeClass} ${TONE_MAP[tone] ?? TONE_MAP.slate} flex shrink-0 items-center justify-center rounded-full font-bold ${className}`} {...rest}>
|
|
41
|
-
{label}
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
}
|