@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,323 @@
1
+ import { Button as BaseButton } from "@base-ui/react";
2
+ import React from "react";
3
+ import { cn } from "../utils";
4
+ import {
5
+ ComponentColor,
6
+ FillVariant,
7
+ ComponentSize,
8
+ ComponentRadius,
9
+ } from "../types";
10
+
11
+ export type ButtonVariant =
12
+ | FillVariant
13
+ | "primary"
14
+ | "secondary"
15
+ | "danger"
16
+ | "ghostly-danger"
17
+ | "marketing-teal"
18
+ | "marketing-pink"
19
+ | "teal"
20
+ | "pink";
21
+
22
+ export interface ButtonProps
23
+ extends React.ButtonHTMLAttributes<HTMLButtonElement | HTMLAnchorElement> {
24
+ /**
25
+ * Visual fill style / treatment or legacy variant.
26
+ * @default "solid"
27
+ */
28
+ variant?: ButtonVariant;
29
+ /**
30
+ * Palette color or semantic intent.
31
+ * @default "primary"
32
+ */
33
+ color?: ComponentColor;
34
+ /**
35
+ * Button scale / padding.
36
+ * @default "md"
37
+ */
38
+ size?: ComponentSize;
39
+ /**
40
+ * Border radius geometry.
41
+ * @default "md" (rounded-xl)
42
+ */
43
+ radius?: ComponentRadius;
44
+ /** Loading spinner indicator state */
45
+ isLoading?: boolean;
46
+ /** Icon placed before children */
47
+ leftIcon?: React.ReactNode;
48
+ /** Icon placed after children */
49
+ rightIcon?: React.ReactNode;
50
+ render?: BaseButton.Props["render"];
51
+ nativeButton?: boolean;
52
+ isIconOnly?: boolean;
53
+ href?: string;
54
+ target?: string;
55
+ rel?: string;
56
+ }
57
+
58
+ const variantColorMap: Record<FillVariant, Record<string, string>> = {
59
+ solid: {
60
+ primary:
61
+ "pb:bg-primary pb:text-gray-900 pb:hover:bg-dark-primary pb:hover:text-white pb:shadow-xs pb:font-bold",
62
+ neutral:
63
+ "pb:bg-gray-100 pb:text-gray-900 pb:hover:bg-gray-200 pb:border pb:border-gray-300 pb:font-medium",
64
+ teal: "pb:bg-peerbots-teal pb:text-gray-900 pb:hover:bg-peerbots-darkteal pb:hover:text-white pb:shadow-md pb:font-bold",
65
+ pink: "pb:bg-peerbots-pink pb:text-gray-950 pb:hover:bg-rose-700 pb:hover:text-white pb:shadow-md pb:font-bold",
66
+ darkblue:
67
+ "pb:bg-peerbots-darkblue pb:text-white pb:hover:opacity-90 pb:shadow-md pb:font-bold",
68
+ danger:
69
+ "pb:bg-red-600 pb:text-white pb:hover:bg-red-700 pb:shadow-xs pb:font-bold",
70
+ success:
71
+ "pb:bg-emerald-700 pb:text-white pb:hover:bg-emerald-800 pb:shadow-xs pb:font-bold",
72
+ warning:
73
+ "pb:bg-amber-500 pb:text-gray-900 pb:hover:bg-amber-600 pb:shadow-xs pb:font-bold",
74
+ },
75
+ soft: {
76
+ primary:
77
+ "pb:bg-primary/20 pb:text-teal-950 pb:hover:bg-primary/30 pb:font-bold",
78
+ neutral:
79
+ "pb:bg-gray-100 pb:text-gray-800 pb:hover:bg-gray-200 pb:font-medium",
80
+ teal: "pb:bg-peerbots-teal/20 pb:text-teal-950 pb:hover:bg-peerbots-teal/30 pb:font-bold",
81
+ pink: "pb:bg-peerbots-pink/20 pb:text-rose-950 pb:hover:bg-peerbots-pink/30 pb:font-bold",
82
+ darkblue:
83
+ "pb:bg-peerbots-darkblue/15 pb:text-peerbots-darkblue pb:hover:bg-peerbots-darkblue/25 pb:font-bold",
84
+ danger:
85
+ "pb:bg-red-50 pb:text-red-800 pb:hover:bg-red-100 pb:font-bold",
86
+ success:
87
+ "pb:bg-emerald-50 pb:text-emerald-800 pb:hover:bg-emerald-100 pb:font-bold",
88
+ warning:
89
+ "pb:bg-amber-50 pb:text-amber-900 pb:hover:bg-amber-100 pb:font-bold",
90
+ },
91
+ outline: {
92
+ primary:
93
+ "pb:bg-transparent pb:border-2 pb:border-teal-700 pb:text-teal-900 pb:hover:bg-primary/10 pb:font-bold",
94
+ neutral:
95
+ "pb:bg-transparent pb:border-2 pb:border-gray-300 pb:text-gray-800 pb:hover:border-gray-400 pb:hover:bg-gray-50 pb:font-medium",
96
+ teal: "pb:bg-transparent pb:border-2 pb:border-peerbots-darkteal pb:text-peerbots-darkteal pb:hover:bg-peerbots-teal/10 pb:font-bold",
97
+ pink: "pb:bg-transparent pb:border-2 pb:border-rose-700 pb:text-rose-800 pb:hover:bg-peerbots-pink/10 pb:font-bold",
98
+ darkblue:
99
+ "pb:bg-transparent pb:border-2 pb:border-peerbots-darkblue pb:text-peerbots-darkblue pb:hover:bg-peerbots-darkblue/10 pb:font-bold",
100
+ danger:
101
+ "pb:bg-transparent pb:border-2 pb:border-red-600 pb:text-red-700 pb:hover:bg-red-50 pb:font-bold",
102
+ success:
103
+ "pb:bg-transparent pb:border-2 pb:border-emerald-600 pb:text-emerald-700 pb:hover:bg-emerald-50 pb:font-bold",
104
+ warning:
105
+ "pb:bg-transparent pb:border-2 pb:border-amber-600 pb:text-amber-900 pb:hover:bg-amber-50 pb:font-bold",
106
+ },
107
+ ghost: {
108
+ primary:
109
+ "pb:bg-transparent pb:text-teal-900 pb:hover:bg-primary/10 pb:font-bold",
110
+ neutral:
111
+ "pb:bg-transparent pb:text-gray-800 pb:hover:bg-gray-100 pb:hover:text-gray-900 pb:font-medium",
112
+ teal: "pb:bg-transparent pb:text-peerbots-darkteal pb:hover:bg-peerbots-teal/10 pb:font-bold",
113
+ pink: "pb:bg-transparent pb:text-rose-800 pb:hover:bg-peerbots-pink/10 pb:font-bold",
114
+ darkblue:
115
+ "pb:bg-transparent pb:text-peerbots-darkblue pb:hover:bg-peerbots-darkblue/10 pb:font-bold",
116
+ danger:
117
+ "pb:bg-transparent pb:text-red-700 pb:hover:bg-red-50 pb:font-bold",
118
+ success:
119
+ "pb:bg-transparent pb:text-emerald-700 pb:hover:bg-emerald-50 pb:font-bold",
120
+ warning:
121
+ "pb:bg-transparent pb:text-amber-900 pb:hover:bg-amber-50 pb:font-bold",
122
+ },
123
+ link: {
124
+ primary:
125
+ "pb:bg-transparent pb:p-0 pb:text-teal-800 pb:hover:underline pb:font-medium",
126
+ neutral:
127
+ "pb:bg-transparent pb:p-0 pb:text-gray-700 pb:hover:text-gray-900 pb:hover:underline pb:font-medium",
128
+ teal: "pb:bg-transparent pb:p-0 pb:text-peerbots-darkteal pb:hover:underline pb:font-medium",
129
+ pink: "pb:bg-transparent pb:p-0 pb:text-rose-800 pb:hover:underline pb:font-medium",
130
+ darkblue:
131
+ "pb:bg-transparent pb:p-0 pb:text-peerbots-darkblue pb:hover:underline pb:font-medium",
132
+ danger:
133
+ "pb:bg-transparent pb:p-0 pb:text-red-600 pb:hover:underline pb:font-medium",
134
+ success:
135
+ "pb:bg-transparent pb:p-0 pb:text-emerald-600 pb:hover:underline pb:font-medium",
136
+ warning:
137
+ "pb:bg-transparent pb:p-0 pb:text-amber-600 pb:hover:underline pb:font-medium",
138
+ },
139
+ };
140
+
141
+ const radiusClasses: Record<ComponentRadius, string> = {
142
+ none: "pb:rounded-none",
143
+ sm: "pb:rounded-md",
144
+ md: "pb:rounded-xl",
145
+ lg: "pb:rounded-2xl",
146
+ "2xl": "pb:rounded-2xl",
147
+ pill: "pb:rounded-full",
148
+ };
149
+
150
+ export const Button = React.forwardRef<
151
+ HTMLButtonElement | HTMLAnchorElement,
152
+ ButtonProps
153
+ >(
154
+ (
155
+ {
156
+ className,
157
+ variant = "solid",
158
+ color,
159
+ size = "md",
160
+ radius = "md",
161
+ isLoading = false,
162
+ leftIcon,
163
+ rightIcon,
164
+ children,
165
+ disabled,
166
+ render,
167
+ nativeButton,
168
+ isIconOnly,
169
+ href,
170
+ target,
171
+ rel,
172
+ ...props
173
+ },
174
+ ref,
175
+ ) => {
176
+ // Resolve legacy variants to orthogonal (FillVariant, ComponentColor, ComponentRadius)
177
+ let resolvedVariant: FillVariant = "solid";
178
+ let resolvedColor: ComponentColor = color || "primary";
179
+ let resolvedRadius: ComponentRadius = radius;
180
+
181
+ if (variant === "secondary") {
182
+ resolvedVariant = "solid";
183
+ resolvedColor = color || "neutral";
184
+ } else if (variant === "danger") {
185
+ resolvedVariant = "solid";
186
+ resolvedColor = color || "danger";
187
+ } else if (variant === "ghostly-danger") {
188
+ resolvedVariant = "ghost";
189
+ resolvedColor = color || "danger";
190
+ } else if (variant === "marketing-teal" || variant === "teal") {
191
+ resolvedVariant = "solid";
192
+ resolvedColor = color || "teal";
193
+ resolvedRadius = radius === "md" ? "pill" : radius;
194
+ } else if (variant === "marketing-pink" || variant === "pink") {
195
+ resolvedVariant = "solid";
196
+ resolvedColor = color || "pink";
197
+ resolvedRadius = radius === "md" ? "pill" : radius;
198
+ } else if (
199
+ variant === "solid" ||
200
+ variant === "soft" ||
201
+ variant === "outline" ||
202
+ variant === "ghost" ||
203
+ variant === "link"
204
+ ) {
205
+ resolvedVariant = variant;
206
+ }
207
+
208
+ const isActuallyIconOnly =
209
+ isIconOnly || (!children && (!!leftIcon || !!rightIcon));
210
+
211
+ const sizes: Record<ComponentSize, string> = {
212
+ xs: isActuallyIconOnly
213
+ ? "pb:p-1 pb:text-xs"
214
+ : "pb:px-2.5 pb:py-1 pb:text-xs",
215
+ sm: isActuallyIconOnly
216
+ ? "pb:p-1.5 pb:text-xs"
217
+ : "pb:px-3.5 pb:py-1.5 pb:text-xs",
218
+ md: isActuallyIconOnly
219
+ ? "pb:p-2 pb:text-sm"
220
+ : "pb:px-4.5 pb:py-2.5 pb:text-sm",
221
+ lg: isActuallyIconOnly
222
+ ? "pb:p-3 pb:text-base"
223
+ : "pb:px-6 pb:py-3.5 pb:text-base sm:pb:text-lg",
224
+ xl: isActuallyIconOnly
225
+ ? "pb:p-3.5 pb:text-lg"
226
+ : "pb:px-8 pb:py-4 pb:text-lg sm:pb:text-xl",
227
+ };
228
+
229
+ const variantStyles =
230
+ variantColorMap[resolvedVariant]?.[resolvedColor] ||
231
+ variantColorMap[resolvedVariant]?.primary ||
232
+ variantColorMap.solid.primary;
233
+
234
+ const commonClasses = cn(
235
+ "pb:inline-flex pb:items-center pb:justify-center pb:transition-all pb:duration-150 pb:focus:outline-none pb:focus-visible:ring-2 pb:focus-visible:ring-peerbots-teal pb:focus-visible:ring-offset-2 pb:disabled:cursor-not-allowed pb:disabled:opacity-50 pb:cursor-pointer pb:font-medium",
236
+ radiusClasses[resolvedRadius],
237
+ variantStyles,
238
+ sizes[size],
239
+ className,
240
+ );
241
+
242
+ const content = (
243
+ <>
244
+ {isLoading && (
245
+ <svg
246
+ className={cn(
247
+ "pb:animate-spin pb:h-4 pb:w-4",
248
+ !isActuallyIconOnly && "pb:mr-2 pb:-ml-1",
249
+ )}
250
+ xmlns="http://www.w3.org/2000/svg"
251
+ fill="none"
252
+ viewBox="0 0 24 24"
253
+ >
254
+ <circle
255
+ className="pb:opacity-25"
256
+ cx="12"
257
+ cy="12"
258
+ r="10"
259
+ stroke="currentColor"
260
+ strokeWidth="4"
261
+ ></circle>
262
+ <path
263
+ className="pb:opacity-75"
264
+ fill="currentColor"
265
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
266
+ ></path>
267
+ </svg>
268
+ )}
269
+ {!isLoading && leftIcon && (
270
+ <span
271
+ className={cn(
272
+ "pb:inline-flex pb:items-center pb:justify-center",
273
+ !isActuallyIconOnly && "pb:mr-2 pb:-ml-1",
274
+ )}
275
+ >
276
+ {leftIcon}
277
+ </span>
278
+ )}
279
+ {children}
280
+ {!isLoading && rightIcon && (
281
+ <span
282
+ className={cn(
283
+ "pb:inline-flex pb:items-center pb:justify-center",
284
+ !isActuallyIconOnly && "pb:ml-2 pb:-mr-1",
285
+ )}
286
+ >
287
+ {rightIcon}
288
+ </span>
289
+ )}
290
+ </>
291
+ );
292
+
293
+ if (href) {
294
+ return (
295
+ <a
296
+ href={href}
297
+ target={target}
298
+ rel={rel}
299
+ className={commonClasses}
300
+ ref={ref as React.Ref<HTMLAnchorElement>}
301
+ {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}
302
+ >
303
+ {content}
304
+ </a>
305
+ );
306
+ }
307
+
308
+ return (
309
+ <BaseButton
310
+ ref={ref as React.Ref<HTMLButtonElement>}
311
+ render={render}
312
+ nativeButton={nativeButton}
313
+ className={commonClasses}
314
+ disabled={disabled || isLoading}
315
+ {...(props as React.ButtonHTMLAttributes<HTMLButtonElement>)}
316
+ >
317
+ {content}
318
+ </BaseButton>
319
+ );
320
+ },
321
+ );
322
+
323
+ Button.displayName = "Button";
@@ -1,10 +1,10 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Checkbox } from "./Checkbox";
3
- import { Heading } from "./Typography";
3
+ import { Heading } from "../foundations/Typography";
4
4
  import React from "react";
