Open History to see the last commit
1import { mergeProps } from "@base-ui/react/merge-props";
2import { useRender } from "@base-ui/react/use-render";
3import type { ComponentProps } from "react";
4
5import { cn } from "~/lib/utils";
6import { Button } from "../ui/button";
7import {
8 THREAD_DETAILS_PANEL_ROW_CLASS,
9 THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
10 THREAD_DETAILS_PANEL_SPLIT_PRIMARY_CLASS,
11 THREAD_DETAILS_PANEL_SPLIT_SECONDARY_CLASS,
12 THREAD_DETAILS_PANEL_ICON_ACTION_CLASS,
13 THREAD_DETAILS_PANEL_META_ACTION_CLASS,
14 THREAD_DETAILS_PANEL_CHEVRON_CLASS,
15 THREAD_DETAILS_PANEL_SELECT_LABEL_CLASS,
16 THREAD_DETAILS_PANEL_SELECT_ICON_CLASS,
17} from "./threadDetailsPanelStyles";
18import { ChevronDownIcon } from "lucide-react";
19
20const parts = {
21 row: THREAD_DETAILS_PANEL_ROW_CLASS,
22 select: THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
23 primary: THREAD_DETAILS_PANEL_SPLIT_PRIMARY_CLASS,
24 secondary: THREAD_DETAILS_PANEL_SPLIT_SECONDARY_CLASS,
25 icon: THREAD_DETAILS_PANEL_ICON_ACTION_CLASS,
26 meta: THREAD_DETAILS_PANEL_META_ACTION_CLASS,
27};
28
29export function ThreadDetailsControl({
30 panel = true,
31 part = "row",
32 multiline = false,
33 tone = "default",
34 className,
35 children,
36 size = "default",
37 variant = "default",
38 render,
39 ...props
40}: ComponentProps<typeof Button> & {
41 panel?: boolean;
42 part?: keyof typeof parts;
43 multiline?: boolean;
44 tone?: "default" | "muted" | "primary" | "destructive";
45}) {
46 const control = useRender({
47 defaultTagName: "button",
48 render,
49 props: mergeProps<"button">(
50 {
51 type: render ? undefined : "button",
52 className: cn(
53 "relative inline-flex shrink-0 cursor-pointer items-center whitespace-nowrap border outline-none transition-[box-shadow,scale] [&:not([aria-haspopup]):not(:disabled):not([aria-disabled=true])]:will-change-[scale] [&:active:not([aria-haspopup])]:scale-[0.97] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 disabled:pointer-events-none disabled:opacity-64 aria-disabled:cursor-not-allowed aria-disabled:opacity-64 [&_svg]:shrink-0",
54 parts[part],
55 multiline && "h-auto min-h-8 py-1.5 disabled:opacity-100 sm:h-auto",
56 tone === "muted" && "text-muted-foreground hover:text-foreground/80",
57 tone === "primary" &&
58 // The row surface forces a faint hover fill with !important; the solid pill keeps its own.
59 "bg-primary text-primary-foreground hover:text-primary-foreground hover:!bg-primary/85 data-pressed:!bg-primary/85 dark:hover:!bg-primary/85 dark:data-pressed:!bg-primary/85",
60 tone === "destructive" &&
61 "text-destructive hover:text-destructive data-pressed:text-destructive",
62 // PR row actions share one 20px pill with a 1px inset focus ring that stays clear of the
63 // label. The solid pill draws it in the canvas color so it shows against the fill.
64 part === "meta" &&
65 (tone === "primary" || tone === "destructive") &&
66 "h-5 focus-visible:ring-1 focus-visible:ring-offset-0",
67 part === "meta" && tone === "primary" && "focus-visible:ring-background",
68 part === "meta" &&
69 tone === "destructive" &&
70 "bg-destructive/10 hover:!bg-destructive/16 data-pressed:!bg-destructive/16 dark:hover:!bg-destructive/16 dark:data-pressed:!bg-destructive/16",
71 className,
72 ),
73 },
74 props,
75 {
76 children:
77 part === "select" ? (
78 <>
79 <span data-slot="select-label" className={THREAD_DETAILS_PANEL_SELECT_LABEL_CLASS}>
80 {children}
81 </span>
82 <span data-slot="select-icon" className={THREAD_DETAILS_PANEL_SELECT_ICON_CLASS}>
83 <ChevronDownIcon className={THREAD_DETAILS_PANEL_CHEVRON_CLASS} />
84 </span>
85 </>
86 ) : (
87 children
88 ),
89 },
90 ),
91 });
92 if (!panel) {
93 return (
94 <Button
95 {...props}
96 render={render}
97 size={multiline ? "sm-multiline" : size}
98 variant={variant}
99 className={className}
100 >
101 {children}
102 </Button>
103 );
104 }
105 return control;
106}