@orsetra/shared-ui 1.3.3 → 1.3.4
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.
|
@@ -4,20 +4,21 @@ import { ArrowLeft } from "lucide-react"
|
|
|
4
4
|
import Link from "next/link"
|
|
5
5
|
import type { ReactNode, ElementType } from "react"
|
|
6
6
|
import { cn } from "../../lib/utils"
|
|
7
|
+
import { Button } from "./button"
|
|
7
8
|
|
|
8
9
|
export interface DetailPageHeaderTab {
|
|
9
10
|
id: string
|
|
10
11
|
label: string
|
|
11
12
|
icon?: ElementType
|
|
12
|
-
/**
|
|
13
|
+
/** URL-based navigation */
|
|
13
14
|
href?: string
|
|
15
|
+
/** State-based or programmatic navigation */
|
|
16
|
+
action?: () => void
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export interface DetailPageHeaderProps {
|
|
17
|
-
/** Back
|
|
18
|
-
|
|
19
|
-
/** Back breadcrumb label */
|
|
20
|
-
backLabel?: string
|
|
20
|
+
/** If provided, renders a secondary Back button on the right that calls history.back() */
|
|
21
|
+
backText?: string
|
|
21
22
|
/** Optional icon rendered in a blue square container */
|
|
22
23
|
icon?: ReactNode
|
|
23
24
|
/** Page title */
|
|
@@ -26,7 +27,7 @@ export interface DetailPageHeaderProps {
|
|
|
26
27
|
description?: string
|
|
27
28
|
/** Slot for badges or status nodes placed next to the title */
|
|
28
29
|
statusNode?: ReactNode
|
|
29
|
-
/** Slot for action buttons placed to the right */
|
|
30
|
+
/** Slot for action buttons placed to the right, after the back button */
|
|
30
31
|
actions?: ReactNode
|
|
31
32
|
/** Optional tab navigation */
|
|
32
33
|
tabBar?: DetailPageHeaderTab[]
|
|
@@ -34,15 +35,12 @@ export interface DetailPageHeaderProps {
|
|
|
34
35
|
tabBarPosition?: "left" | "right"
|
|
35
36
|
/** Currently active tab id */
|
|
36
37
|
activeTab?: string
|
|
37
|
-
/** State-based tab change handler. When provided, tabs render as buttons instead of Links */
|
|
38
|
-
onTabChange?: (id: string) => void
|
|
39
38
|
/** Extra classes on the root element – use to adjust spacing/height */
|
|
40
39
|
className?: string
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export function DetailPageHeader({
|
|
44
|
-
|
|
45
|
-
backLabel,
|
|
43
|
+
backText,
|
|
46
44
|
icon,
|
|
47
45
|
title,
|
|
48
46
|
description,
|
|
@@ -51,25 +49,13 @@ export function DetailPageHeader({
|
|
|
51
49
|
tabBar,
|
|
52
50
|
tabBarPosition = "left",
|
|
53
51
|
activeTab,
|
|
54
|
-
onTabChange,
|
|
55
52
|
className,
|
|
56
53
|
}: DetailPageHeaderProps) {
|
|
57
54
|
return (
|
|
58
|
-
<div className={cn("mb-
|
|
59
|
-
|
|
60
|
-
{/* Back link */}
|
|
61
|
-
{backHref && backLabel && (
|
|
62
|
-
<Link
|
|
63
|
-
href={backHref}
|
|
64
|
-
className="inline-flex items-center gap-1.5 text-xs text-text-secondary hover:text-ibm-blue-60 transition-colors mb-3"
|
|
65
|
-
>
|
|
66
|
-
<ArrowLeft className="h-3.5 w-3.5" />
|
|
67
|
-
{backLabel}
|
|
68
|
-
</Link>
|
|
69
|
-
)}
|
|
55
|
+
<div className={cn("mb-3", className)}>
|
|
70
56
|
|
|
71
57
|
{/* Title row */}
|
|
72
|
-
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-
|
|
58
|
+
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
|
73
59
|
<div className="flex items-center gap-3 min-w-0">
|
|
74
60
|
{icon && (
|
|
75
61
|
<div className="h-9 w-9 rounded bg-ibm-blue-10 flex items-center justify-center flex-shrink-0">
|
|
@@ -87,14 +73,26 @@ export function DetailPageHeader({
|
|
|
87
73
|
</div>
|
|
88
74
|
</div>
|
|
89
75
|
|
|
90
|
-
{actions && (
|
|
91
|
-
<div className="flex items-center gap-2 shrink-0">
|
|
76
|
+
{(backText || actions) && (
|
|
77
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
78
|
+
{backText && (
|
|
79
|
+
<Button
|
|
80
|
+
variant="secondary"
|
|
81
|
+
size="sm"
|
|
82
|
+
onClick={() => window.history.back()}
|
|
83
|
+
>
|
|
84
|
+
<ArrowLeft className="h-4 w-4 mr-1.5" />
|
|
85
|
+
{backText}
|
|
86
|
+
</Button>
|
|
87
|
+
)}
|
|
88
|
+
{actions}
|
|
89
|
+
</div>
|
|
92
90
|
)}
|
|
93
91
|
</div>
|
|
94
92
|
|
|
95
93
|
{/* Tab bar */}
|
|
96
94
|
{tabBar && tabBar.length > 0 && (
|
|
97
|
-
<div className={cn("mt-
|
|
95
|
+
<div className={cn("mt-3 flex", tabBarPosition === "right" ? "justify-end" : "justify-start")}>
|
|
98
96
|
<div className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
|
|
99
97
|
{tabBar.map((tab) => {
|
|
100
98
|
const Icon = tab.icon
|
|
@@ -106,12 +104,12 @@ export function DetailPageHeader({
|
|
|
106
104
|
: "hover:text-ibm-gray-100"
|
|
107
105
|
)
|
|
108
106
|
|
|
109
|
-
if (
|
|
107
|
+
if (tab.action) {
|
|
110
108
|
return (
|
|
111
109
|
<button
|
|
112
110
|
key={tab.id}
|
|
113
111
|
type="button"
|
|
114
|
-
onClick={
|
|
112
|
+
onClick={tab.action}
|
|
115
113
|
className={triggerClass}
|
|
116
114
|
>
|
|
117
115
|
{Icon && <Icon className="h-4 w-4" />}
|