5
5
 
6
6
  const meta: Meta<typeof Checkbox> = {
7
- title: "UI/Checkbox",
7
+ title: "Forms/Checkbox",
8
8
  component: Checkbox,
9
9
  tags: ["autodocs"],
10
10
  };
@@ -1,7 +1,7 @@
1
1
  import { Checkbox as BaseCheckbox } from "@base-ui/react";
2
2
  import React, { ComponentPropsWithoutRef } from "react";
3
- import { cn } from "./utils";
4
- import { Icon } from "./Icon";
3
+ import { cn } from "../utils";
4
+ import { Icon } from "../foundations/Icon";
5
5
 
6
6
  export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement>;
7
7
 
@@ -0,0 +1,118 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ChipGroup } from "./ChipGroup";
3
+ import { Icon } from "../foundations/Icon";
4
+ import { Heading, Text } from "../foundations/Typography";
5
+ import React, { useState } from "react";
6
+
7
+ const meta: Meta<typeof ChipGroup> = {
8
+ title: "Forms/ChipGroup",
9
+ component: ChipGroup,
10
+ parameters: {
11
+ layout: "centered",
12
+ },
13
+ tags: ["autodocs"],
14
+ argTypes: {
15
+ variant: {
16
+ control: "select",
17
+ options: ["soft", "solid", "outline"],
18
+ },
19
+ color: {
20
+ control: "select",
21
+ options: ["teal", "pink", "primary", "neutral", "danger"],
22
+ },
23
+ size: {
24
+ control: "select",
25
+ options: ["xs", "sm", "md", "lg", "xl"],
26
+ },
27
+ radius: {
28
+ control: "select",
29
+ options: ["none", "sm", "md", "lg", "pill"],
30
+ },
31
+ },
32
+ };
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof ChipGroup>;
36
+
37
+ const sampleOptions = [
38
+ { value: "all", label: "All Bots" },
39
+ { value: "moddy", label: "Moddy Face", badge: "New" },
40
+ { value: "eva", label: "EVA Head" },
41
+ { value: "buddy", label: "Buddy v2" },
42
+ { value: "biped", label: "Biped 3D", badge: "Soon", disabled: true },
43
+ ];
44
+
45
+ export const Default: Story = {
46
+ args: {
47
+ options: sampleOptions,
48
+ defaultValue: "moddy",
49
+ color: "teal",
50
+ variant: "soft",
51
+ size: "md",
52
+ radius: "pill",
53
+ },
54
+ };
55
+
56
+ export const MultiSelectFilter: Story = {
57
+ render: () => {
58
+ const [selected, setSelected] = useState<string[]>(["audio", "video"]);
59
+ return (
60
+ <div className="pb:flex pb:flex-col pb:gap-3 pb:p-6 pb:bg-white pb:rounded-2xl pb:max-w-xl">
61
+ <Heading level={4} className="pb:text-sm pb:font-bold">
62
+ Capabilities Filter (Multi-Select)
63
+ </Heading>
64
+ <ChipGroup
65
+ multiple
66
+ values={selected}
67
+ onMultipleChange={setSelected}
68
+ color="pink"
69
+ variant="soft"
70
+ options={[
71
+ { value: "audio", label: "Audio Output", icon: <Icon name="speakerWave" className="pb:w-3.5 pb:h-3.5 pb:mr-1" /> },
72
+ { value: "video", label: "Camera Vision", icon: <Icon name="camera" className="pb:w-3.5 pb:h-3.5 pb:mr-1" /> },
73
+ { value: "motors", label: "Servos & Motors" },
74
+ { value: "leds", label: "RGB Matrix" },
75
+ { value: "imu", label: "Gyro/IMU", disabled: true, badge: "Experimental" },
76
+ ]}
77
+ />
78
+ <Text size="xs" color="muted">
79
+ Selected: {selected.join(", ") || "None"}
80
+ </Text>
81
+ </div>
82
+ );
83
+ },
84
+ };
85
+
86
+ export const VariantsAndColors: Story = {
87
+ render: () => (
88
+ <div className="pb:flex pb:flex-col pb:gap-6 pb:p-6 pb:bg-gray-50 pb:rounded-2xl pb:max-w-2xl">
89
+ <div>
90
+ <Text size="xs" weight="bold" color="muted" className="pb:uppercase pb:mb-2">
91
+ Teal Soft (Default)
92
+ </Text>
93
+ <ChipGroup options={sampleOptions} defaultValue="moddy" color="teal" variant="soft" />
94
+ </div>
95
+
96
+ <div>
97
+ <Text size="xs" weight="bold" color="muted" className="pb:uppercase pb:mb-2">
98
+ Pink Solid Pill
99
+ </Text>
100
+ <ChipGroup options={sampleOptions} defaultValue="eva" color="pink" variant="solid" />
101
+ </div>
102
+
103
+ <div>
104
+ <Text size="xs" weight="bold" color="muted" className="pb:uppercase pb:mb-2">
105
+ Primary Electric Outline
106
+ </Text>
107
+ <ChipGroup options={sampleOptions} defaultValue="all" color="primary" variant="outline" />
108
+ </div>
109
+
110
+ <div>
111
+ <Text size="xs" weight="bold" color="muted" className="pb:uppercase pb:mb-2">
112
+ Rounded Rect (radius="md")
113
+ </Text>
114
+ <ChipGroup options={sampleOptions} defaultValue="buddy" color="teal" radius="md" />
115
+ </div>
116
+ </div>
117
+ ),
118
+ };