@peerbots/core 0.2.6 → 1.0.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 (121) hide show
  1. package/.changeset/config.json +1 -1
  2. package/.github/workflows/storybook.yml +9 -1
  3. package/CHANGELOG.md +94 -0
  4. package/dist/index.d.mts +643 -131
  5. package/dist/index.d.ts +643 -131
  6. package/dist/index.js +7 -6
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +7 -6
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +6 -3
  11. package/src/charts/DistributionBarChart.tsx +10 -4
  12. package/src/charts/DistributionHistogram.tsx +13 -5
  13. package/src/charts/LineGraph.stories.tsx +60 -0
  14. package/src/charts/LineGraph.tsx +216 -0
  15. package/src/charts/index.ts +2 -0
  16. package/src/docs/DesignSystem.mdx +241 -25
  17. package/src/styles/theme.css +8 -0
  18. package/src/ui/feedback/Alert.stories.tsx +56 -0
  19. package/src/ui/feedback/Alert.tsx +168 -0
  20. package/src/ui/{Dialog.stories.tsx → feedback/Dialog.stories.tsx} +2 -2
  21. package/src/ui/{Dialog.tsx → feedback/Dialog.tsx} +2 -2
  22. package/src/ui/feedback/EmptyState.stories.tsx +57 -0
  23. package/src/ui/feedback/EmptyState.tsx +123 -0
  24. package/src/ui/feedback/Popover.stories.tsx +130 -0
  25. package/src/ui/feedback/Popover.tsx +135 -0
  26. package/src/ui/feedback/Skeleton.stories.tsx +88 -0
  27. package/src/ui/{Skeleton.tsx → feedback/Skeleton.tsx} +2 -2
  28. package/src/ui/feedback/Spinner.stories.tsx +77 -0
  29. package/src/ui/feedback/Spinner.tsx +62 -0
  30. package/src/ui/{Tooltip.stories.tsx → feedback/Tooltip.stories.tsx} +3 -3
  31. package/src/ui/{Tooltip.tsx → feedback/Tooltip.tsx} +2 -2
  32. package/src/ui/feedback/index.ts +7 -0
  33. package/src/ui/forms/Button.stories.tsx +181 -0
  34. package/src/ui/forms/Button.tsx +323 -0
  35. package/src/ui/{Checkbox.stories.tsx → forms/Checkbox.stories.tsx} +2 -2
  36. package/src/ui/{Checkbox.tsx → forms/Checkbox.tsx} +2 -2
  37. package/src/ui/forms/ChipGroup.stories.tsx +118 -0
  38. package/src/ui/forms/ChipGroup.tsx +239 -0
  39. package/src/ui/{Dropdown.tsx → forms/Dropdown.tsx} +1 -1
  40. package/src/ui/{Field.stories.tsx → forms/Field.stories.tsx} +3 -3
  41. package/src/ui/{Field.tsx → forms/Field.tsx} +1 -1
  42. package/src/ui/{Input.stories.tsx → forms/Input.stories.tsx} +6 -6
  43. package/src/ui/{Input.tsx → forms/Input.tsx} +1 -1
  44. package/src/ui/{Label.stories.tsx → forms/Label.stories.tsx} +2 -2
  45. package/src/ui/{Label.tsx → forms/Label.tsx} +2 -2
  46. package/src/ui/{NumberField.stories.tsx → forms/NumberField.stories.tsx} +2 -2
  47. package/src/ui/{NumberField.tsx → forms/NumberField.tsx} +8 -9
  48. package/src/ui/forms/PeerbotsColorSelector.stories.tsx +49 -0
  49. package/src/ui/forms/PeerbotsColorSelector.tsx +100 -0
  50. package/src/ui/{Select.stories.tsx → forms/Select.stories.tsx} +2 -2
  51. package/src/ui/{Select.tsx → forms/Select.tsx} +5 -3
  52. package/src/ui/{Slider.stories.tsx → forms/Slider.stories.tsx} +6 -4
  53. package/src/ui/{Slider.tsx → forms/Slider.tsx} +7 -3
  54. package/src/ui/forms/SliderWithNumberField.stories.tsx +136 -0
  55. package/src/ui/{SliderWithNumberField.tsx → forms/SliderWithNumberField.tsx} +19 -7
  56. package/src/ui/{Switch.stories.tsx → forms/Switch.stories.tsx} +2 -2
  57. package/src/ui/{Switch.tsx → forms/Switch.tsx} +1 -1
  58. package/src/ui/{TabRadio.stories.tsx → forms/TabRadio.stories.tsx} +3 -3
  59. package/src/ui/{TabRadio.tsx → forms/TabRadio.tsx} +1 -1
  60. package/src/ui/{TextArea.stories.tsx → forms/TextArea.stories.tsx} +5 -3
  61. package/src/ui/{TextArea.tsx → forms/TextArea.tsx} +1 -1
  62. package/src/ui/forms/index.ts +15 -0
  63. package/src/ui/foundations/Anchor.stories.tsx +39 -0
  64. package/src/ui/foundations/Anchor.tsx +96 -0
  65. package/src/ui/foundations/CodeBlock.stories.tsx +86 -0
  66. package/src/ui/foundations/CodeBlock.tsx +148 -0
  67. package/src/ui/foundations/Colors.stories.tsx +80 -0
  68. package/src/ui/{Icon.stories.tsx → foundations/Icon.stories.tsx} +1 -1
  69. package/src/ui/{Icon.tsx → foundations/Icon.tsx} +1 -1
  70. package/src/ui/{IconRegistry.tsx → foundations/IconRegistry.tsx} +261 -57
  71. package/src/ui/foundations/SocialLinks.stories.tsx +38 -0
  72. package/src/ui/foundations/SocialLinks.tsx +117 -0
  73. package/src/ui/foundations/Typography.stories.tsx +58 -0
  74. package/src/ui/foundations/Typography.tsx +163 -0
  75. package/src/ui/foundations/index.ts +6 -0
  76. package/src/ui/index.ts +7 -27
  77. package/src/ui/layout/Accordion.stories.tsx +86 -0
  78. package/src/ui/layout/Accordion.tsx +205 -0
  79. package/src/ui/layout/Card.stories.tsx +151 -0
  80. package/src/ui/layout/Card.tsx +124 -0
  81. package/src/ui/layout/Collapsible.stories.tsx +54 -0
  82. package/src/ui/layout/Collapsible.tsx +54 -0
  83. package/src/ui/{Separator.stories.tsx → layout/Separator.stories.tsx} +2 -2
  84. package/src/ui/{Separator.tsx → layout/Separator.tsx} +1 -1
  85. package/src/ui/{SettingsPanel.stories.tsx → layout/SettingsPanel.stories.tsx} +5 -5
  86. package/src/ui/{SettingsPanel.tsx → layout/SettingsPanel.tsx} +33 -29
  87. package/src/ui/layout/index.ts +5 -0
  88. package/src/ui/media/Avatar.stories.tsx +89 -0
  89. package/src/ui/media/Avatar.tsx +122 -0
  90. package/src/ui/media/Dropzone.stories.tsx +95 -0
  91. package/src/ui/media/Dropzone.tsx +328 -0
  92. package/src/ui/media/FileUpload.stories.tsx +62 -0
  93. package/src/ui/media/FileUpload.tsx +175 -0
  94. package/src/ui/media/index.ts +3 -0
  95. package/src/ui/{AuthFormUI.stories.tsx → patterns/AuthFormUI.stories.tsx} +1 -1
  96. package/src/ui/{AuthFormUI.tsx → patterns/AuthFormUI.tsx} +24 -23
  97. package/src/ui/patterns/CopyButton.stories.tsx +77 -0
  98. package/src/ui/patterns/CopyButton.tsx +93 -0
  99. package/src/ui/patterns/SearchInput.stories.tsx +51 -0
  100. package/src/ui/patterns/SearchInput.tsx +93 -0
  101. package/src/ui/patterns/Stepper.stories.tsx +129 -0
  102. package/src/ui/patterns/Stepper.tsx +295 -0
  103. package/src/ui/{TabSelection.stories.tsx → patterns/TabSelection.stories.tsx} +1 -1
  104. package/src/ui/{TabSelection.tsx → patterns/TabSelection.tsx} +2 -2
  105. package/src/ui/patterns/index.ts +6 -0
  106. package/src/ui/patterns/useClipboard.ts +75 -0
  107. package/src/ui/types.ts +48 -0
  108. package/src/ui/Alert.stories.tsx +0 -41
  109. package/src/ui/Alert.tsx +0 -72
  110. package/src/ui/Anchor.stories.tsx +0 -25
  111. package/src/ui/Anchor.tsx +0 -32
  112. package/src/ui/Button.stories.tsx +0 -108
  113. package/src/ui/Button.tsx +0 -121
  114. package/src/ui/Collapsible.stories.tsx +0 -91
  115. package/src/ui/Collapsible.tsx +0 -52
  116. package/src/ui/Colors.stories.tsx +0 -67
  117. package/src/ui/Popover.tsx +0 -42
  118. package/src/ui/Skeleton.stories.tsx +0 -43
  119. package/src/ui/SliderWithNumberField.stories.tsx +0 -101
  120. package/src/ui/Typography.stories.tsx +0 -87
  121. package/src/ui/Typography.tsx +0 -80
