ai-design-system 0.1.115 → 0.1.116
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/components/composites/DocumentTabBar/DocumentTabBar.tsx +207 -62
- package/components/composites/DocumentTabBar/index.ts +1 -1
- package/components/composites/index.ts +1 -1
- package/components/ui/scroll-area.tsx +13 -2
- package/dist/index.cjs +187 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +8 -0
- package/dist/index.d.ts +84 -78
- package/dist/index.js +187 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* Displays open documents as closeable tabs with dirty indicators.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React from 'react'
|
|
8
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
|
9
9
|
import { Tabs, TabsList, TabsTrigger, Button, ScrollArea } from '@/components/primitives'
|
|
10
10
|
import { Icon } from '@/components/primitives/Icon'
|
|
11
11
|
import { cn } from '@/lib/utils'
|
|
12
|
+
|
|
12
13
|
export interface DocumentFile {
|
|
13
14
|
id: string
|
|
14
15
|
name: string
|
|
@@ -50,6 +51,12 @@ export interface DocumentTabBarProps {
|
|
|
50
51
|
* Callback to open/toggle the file explorer (e.g. mobile drawer)
|
|
51
52
|
*/
|
|
52
53
|
onToggleExplorer?: () => void
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether to show navigation scroll arrows when tabs overflow
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
showScrollArrows?: boolean
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
/**
|
|
@@ -62,13 +69,103 @@ export interface DocumentTabBarProps {
|
|
|
62
69
|
* - Close button (X)
|
|
63
70
|
*
|
|
64
71
|
* Features:
|
|
65
|
-
* - Scrollable when tabs overflow horizontally
|
|
72
|
+
* - Scrollable when tabs overflow horizontally with multi-device support
|
|
73
|
+
* - Mouse wheel vertical-to-horizontal scroll translation
|
|
74
|
+
* - Auto-scroll active tab into view
|
|
75
|
+
* - Overflow indicators (edge gradients & navigation chevrons)
|
|
66
76
|
* - Accessible tab navigation via Radix UI Tabs
|
|
67
77
|
* - Close button to remove tabs
|
|
68
78
|
* - Dirty state indicator
|
|
69
79
|
*/
|
|
70
80
|
export const DocumentTabBar = React.memo<DocumentTabBarProps>(
|
|
71
|
-
({
|
|
81
|
+
({
|
|
82
|
+
tabs,
|
|
83
|
+
activeTabId,
|
|
84
|
+
onTabSelect,
|
|
85
|
+
onTabClose,
|
|
86
|
+
onToggleExplorer,
|
|
87
|
+
className,
|
|
88
|
+
showScrollArrows = true,
|
|
89
|
+
}) => {
|
|
90
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
|
91
|
+
const [canScrollLeft, setCanScrollLeft] = useState(false)
|
|
92
|
+
const [canScrollRight, setCanScrollRight] = useState(false)
|
|
93
|
+
|
|
94
|
+
const getViewport = useCallback((): HTMLElement | null => {
|
|
95
|
+
return (
|
|
96
|
+
scrollContainerRef.current?.querySelector<HTMLElement>(
|
|
97
|
+
'[data-slot="scroll-area-viewport"]'
|
|
98
|
+
) ?? null
|
|
99
|
+
)
|
|
100
|
+
}, [])
|
|
101
|
+
|
|
102
|
+
const updateScrollState = useCallback(() => {
|
|
103
|
+
const viewport = getViewport()
|
|
104
|
+
if (!viewport) return
|
|
105
|
+
const { scrollLeft, scrollWidth, clientWidth } = viewport
|
|
106
|
+
setCanScrollLeft(scrollLeft > 2)
|
|
107
|
+
setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 2)
|
|
108
|
+
}, [getViewport])
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const viewport = getViewport()
|
|
112
|
+
if (!viewport) return
|
|
113
|
+
|
|
114
|
+
updateScrollState()
|
|
115
|
+
viewport.addEventListener('scroll', updateScrollState, { passive: true })
|
|
116
|
+
|
|
117
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
118
|
+
updateScrollState()
|
|
119
|
+
})
|
|
120
|
+
resizeObserver.observe(viewport)
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
viewport.removeEventListener('scroll', updateScrollState)
|
|
124
|
+
resizeObserver.disconnect()
|
|
125
|
+
}
|
|
126
|
+
}, [getViewport, updateScrollState, tabs])
|
|
127
|
+
|
|
128
|
+
// Convert mouse wheel vertical scroll to horizontal scroll
|
|
129
|
+
const handleWheel = useCallback(
|
|
130
|
+
(e: React.WheelEvent) => {
|
|
131
|
+
if (e.deltaY === 0) return
|
|
132
|
+
const viewport = getViewport()
|
|
133
|
+
if (viewport) {
|
|
134
|
+
viewport.scrollLeft += e.deltaY
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
[getViewport]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
// Auto-scroll active tab into view
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (!activeTabId || !scrollContainerRef.current) return
|
|
143
|
+
const activeEl = scrollContainerRef.current.querySelector<HTMLElement>(
|
|
144
|
+
'[data-state="active"]'
|
|
145
|
+
)
|
|
146
|
+
if (activeEl) {
|
|
147
|
+
activeEl.scrollIntoView({
|
|
148
|
+
behavior: 'smooth',
|
|
149
|
+
block: 'nearest',
|
|
150
|
+
inline: 'nearest',
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}, [activeTabId, tabs])
|
|
154
|
+
|
|
155
|
+
const handleScrollLeft = useCallback(() => {
|
|
156
|
+
const viewport = getViewport()
|
|
157
|
+
if (viewport) {
|
|
158
|
+
viewport.scrollBy({ left: -200, behavior: 'smooth' })
|
|
159
|
+
}
|
|
160
|
+
}, [getViewport])
|
|
161
|
+
|
|
162
|
+
const handleScrollRight = useCallback(() => {
|
|
163
|
+
const viewport = getViewport()
|
|
164
|
+
if (viewport) {
|
|
165
|
+
viewport.scrollBy({ left: 200, behavior: 'smooth' })
|
|
166
|
+
}
|
|
167
|
+
}, [getViewport])
|
|
168
|
+
|
|
72
169
|
if (tabs.length === 0 && !onToggleExplorer) {
|
|
73
170
|
return null
|
|
74
171
|
}
|
|
@@ -76,7 +173,7 @@ export const DocumentTabBar = React.memo<DocumentTabBarProps>(
|
|
|
76
173
|
return (
|
|
77
174
|
<div
|
|
78
175
|
className={cn(
|
|
79
|
-
'flex items-center border-b border-border bg-background',
|
|
176
|
+
'flex items-center border-b border-border bg-background relative',
|
|
80
177
|
className
|
|
81
178
|
)}
|
|
82
179
|
data-slot="document-tab-bar"
|
|
@@ -92,67 +189,114 @@ export const DocumentTabBar = React.memo<DocumentTabBarProps>(
|
|
|
92
189
|
<Icon name="folder" size="xs" />
|
|
93
190
|
</Button>
|
|
94
191
|
)}
|
|
192
|
+
|
|
95
193
|
{tabs.length > 0 && (
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
194
|
+
<div
|
|
195
|
+
ref={scrollContainerRef}
|
|
196
|
+
onWheel={handleWheel}
|
|
197
|
+
className="flex-1 min-w-0 flex items-center relative"
|
|
198
|
+
>
|
|
199
|
+
{showScrollArrows && canScrollLeft && (
|
|
200
|
+
<Button
|
|
201
|
+
variant="ghost"
|
|
202
|
+
size="icon-sm"
|
|
203
|
+
onClick={handleScrollLeft}
|
|
204
|
+
className="h-10 w-7 shrink-0 rounded-none border-r border-border/50 text-muted-foreground hover:text-foreground transition-colors z-10"
|
|
205
|
+
aria-label="Scroll tabs left"
|
|
206
|
+
>
|
|
207
|
+
<Icon name="chevron-left" size="xs" />
|
|
208
|
+
</Button>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
<div className="flex-1 min-w-0 relative h-10">
|
|
212
|
+
{canScrollLeft && (
|
|
104
213
|
<div
|
|
105
|
-
|
|
106
|
-
|
|
214
|
+
className="pointer-events-none absolute left-0 top-0 bottom-0 w-5 bg-gradient-to-r from-background to-transparent z-10"
|
|
215
|
+
aria-hidden="true"
|
|
216
|
+
/>
|
|
217
|
+
)}
|
|
218
|
+
|
|
219
|
+
<ScrollArea orientation="horizontal" className="w-full h-10">
|
|
220
|
+
<Tabs
|
|
221
|
+
value={activeTabId || tabs[0]?.id}
|
|
222
|
+
onValueChange={onTabSelect}
|
|
223
|
+
className="h-auto flex-1"
|
|
107
224
|
>
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
225
|
+
<TabsList className="h-10 rounded-none border-none bg-transparent p-0 w-full justify-start gap-0">
|
|
226
|
+
{tabs.map((tab) => (
|
|
227
|
+
<div
|
|
228
|
+
key={tab.id}
|
|
229
|
+
className="flex items-center shrink-0 border-r border-border/50 last:border-r-0"
|
|
230
|
+
>
|
|
231
|
+
<TabsTrigger
|
|
232
|
+
value={tab.id}
|
|
233
|
+
className={cn(
|
|
234
|
+
'data-[state=inactive]:bg-muted/20 data-[state=inactive]:text-muted-foreground',
|
|
235
|
+
'rounded-none border-b-2 border-transparent data-[state=active]:border-primary',
|
|
236
|
+
'px-2.5 sm:px-3 py-2 text-xs sm:text-sm font-medium whitespace-nowrap',
|
|
237
|
+
'flex items-center gap-1.5 sm:gap-2 h-10',
|
|
238
|
+
'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0',
|
|
239
|
+
'transition-colors'
|
|
240
|
+
)}
|
|
241
|
+
>
|
|
242
|
+
<div className="flex items-center gap-1.5 sm:gap-2">
|
|
243
|
+
{/* Dirty indicator */}
|
|
244
|
+
{tab.isDirty && (
|
|
245
|
+
<Icon
|
|
246
|
+
name="circle"
|
|
247
|
+
className="size-2 fill-primary text-primary"
|
|
248
|
+
aria-label="unsaved changes"
|
|
249
|
+
/>
|
|
250
|
+
)}
|
|
251
|
+
{/* Tab name */}
|
|
252
|
+
<span className="truncate max-w-[120px] sm:max-w-[200px]">{tab.name}</span>
|
|
253
|
+
</div>
|
|
254
|
+
</TabsTrigger>
|
|
255
|
+
|
|
256
|
+
{/* Close button */}
|
|
257
|
+
<Button
|
|
258
|
+
variant="ghost"
|
|
259
|
+
size="icon-sm"
|
|
260
|
+
onClick={(e) => {
|
|
261
|
+
e.stopPropagation()
|
|
262
|
+
onTabClose?.(tab.id)
|
|
263
|
+
}}
|
|
264
|
+
className={cn(
|
|
265
|
+
'h-8 w-8 mr-0.5',
|
|
266
|
+
'hover:bg-destructive/10 hover:text-destructive',
|
|
267
|
+
'focus-visible:ring-2 focus-visible:ring-ring',
|
|
268
|
+
'transition-colors'
|
|
269
|
+
)}
|
|
270
|
+
aria-label={`Close ${tab.name}`}
|
|
271
|
+
>
|
|
272
|
+
<Icon name="x" size="xs" />
|
|
273
|
+
</Button>
|
|
274
|
+
</div>
|
|
275
|
+
))}
|
|
276
|
+
</TabsList>
|
|
277
|
+
</Tabs>
|
|
278
|
+
</ScrollArea>
|
|
279
|
+
|
|
280
|
+
{canScrollRight && (
|
|
281
|
+
<div
|
|
282
|
+
className="pointer-events-none absolute right-0 top-0 bottom-0 w-5 bg-gradient-to-l from-background to-transparent z-10"
|
|
283
|
+
aria-hidden="true"
|
|
284
|
+
/>
|
|
285
|
+
)}
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{showScrollArrows && canScrollRight && (
|
|
289
|
+
<Button
|
|
290
|
+
variant="ghost"
|
|
291
|
+
size="icon-sm"
|
|
292
|
+
onClick={handleScrollRight}
|
|
293
|
+
className="h-10 w-7 shrink-0 rounded-none border-l border-border/50 text-muted-foreground hover:text-foreground transition-colors z-10"
|
|
294
|
+
aria-label="Scroll tabs right"
|
|
295
|
+
>
|
|
296
|
+
<Icon name="chevron-right" size="xs" />
|
|
297
|
+
</Button>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
156
300
|
)}
|
|
157
301
|
</div>
|
|
158
302
|
)
|
|
@@ -160,3 +304,4 @@ export const DocumentTabBar = React.memo<DocumentTabBarProps>(
|
|
|
160
304
|
)
|
|
161
305
|
|
|
162
306
|
DocumentTabBar.displayName = 'DocumentTabBar'
|
|
307
|
+
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DocumentTabBar } from './DocumentTabBar'
|
|
2
|
-
export type { DocumentTabBarProps } from './DocumentTabBar'
|
|
2
|
+
export type { DocumentTabBarProps, DocumentFile } from './DocumentTabBar'
|
|
@@ -75,7 +75,7 @@ export type { ModeToggleProps } from './ModeToggle'
|
|
|
75
75
|
|
|
76
76
|
// DocumentTabBar Composite
|
|
77
77
|
export { DocumentTabBar } from './DocumentTabBar'
|
|
78
|
-
export type { DocumentTabBarProps } from './DocumentTabBar'
|
|
78
|
+
export type { DocumentTabBarProps, DocumentFile } from './DocumentTabBar'
|
|
79
79
|
|
|
80
80
|
// DefaultSwitcher Composite
|
|
81
81
|
export { DefaultSwitcher } from './DefaultSwitcher'
|
|
@@ -5,11 +5,17 @@ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
|
|
5
5
|
|
|
6
6
|
import { cn } from "@/lib/utils"
|
|
7
7
|
|
|
8
|
+
export interface ScrollAreaProps
|
|
9
|
+
extends React.ComponentProps<typeof ScrollAreaPrimitive.Root> {
|
|
10
|
+
orientation?: "vertical" | "horizontal" | "both"
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
function ScrollArea({
|
|
9
14
|
className,
|
|
10
15
|
children,
|
|
16
|
+
orientation = "vertical",
|
|
11
17
|
...props
|
|
12
|
-
}:
|
|
18
|
+
}: ScrollAreaProps) {
|
|
13
19
|
return (
|
|
14
20
|
<ScrollAreaPrimitive.Root
|
|
15
21
|
data-slot="scroll-area"
|
|
@@ -22,7 +28,12 @@ function ScrollArea({
|
|
|
22
28
|
>
|
|
23
29
|
{children}
|
|
24
30
|
</ScrollAreaPrimitive.Viewport>
|
|
25
|
-
|
|
31
|
+
{(orientation === "vertical" || orientation === "both") && (
|
|
32
|
+
<ScrollBar orientation="vertical" />
|
|
33
|
+
)}
|
|
34
|
+
{(orientation === "horizontal" || orientation === "both") && (
|
|
35
|
+
<ScrollBar orientation="horizontal" className="h-1.5 border-t border-t-transparent" />
|
|
36
|
+
)}
|
|
26
37
|
<ScrollAreaPrimitive.Corner />
|
|
27
38
|
</ScrollAreaPrimitive.Root>
|
|
28
39
|
)
|
package/dist/index.cjs
CHANGED
|
@@ -4996,10 +4996,12 @@ Confirmation2.displayName = "Confirmation";
|
|
|
4996
4996
|
function ScrollArea(_a) {
|
|
4997
4997
|
var _b = _a, {
|
|
4998
4998
|
className,
|
|
4999
|
-
children
|
|
4999
|
+
children,
|
|
5000
|
+
orientation = "vertical"
|
|
5000
5001
|
} = _b, props = __objRest(_b, [
|
|
5001
5002
|
"className",
|
|
5002
|
-
"children"
|
|
5003
|
+
"children",
|
|
5004
|
+
"orientation"
|
|
5003
5005
|
]);
|
|
5004
5006
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5005
5007
|
ScrollAreaPrimitive__namespace.Root,
|
|
@@ -5016,7 +5018,8 @@ function ScrollArea(_a) {
|
|
|
5016
5018
|
children
|
|
5017
5019
|
}
|
|
5018
5020
|
),
|
|
5019
|
-
/* @__PURE__ */ jsxRuntime.jsx(ScrollBar, {}),
|
|
5021
|
+
(orientation === "vertical" || orientation === "both") && /* @__PURE__ */ jsxRuntime.jsx(ScrollBar, { orientation: "vertical" }),
|
|
5022
|
+
(orientation === "horizontal" || orientation === "both") && /* @__PURE__ */ jsxRuntime.jsx(ScrollBar, { orientation: "horizontal", className: "h-1.5 border-t border-t-transparent" }),
|
|
5020
5023
|
/* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Corner, {})
|
|
5021
5024
|
]
|
|
5022
5025
|
})
|
|
@@ -11058,8 +11061,81 @@ var ModeToggle = React3__namespace.memo(({ className }) => {
|
|
|
11058
11061
|
});
|
|
11059
11062
|
ModeToggle.displayName = "ModeToggle";
|
|
11060
11063
|
var DocumentTabBar = React3__namespace.default.memo(
|
|
11061
|
-
({
|
|
11064
|
+
({
|
|
11065
|
+
tabs,
|
|
11066
|
+
activeTabId,
|
|
11067
|
+
onTabSelect,
|
|
11068
|
+
onTabClose,
|
|
11069
|
+
onToggleExplorer,
|
|
11070
|
+
className,
|
|
11071
|
+
showScrollArrows = true
|
|
11072
|
+
}) => {
|
|
11062
11073
|
var _a;
|
|
11074
|
+
const scrollContainerRef = React3.useRef(null);
|
|
11075
|
+
const [canScrollLeft, setCanScrollLeft] = React3.useState(false);
|
|
11076
|
+
const [canScrollRight, setCanScrollRight] = React3.useState(false);
|
|
11077
|
+
const getViewport = React3.useCallback(() => {
|
|
11078
|
+
var _a2, _b;
|
|
11079
|
+
return (_b = (_a2 = scrollContainerRef.current) == null ? void 0 : _a2.querySelector(
|
|
11080
|
+
'[data-slot="scroll-area-viewport"]'
|
|
11081
|
+
)) != null ? _b : null;
|
|
11082
|
+
}, []);
|
|
11083
|
+
const updateScrollState = React3.useCallback(() => {
|
|
11084
|
+
const viewport = getViewport();
|
|
11085
|
+
if (!viewport) return;
|
|
11086
|
+
const { scrollLeft, scrollWidth, clientWidth } = viewport;
|
|
11087
|
+
setCanScrollLeft(scrollLeft > 2);
|
|
11088
|
+
setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 2);
|
|
11089
|
+
}, [getViewport]);
|
|
11090
|
+
React3.useEffect(() => {
|
|
11091
|
+
const viewport = getViewport();
|
|
11092
|
+
if (!viewport) return;
|
|
11093
|
+
updateScrollState();
|
|
11094
|
+
viewport.addEventListener("scroll", updateScrollState, { passive: true });
|
|
11095
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
11096
|
+
updateScrollState();
|
|
11097
|
+
});
|
|
11098
|
+
resizeObserver.observe(viewport);
|
|
11099
|
+
return () => {
|
|
11100
|
+
viewport.removeEventListener("scroll", updateScrollState);
|
|
11101
|
+
resizeObserver.disconnect();
|
|
11102
|
+
};
|
|
11103
|
+
}, [getViewport, updateScrollState, tabs]);
|
|
11104
|
+
const handleWheel = React3.useCallback(
|
|
11105
|
+
(e) => {
|
|
11106
|
+
if (e.deltaY === 0) return;
|
|
11107
|
+
const viewport = getViewport();
|
|
11108
|
+
if (viewport) {
|
|
11109
|
+
viewport.scrollLeft += e.deltaY;
|
|
11110
|
+
}
|
|
11111
|
+
},
|
|
11112
|
+
[getViewport]
|
|
11113
|
+
);
|
|
11114
|
+
React3.useEffect(() => {
|
|
11115
|
+
if (!activeTabId || !scrollContainerRef.current) return;
|
|
11116
|
+
const activeEl = scrollContainerRef.current.querySelector(
|
|
11117
|
+
'[data-state="active"]'
|
|
11118
|
+
);
|
|
11119
|
+
if (activeEl) {
|
|
11120
|
+
activeEl.scrollIntoView({
|
|
11121
|
+
behavior: "smooth",
|
|
11122
|
+
block: "nearest",
|
|
11123
|
+
inline: "nearest"
|
|
11124
|
+
});
|
|
11125
|
+
}
|
|
11126
|
+
}, [activeTabId, tabs]);
|
|
11127
|
+
const handleScrollLeft = React3.useCallback(() => {
|
|
11128
|
+
const viewport = getViewport();
|
|
11129
|
+
if (viewport) {
|
|
11130
|
+
viewport.scrollBy({ left: -200, behavior: "smooth" });
|
|
11131
|
+
}
|
|
11132
|
+
}, [getViewport]);
|
|
11133
|
+
const handleScrollRight = React3.useCallback(() => {
|
|
11134
|
+
const viewport = getViewport();
|
|
11135
|
+
if (viewport) {
|
|
11136
|
+
viewport.scrollBy({ left: 200, behavior: "smooth" });
|
|
11137
|
+
}
|
|
11138
|
+
}, [getViewport]);
|
|
11063
11139
|
if (tabs.length === 0 && !onToggleExplorer) {
|
|
11064
11140
|
return null;
|
|
11065
11141
|
}
|
|
@@ -11067,7 +11143,7 @@ var DocumentTabBar = React3__namespace.default.memo(
|
|
|
11067
11143
|
"div",
|
|
11068
11144
|
{
|
|
11069
11145
|
className: cn(
|
|
11070
|
-
"flex items-center border-b border-border bg-background",
|
|
11146
|
+
"flex items-center border-b border-border bg-background relative",
|
|
11071
11147
|
className
|
|
11072
11148
|
),
|
|
11073
11149
|
"data-slot": "document-tab-bar",
|
|
@@ -11083,67 +11159,115 @@ var DocumentTabBar = React3__namespace.default.memo(
|
|
|
11083
11159
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "folder", size: "xs" })
|
|
11084
11160
|
}
|
|
11085
11161
|
),
|
|
11086
|
-
tabs.length > 0 && /* @__PURE__ */ jsxRuntime.
|
|
11087
|
-
|
|
11162
|
+
tabs.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11163
|
+
"div",
|
|
11088
11164
|
{
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
className: "
|
|
11092
|
-
children:
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11165
|
+
ref: scrollContainerRef,
|
|
11166
|
+
onWheel: handleWheel,
|
|
11167
|
+
className: "flex-1 min-w-0 flex items-center relative",
|
|
11168
|
+
children: [
|
|
11169
|
+
showScrollArrows && canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11170
|
+
Button2,
|
|
11171
|
+
{
|
|
11172
|
+
variant: "ghost",
|
|
11173
|
+
size: "icon-sm",
|
|
11174
|
+
onClick: handleScrollLeft,
|
|
11175
|
+
className: "h-10 w-7 shrink-0 rounded-none border-r border-border/50 text-muted-foreground hover:text-foreground transition-colors z-10",
|
|
11176
|
+
"aria-label": "Scroll tabs left",
|
|
11177
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-left", size: "xs" })
|
|
11178
|
+
}
|
|
11179
|
+
),
|
|
11180
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 relative h-10", children: [
|
|
11181
|
+
canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11182
|
+
"div",
|
|
11183
|
+
{
|
|
11184
|
+
className: "pointer-events-none absolute left-0 top-0 bottom-0 w-5 bg-gradient-to-r from-background to-transparent z-10",
|
|
11185
|
+
"aria-hidden": "true"
|
|
11186
|
+
}
|
|
11187
|
+
),
|
|
11188
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollArea2, { orientation: "horizontal", className: "w-full h-10", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11189
|
+
Tabs2,
|
|
11190
|
+
{
|
|
11191
|
+
value: activeTabId || ((_a = tabs[0]) == null ? void 0 : _a.id),
|
|
11192
|
+
onValueChange: onTabSelect,
|
|
11193
|
+
className: "h-auto flex-1",
|
|
11194
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TabsList, { className: "h-10 rounded-none border-none bg-transparent p-0 w-full justify-start gap-0", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11195
|
+
"div",
|
|
11196
|
+
{
|
|
11197
|
+
className: "flex items-center shrink-0 border-r border-border/50 last:border-r-0",
|
|
11198
|
+
children: [
|
|
11199
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11200
|
+
TabsTrigger,
|
|
11201
|
+
{
|
|
11202
|
+
value: tab.id,
|
|
11203
|
+
className: cn(
|
|
11204
|
+
"data-[state=inactive]:bg-muted/20 data-[state=inactive]:text-muted-foreground",
|
|
11205
|
+
"rounded-none border-b-2 border-transparent data-[state=active]:border-primary",
|
|
11206
|
+
"px-2.5 sm:px-3 py-2 text-xs sm:text-sm font-medium whitespace-nowrap",
|
|
11207
|
+
"flex items-center gap-1.5 sm:gap-2 h-10",
|
|
11208
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0",
|
|
11209
|
+
"transition-colors"
|
|
11210
|
+
),
|
|
11211
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2", children: [
|
|
11212
|
+
tab.isDirty && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11213
|
+
Icon,
|
|
11214
|
+
{
|
|
11215
|
+
name: "circle",
|
|
11216
|
+
className: "size-2 fill-primary text-primary",
|
|
11217
|
+
"aria-label": "unsaved changes"
|
|
11218
|
+
}
|
|
11219
|
+
),
|
|
11220
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[120px] sm:max-w-[200px]", children: tab.name })
|
|
11221
|
+
] })
|
|
11222
|
+
}
|
|
11223
|
+
),
|
|
11224
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11225
|
+
Button2,
|
|
11226
|
+
{
|
|
11227
|
+
variant: "ghost",
|
|
11228
|
+
size: "icon-sm",
|
|
11229
|
+
onClick: (e) => {
|
|
11230
|
+
e.stopPropagation();
|
|
11231
|
+
onTabClose == null ? void 0 : onTabClose(tab.id);
|
|
11232
|
+
},
|
|
11233
|
+
className: cn(
|
|
11234
|
+
"h-8 w-8 mr-0.5",
|
|
11235
|
+
"hover:bg-destructive/10 hover:text-destructive",
|
|
11236
|
+
"focus-visible:ring-2 focus-visible:ring-ring",
|
|
11237
|
+
"transition-colors"
|
|
11238
|
+
),
|
|
11239
|
+
"aria-label": `Close ${tab.name}`,
|
|
11240
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", size: "xs" })
|
|
11241
|
+
}
|
|
11242
|
+
)
|
|
11243
|
+
]
|
|
11130
11244
|
},
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
},
|
|
11143
|
-
|
|
11144
|
-
|
|
11245
|
+
tab.id
|
|
11246
|
+
)) })
|
|
11247
|
+
}
|
|
11248
|
+
) }),
|
|
11249
|
+
canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11250
|
+
"div",
|
|
11251
|
+
{
|
|
11252
|
+
className: "pointer-events-none absolute right-0 top-0 bottom-0 w-5 bg-gradient-to-l from-background to-transparent z-10",
|
|
11253
|
+
"aria-hidden": "true"
|
|
11254
|
+
}
|
|
11255
|
+
)
|
|
11256
|
+
] }),
|
|
11257
|
+
showScrollArrows && canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11258
|
+
Button2,
|
|
11259
|
+
{
|
|
11260
|
+
variant: "ghost",
|
|
11261
|
+
size: "icon-sm",
|
|
11262
|
+
onClick: handleScrollRight,
|
|
11263
|
+
className: "h-10 w-7 shrink-0 rounded-none border-l border-border/50 text-muted-foreground hover:text-foreground transition-colors z-10",
|
|
11264
|
+
"aria-label": "Scroll tabs right",
|
|
11265
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-right", size: "xs" })
|
|
11266
|
+
}
|
|
11267
|
+
)
|
|
11268
|
+
]
|
|
11145
11269
|
}
|
|
11146
|
-
)
|
|
11270
|
+
)
|
|
11147
11271
|
]
|
|
11148
11272
|
}
|
|
11149
11273
|
);
|