@lateralus-ai/shipping-ui 2.0.0-dev.20 → 2.0.0-dev.3

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 (41) hide show
  1. package/dist/components/Entry.d.ts +7 -27
  2. package/dist/components/Tabs.d.ts +5 -5
  3. package/dist/components/index.d.ts +2 -4
  4. package/dist/index.cjs +25 -25
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.esm.js +4628 -4857
  7. package/dist/patterns/Search/ResultRow.d.ts +5 -11
  8. package/dist/patterns/Sidebar/sidebar-styles.d.ts +1 -2
  9. package/dist/primitives/Badge.d.ts +4 -7
  10. package/dist/primitives/index.d.ts +1 -1
  11. package/dist/tailwind-theme.d.ts +4 -8
  12. package/dist/{theme-entry-tLBc6zGT.mjs → theme-entry-CxDa1D0_.mjs} +8 -12
  13. package/dist/theme-entry-D2X3Ptjf.js +1 -0
  14. package/dist/theme.cjs +1 -1
  15. package/dist/theme.esm.js +1 -1
  16. package/package.json +2 -2
  17. package/src/components/Entry.tsx +45 -119
  18. package/src/components/Tabs.tsx +139 -146
  19. package/src/components/index.ts +42 -56
  20. package/src/index.ts +74 -85
  21. package/src/patterns/Search/ResultRow.tsx +58 -44
  22. package/src/patterns/Search/SearchModal.tsx +292 -310
  23. package/src/patterns/Search/index.ts +31 -31
  24. package/src/patterns/Sidebar/CollapsibleNavGroup.tsx +1 -1
  25. package/src/patterns/Sidebar/SidebarAction.tsx +17 -26
  26. package/src/patterns/Sidebar/SidebarEntry.tsx +39 -54
  27. package/src/patterns/Sidebar/SidebarLink.tsx +20 -30
  28. package/src/patterns/Sidebar/sidebar-styles.ts +1 -2
  29. package/src/patterns/Skeleton/Skeleton.tsx +56 -56
  30. package/src/primitives/Badge.tsx +10 -20
  31. package/src/primitives/Button.tsx +11 -16
  32. package/src/primitives/index.ts +1 -1
  33. package/src/stories/canvases/ContentCanvas.tsx +114 -353
  34. package/src/stories/canvases/SearchCanvas.tsx +150 -150
  35. package/src/tailwind-theme.ts +182 -186
  36. package/src/utils/cn.ts +1 -28
  37. package/dist/components/PageHeader.d.ts +0 -41
  38. package/dist/components/ScrollableList.d.ts +0 -22
  39. package/dist/theme-entry-Dwr2mV7_.js +0 -1
  40. package/src/components/PageHeader.tsx +0 -132
  41. package/src/components/ScrollableList.tsx +0 -61
@@ -45,6 +45,7 @@ export const CollapsibleNavGroup = ({
45
45
  href={href}
46
46
  label={label}
47
47
  icon={icon}
48
+ tall
48
49
  onNavigate={onNavigate}
49
50
  trailing={
50
51
  <IconButton
@@ -92,7 +93,6 @@ export const CollapsibleNavGroup = ({
92
93
  ) : undefined
93
94
  }
94
95
  menuItems={entryMenuItems}
95
- onNavigate={onNavigate}
96
96
  />
97
97
  ))}
98
98
  </div>
@@ -1,14 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { Tooltip } from "../../primitives";
3
3
  import { cn } from "../../utils/cn";
4
- import {
5
- sidebarLinkActive,
6
- sidebarLinkIdle,
7
- sidebarRowInteractiveHover,
8
- sidebarSectionIconClass,
9
- sidebarSectionShell,
10
- sidebarSectionShellCollapsed,
11
- } from "./sidebar-styles";
4
+ import { sidebarLinkActive, sidebarLinkIdle, sidebarRowClass, sidebarRowInteractiveHover, sidebarSectionAnchorClass } from "./sidebar-styles";
12
5
 