@@ -0,0 +1,151 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Card } from "./Card";
3
+ import { Heading, Text } from "../foundations/Typography";
4
+ import { Button } from "../forms/Button";
5
+ import { Icon } from "../foundations/Icon";
6
+ import React from "react";
7
+
8
+ const meta: Meta<typeof Card> = {
9
+ title: "Layout/Card",
10
+ component: Card,
11
+ parameters: {
12
+ layout: "centered",
13
+ },
14
+ };
15
+
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof Card>;
19
+
20
+ export const Default: Story = {
21
+ args: {
22
+ variant: "default",
23
+ padding: "md",
24
+ hoverable: true,
25
+ children: (
26
+ <div className="pb:space-y-3 pb:w-80">
27
+ <Heading level={3}>Card Title</Heading>
28
+ <Text variant="muted">This is a standard card with elevation and rounded borders.</Text>
29
+ <Button variant="primary" size="sm">Action</Button>
30
+ </div>
31
+ ),
32
+ },
33
+ };
34
+
35
+ export const GlassInContext: Story = {
36
+ render: () => (
37
+ <div className="pb:flex pb:flex-col pb:gap-8 pb:max-w-4xl pb:p-6">
38
+ {/* 3D Canvas Viewport HUD Context */}
39
+ <div>
40
+ <h4 className="pb:text-xs pb:font-bold pb:text-gray-700 pb:uppercase pb:tracking-wider pb:mb-2">
41
+ Recommended Use: Floating Viewport HUD / Canvas Overlays
42
+ </h4>
43
+ <div className="pb:relative pb:w-full pb:h-80 pb:rounded-3xl pb:overflow-hidden pb:bg-gradient-to-tr pb:from-slate-900 pb:via-slate-800 pb:to-teal-950 pb:border pb:border-slate-700 pb:p-6 pb:flex pb:items-center pb:justify-center">
44
+ {/* Simulated 3D Model Center Graphic */}
45
+ <div className="pb:flex pb:flex-col pb:items-center pb:justify-center pb:text-center pb:opacity-80">
46
+ <div className="pb:w-28 pb:h-28 pb:rounded-2xl pb:bg-peerbots-teal/20 pb:border-2 pb:border-peerbots-teal/40 pb:flex pb:items-center pb:justify-center pb:shadow-lg pb:shadow-peerbots-teal/10 pb:animate-pulse">
47
+ <Icon name="face" className="pb:w-16 pb:h-16 pb:text-peerbots-teal" />
48
+ </div>
49
+ <span className="pb:text-xs pb:font-mono pb:text-teal-200 pb:mt-3">
50
+ WebGL 3D Robot Face Stage
51
+ </span>
52
+ </div>
53
+
54
+ {/* Floating HUD Camera Toolbar (Glass) */}
55
+ <div className="pb:absolute pb:left-6 pb:top-1/2 pb:-translate-y-1/2">
56
+ <Card variant="glass" padding="none" className="pb:p-1.5 pb:flex pb:flex-col pb:gap-2 pb:shadow-2xl">
57
+ <button aria-label="Zoom in" className="pb:w-9 pb:h-9 pb:flex pb:items-center pb:justify-center pb:rounded-xl pb:hover:bg-black/5 pb:text-gray-900 pb:font-bold pb:text-lg">
58
+ +
59
+ </button>
60
+ <div className="pb:w-full pb:h-px pb:bg-black/10" />
61
+ <button aria-label="Zoom out" className="pb:w-9 pb:h-9 pb:flex pb:items-center pb:justify-center pb:rounded-xl pb:hover:bg-black/5 pb:text-gray-900 pb:font-bold pb:text-lg">
62
+ -
63
+ </button>
64
+ <div className="pb:w-full pb:h-px pb:bg-black/10" />
65
+ <button aria-label="Reset camera" className="pb:w-9 pb:h-9 pb:flex pb:items-center pb:justify-center pb:rounded-xl pb:hover:bg-black/5 pb:text-gray-900">
66
+ <Icon name="arrowPath" className="pb:w-4 pb:h-4" />
67
+ </button>
68
+ </Card>
69
+ </div>
70
+
71
+ {/* Floating Parameter Overlay (Glass) */}
72
+ <div className="pb:absolute pb:right-6 pb:bottom-6 pb:max-w-xs">
73
+ <Card variant="glass" padding="sm" className="pb:shadow-2xl pb:space-y-2">
74
+ <div className="pb:flex pb:items-center pb:justify-between">
75
+ <span className="pb:text-xs pb:font-bold pb:text-gray-900">Head Rotation</span>
76
+ <span className="pb:text-[10px] pb:font-mono pb:text-teal-950 pb:bg-peerbots-teal/30 pb:px-1.5 pb:py-0.5 pb:rounded pb:font-bold">
77
+ Yaw: 14°
78
+ </span>
79
+ </div>
80
+ <Text variant="small" className="pb:text-[11px] pb:text-gray-700 pb:block">
81
+ Glass surfaces maintain ambient visual depth without occluding the rendered model.
82
+ </Text>
83
+ </Card>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ {/* When NOT to use: Comparison */}
89
+ <div>
90
+ <h4 className="pb:text-xs pb:font-bold pb:text-gray-700 pb:uppercase pb:tracking-wider pb:mb-2">
91
+ Standard Content vs Glass Comparison
92
+ </h4>
93
+ <div className="pb:grid pb:grid-cols-2 pb:gap-6 pb:p-6 pb:bg-gray-100 pb:rounded-2xl">
94
+ <Card variant="elevated" padding="md">
95
+ <span className="pb:text-[10px] pb:font-bold pb:uppercase pb:tracking-wider pb:text-gray-700">
96
+ Standard Opaque Surface (Recommended for forms & text)
97
+ </span>
98
+ <Heading level={4} className="pb:mt-1">Account & Settings</Heading>
99
+ <Text variant="muted" className="pb:text-xs pb:mt-1">
100
+ Solid white opaque cards ensure maximum readability, high contrast, and crisp text rendering on light backgrounds.
101
+ </Text>
102
+ </Card>
103
+ <Card variant="glass" padding="md">
104
+ <span className="pb:text-[10px] pb:font-bold pb:uppercase pb:tracking-wider pb:text-peerbots-darkteal">
105
+ Glass Surface (For visual layering & HUDs)
106
+ </span>
107
+ <Heading level={4} className="pb:mt-1">Floating Modal Panel</Heading>
108
+ <Text variant="muted" className="pb:text-xs pb:mt-1">
109
+ Translucent backdrop with subtle border blur creates a tactile glass feel over rich colored backgrounds.
110
+ </Text>
111
+ </Card>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ ),
116
+ };
117
+
118
+ export const Variants: Story = {
119
+ render: () => (
120
+ <div className="pb:grid pb:grid-cols-2 pb:gap-6 pb:max-w-3xl pb:p-6 pb:bg-gray-100 pb:rounded-2xl">
121
+ <Card variant="default">
122
+ <Heading level={4}>Default</Heading>
123
+ <Text variant="muted">Default subtle shadow</Text>
124
+ </Card>
125
+ <Card variant="elevated">
126
+ <Heading level={4}>Elevated</Heading>
127
+ <Text variant="muted">Higher elevation shadow</Text>
128
+ </Card>
129
+ <Card variant="glass">
130
+ <Heading level={4}>Glass</Heading>
131
+ <Text variant="muted">Backdrop blur surface</Text>
132
+ </Card>
133
+ <Card variant="flat">
134
+ <Heading level={4}>Flat</Heading>
135
+ <Text variant="muted">Subtle background fill</Text>
136
+ </Card>
137
+ <Card variant="outline">
138
+ <Heading level={4}>Outline</Heading>
139
+ <Text variant="muted">Clean 2px border</Text>
140
+ </Card>
141
+ <Card variant="teal-tint">
142
+ <Heading level={4} variant="marketing-teal">Teal Tint</Heading>
143
+ <Text variant="muted">Brand teal tinted background</Text>
144
+ </Card>
145
+ <Card variant="pink-tint">
146
+ <Heading level={4} variant="marketing-pink">Pink Tint</Heading>
147
+ <Text variant="muted">Brand pink tinted background</Text>
148
+ </Card>
149
+ </div>
150
+ ),
151
+ };
@@ -0,0 +1,124 @@
1
+ import React from "react";
2
+ import { cn } from "../utils";
3
+ import { ComponentSize, ComponentRadius, SurfaceVariant } from "../types";
4
+
5
+ export interface CardProps extends React.HTMLAttributes<HTMLElement> {
6
+ children: React.ReactNode;
7
+ /**
8
+ * Surface treatment / fill style.
9
+ * @default "surface"
10
+ */
11
+ variant?: SurfaceVariant | "default" | "elevated";
12
+ /**
13
+ * Background color tinting.
14
+ * @default "default"
15
+ */
16
+ color?: "default" | "teal" | "pink";
17
+ /**
18
+ * Card padding scale.
19
+ * @default "md"
20
+ */
21
+ padding?: "none" | ComponentSize;
22
+ /**
23
+ * Border radius scale.
24
+ * @default "2xl"
25
+ */
26
+ radius?: ComponentRadius;
27
+ /** Whether the card shows interactive hover elevation */
28
+ hoverable?: boolean;
29
+ id?: string;
30
+ as?: React.ElementType;
31
+ href?: string;
32
+ target?: string;
33
+ rel?: string;
34
+ }
35
+
36
+ const variantStyles: Record<string, string> = {
37
+ surface: "pb:bg-white pb:border pb:border-gray-200 pb:shadow-md",
38
+ elevated: "pb:bg-white pb:border pb:border-gray-200 pb:shadow-md",
39
+ default: "pb:bg-white pb:border pb:border-gray-200 pb:shadow-xs",
40
+ glass:
41
+ "pb:bg-white/90 pb:backdrop-blur-xl pb:border pb:border-white/40 pb:shadow-xl",
42
+ flat: "pb:bg-gray-50/80 pb:border pb:border-gray-200",
43
+ outline: "pb:bg-transparent pb:border-2 pb:border-gray-300",
44
+ };
45
+
46
+ const tintStyles: Record<string, string> = {
47
+ default: "",
48
+ teal: "pb:bg-peerbots-teal/5 pb:border pb:border-peerbots-darkteal/30",
49
+ pink: "pb:bg-peerbots-pink/5 pb:border pb:border-rose-300",
50
+ };
51
+
52
+ const radiusClasses: Record<ComponentRadius, string> = {
53
+ none: "pb:rounded-none",
54
+ sm: "pb:rounded-md",
55
+ md: "pb:rounded-xl",
56
+ lg: "pb:rounded-2xl",
57
+ "2xl": "pb:rounded-2xl",
58
+ pill: "pb:rounded-full",
59
+ };
60
+
61
+ const paddingClasses: Record<string, string> = {
62
+ none: "",
63
+ xs: "pb:p-2.5",
64
+ sm: "pb:p-4",
65
+ md: "pb:p-6 sm:pb:p-8",
66
+ lg: "pb:p-8 sm:pb:p-10 md:pb:p-12",
67
+ xl: "pb:p-10 sm:pb:p-12 md:pb:p-16",
68
+ };
69
+
70
+ export const Card = React.forwardRef<HTMLElement, CardProps>(
71
+ (
72
+ {
73
+ children,
74
+ className,
75
+ variant = "surface",
76
+ color = "default",
77
+ padding = "md",
78
+ radius = "2xl",
79
+ hoverable = true,
80
+ id,
81
+ as: Component = "div",
82
+ href,
83
+ target,
84
+ rel,
85
+ ...rest
86
+ },
87
+ ref,
88
+ ) => {
89
+ const baseStyles = "pb:overflow-hidden pb:transition-all pb:duration-300";
90
+
91
+ const hoverStyles = hoverable
92
+ ? "pb:hover:shadow-lg pb:hover:-translate-y-0.5 pb:hover:border-peerbots-teal/30 pb:focus-within:ring-2 pb:focus-within:ring-peerbots-teal"
93
+ : "";
94
+
95
+ const isLink = Boolean(href) || Component === "a";
96
+ const TargetTag = href ? "a" : Component;
97
+
98
+ return (
99
+ <TargetTag
100
+ id={id}
101
+ href={href}
102
+ target={target}
103
+ rel={rel}
104
+ ref={ref as any}
105
+ className={cn(
106
+ baseStyles,
107
+ radiusClasses[radius] || radiusClasses["2xl"],
108
+ color === "default"
109
+ ? variantStyles[variant] || variantStyles.surface
110
+ : tintStyles[color],
111
+ hoverStyles,
112
+ paddingClasses[padding] || paddingClasses.md,
113
+ isLink && "pb:cursor-pointer pb:block",
114
+ className,
115
+ )}
116
+ {...(rest as any)}
117
+ >
118
+ {children}
119
+ </TargetTag>
120
+ );
121
+ },
122
+ );
123
+
124
+ Card.displayName = "Card";
@@ -0,0 +1,54 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Collapsible } from "./Collapsible";
3
+ import { Heading } from "../foundations/Typography";
4
+ import { Input } from "../forms/Input";
5
+ import { Button } from "../forms/Button";
6
+ import React from "react";
7
+
8
+ const meta: Meta<typeof Collapsible> = {
9
+ title: "Layout/Collapsible",
10
+ component: Collapsible,
11
+ tags: ["autodocs"],
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof Collapsible>;
16
+
17
+ export const Default: Story = {
18
+ args: {
19
+ title: "Click to expand",
20
+ children: (
21
+ <div className="pb:p-4 pb:bg-gray-50 pb:rounded-md">
22
+ This is the collapsible content. It can contain any elements.
23
+ </div>
24
+ ),
25
+ },
26
+ };
27
+
28
+ export const Variations: Story = {
29
+ render: () => (
30
+ <div className="pb:flex pb:flex-col pb:gap-8 pb:p-4 pb:max-w-xl">
31
+ <div className="pb:space-y-4">
32
+ <Heading level={4} className="pb:text-sm pb:font-medium pb:text-black pb:uppercase">
33
+ States & Styling
34
+ </Heading>
35
+ <div className="pb:space-y-4">
36
+ <Collapsible title="Initially Open" defaultOpen={true}>
37
+ <div className="pb:p-4 pb:bg-gray-50 pb:rounded-md pb:space-y-2">
38
+ <p>This content is visible by default.</p>
39
+ <Input placeholder="Settings value" />
40
+ </div>
41
+ </Collapsible>
42
+ <Collapsible
43
+ title={<span className="pb:font-bold pb:text-peerbots-darkteal">Styled Title Section</span>}
44
+ >
45
+ <div className="pb:p-4 pb:space-y-3">
46
+ <p>Custom styled title disclosure.</p>
47
+ <Button variant="primary" size="sm">Action</Button>
48
+ </div>
49
+ </Collapsible>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ ),
54
+ };
@@ -0,0 +1,54 @@
1
+ import React, { useState } from "react";
2
+ import { Accordion } from "./Accordion";
3
+ import { ButtonProps } from "../forms/Button";
4
+
5
+ export interface CollapsibleProps {
6
+ title: React.ReactNode;
7
+ children: React.ReactNode;
8
+ /** The variant of the trigger or panel */
9
+ variant?: ButtonProps["variant"] | "default" | "bordered" | "flat";
10
+ /** The size of the trigger button */
11
+ size?: ButtonProps["size"];
12
+ defaultOpen?: boolean;
13
+ isOpen?: boolean;
14
+ onToggle?: (open: boolean) => void;
15
+ className?: string;
16
+ }
17
+
18
+ export function Collapsible({
19
+ title,
20
+ children,
21
+ variant = "default",
22
+ defaultOpen = false,
23
+ isOpen: controlledIsOpen,
24
+ onToggle,
25
+ className,
26
+ }: CollapsibleProps) {
27
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
28
+ const isOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalOpen;
29
+
30
+ const handleToggle = (nextOpen: boolean) => {
31
+ if (controlledIsOpen === undefined) {
32
+ setInternalOpen(nextOpen);
33
+ }
34
+ if (onToggle) {
35
+ onToggle(nextOpen);
36
+ }
37
+ };
38
+
39
+ const accordionVariant =
40
+ variant === "bordered" || variant === "flat" ? variant : "default";
41
+
42
+ return (
43
+ <Accordion
44
+ variant={accordionVariant}
45
+ title={title}
46
+ isOpen={isOpen}
47
+ onToggle={handleToggle}
48
+ defaultOpen={defaultOpen}
49
+ className={className}
50
+ >
51
+ {children}
52
+ </Accordion>
53
+ );
54
+ }
@@ -1,9 +1,9 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Separator } from "./Separator";
3
- import { Heading, Text } from "./Typography";
3
+ import { Heading, Text } from "../foundations/Typography";
4
4
 
