@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.
Files changed (46) hide show
  1. package/.a4drules/features/command-center-dashboard-rule.md +1 -1
  2. package/.a4drules/skills/command-center-project/SKILL.md +4 -4
  3. package/.a4drules/skills/component-library/SKILL.md +4 -1
  4. package/dist/components/library/ui/Avatar.d.ts +22 -10
  5. package/dist/components/library/ui/Avatar.js +40 -17
  6. package/dist/components/library/ui/Avatar.js.map +1 -1
  7. package/dist/components/library/ui/Card.d.ts +23 -37
  8. package/dist/components/library/ui/EmptyState.d.ts +34 -7
  9. package/dist/components/library/ui/EmptyState.js +5 -6
  10. package/dist/components/library/ui/EmptyState.js.map +1 -1
  11. package/dist/components/library/ui/Spinner.d.ts +21 -5
  12. package/dist/components/library/ui/UIButton.d.ts +22 -11
  13. package/dist/components/library/ui/UIButton.js +23 -28
  14. package/dist/components/library/ui/UIButton.js.map +1 -1
  15. package/dist/components/library/ui/UIInput.d.ts +4 -5
  16. package/dist/components/library/ui/UIInput.js +5 -6
  17. package/dist/components/library/ui/UIInput.js.map +1 -1
  18. package/dist/components/library/ui/alert.d.ts +23 -21
  19. package/dist/components/library/ui/alert.js +13 -14
  20. package/dist/components/library/ui/alert.js.map +1 -1
  21. package/dist/components/library/ui/card.js +24 -26
  22. package/dist/components/library/ui/card.js.map +1 -1
  23. package/dist/components/library/ui/checkbox.d.ts +4 -4
  24. package/dist/components/library/ui/checkbox.js +4 -5
  25. package/dist/components/library/ui/checkbox.js.map +1 -1
  26. package/dist/components/library/ui/dialog.d.ts +1 -1
  27. package/dist/components/library/ui/label.d.ts +5 -7
  28. package/dist/components/library/ui/label.js +9 -10
  29. package/dist/components/library/ui/label.js.map +1 -1
  30. package/dist/components/library/ui/popover.d.ts +1 -1
  31. package/dist/components/library/ui/spinner.js +16 -17
  32. package/dist/components/library/ui/spinner.js.map +1 -1
  33. package/package.json +7 -3
  34. package/scripts/convert-to-typescript.sh +52 -0
  35. package/src/components/library/ui/{Alert.jsx → Alert.tsx} +17 -7
  36. package/src/components/library/ui/Avatar.tsx +68 -0
  37. package/src/components/library/ui/{Card.jsx → Card.tsx} +24 -10
  38. package/src/components/library/ui/{Checkbox.jsx → Checkbox.tsx} +4 -2
  39. package/src/components/library/ui/Dialog.tsx +1 -1
  40. package/src/components/library/ui/{EmptyState.jsx → EmptyState.tsx} +13 -10
  41. package/src/components/library/ui/{Label.jsx → Label.tsx} +9 -8
  42. package/src/components/library/ui/Popover.tsx +1 -1
  43. package/src/components/library/ui/{Spinner.jsx → Spinner.tsx} +12 -10
  44. package/src/components/library/ui/{UIButton.jsx → UIButton.tsx} +15 -14
  45. package/src/components/library/ui/{UIInput.jsx → UIInput.tsx} +5 -5
  46. 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 default function UICard({ children, padding = "p-5", style, className = "", ...rest }) {
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 function Card({ className = "", children, ...rest }) {
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 function CardHeader({ className = "", children, ...rest }) {
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 function CardTitle({ className = "", children, ...rest }) {
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 function CardDescription({ className = "", children, ...rest }) {
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 function CardContent({ className = "", children, ...rest }) {
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 function CardFooter({ className = "", children, ...rest }) {
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 default function Checkbox({ className = "", ...rest }) {
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 { Dialog as DialogPrimitive } from "radix-ui";
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] ?? SIZES.md;
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 default function Label({ children, htmlFor, required, className = "", ...rest }) {
4
- // If no htmlFor provided, render as div for compatibility
5
- const Tag = htmlFor ? 'label' : 'div';
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
- <Tag
10
- {...props}
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
- </Tag>
21
+ </label>
21
22
  );
22
23
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { Popover as PopoverPrimitive } from "radix-ui";
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] ?? SIZE_CLASSES.md,
38
- TONE_CLASSES[tone] ?? TONE_CLASSES.brand,
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] ?? VARIANT_CLASSES.primary;
35
- const sizeClass = SIZE_CLASSES[size] ?? SIZE_CLASSES.md;
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 default function UIInput({ style = undefined, className = "", ...rest }) {
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
- }