@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.
- package/.changeset/config.json +1 -1
- package/.github/workflows/storybook.yml +9 -1
- package/CHANGELOG.md +94 -0
- package/dist/index.d.mts +643 -131
- package/dist/index.d.ts +643 -131
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
- package/src/charts/DistributionBarChart.tsx +10 -4
- package/src/charts/DistributionHistogram.tsx +13 -5
- package/src/charts/LineGraph.stories.tsx +60 -0
- package/src/charts/LineGraph.tsx +216 -0
- package/src/charts/index.ts +2 -0
- package/src/docs/DesignSystem.mdx +241 -25
- package/src/styles/theme.css +8 -0
- package/src/ui/feedback/Alert.stories.tsx +56 -0
- package/src/ui/feedback/Alert.tsx +168 -0
- package/src/ui/{Dialog.stories.tsx → feedback/Dialog.stories.tsx} +2 -2
- package/src/ui/{Dialog.tsx → feedback/Dialog.tsx} +2 -2
- package/src/ui/feedback/EmptyState.stories.tsx +57 -0
- package/src/ui/feedback/EmptyState.tsx +123 -0
- package/src/ui/feedback/Popover.stories.tsx +130 -0
- package/src/ui/feedback/Popover.tsx +135 -0
- package/src/ui/feedback/Skeleton.stories.tsx +88 -0
- package/src/ui/{Skeleton.tsx → feedback/Skeleton.tsx} +2 -2
- package/src/ui/feedback/Spinner.stories.tsx +77 -0
- package/src/ui/feedback/Spinner.tsx +62 -0
- package/src/ui/{Tooltip.stories.tsx → feedback/Tooltip.stories.tsx} +3 -3
- package/src/ui/{Tooltip.tsx → feedback/Tooltip.tsx} +2 -2
- package/src/ui/feedback/index.ts +7 -0
- package/src/ui/forms/Button.stories.tsx +181 -0
- package/src/ui/forms/Button.tsx +323 -0
- package/src/ui/{Checkbox.stories.tsx → forms/Checkbox.stories.tsx} +2 -2
- package/src/ui/{Checkbox.tsx → forms/Checkbox.tsx} +2 -2
- package/src/ui/forms/ChipGroup.stories.tsx +118 -0
- package/src/ui/forms/ChipGroup.tsx +239 -0
- package/src/ui/{Dropdown.tsx → forms/Dropdown.tsx} +1 -1
- package/src/ui/{Field.stories.tsx → forms/Field.stories.tsx} +3 -3
- package/src/ui/{Field.tsx → forms/Field.tsx} +1 -1
- package/src/ui/{Input.stories.tsx → forms/Input.stories.tsx} +6 -6
- package/src/ui/{Input.tsx → forms/Input.tsx} +1 -1
- package/src/ui/{Label.stories.tsx → forms/Label.stories.tsx} +2 -2
- package/src/ui/{Label.tsx → forms/Label.tsx} +2 -2
- package/src/ui/{NumberField.stories.tsx → forms/NumberField.stories.tsx} +2 -2
- package/src/ui/{NumberField.tsx → forms/NumberField.tsx} +8 -9
- package/src/ui/forms/PeerbotsColorSelector.stories.tsx +49 -0
- package/src/ui/forms/PeerbotsColorSelector.tsx +100 -0
- package/src/ui/{Select.stories.tsx → forms/Select.stories.tsx} +2 -2
- package/src/ui/{Select.tsx → forms/Select.tsx} +5 -3
- package/src/ui/{Slider.stories.tsx → forms/Slider.stories.tsx} +6 -4
- package/src/ui/{Slider.tsx → forms/Slider.tsx} +7 -3
- package/src/ui/forms/SliderWithNumberField.stories.tsx +136 -0
- package/src/ui/{SliderWithNumberField.tsx → forms/SliderWithNumberField.tsx} +19 -7
- package/src/ui/{Switch.stories.tsx → forms/Switch.stories.tsx} +2 -2
- package/src/ui/{Switch.tsx → forms/Switch.tsx} +1 -1
- package/src/ui/{TabRadio.stories.tsx → forms/TabRadio.stories.tsx} +3 -3
- package/src/ui/{TabRadio.tsx → forms/TabRadio.tsx} +1 -1
- package/src/ui/{TextArea.stories.tsx → forms/TextArea.stories.tsx} +5 -3
- package/src/ui/{TextArea.tsx → forms/TextArea.tsx} +1 -1
- package/src/ui/forms/index.ts +15 -0
- package/src/ui/foundations/Anchor.stories.tsx +39 -0
- package/src/ui/foundations/Anchor.tsx +96 -0
- package/src/ui/foundations/CodeBlock.stories.tsx +86 -0
- package/src/ui/foundations/CodeBlock.tsx +148 -0
- package/src/ui/foundations/Colors.stories.tsx +80 -0
- package/src/ui/{Icon.stories.tsx → foundations/Icon.stories.tsx} +1 -1
- package/src/ui/{Icon.tsx → foundations/Icon.tsx} +1 -1
- package/src/ui/{IconRegistry.tsx → foundations/IconRegistry.tsx} +261 -57
- package/src/ui/foundations/SocialLinks.stories.tsx +38 -0
- package/src/ui/foundations/SocialLinks.tsx +117 -0
- package/src/ui/foundations/Typography.stories.tsx +58 -0
- package/src/ui/foundations/Typography.tsx +163 -0
- package/src/ui/foundations/index.ts +6 -0
- package/src/ui/index.ts +7 -27
- package/src/ui/layout/Accordion.stories.tsx +86 -0
- package/src/ui/layout/Accordion.tsx +205 -0
- package/src/ui/layout/Card.stories.tsx +151 -0
- package/src/ui/layout/Card.tsx +124 -0
- package/src/ui/layout/Collapsible.stories.tsx +54 -0
- package/src/ui/layout/Collapsible.tsx +54 -0
- package/src/ui/{Separator.stories.tsx → layout/Separator.stories.tsx} +2 -2
- package/src/ui/{Separator.tsx → layout/Separator.tsx} +1 -1
- package/src/ui/{SettingsPanel.stories.tsx → layout/SettingsPanel.stories.tsx} +5 -5
- package/src/ui/{SettingsPanel.tsx → layout/SettingsPanel.tsx} +33 -29
- package/src/ui/layout/index.ts +5 -0
- package/src/ui/media/Avatar.stories.tsx +89 -0
- package/src/ui/media/Avatar.tsx +122 -0
- package/src/ui/media/Dropzone.stories.tsx +95 -0
- package/src/ui/media/Dropzone.tsx +328 -0
- package/src/ui/media/FileUpload.stories.tsx +62 -0
- package/src/ui/media/FileUpload.tsx +175 -0
- package/src/ui/media/index.ts +3 -0
- package/src/ui/{AuthFormUI.stories.tsx → patterns/AuthFormUI.stories.tsx} +1 -1
- package/src/ui/{AuthFormUI.tsx → patterns/AuthFormUI.tsx} +24 -23
- package/src/ui/patterns/CopyButton.stories.tsx +77 -0
- package/src/ui/patterns/CopyButton.tsx +93 -0
- package/src/ui/patterns/SearchInput.stories.tsx +51 -0
- package/src/ui/patterns/SearchInput.tsx +93 -0
- package/src/ui/patterns/Stepper.stories.tsx +129 -0
- package/src/ui/patterns/Stepper.tsx +295 -0
- package/src/ui/{TabSelection.stories.tsx → patterns/TabSelection.stories.tsx} +1 -1
- package/src/ui/{TabSelection.tsx → patterns/TabSelection.tsx} +2 -2
- package/src/ui/patterns/index.ts +6 -0
- package/src/ui/patterns/useClipboard.ts +75 -0
- package/src/ui/types.ts +48 -0
- package/src/ui/Alert.stories.tsx +0 -41
- package/src/ui/Alert.tsx +0 -72
- package/src/ui/Anchor.stories.tsx +0 -25
- package/src/ui/Anchor.tsx +0 -32
- package/src/ui/Button.stories.tsx +0 -108
- package/src/ui/Button.tsx +0 -121
- package/src/ui/Collapsible.stories.tsx +0 -91
- package/src/ui/Collapsible.tsx +0 -52
- package/src/ui/Colors.stories.tsx +0 -67
- package/src/ui/Popover.tsx +0 -42
- package/src/ui/Skeleton.stories.tsx +0 -43
- package/src/ui/SliderWithNumberField.stories.tsx +0 -101
- package/src/ui/Typography.stories.tsx +0 -87
- 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 "
|
|
3
|
+
import { Heading, Text } from "../foundations/Typography";
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Separator> = {
|
|
6
|
-
title: "
|
|
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 "
|
|
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 "
|
|
4
|
-
import { Checkbox } from "
|
|
5
|
-
import { Heading, Text } from "
|
|
6
|
-
import { Label } from "
|
|
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: "
|
|
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 "
|
|
3
|
-
import { cn } from "
|
|
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
|
-
*
|
|
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)}>
|
|
38
|
+
<div className={cn("pb:flex pb:flex-col pb:gap-3", contentClassName)}>
|
|
39
|
+
{children}
|
|
40
|
+
</div>
|
|
38
41
|
);
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
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-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
</>
|
|
80
|
+
</div>
|
|
78
81
|
)}
|
|
79
|
-
|
|
82
|
+
{content}
|
|
83
|
+
</Card>
|
|
80
84
|
);
|
|
81
85
|
}
|
|
@@ -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
|
+
};
|