5
5
  const meta: Meta<typeof Separator> = {
6
- title: "UI/Separator",
6
+ title: "Layout/Separator",
7
7
  component: Separator,
8
8
  tags: ["autodocs"],
9
9
  argTypes: {
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { Separator as BaseSeparator } from "@base-ui/react/separator";
3
- import { cn } from "./utils";
3
+ import { cn } from "../utils";
4
4
 
5
5
  export interface SeparatorProps extends React.ComponentPropsWithoutRef<
6
6
  typeof BaseSeparator
@@ -1,12 +1,12 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { SettingsPanel } from "./SettingsPanel";
3
- import { Button } from "./Button";
4
- import { Checkbox } from "./Checkbox";
5
- import { Heading, Text } from "./Typography";
6
- import { Label } from "./Label";
3
+ import { Button } from "../forms/Button";
4
+ import { Checkbox } from "../forms/Checkbox";
5
+ import { Heading, Text } from "../foundations/Typography";
6
+ import { Label } from "../forms/Label";
7
7
 
8
8
  const meta: Meta<typeof SettingsPanel> = {
9
- title: "UI/SettingsPanel",
9
+ title: "Layout/SettingsPanel",
10
10
  component: SettingsPanel,
11
11
  tags: ["autodocs"],
12
12
  argTypes: {},
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from "react";
2
- import { Heading } from "./Typography";
3
- import { cn } from "./utils";
2
+ import { Heading } from "../foundations/Typography";
3
+ import { cn } from "../utils";
4
+ import { Card } from "./Card";
4
5
  import { Collapsible } from "./Collapsible";
5
6
 
6
7
  export interface SettingsPanelProps {
@@ -22,7 +23,7 @@ export interface SettingsPanelProps {
22
23
 
23
24
  /**
24
25
  * A standardized panel for settings sections.
25
- * Provides a consistent look with white background, shadow, and rounded corners.
26
+ * Built on top of Card and Collapsible for design system consistency.
26
27
  */
27
28
  export function SettingsPanel({
28
29
  title,
@@ -34,48 +35,51 @@ export function SettingsPanel({
34
35
  headingLevel = 3,
35
36
  }: SettingsPanelProps) {
36
37
  const content = (
37
- <div className={cn("pb:flex pb:flex-col", contentClassName)}>{children}</div>
38
+ <div className={cn("pb:flex pb:flex-col pb:gap-3", contentClassName)}>
39
+ {children}
40
+ </div>
38
41
  );
39
42
 
40
- return (
41
- <div
42
- className={cn(
43
- "pb:m-2 pb:p-4 pb:bg-white pb:shadow-xl pb:rounded-lg pb:flex pb:flex-1 pb:flex-col",
44
- className,
45
- )}
46
- >
47
- {collapsible ? (
43
+ if (collapsible) {
44
+ return (
45
+ <div className={cn("pb:w-full pb:mb-3", className)}>
48
46
  <Collapsible
49
47
  title={
50
48
  typeof title === "string" ? (
51
- <Heading level={headingLevel} className="pb:text-gray-600">
49
+ <Heading level={headingLevel} className="pb:text-gray-800 pb:font-bold">
52
50
  {title}
53
51
  </Heading>
54
52
  ) : (
55
53
  title
56
54
  )
57
55
  }
58
- variant="ghost"
59
56
  defaultOpen={defaultOpen}
60
57
  >
61
58
  {content}
62
59
  </Collapsible>
63
- ) : (
64
- <>
65
- {title && (
66
- <div className="pb:flex pb:items-center pb:justify-between pb:mb-2 empty:hidden">
67
- {typeof title === "string" ? (
68
- <Heading level={headingLevel} className="pb:text-gray-600">
69
- {title}
70
- </Heading>
71
- ) : (
72
- title
73
- )}
74
- </div>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ return (
65
+ <Card
66
+ variant="elevated"
67
+ padding="sm"
68
+ hoverable={false}
69
+ className={cn("pb:w-full pb:mb-3 pb:flex pb:flex-col", className)}
70
+ >
71
+ {title && (
72
+ <div className="pb:flex pb:items-center pb:justify-between pb:mb-3 pb:pb-2 pb:border-b pb:border-gray-100 empty:hidden">
73
+ {typeof title === "string" ? (
74
+ <Heading level={headingLevel} className="pb:text-gray-800 pb:font-bold">
75
+ {title}
76
+ </Heading>
77
+ ) : (
78
+ title
75
79
  )}
76
- {content}
77
- </>
80
+ </div>
78
81
  )}
79
- </div>
82
+ {content}
83
+ </Card>
80
84
  );
81
85
  }
@@ -0,0 +1,5 @@
1
+ export * from "./Card";
2
+ export * from "./Accordion";
3
+ export * from "./Collapsible";
4
+ export * from "./Separator";
5
+ export * from "./SettingsPanel";
@@ -0,0 +1,89 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Avatar } from "./Avatar";
3
+ import { Heading } from "../foundations/Typography";
4
+
5
+ const meta: Meta<typeof Avatar> = {
6
+ title: "Media & Files/Avatar",
7
+ component: Avatar,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ size: {
14
+ control: "select",
15
+ options: ["xs", "sm", "md", "lg", "xl"],
16
+ },
17
+ shape: {
18
+ control: "select",
19
+ options: ["circle", "rounded"],
20
+ },
21
+ name: { control: "text" },
22
+ src: { control: "text" },
23
+ },
24
+ };
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof Avatar>;
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ name: "Ada Lovelace",
32
+ size: "md",
33
+ shape: "circle",
34
+ },
35
+ };
36
+
37
+ export const Variations: Story = {
38
+ render: () => (
39
+ <div className="pb:flex pb:flex-col pb:gap-6 pb:p-4">
40
+ <div className="pb:space-y-3">
41
+ <Heading level={4} className="pb:text-sm pb:font-medium pb:text-black">
42
+ Sizes with Image
43
+ </Heading>
44
+ <div className="pb:flex pb:flex-wrap pb:gap-3 pb:items-end">
45
+ <Avatar size="xs" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=120&q=80" />
46
+ <Avatar size="sm" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=120&q=80" />
47
+ <Avatar size="md" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=120&q=80" />
48
+ <Avatar size="lg" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=120&q=80" />
49
+ <Avatar size="xl" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=120&q=80" />
50
+ </div>
51
+ </div>
52
+
53
+ <div className="pb:space-y-3">
54
+ <Heading level={4} className="pb:text-sm pb:font-medium pb:text-black">
55
+ Initials Fallback (Dynamic Colors)
56
+ </Heading>
57
+ <div className="pb:flex pb:flex-wrap pb:gap-3 pb:items-center">
58
+ <Avatar name="Ada Lovelace" size="md" />
59
+ <Avatar name="BMO Bot" size="md" />
60
+ <Avatar name="Coraline Maker" size="md" />
61
+ <Avatar name="David Porfirio" size="md" />
62
+ <Avatar name="Frankenstein Robot" size="md" />
63
+ <Avatar name="Jacob Roberts" size="md" />
64
+ </div>
65
+ </div>
66
+
67
+ <div className="pb:space-y-3">
68
+ <Heading level={4} className="pb:text-sm pb:font-medium pb:text-black">
69
+ Shapes
70
+ </Heading>
71
+ <div className="pb:flex pb:flex-wrap pb:gap-3 pb:items-center">
72
+ <Avatar name="Circle Shape" shape="circle" size="lg" />
73
+ <Avatar name="Rounded Shape" shape="rounded" size="lg" />
74
+ </div>
75
+ </div>
76
+
77
+ <div className="pb:space-y-3">
78
+ <Heading level={4} className="pb:text-sm pb:font-medium pb:text-black">
79
+ Generic Icon Fallback
80
+ </Heading>
81
+ <div className="pb:flex pb:flex-wrap pb:gap-3 pb:items-center">
82
+ <Avatar size="sm" />
83
+ <Avatar size="md" />
84
+ <Avatar size="lg" />
85
+ </div>
86
+ </div>
87
+ </div>
88
+ ),
89
+ };