@boyernick/standard-ui-react 0.1.1-canary.3 → 0.1.1-canary.34

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 (89) hide show
  1. package/package.json +5 -3
  2. package/src/accordion.tsx +5 -2
  3. package/src/alert-dialog.tsx +1 -1
  4. package/src/attachment.tsx +5 -2
  5. package/src/autocomplete.tsx +12 -9
  6. package/src/badge.tsx +73 -9
  7. package/src/block-editor.tsx +1747 -0
  8. package/src/brand.tsx +2 -2
  9. package/src/breadcrumb.tsx +46 -21
  10. package/src/button.tsx +23 -12
  11. package/src/calendar.tsx +25 -11
  12. package/src/card.tsx +57 -14
  13. package/src/carousel.tsx +339 -15
  14. package/src/checkbox.tsx +36 -13
  15. package/src/code-block.tsx +66 -5
  16. package/src/collapsible.tsx +7 -3
  17. package/src/combobox.tsx +17 -15
  18. package/src/command.tsx +41 -14
  19. package/src/context-menu.tsx +6 -3
  20. package/src/date-picker.tsx +174 -0
  21. package/src/dialog.tsx +33 -10
  22. package/src/empty.tsx +4 -4
  23. package/src/field.tsx +4 -3
  24. package/src/filter-group.tsx +143 -0
  25. package/src/icons.tsx +55 -1
  26. package/src/illustrations.tsx +219 -141
  27. package/src/index.ts +263 -18
  28. package/src/input.tsx +8 -5
  29. package/src/kbd.tsx +49 -0
  30. package/src/lib/focus.ts +41 -0
  31. package/src/lib/motion.ts +16 -1
  32. package/src/lib/popup.ts +45 -0
  33. package/src/lifeline/company-icon.tsx +71 -0
  34. package/src/lifeline/index.ts +42 -0
  35. package/src/lifeline/lifeline-data.ts +97 -0
  36. package/src/lifeline/lifeline-desktop.tsx +204 -0
  37. package/src/lifeline/lifeline-event.tsx +108 -0
  38. package/src/lifeline/lifeline-fireworks.tsx +302 -0
  39. package/src/lifeline/lifeline-hover-image.tsx +269 -0
  40. package/src/lifeline/lifeline-icons.tsx +39 -0
  41. package/src/lifeline/lifeline-intro-timing.ts +105 -0
  42. package/src/lifeline/lifeline-labels.tsx +24 -0
  43. package/src/lifeline/lifeline-layout.ts +1 -0
  44. package/src/lifeline/lifeline-legend.tsx +31 -0
  45. package/src/lifeline/lifeline-lightbox.tsx +309 -0
  46. package/src/lifeline/lifeline-marker.tsx +183 -0
  47. package/src/lifeline/lifeline-people.tsx +99 -0
  48. package/src/lifeline/lifeline-photos.tsx +366 -0
  49. package/src/lifeline/lifeline-shell.tsx +135 -0
  50. package/src/lifeline/lifeline-utils.ts +83 -0
  51. package/src/lifeline/lifeline-vertical.tsx +482 -0
  52. package/src/lifeline/lifeline.tsx +69 -0
  53. package/src/lifeline/types.ts +112 -0
  54. package/src/lifeline/use-lifeline-intro.ts +130 -0
  55. package/src/lifeline/use-lifeline-scroll.ts +1011 -0
  56. package/src/lifeline/use-lifeline-vertical-scroll.ts +271 -0
  57. package/src/menu.tsx +7 -4
  58. package/src/menubar.tsx +9 -2
  59. package/src/minimap.tsx +296 -0
  60. package/src/modal.tsx +608 -0
  61. package/src/navigation-menu.tsx +340 -67
  62. package/src/number-field.tsx +336 -61
  63. package/src/otp-field.tsx +4 -3
  64. package/src/pagination.tsx +262 -42
  65. package/src/password-protection.tsx +345 -0
  66. package/src/popover.tsx +3 -1
  67. package/src/preview-card.tsx +3 -1
  68. package/src/progress.tsx +5 -0
  69. package/src/questionnaire.tsx +284 -0
  70. package/src/radio.tsx +4 -2
  71. package/src/scroll-area.tsx +4 -1
  72. package/src/select.tsx +12 -7
  73. package/src/sidebar.tsx +113 -28
  74. package/src/slider.tsx +66 -6
  75. package/src/sounds.tsx +6 -10
  76. package/src/spinner.tsx +105 -32
  77. package/src/switch.tsx +4 -1
  78. package/src/table.tsx +82 -15
  79. package/src/tabs.tsx +189 -38
  80. package/src/text-animate.tsx +71 -28
  81. package/src/textarea.tsx +6 -1
  82. package/src/timeline.tsx +378 -0
  83. package/src/toast.tsx +214 -35
  84. package/src/toggle.tsx +4 -2
  85. package/src/toolbar.tsx +12 -7
  86. package/src/tooltip.tsx +43 -3
  87. package/src/video-player.tsx +396 -127
  88. package/src/image-modal.tsx +0 -110
  89. package/src/markdown-editor.tsx +0 -302