13
6
  export type SidebarActionProps = {
14
7
  label: string;
@@ -30,28 +23,26 @@ export const SidebarAction = ({
30
23
  className,
31
24
  }: SidebarActionProps) => {
32
25
  const row = (
33
- <button
34
- type="button"
35
- onClick={onClick}
36
- aria-label={label}
37
- className={cn(
38
- collapsed ? sidebarSectionShellCollapsed : sidebarSectionShell,
39
- "cursor-pointer text-left",
40
- active ? sidebarLinkActive : sidebarLinkIdle,
41
- !active && sidebarRowInteractiveHover,
42
- !collapsed && "gap-2",
43
- className,
26
+ <div
27
+ className={sidebarRowClass(
28
+ cn(
29
+ active ? sidebarLinkActive : sidebarLinkIdle,
30
+ !active && sidebarRowInteractiveHover,
31
+ className,
32
+ ),
44
33
  )}
45
34
  data-active={active}
46
35
  data-collapsed={collapsed}
47
36
  >
48
- {icon && <span className={sidebarSectionIconClass}>{icon}</span>}
49
- {!collapsed && (
50
- <span className="min-w-0 flex-1 truncate text-caption-2 tracking-[0.01em]">
51
- {label}
52
- </span>
53
- )}
54
- </button>
37
+ <button
38
+ type="button"
39
+ className={sidebarSectionAnchorClass(collapsed ? "justify-center" : undefined)}
40
+ onClick={onClick}
41
+ >
42
+ {icon && <span className="shrink-0 [&>svg]:size-5">{icon}</span>}
43
+ {!collapsed && <span className="truncate">{label}</span>}
44
+ </button>
45
+ </div>
55
46
  );
56
47
 
57
48
  if (!collapsed || !tooltip) return row;
@@ -3,6 +3,7 @@ import { DropdownMenu, DropdownMenuItem, IconButton } from "../../primitives";
3
3
  import { MoreIcon } from "../../icons";
4
4
  import { cn } from "../../utils/cn";
5
5
  import {
6
+ sidebarAnchorClass,
6
7
  sidebarEntryHover,
7
8
  sidebarEntryIdle,
8
9
  sidebarEntrySelected,
@@ -65,74 +66,58 @@ export const SidebarEntry = ({
65
66
  )}
66
67
  data-state={state}
67
68
  >
68
- {/* Stretch hit target over shell padding (hover was on the shell, link was content-only). */}
69
69
  <a
70
70
  href={href}
71
- className="absolute inset-0 z-0 rounded-control"
72
- aria-label={label}
71
+ className={sidebarAnchorClass("gap-1")}
73
72
  aria-current={state === "selected" ? "true" : undefined}
74
73
  onClick={onNavigate}
75
- />
76
-
77
- <div className="pointer-events-none relative z-10 flex min-w-0 flex-1 items-center gap-2 text-caption-2">
78
- {icon && <span className="shrink-0 [&>svg]:size-4">{icon}</span>}
74
+ >
75
+ {icon && <span className="shrink-0 px-1 py-0.5 [&>svg]:size-4">{icon}</span>}
79
76
  <span className="truncate">{label}</span>
80
- </div>
77
+ </a>
81
78
 
82
79
  {badge && !(showMenu && menuVisible) && (
83
80
  <span
84
- className={cn(
85
- "pointer-events-none relative z-10 shrink-0",
86
- showMenu && !forceMenuVisible && "group-hover/row:hidden",
87
- )}
81
+ className={cn("shrink-0", showMenu && !forceMenuVisible && "group-hover/row:hidden")}
88
82
  >
89
83
  {badge}
90
84
  </span>
91
85
  )}
92
86
 
93
87
  {showMenu && (
94
- <div className="relative z-10 shrink-0">
95
- <DropdownMenu
96
- open={menuOpen}
97
- onOpenChange={setMenuOpen}
98
- align="end"
99
- trigger={
100
- <IconButton
101
- hierarchy="quaternary"
102
- size="small"
103
- aria-label={`Actions for ${label}`}
104
- aria-haspopup="menu"
105
- aria-expanded={menuOpen}
106
- className={cn(
107
- "size-6 min-h-6 shrink-0 p-1",
108
- sidebarQuaternaryIconButton,
109
- !forceMenuVisible &&
110
- !menuOpen &&
111
- "hidden group-hover/row:inline-flex",
112
- (forceMenuVisible || menuOpen) && "inline-flex",
113
- )}
114
- >
115
- <MoreIcon size="small" />
116
- </IconButton>
117
- }
118
- >
119
- {menuItems?.map((item) => (
120
- <DropdownMenuItem
121
- key={item.id}
122
- className={cn(
123
- item.destructive &&
124
- "text-red-500 hover:bg-red-50 hover:text-red-700",
125
- )}
126
- onSelect={() => item.onSelect()}
127
- >
128
- {item.icon && (
129
- <span className="shrink-0 [&>svg]:size-4">{item.icon}</span>
130
- )}
131
- {item.label}
132
- </DropdownMenuItem>
133
- ))}
134
- </DropdownMenu>
135
- </div>
88
+ <DropdownMenu
89
+ open={menuOpen}
90
+ onOpenChange={setMenuOpen}
91
+ align="end"
92
+ trigger={
93
+ <IconButton
94
+ hierarchy="quaternary"
95
+ size="small"
96
+ aria-label={`Actions for ${label}`}
97
+ aria-haspopup="menu"
98
+ aria-expanded={menuOpen}
99
+ className={cn(
100
+ "size-6 min-h-6 shrink-0 p-1",
101
+ sidebarQuaternaryIconButton,
102
+ !forceMenuVisible && !menuOpen && "hidden group-hover/row:inline-flex",
103
+ (forceMenuVisible || menuOpen) && "inline-flex",
104
+ )}
105
+ >
106
+ <MoreIcon size="small" />
107
+ </IconButton>
108
+ }
109
+ >
110
+ {menuItems?.map((item) => (
111
+ <DropdownMenuItem
112
+ key={item.id}
113
+ className={cn(item.destructive && "text-red-500 hover:bg-red-50 hover:text-red-700")}
114
+ onSelect={() => item.onSelect()}
115
+ >
116
+ {item.icon && <span className="shrink-0 [&>svg]:size-4">{item.icon}</span>}
117
+ {item.label}
118
+ </DropdownMenuItem>
119
+ ))}
120
+ </DropdownMenu>
136
121
  )}
137
122
  </div>
138
123
  );
@@ -58,7 +58,6 @@ export const SidebarLink = ({
58
58
  const row = collapsed ? (
59
59
  <div
60
60
  className={cn(
61
- "relative",
62
61
  sidebarSectionShellCollapsed,
63
62
  stateStyles[state],
64
63
  state === "idle" && sidebarRowInteractiveHover,
@@ -69,24 +68,24 @@ export const SidebarLink = ({
69
68
  >
70
69
  <a
71
70
  href={href}
72
- className="absolute inset-0 z-0 rounded-control"
71
+ className="flex w-full items-center justify-center"
73
72
  aria-current={state === "active" ? "page" : undefined}
74
73
  aria-label={label}
75
74
  onClick={onNavigate}
76
- />
77
- <span className="pointer-events-none relative z-10 inline-flex shrink-0 items-center">
78
- {icon && <span className={sidebarSectionIconClass}>{icon}</span>}
79
- {unread && (
80
- <span aria-hidden className={sidebarUnreadOverlay}>
81
- <span className={sidebarUnreadDot} />
82
- </span>
83
- )}
84
- </span>
75
+ >
76
+ <span className="relative inline-flex shrink-0 items-center">
77
+ {icon && <span className={sidebarSectionIconClass}>{icon}</span>}
78
+ {unread && (
79
+ <span aria-hidden className={sidebarUnreadOverlay}>
80
+ <span className={sidebarUnreadDot} />
81
+ </span>
82
+ )}
83
+ </span>
84
+ </a>
85
85
  </div>
86
86
  ) : (
87
87
  <div
88
88
  className={cn(
89
- "relative",
90
89
  tall ? sidebarSectionShellTall : sidebarSectionShell,
91
90
  stateStyles[state],
92
91
  state === "idle" && sidebarRowInteractiveHover,
@@ -95,27 +94,18 @@ export const SidebarLink = ({
95
94
  data-state={state}
96
95
  data-collapsed={collapsed}
97
96
  >
98
- <a
99
- href={href}
100
- className="absolute inset-0 z-0 rounded-control"
101
- aria-current={state === "active" ? "page" : undefined}
102
- aria-label={label}
103
- onClick={onNavigate}
104
- />
105
- <div
106
- className={cn(
107
- sidebarSectionContent,
108
- "pointer-events-none relative z-10",
109
- )}
110
- >
111
- <span className="flex min-w-0 flex-1 items-center gap-2 truncate text-left">
97
+ <div className={sidebarSectionContent}>
98
+ <a
99
+ href={href}
100
+ className="flex min-w-0 flex-1 items-center gap-2 truncate text-left"
101
+ aria-current={state === "active" ? "page" : undefined}
102
+ onClick={onNavigate}
103
+ >
112
104
  {icon && <span className={sidebarSectionIconClass}>{icon}</span>}
113
105
  <span className={sidebarSectionLabelClass}>{label}</span>
114
106
  {badge}
115
- </span>
116
- {trailing != null && (
117
- <span className="pointer-events-auto shrink-0">{trailing}</span>
118
- )}
107
+ </a>
108
+ {trailing}
119
109
  </div>
120
110
  </div>
121
111
  );
@@ -30,8 +30,7 @@ export const sidebarUnreadOverlay =
30
30
  export const sidebarUnreadDot =
31
31
  "absolute right-0 top-0 size-1.5 rounded-full bg-[#802c20]";
32
32
 
33
- /** Icon + label row inside a section shell — no extra inset (shell already has p-2). */
34
- export const sidebarSectionContent = "flex w-full min-w-0 items-center gap-2";
33
+ export const sidebarSectionContent = "flex w-full min-w-0 items-center gap-2 pl-1";
35
34
 
36
35
  export const sidebarSectionIconClass = "shrink-0 [&>svg]:size-4";
37
36
 
@@ -1,56 +1,56 @@
1
- import { cn } from "../../utils/cn";
2
-
3
- export type SkeletonVariant = "content" | "search";
4
-
5
- export type SkeletonProps = {
6
- variant?: SkeletonVariant;
7
- className?: string;
8
- };
9
-
10
- const ContentSkeleton = () => (
11
- <div className="flex flex-col gap-4 p-4" aria-hidden>
12
- <div className="flex items-center gap-3">
13
- <div className="size-8 animate-pulse rounded-full bg-grey-200" />
14
- <div className="flex flex-1 flex-col gap-2">
15
- <div className="h-4 w-1/3 animate-pulse rounded bg-grey-200" />
16
- <div className="h-3 w-1/4 animate-pulse rounded bg-grey-100" />
17
- </div>
18
- </div>
19
- <div className="flex flex-col gap-2">
20
- <div className="h-4 w-full animate-pulse rounded bg-grey-200" />
21
- <div className="h-4 w-5/6 animate-pulse rounded bg-grey-200" />
22
- <div className="h-4 w-2/3 animate-pulse rounded bg-grey-100" />
23
- </div>
24
- <div className="h-24 animate-pulse rounded-control bg-grey-100" />
25
- </div>
26
- );
27
-
28
- /** Figma Search loading — 8 rows of icon + bar (no input/pills). */
29
- const SearchSkeleton = () => {
30
- const widths = [250, 300, 220, 255, 250, 300, 220, 255];
31
-
32
- return (
33
- <div className="flex flex-col gap-1.5 p-4" aria-hidden>
34
- {widths.map((width, index) => (
35
- <div key={index} className="flex items-start gap-2 py-2">
36
- <div className="size-3 shrink-0 animate-pulse rounded-md bg-grey-200" />
37
- <div
38
- className="h-3 animate-pulse rounded-md bg-grey-200"
39
- style={{ width }}
40
- />
41
- </div>
42
- ))}
43
- </div>
44
- );
45
- };
46
-
47
- export const Skeleton = ({ variant = "content", className }: SkeletonProps) => (
48
- <div
49
- className={cn("rounded-control bg-background-primary", className)}
50
- role="status"
51
- aria-label="Loading"
52
- data-variant={variant}
53
- >
54
- {variant === "content" ? <ContentSkeleton /> : <SearchSkeleton />}
55
- </div>
56
- );
1
+ import { cn } from "../../utils/cn";
2
+
3
+ export type SkeletonVariant = "content" | "search";
4
+
5
+ export type SkeletonProps = {
6
+ variant?: SkeletonVariant;
7
+ className?: string;
8
+ };
9
+
10
+ const ContentSkeleton = () => (
11
+ <div className="flex flex-col gap-4 p-4" aria-hidden>
12
+ <div className="flex items-center gap-3">
13
+ <div className="size-8 animate-pulse rounded-full bg-grey-200" />
14
+ <div className="flex flex-1 flex-col gap-2">
15
+ <div className="h-4 w-1/3 animate-pulse rounded bg-grey-200" />
16
+ <div className="h-3 w-1/4 animate-pulse rounded bg-grey-100" />
17
+ </div>
18
+ </div>
19
+ <div className="flex flex-col gap-2">
20
+ <div className="h-4 w-full animate-pulse rounded bg-grey-200" />
21
+ <div className="h-4 w-5/6 animate-pulse rounded bg-grey-200" />
22
+ <div className="h-4 w-2/3 animate-pulse rounded bg-grey-100" />
23
+ </div>
24
+ <div className="h-24 animate-pulse rounded-control bg-grey-100" />
25
+ </div>
26
+ );
27
+
28
+ /** Figma Search loading — 8 rows of icon + bar (no input/pills). */
29
+ const SearchSkeleton = () => {
30
+ const widths = [250, 300, 220, 255, 250, 300, 220, 255];
31
+
32
+ return (
33
+ <div className="flex flex-col gap-1.5 p-4" aria-hidden>
34
+ {widths.map((width, index) => (
35
+ <div key={index} className="flex items-start gap-2 py-2">
36
+ <div className="size-3 shrink-0 animate-pulse rounded-md bg-grey-200" />
37
+ <div
38
+ className="h-3 animate-pulse rounded-md bg-grey-200"
39
+ style={{ width }}
40
+ />
41
+ </div>
42
+ ))}
43
+ </div>
44
+ );
45
+ };
46
+
47
+ export const Skeleton = ({ variant = "content", className }: SkeletonProps) => (
48
+ <div
49
+ className={cn("rounded-control bg-background-primary", className)}
50
+ role="status"
51
+ aria-label="Loading"
52
+ data-variant={variant}
53
+ >
54
+ {variant === "content" ? <ContentSkeleton /> : <SearchSkeleton />}
55
+ </div>
56
+ );
@@ -1,40 +1,30 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { cn } from "../utils/cn";
3
3
 
4
- export type BadgeColor = "blue" | "green" | "red" | "orange" | "purple" | "grey";
5
- export type BadgeType = "label" | "icon";
4
+ type BadgeColor = "blue" | "green" | "red" | "orange" | "purple" | "grey";
5
+ type BadgeType = "label" | "icon";
6
6
 
7
7
  export type BadgeProps = {
8
8
  color: BadgeColor;
9
- /** `label` grows with content (pill). `icon` is a compact circle for a glyph/digit. */
10
9
  type?: BadgeType;
11
10
  children: ReactNode;
12
11
  className?: string;
13
12
  };
14
13
 
15
14
  const colorStyles: Record<BadgeColor, string> = {
16
- blue: "bg-meta-blue text-meta-onBlue",
17
- green: "bg-meta-green text-meta-onGreen",
18
- red: "bg-meta-red text-meta-onRed",
19
- orange: "bg-meta-orange text-meta-onOrange",
20
- purple: "bg-meta-purple text-meta-onPurple",
15
+ blue: "bg-meta-blue text-meta-on-blue",
16
+ green: "bg-meta-green text-meta-on-green",
17
+ red: "bg-meta-red text-meta-on-red",
18
+ orange: "bg-meta-orange text-meta-on-orange",
19
+ purple: "bg-meta-purple text-meta-on-purple",
21
20
  grey: "bg-grey-100 text-display-on-light-secondary",
22
21
  };
23
22
 
24
- /**
25
- * Figma Badge (4365:74363) — padding + rounded-full; no fixed width on labels.
26
- */
27
- export const Badge = ({
28
- color,
29
- type = "label",
30
- children,
31
- className,
32
- }: BadgeProps) => (
23
+ export const Badge = ({ color, type = "label", children, className }: BadgeProps) => (
33
24
  <span
34
25
  className={cn(
35
- "inline-flex shrink-0 items-center justify-center text-footnote-em leading-none",
36
- type === "label" && "rounded-full px-1.5 py-0.5",
37
- type === "icon" && "size-5 rounded-full [&>svg]:size-3",
26
+ "inline-flex shrink-0 items-center justify-center text-footnote-em",
27
+ type === "label" ? "rounded-control px-2 py-0.5" : "size-6 rounded-full",
38
28
  colorStyles[color],
39
29
  className,
40
30
  )}
@@ -121,8 +121,6 @@ export const Button = ({
121
121
  ...props
122
122
  }: ButtonProps) => {
123
123
  const resolvedIcon = icon ?? startIcon;
124
- const renderedIcon = renderIcon(resolvedIcon);
125
- const hasIcon = renderedIcon != null;
126
124
  const hasDropdown = Boolean(dropdownOptions?.length);
127
125
  const dropdownDisabled = !dropdownOptions?.some((option) => !option.disabled);
128
126
  const mainAppearance = getAppearance(disabled, forcedState);
@@ -137,18 +135,11 @@ export const Button = ({
137
135
  dropdownAppearance,
138
136
  forcedState,
139
137
  );
140
-
141
- /** Label-only: centered text. With start icon: gap between icon and label. */
142
- const content = (
143
- <span
144
- className={cn(
145
- "flex min-h-6 items-center justify-center",
146
- hasIcon && "gap-2",
147
- )}
148
- >
149
- {renderedIcon}
150
- <span>{children}</span>
151
- </span>
138
+ const label = (
139
+ <>
140
+ {renderIcon(resolvedIcon)}
141
+ <span className="px-2">{children}</span>
142
+ </>
152
143
  );
153
144
 
154
145
  if (!hasDropdown) {
@@ -165,7 +156,9 @@ export const Button = ({
165
156
  )}
166
157
  {...props}
167
158
  >
168
- {content}
159
+ <span className="flex min-h-6 items-center [&>*:first-child]:-mr-1 [&>*:last-child]:-mr-1">
160
+ {label}
161
+ </span>
169
162
  </button>
170
163
  );
171
164
  }
@@ -182,7 +175,9 @@ export const Button = ({
182
175
  )}
183
176
  {...props}
184
177
  >
185
- {content}
178
+ <span className="flex min-h-6 items-center [&>*:first-child]:-mr-1 [&>*:last-child]:-mr-1">
179
+ {label}
180
+ </span>
186
181
  </button>
187
182
  );
188
183
 
@@ -14,5 +14,5 @@ export { Tooltip, TooltipProvider, type TooltipProps, type TooltipSide } from ".
14
14
  export { ThinkingDot, type ThinkingDotProps } from "./ThinkingDot";
15
15
  export { Count, type CountProps } from "./Count";
16
16
  export { Switch, type SwitchProps } from "./Switch";
17
- export { Badge, type BadgeProps, type BadgeColor, type BadgeType } from "./Badge";
17
+ export { Badge, type BadgeProps } from "./Badge";
18
18
  export { Callout, type CalloutProps } from "./Callout";