@trycompai/design-system 1.0.9 → 1.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycompai/design-system",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "Design system for Comp AI - shadcn-style components with Tailwind CSS",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -5,7 +5,7 @@ import * as React from 'react';
5
5
  import { Spinner } from './spinner';
6
6
 
7
7
  const buttonVariants = cva(
8
- "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent text-sm font-medium leading-none [text-box-trim:both] [text-box-edge:cap_alphabetic] focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all duration-200 ease-out active:scale-[0.97] active:duration-75 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
8
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent text-[13px] font-medium leading-none [text-box-trim:both] [text-box-edge:cap_alphabetic] focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all duration-200 ease-out active:scale-[0.97] active:duration-75 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
9
9
  {
10
10
  variants: {
11
11
  variant: {
@@ -16,7 +16,7 @@ const buttonVariants = cva(
16
16
  secondary:
17
17
  'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
18
18
  ghost:
19
- 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
19
+ 'hover:bg-accent hover:text-foreground dark:hover:bg-accent/50 aria-expanded:bg-accent aria-expanded:text-foreground',
20
20
  destructive:
21
21
  'bg-destructive/10 text-destructive hover:bg-destructive/15 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 focus-visible:border-destructive/40 dark:bg-destructive/20 dark:hover:bg-destructive/30',
22
22
  link: 'text-primary underline-offset-4 hover:underline',
@@ -27,21 +27,21 @@ const buttonVariants = cva(
27
27
  },
28
28
  size: {
29
29
  default:
30
- 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
31
- xs: "h-6 gap-1 px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
32
- sm: "h-7 gap-1 px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
33
- lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3',
34
- icon: 'size-8',
30
+ 'h-7 gap-1 px-2 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5',
31
+ xs: "h-5 gap-0.5 px-1.5 text-[11px] has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 [&_svg:not([class*='size-'])]:size-3",
32
+ sm: "h-6 gap-1 px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
33
+ lg: 'h-8 gap-1 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
34
+ icon: 'size-7',
35
35
  'icon-xs':
36
- "size-6 [&_svg:not([class*='size-'])]:size-3",
36
+ "size-5 [&_svg:not([class*='size-'])]:size-3",
37
37
  'icon-sm':
38
- 'size-7',
39
- 'icon-lg': 'size-9',
38
+ 'size-6',
39
+ 'icon-lg': 'size-8',
40
40
  // Round icon buttons - for avatar triggers and circular icons
41
- 'icon-round': 'size-8 rounded-full',
42
- 'icon-round-xs': "size-6 rounded-full [&_svg:not([class*='size-'])]:size-3",
43
- 'icon-round-sm': 'size-7 rounded-full',
44
- 'icon-round-lg': 'size-9 rounded-full',
41
+ 'icon-round': 'size-7 rounded-full',
42
+ 'icon-round-xs': "size-5 rounded-full [&_svg:not([class*='size-'])]:size-3",
43
+ 'icon-round-sm': 'size-6 rounded-full',
44
+ 'icon-round-lg': 'size-8 rounded-full',
45
45
  // Calendar day button - special size for calendar day cells
46
46
  'calendar-day': [
47
47
  // Base sizing
@@ -1,21 +1,25 @@
1
+ import { ArrowLeft } from '@carbon/icons-react';
1
2
  import * as React from 'react';
2
3
 
3
4
  import { Heading } from '../atoms/heading';
4
- import { Text } from '../atoms/text';
5
- import { Stack } from '../atoms/stack';
5
+ import { Breadcrumb, type BreadcrumbItemData } from './breadcrumb';
6
6
 
7
7
  interface PageHeaderProps extends Omit<React.ComponentProps<'div'>, 'className'> {
8
8
  title: string;
9
- description?: string;
10
- /** Additional descriptive text below description */
11
- meta?: string;
12
9
  actions?: React.ReactNode;
10
+ /** Breadcrumb items to display above the title */
11
+ breadcrumbs?: BreadcrumbItemData[];
12
+ /** Back button href - displays a back arrow link above the title */
13
+ backHref?: string;
14
+ /** Back button label (default: "Back") */
15
+ backLabel?: string;
16
+ /** Tabs to display below the header (typically TabsList) */
17
+ tabs?: React.ReactNode;
13
18
  }
14
19
 
15
- function PageHeader({ title, description, meta, actions, children, ...props }: PageHeaderProps) {
20
+ function PageHeader({ title, actions, breadcrumbs, backHref, backLabel = 'Back', tabs, children, ...props }: PageHeaderProps) {
16
21
  const childArray = React.Children.toArray(children);
17
22
  const extractedActionChildren: React.ReactNode[] = [];
18
- const nonActionChildren: React.ReactNode[] = [];
19
23
 
20
24
  childArray.forEach((child) => {
21
25
  if (
@@ -25,9 +29,7 @@ function PageHeader({ title, description, meta, actions, children, ...props }: P
25
29
  (child.type as unknown as { __pageHeaderSlot?: string }).__pageHeaderSlot === 'actions'))
26
30
  ) {
27
31
  extractedActionChildren.push((child.props as { children?: React.ReactNode }).children);
28
- return;
29
32
  }
30
- nonActionChildren.push(child);
31
33
  });
32
34
 
33
35
  const resolvedActions =
@@ -35,46 +37,55 @@ function PageHeader({ title, description, meta, actions, children, ...props }: P
35
37
  (extractedActionChildren.length > 0 ? extractedActionChildren : undefined);
36
38
 
37
39
  return (
38
- <div data-slot="page-header" className="flex items-start justify-between gap-4" {...props}>
39
- <Stack gap="1">
40
- <Heading level="1">{title}</Heading>
41
- {description && (
42
- <Text size="sm" variant="muted">
43
- {description}
44
- </Text>
45
- )}
46
- {meta && (
47
- <Text size="xs" variant="muted">
48
- {meta}
49
- </Text>
40
+ <div data-slot="page-header" className="flex flex-col gap-1" {...props}>
41
+ {/* Navigation: breadcrumbs or back button */}
42
+ {breadcrumbs && breadcrumbs.length > 0 ? (
43
+ <div className="overflow-hidden">
44
+ <Breadcrumb items={breadcrumbs} separator="chevron" />
45
+ </div>
46
+ ) : backHref ? (
47
+ <a
48
+ href={backHref}
49
+ className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground hover:underline underline-offset-2 transition-colors w-fit"
50
+ >
51
+ <ArrowLeft className="size-4" />
52
+ <span>{backLabel}</span>
53
+ </a>
54
+ ) : null}
55
+
56
+ {/* Main header row */}
57
+ <div className="flex items-center justify-between gap-4 min-w-0">
58
+ <div className="min-w-0 flex-1 truncate">
59
+ <Heading level="1">{title}</Heading>
60
+ </div>
61
+ {resolvedActions && (
62
+ <div className="shrink-0">
63
+ {React.isValidElement(resolvedActions) && resolvedActions.type === PageHeaderActions ? (
64
+ resolvedActions
65
+ ) : (
66
+ <PageHeaderActions>{resolvedActions}</PageHeaderActions>
67
+ )}
68
+ </div>
50
69
  )}
51
- {nonActionChildren}
52
- </Stack>
53
- {resolvedActions &&
54
- (React.isValidElement(resolvedActions) && resolvedActions.type === PageHeaderActions ? (
55
- resolvedActions
56
- ) : (
57
- <PageHeaderActions>{resolvedActions}</PageHeaderActions>
58
- ))}
70
+ </div>
71
+
72
+ {/* Tabs section */}
73
+ {tabs && (
74
+ <div className="mt-2 -mb-px">
75
+ {tabs}
76
+ </div>
77
+ )}
59
78
  </div>
60
79
  );
61
80
  }
62
81
 
63
- function PageHeaderTitle({ ...props }: Omit<React.ComponentProps<typeof Heading>, 'className'>) {
64
- return <Heading data-slot="page-header-title" level="1" {...props} />;
65
- }
66
-
67
- function PageHeaderDescription({ ...props }: Omit<React.ComponentProps<typeof Text>, 'className'>) {
68
- return <Text data-slot="page-header-description" size="sm" variant="muted" {...props} />;
69
- }
70
-
71
82
  function PageHeaderActions({ ...props }: Omit<React.ComponentProps<'div'>, 'className'>) {
72
83
  return (
73
- <div data-slot="page-header-actions" className="flex shrink-0 items-center gap-3" {...props} />
84
+ <div data-slot="page-header-actions" className="flex shrink-0 items-center gap-2" {...props} />
74
85
  );
75
86
  }
76
87
 
77
88
  // Mark compound slots so PageHeader can detect them even if module instances differ.
78
89
  (PageHeaderActions as unknown as { __pageHeaderSlot?: string }).__pageHeaderSlot = 'actions';
79
90
 
80
- export { PageHeader, PageHeaderActions, PageHeaderDescription, PageHeaderTitle };
91
+ export { PageHeader, PageHeaderActions };
@@ -63,7 +63,7 @@ function TableCell({ ...props }: Omit<React.ComponentProps<'td'>, 'className'>)
63
63
  return (
64
64
  <td
65
65
  data-slot="table-cell"
66
- className="px-3 py-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [[data-variant=bordered]_&]:border-r [[data-variant=bordered]_&]:last:border-r-0"
66
+ className="px-3 py-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [[data-variant=bordered]_&]:border-r [[data-variant=bordered]_&]:last:border-r-0 [&_[data-slot=button]]:relative [&_[data-slot=button]]:z-10"
67
67
  {...props}
68
68
  />
69
69
  );
@@ -12,9 +12,9 @@ const pageLayoutVariants = cva('min-h-full bg-background text-foreground', {
12
12
  },
13
13
  padding: {
14
14
  none: '',
15
- sm: 'px-4 py-6',
16
- default: 'px-4 py-10 sm:px-6 lg:px-8',
17
- lg: 'px-6 py-12 sm:px-8 lg:px-12',
15
+ sm: 'px-4',
16
+ default: 'px-4 sm:px-6 lg:px-8',
17
+ lg: 'px-6 sm:px-8 lg:px-12',
18
18
  },
19
19
  },
20
20
  defaultVariants: {