@@ -4,6 +4,7 @@ import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card"
4
4
  import type { ComponentProps } from "react"
5
5
  import { cn } from "./lib/cn"
6
6
  import { motion } from "./lib/motion"
7
+ import { popupSurface } from "./lib/popup"
7
8
 
8
9
  export type PreviewCardProps = ComponentProps<typeof BasePreviewCard.Root>
9
10
  export type PreviewCardTriggerProps = ComponentProps<
@@ -67,7 +68,8 @@ export const PreviewCardPopup = ({
67
68
  }: PreviewCardPopupProps) => (
68
69
  <BasePreviewCard.Popup
69
70
  className={cn(
70
- "z-50 w-72 overflow-hidden rounded-xl border border-border-primary bg-surface p-4 shadow-md outline-none",
71
+ "z-50 w-72 overflow-hidden p-4",
72
+ popupSurface,
71
73
  motion.popupAnchor,
72
74
  className,
73
75
  )}
package/src/progress.tsx CHANGED
@@ -53,6 +53,11 @@ export const ProgressIndicator = ({
53
53
  <BaseProgress.Indicator
54
54
  className={cn(
55
55
  "h-full rounded-full bg-brand-primary transition-[width] duration-[var(--duration-lg)] ease-enter motion-reduce:transition-none",
56
+ // Base UI flags a null value with data-indeterminate and leaves the
57
+ // indicator full width, so without this the bar reads as complete.
58
+ // A quarter-width stripe crossing the track on the passive curve —
59
+ // ambient, system-initiated movement.
60
+ "data-indeterminate:w-1/4 data-indeterminate:animate-[progress-indeterminate_1.4s_var(--ease-passive)_infinite] motion-reduce:animate-none",
56
61
  className,
57
62
  )}
58
63
  {...props}
@@ -0,0 +1,284 @@
1
+ "use client"
2
+
3
+ import { Questionnaire as BaseQuestionnaire } from "@shadcn/react/questionnaire"
4
+ import type { VariantProps } from "class-variance-authority"
5
+ import type { ComponentProps } from "react"
6
+ import { buttonVariants } from "./button"
7
+ import { inputVariants } from "./input"
8
+ import { cn } from "./lib/cn"
9
+ import { focusRing, focusRingBorder } from "./lib/focus"
10
+
11
+ type StyledProps<T> = Omit<T, "className"> & { className?: string }
12
+
13
+ export type QuestionnaireProps = StyledProps<
14
+ ComponentProps<typeof BaseQuestionnaire.Root>
15
+ >
16
+ export type QuestionnaireProgressProps = StyledProps<
17
+ ComponentProps<typeof BaseQuestionnaire.Progress>
18
+ >
19
+ export type QuestionnaireItemProps = StyledProps<
20
+ ComponentProps<typeof BaseQuestionnaire.Item>
21
+ >
22
+ export type QuestionnaireTitleProps = StyledProps<
23
+ ComponentProps<typeof BaseQuestionnaire.Title>
24
+ >
25
+ export type QuestionnaireDescriptionProps = StyledProps<
26
+ ComponentProps<typeof BaseQuestionnaire.Description>
27
+ >
28
+ export type QuestionnaireChoicesProps = StyledProps<
29
+ ComponentProps<typeof BaseQuestionnaire.Choices>
30
+ >
31
+ export type QuestionnaireChoiceInputProps = StyledProps<
32
+ ComponentProps<typeof BaseQuestionnaire.ChoiceInput>
33
+ >
34
+ export type QuestionnaireChoiceLabelProps = StyledProps<
35
+ ComponentProps<typeof BaseQuestionnaire.ChoiceLabel>
36
+ >
37
+ export type QuestionnaireChoiceShortcutProps = StyledProps<
38
+ ComponentProps<typeof BaseQuestionnaire.ChoiceShortcut>
39
+ >
40
+ export type QuestionnaireChoiceProps = StyledProps<
41
+ ComponentProps<typeof BaseQuestionnaire.Choice>
42
+ > & {
43
+ inputProps?: QuestionnaireChoiceInputProps
44
+ labelProps?: QuestionnaireChoiceLabelProps
45
+ shortcutProps?: QuestionnaireChoiceShortcutProps
46
+ }
47
+ export type QuestionnaireInputProps = StyledProps<
48
+ Omit<ComponentProps<typeof BaseQuestionnaire.Input>, "size">
49
+ > &
50
+ VariantProps<typeof inputVariants>
51
+ export type QuestionnaireErrorProps = StyledProps<
52
+ ComponentProps<typeof BaseQuestionnaire.Error>
53
+ >
54
+ export type QuestionnaireActionsProps = ComponentProps<"div">
55
+
56
+ type QuestionnaireNavigationProps<T> = StyledProps<
57
+ Omit<T, "size" | "variant">
58
+ > &
59
+ VariantProps<typeof buttonVariants>
60
+
61
+ export type QuestionnairePreviousProps = QuestionnaireNavigationProps<
62
+ ComponentProps<typeof BaseQuestionnaire.Previous>
63
+ >
64
+ export type QuestionnaireSkipProps = QuestionnaireNavigationProps<
65
+ ComponentProps<typeof BaseQuestionnaire.Skip>
66
+ >
67
+ export type QuestionnaireNextProps = QuestionnaireNavigationProps<
68
+ ComponentProps<typeof BaseQuestionnaire.Next>
69
+ >
70
+ export type QuestionnaireSubmitProps = QuestionnaireNavigationProps<
71
+ ComponentProps<typeof BaseQuestionnaire.Submit>
72
+ >
73
+
74
+ export const Questionnaire = ({ className, ...props }: QuestionnaireProps) => (
75
+ <BaseQuestionnaire.Root
76
+ className={cn("flex w-full flex-col gap-6", className)}
77
+ {...props}
78
+ />
79
+ )
80
+
81
+ export const QuestionnaireProgress = ({
82
+ className,
83
+ ...props
84
+ }: QuestionnaireProgressProps) => (
85
+ <BaseQuestionnaire.Progress
86
+ className={cn("text-sm text-fg-secondary tabular-nums", className)}
87
+ {...props}
88
+ />
89
+ )
90
+
91
+ export const QuestionnaireItem = ({
92
+ className,
93
+ ...props
94
+ }: QuestionnaireItemProps) => (
95
+ <BaseQuestionnaire.Item
96
+ className={cn(
97
+ "m-0 min-w-0 border-0 p-0",
98
+ focusRingBorder,
99
+ focusRing,
100
+ className,
101
+ )}
102
+ {...props}
103
+ />
104
+ )
105
+
106
+ export const QuestionnaireTitle = ({
107
+ className,
108
+ ...props
109
+ }: QuestionnaireTitleProps) => (
110
+ <BaseQuestionnaire.Title
111
+ className={cn("heading-sm text-fg-primary", className)}
112
+ {...props}
113
+ />
114
+ )
115
+
116
+ export const QuestionnaireDescription = ({
117
+ className,
118
+ ...props
119
+ }: QuestionnaireDescriptionProps) => (
120
+ <BaseQuestionnaire.Description
121
+ className={cn("text-sm mt-1 text-fg-secondary", className)}
122
+ {...props}
123
+ />
124
+ )
125
+
126
+ export const QuestionnaireChoices = ({
127
+ className,
128
+ ...props
129
+ }: QuestionnaireChoicesProps) => (
130
+ <BaseQuestionnaire.Choices
131
+ className={cn("mt-5 grid gap-2", className)}
132
+ {...props}
133
+ />
134
+ )
135
+
136
+ export const QuestionnaireChoiceInput = ({
137
+ className,
138
+ ...props
139
+ }: QuestionnaireChoiceInputProps) => (
140
+ <BaseQuestionnaire.ChoiceInput
141
+ className={cn(
142
+ "mt-0.5 size-4 shrink-0 cursor-pointer accent-brand-primary disabled:cursor-not-allowed",
143
+ focusRingBorder,
144
+ focusRing,
145
+ className,
146
+ )}
147
+ {...props}
148
+ />
149
+ )
150
+
151
+ export const QuestionnaireChoiceLabel = ({
152
+ className,
153
+ ...props
154
+ }: QuestionnaireChoiceLabelProps) => (
155
+ <BaseQuestionnaire.ChoiceLabel
156
+ className={cn("text-sm min-w-0 text-fg-primary", className)}
157
+ {...props}
158
+ />
159
+ )
160
+
161
+ export const QuestionnaireChoiceShortcut = ({
162
+ className,
163
+ ...props
164
+ }: QuestionnaireChoiceShortcutProps) => (
165
+ <BaseQuestionnaire.ChoiceShortcut
166
+ className={cn(
167
+ "text-xs flex min-w-5 items-center justify-center rounded border border-border-secondary bg-background-secondary px-1 py-0.5 text-fg-tertiary",
168
+ className,
169
+ )}
170
+ {...props}
171
+ />
172
+ )
173
+
174
+ export const QuestionnaireChoice = ({
175
+ className,
176
+ children,
177
+ inputProps,
178
+ labelProps,
179
+ shortcutProps,
180
+ ...props
181
+ }: QuestionnaireChoiceProps) => (
182
+ <BaseQuestionnaire.Choice
183
+ className={cn(
184
+ "group grid cursor-pointer grid-cols-[auto_minmax(0,1fr)_auto] items-start gap-3 rounded-lg border border-border-secondary bg-surface px-3.5 py-3 text-fg-primary outline-none transition-[background-color,border-color,box-shadow] duration-[var(--duration-sm)] ease-enter hover:bg-background-secondary has-[:focus-visible]:border-ring has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-offset-1 has-[:focus-visible]:ring-offset-background-primary has-[:focus-visible]:ring-ring/20 data-checked:border-brand-primary data-checked:bg-background-secondary data-disabled:cursor-not-allowed data-disabled:opacity-50",
185
+ className,
186
+ )}
187
+ {...props}
188
+ >
189
+ <QuestionnaireChoiceInput {...inputProps} />
190
+ <QuestionnaireChoiceLabel {...labelProps}>
191
+ {children}
192
+ </QuestionnaireChoiceLabel>
193
+ <QuestionnaireChoiceShortcut {...shortcutProps} />
194
+ </BaseQuestionnaire.Choice>
195
+ )
196
+
197
+ export const QuestionnaireInput = ({
198
+ className,
199
+ variant,
200
+ size,
201
+ invalid,
202
+ ...props
203
+ }: QuestionnaireInputProps) => (
204
+ <BaseQuestionnaire.Input
205
+ className={cn(inputVariants({ variant, size, invalid }), "mt-5", className)}
206
+ {...props}
207
+ />
208
+ )
209
+
210
+ export const QuestionnaireError = ({
211
+ className,
212
+ ...props
213
+ }: QuestionnaireErrorProps) => (
214
+ <BaseQuestionnaire.Error
215
+ className={cn("text-sm mt-2 text-destructive", className)}
216
+ {...props}
217
+ />
218
+ )
219
+
220
+ export const QuestionnaireActions = ({
221
+ className,
222
+ ...props
223
+ }: QuestionnaireActionsProps) => (
224
+ <div
225
+ className={cn("flex min-h-9 items-center justify-end gap-2", className)}
226
+ {...props}
227
+ />
228
+ )
229
+
230
+ export const QuestionnairePrevious = ({
231
+ className,
232
+ variant = "outline",
233
+ size,
234
+ ...props
235
+ }: QuestionnairePreviousProps) => (
236
+ <BaseQuestionnaire.Previous
237
+ className={cn(buttonVariants({ variant, size }), "mr-auto", className)}
238
+ {...props}
239
+ />
240
+ )
241
+
242
+ export const QuestionnaireSkip = ({
243
+ className,
244
+ variant = "ghost",
245
+ size,
246
+ ...props
247
+ }: QuestionnaireSkipProps) => (
248
+ <BaseQuestionnaire.Skip
249
+ className={cn(buttonVariants({ variant, size }), className)}
250
+ {...props}
251
+ />
252
+ )
253
+
254
+ export const QuestionnaireNext = ({
255
+ className,
256
+ variant = "primary",
257
+ size,
258
+ ...props
259
+ }: QuestionnaireNextProps) => (
260
+ <BaseQuestionnaire.Next
261
+ className={cn(buttonVariants({ variant, size }), className)}
262
+ {...props}
263
+ />
264
+ )
265
+
266
+ export const QuestionnaireSubmit = ({
267
+ className,
268
+ variant = "primary",
269
+ size,
270
+ ...props
271
+ }: QuestionnaireSubmitProps) => (
272
+ <BaseQuestionnaire.Submit
273
+ className={cn(buttonVariants({ variant, size }), className)}
274
+ {...props}
275
+ />
276
+ )
277
+
278
+ export type {
279
+ QuestionnaireChoiceDefinition,
280
+ QuestionnaireInputType,
281
+ QuestionnaireItemDefinition,
282
+ QuestionnaireItemStatus,
283
+ QuestionnaireShortcutMode,
284
+ } from "@shadcn/react/questionnaire"
package/src/radio.tsx CHANGED
@@ -4,6 +4,7 @@ import { Radio as BaseRadio } from "@base-ui/react/radio"
4
4
  import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group"
5
5
  import type { ComponentProps } from "react"
6
6
  import { cn } from "./lib/cn"
7
+ import { focusRing } from "./lib/focus"
7
8
  import { motion } from "./lib/motion"
8
9
 
9
10
  export type RadioGroupProps = ComponentProps<typeof BaseRadioGroup>
@@ -20,9 +21,10 @@ export const RadioGroup = ({ className, ...props }: RadioGroupProps) => (
20
21
  export const Radio = ({ className, ...props }: RadioProps) => (
21
22
  <BaseRadio.Root
22
23
  className={cn(
23
- "flex size-4 shrink-0 items-center justify-center rounded-full border border-border-secondary bg-surface outline-none",
24
+ "flex size-4 shrink-0 items-center justify-center rounded-full border border-border-secondary bg-surface",
24
25
  motion.colors,
25
- "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 data-checked:border-brand-primary data-checked:bg-brand-primary data-disabled:cursor-not-allowed data-disabled:opacity-50",
26
+ focusRing,
27
+ "data-checked:border-brand-primary data-checked:bg-brand-primary data-disabled:cursor-not-allowed data-disabled:opacity-50",
26
28
  className,
27
29
  )}
28
30
  {...props}
@@ -3,6 +3,7 @@
3
3
  import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area"
4
4
  import type { ComponentProps } from "react"
5
5
  import { cn } from "./lib/cn"
6
+ import { focusRing, focusRingBorder } from "./lib/focus"
6
7
 
7
8
  export type ScrollAreaProps = ComponentProps<typeof BaseScrollArea.Root>
8
9
  export type ScrollAreaViewportProps = ComponentProps<
@@ -29,7 +30,9 @@ export const ScrollAreaViewport = ({
29
30
  }: ScrollAreaViewportProps) => (
30
31
  <BaseScrollArea.Viewport
31
32
  className={cn(
32
- "size-full rounded-[inherit] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20",
33
+ "size-full rounded-[inherit]",
34
+ focusRingBorder,
35
+ focusRing,
33
36
  className,
34
37
  )}
35
38
  {...props}
package/src/select.tsx CHANGED
@@ -4,7 +4,9 @@ import { Select as BaseSelect } from "@base-ui/react/select"
4
4
  import type { ComponentProps } from "react"
5
5
  import { IconChevronDownSmall } from "./icons"
6
6
  import { cn } from "./lib/cn"
7
+ import { focusRing, focusRingInvalid } from "./lib/focus"
7
8
  import { motion } from "./lib/motion"
9
+ import { popupInset, popupItem, popupLabel, popupSurface } from "./lib/popup"
8
10
 
9
11
  export type SelectProps = ComponentProps<typeof BaseSelect.Root>
10
12
  export type SelectTriggerProps = ComponentProps<typeof BaseSelect.Trigger>
@@ -31,9 +33,11 @@ export const SelectTrigger = ({
31
33
  }: SelectTriggerProps) => (
32
34
  <BaseSelect.Trigger
33
35
  className={cn(
34
- "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-border-secondary bg-surface px-3 text-sm text-fg-primary inset-shadow-outline-top outline-none",
36
+ "flex h-9 w-full cursor-pointer items-center justify-between gap-2 rounded-md border border-border-secondary bg-surface px-3 text-sm text-fg-primary inset-shadow-outline-top",
35
37
  motion.all,
36
- "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 aria-invalid:border-destructive aria-invalid:focus-visible:border-destructive aria-invalid:focus-visible:ring-destructive/20 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-placeholder:text-fg-quaternary data-popup-open:border-border-secondary data-popup-open:bg-background-tertiary/60",
38
+ focusRing,
39
+ focusRingInvalid,
40
+ "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-placeholder:text-fg-quaternary data-popup-open:border-border-secondary",
37
41
  className,
38
42
  )}
39
43
  {...props}
@@ -71,7 +75,7 @@ export const SelectPortal = (props: SelectPortalProps) => (
71
75
 
72
76
  export const SelectPositioner = ({
73
77
  alignItemWithTrigger = false,
74
- sideOffset = 4,
78
+ sideOffset = 6,
75
79
  className,
76
80
  ...props
77
81
  }: SelectPositionerProps) => (
@@ -86,7 +90,8 @@ export const SelectPositioner = ({
86
90
  export const SelectPopup = ({ className, ...props }: SelectPopupProps) => (
87
91
  <BaseSelect.Popup
88
92
  className={cn(
89
- "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden rounded-md border border-border-primary bg-surface shadow-md outline-none",
93
+ "z-50 max-h-[min(24rem,var(--available-height))] w-[var(--anchor-width)] overflow-hidden",
94
+ popupSurface,
90
95
  motion.popupAnchor,
91
96
  className,
92
97
  )}
@@ -96,7 +101,7 @@ export const SelectPopup = ({ className, ...props }: SelectPopupProps) => (
96
101
 
97
102
  export const SelectList = ({ className, ...props }: SelectListProps) => (
98
103
  <BaseSelect.List
99
- className={cn("max-h-[inherit] overflow-y-auto p-1 outline-none", className)}
104
+ className={cn("max-h-[inherit] overflow-y-auto outline-none", popupInset, className)}
100
105
  {...props}
101
106
  />
102
107
  )
@@ -104,7 +109,7 @@ export const SelectList = ({ className, ...props }: SelectListProps) => (
104
109
  export const SelectItem = ({ className, ...props }: SelectItemProps) => (
105
110
  <BaseSelect.Item
106
111
  className={cn(
107
- "flex min-h-8 cursor-default items-center gap-2 rounded-xs px-2.5 py-1.5 text-sm text-fg-primary outline-none select-none",
112
+ popupItem,
108
113
  motion.colors,
109
114
  "data-disabled:cursor-not-allowed data-disabled:opacity-50 data-highlighted:bg-background-tertiary data-selected:text-fg-primary",
110
115
  className,
@@ -145,7 +150,7 @@ export const SelectGroupLabel = ({
145
150
  ...props
146
151
  }: SelectGroupLabelProps) => (
147
152
  <BaseSelect.GroupLabel
148
- className={cn("px-2.5 py-1.5 text-xs text-fg-tertiary", className)}
153
+ className={cn(popupLabel, className)}
149
154
  {...props}
150
155
  />
151
156
  )
package/src/sidebar.tsx CHANGED
@@ -1,56 +1,119 @@
1
- import type { ComponentProps } from "react";
2
- import { cn } from "./lib/cn";
3
- import { motion } from "./lib/motion";
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+ import type { ComponentProps } from "react"
3
+ import { cn } from "./lib/cn"
4
+ import { focusRing } from "./lib/focus"
5
+ import { motion } from "./lib/motion"
4
6
 
5
- export type SidebarProps = ComponentProps<"aside">;
6
- export type SidebarHeaderProps = ComponentProps<"div">;
7
- export type SidebarContentProps = ComponentProps<"div">;
8
- export type SidebarFooterProps = ComponentProps<"div">;
9
- export type SidebarNavProps = ComponentProps<"nav">;
10
- export type SidebarNavItemProps = ComponentProps<"button"> & {
11
- active?: boolean;
12
- };
13
- export type SidebarGroupProps = ComponentProps<"div">;
14
- export type SidebarGroupLabelProps = ComponentProps<"h3">;
7
+ const sidebarVariants = cva(
8
+ "flex h-full shrink-0 flex-col overflow-hidden text-fg-primary",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "border-r border-border-primary bg-surface",
13
+ inset: "rounded-lg bg-background-secondary",
14
+ floating:
15
+ "rounded-xl border border-border-primary bg-surface-raised shadow-sm",
16
+ },
17
+ size: {
18
+ sm: "w-52",
19
+ md: "w-60",
20
+ lg: "w-72",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ size: "md",
26
+ },
27
+ },
28
+ )
15
29
 
16
- export const Sidebar = ({ className, ...props }: SidebarProps) => (
30
+ const sidebarNavItemVariants = cva(
31
+ cn(
32
+ "group/nav-item relative flex w-full cursor-pointer items-center gap-2 border border-transparent text-left text-fg-secondary disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
33
+ focusRing,
34
+ ),
35
+ {
36
+ variants: {
37
+ variant: {
38
+ default:
39
+ "rounded-md hover:bg-background-tertiary hover:text-fg-primary data-[active]:bg-background-tertiary data-[active]:font-medium data-[active]:text-fg-primary",
40
+ pill: "rounded-full hover:bg-background-tertiary hover:text-fg-primary data-[active]:border-brand-primary-border data-[active]:bg-brand-primary data-[active]:font-medium data-[active]:text-brand-foreground",
41
+ indicator:
42
+ "rounded-md before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-transparent before:content-[''] hover:bg-background-tertiary hover:text-fg-primary data-[active]:bg-background-tertiary data-[active]:font-medium data-[active]:text-fg-primary data-[active]:before:bg-brand-primary",
43
+ },
44
+ size: {
45
+ sm: "h-8 px-2.5 text-xs",
46
+ md: "h-9 px-3 text-sm",
47
+ lg: "h-10 px-3.5 text-sm",
48
+ },
49
+ },
50
+ defaultVariants: {
51
+ variant: "default",
52
+ size: "md",
53
+ },
54
+ },
55
+ )
56
+
57
+ export type SidebarProps = Omit<ComponentProps<"aside">, "size"> &
58
+ VariantProps<typeof sidebarVariants>
59
+ export type SidebarHeaderProps = ComponentProps<"div">
60
+ export type SidebarContentProps = ComponentProps<"div">
61
+ export type SidebarFooterProps = ComponentProps<"div">
62
+ export type SidebarNavProps = ComponentProps<"nav">
63
+ export type SidebarNavItemProps = Omit<ComponentProps<"button">, "size"> &
64
+ VariantProps<typeof sidebarNavItemVariants> & {
65
+ active?: boolean
66
+ }
67
+ export type SidebarNavItemLabelProps = ComponentProps<"span">
68
+ export type SidebarNavBadgeProps = ComponentProps<"span">
69
+ export type SidebarGroupProps = ComponentProps<"div">
70
+ export type SidebarGroupLabelProps = ComponentProps<"h3">
71
+
72
+ export const Sidebar = ({
73
+ className,
74
+ variant,
75
+ size,
76
+ ...props
77
+ }: SidebarProps) => (
17
78
  <aside
18
- className={cn(
19
- "flex h-full w-60 shrink-0 flex-col border-r border-border-primary bg-surface",
20
- className,
21
- )}
79
+ className={cn(sidebarVariants({ variant, size }), className)}
22
80
  {...props}
23
81
  />
24
- );
82
+ )
25
83
 
26
84
  export const SidebarHeader = ({ className, ...props }: SidebarHeaderProps) => (
27
85
  <div
28
86
  className={cn("border-b border-border-primary p-4", className)}
29
87
  {...props}
30
88
  />
31
- );
89
+ )
32
90
 
33
91
  export const SidebarContent = ({
34
92
  className,
35
93
  ...props
36
94
  }: SidebarContentProps) => (
37
- <div className={cn("flex-1 overflow-y-auto p-3", className)} {...props} />
38
- );
95
+ <div
96
+ className={cn("min-h-0 flex-1 overflow-y-auto p-3", className)}
97
+ {...props}
98
+ />
99
+ )
39
100
 
40
101
  export const SidebarFooter = ({ className, ...props }: SidebarFooterProps) => (
41
102
  <div
42
103
  className={cn("border-t border-border-primary p-3", className)}
43
104
  {...props}
44
105
  />
45
- );
106
+ )
46
107
 
47
108
  export const SidebarNav = ({ className, ...props }: SidebarNavProps) => (
48
109
  <nav className={cn("flex flex-col gap-1", className)} {...props} />
49
- );
110
+ )
50
111
 
51
112
  export const SidebarNavItem = ({
52
113
  className,
53
114
  active = false,
115
+ variant,
116
+ size,
54
117
  type = "button",
55
118
  ...props
56
119
  }: SidebarNavItemProps) => (
@@ -59,17 +122,37 @@ export const SidebarNavItem = ({
59
122
  data-active={active || undefined}
60
123
  aria-current={active ? "page" : undefined}
61
124
  className={cn(
62
- "flex h-9 w-full cursor-pointer items-center gap-2 rounded-md border border-transparent px-3 text-left text-sm text-fg-secondary outline-none hover:bg-background-tertiary hover:text-fg-primary focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 data-[active]:bg-background-tertiary data-[active]:font-medium data-[active]:text-fg-primary [&_svg]:size-4 [&_svg]:shrink-0",
125
+ sidebarNavItemVariants({ variant, size }),
63
126
  motion.colors,
64
127
  className,
65
128
  )}
66
129
  {...props}
67
130
  />
68
- );
131
+ )
132
+
133
+ export const SidebarNavItemLabel = ({
134
+ className,
135
+ ...props
136
+ }: SidebarNavItemLabelProps) => (
137
+ <span className={cn("min-w-0 flex-1 truncate", className)} {...props} />
138
+ )
139
+
140
+ export const SidebarNavBadge = ({
141
+ className,
142
+ ...props
143
+ }: SidebarNavBadgeProps) => (
144
+ <span
145
+ className={cn(
146
+ "text-2xs-strong ml-auto inline-flex min-w-5 shrink-0 items-center justify-center rounded-full border border-border-primary bg-surface px-1.5 py-0.5 text-fg-secondary",
147
+ className,
148
+ )}
149
+ {...props}
150
+ />
151
+ )
69
152
 
70
153
  export const SidebarGroup = ({ className, ...props }: SidebarGroupProps) => (
71
154
  <div className={cn("flex flex-col gap-1 py-2", className)} {...props} />
72
- );
155
+ )
73
156
 
74
157
  export const SidebarGroupLabel = ({
75
158
  className,
@@ -79,4 +162,6 @@ export const SidebarGroupLabel = ({
79
162
  className={cn("px-3 py-1.5 text-xs-strong text-fg-tertiary", className)}
80
163
  {...props}
81
164
  />
82
- );
165
+ )
166
+
167
+ export { sidebarNavItemVariants, sidebarVariants }