mirror of
https://github.com/langgenius/dify.git
synced 2026-07-26 22:28:32 +08:00
fix: simplify scroll area composition (#38113)
This commit is contained in:
parent
7a111c2226
commit
9d3ac1b7b3
@ -17,7 +17,7 @@ const meta = {
|
||||
layout: 'padded',
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Compound scroll container built on Base UI Scroll Area. The examples mirror the upstream anatomy and focus patterns while applying Dify UI tokens, panel surfaces, and scrollbar spacing. Base UI ScrollArea.Content defaults to min-width: fit-content, so vertical-only regions that should truncate long content must set min-width: 0 on the content slot.',
|
||||
component: 'Compound scroll container built on Base UI Scroll Area. The examples mirror the upstream anatomy and focus patterns while applying Dify UI tokens and surface treatments. Base UI ScrollArea.Content defaults to min-width: fit-content, so vertical-only regions that should truncate long content must set min-width: 0 on the content slot.',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -27,27 +27,7 @@ const meta = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
const scrollFadeRootClassName = cn(
|
||||
'has-[>_:first-child:focus-visible]:outline-2',
|
||||
'has-[>_:first-child:focus-visible]:outline-offset-0',
|
||||
'has-[>_:first-child:focus-visible]:outline-state-accent-solid',
|
||||
)
|
||||
const rootClassName = 'relative min-h-0 min-w-0'
|
||||
const viewportClassName = 'h-full max-h-full max-w-full rounded-xl border border-divider-subtle bg-components-panel-bg'
|
||||
const fadeViewportClassName = cn(
|
||||
'h-full max-h-full max-w-full rounded-xl bg-components-panel-bg outline-none focus-visible:outline-none',
|
||||
'mask-linear-[to_bottom,transparent_0,black_min(40px,var(--scroll-area-overflow-y-start)),black_calc(100%_-_min(40px,var(--scroll-area-overflow-y-end,40px))),transparent_100%] mask-no-repeat',
|
||||
)
|
||||
const scrollbarClassName = cn(
|
||||
'data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1',
|
||||
'data-[orientation=horizontal]:mx-1 data-[orientation=horizontal]:mb-1',
|
||||
)
|
||||
const verticalContentClassName = 'w-full max-w-full min-w-0'
|
||||
const verticalContentStyle = { minWidth: 0 } satisfies React.CSSProperties
|
||||
const panelClassName = 'min-w-0 rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg shadow-shadow-shadow-5'
|
||||
const pageClassName = 'min-w-0 rounded-[28px] border border-divider-subtle bg-background-body p-5'
|
||||
const labelClassName = 'system-xs-medium-uppercase text-text-tertiary'
|
||||
const headingClassName = 'system-md-semibold text-text-primary'
|
||||
|
||||
const appRows = [
|
||||
{ name: 'Invoice Copilot', meta: 'Pinned', icon: 'i-ri-file-list-3-line', selected: true, pinned: true },
|
||||
@ -99,10 +79,10 @@ function StorySection({
|
||||
className?: string
|
||||
}) {
|
||||
return (
|
||||
<section className={cn(pageClassName, className)}>
|
||||
<section className={cn('min-w-0 rounded-[28px] border border-divider-subtle bg-background-body p-5', className)}>
|
||||
<div className="space-y-1">
|
||||
<div className={labelClassName}>{eyebrow}</div>
|
||||
<h3 className={headingClassName}>{title}</h3>
|
||||
<div className="system-xs-medium-uppercase text-text-tertiary">{eyebrow}</div>
|
||||
<h3 className="system-md-semibold text-text-primary">{title}</h3>
|
||||
<p className="max-w-[72ch] text-pretty system-sm-regular text-text-secondary">{description}</p>
|
||||
</div>
|
||||
<div className="mt-5 flex justify-center">
|
||||
@ -122,7 +102,7 @@ function VerticalContent({
|
||||
return (
|
||||
<ScrollAreaContent
|
||||
style={verticalContentStyle}
|
||||
className={cn(verticalContentClassName, className)}
|
||||
className={cn('w-full max-w-full min-w-0', className)}
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaContent>
|
||||
@ -136,22 +116,20 @@ export const Anatomy: Story = {
|
||||
title="Base UI compound parts"
|
||||
description="The baseline story mirrors the official Scroll Area anatomy: Root, Viewport, Content, Scrollbar, and Thumb, with keyboard focus drawn by the viewport."
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-75 w-full max-w-105')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Scrollable anatomy example" role="region" className={viewportClassName}>
|
||||
<VerticalContent className="flex flex-col gap-4 py-2 pl-3 pr-5 text-text-secondary system-sm-regular leading-6">
|
||||
{articleParagraphs.map(paragraph => (
|
||||
<p key={paragraph}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-75 w-full max-w-105 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Scrollable anatomy example" role="region" className="h-full max-h-full max-w-full rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg">
|
||||
<VerticalContent className="flex flex-col gap-4 py-2 pl-3 pr-5 text-text-secondary system-sm-regular leading-6">
|
||||
{articleParagraphs.map(paragraph => (
|
||||
<p key={paragraph}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -163,28 +141,26 @@ export const Vertical: Story = {
|
||||
title="Long form content"
|
||||
description="Vertical overflow keeps the official viewport focus pattern while constraining content width so text never leaks outside the frame."
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-90 w-full max-w-130')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Long form content" role="region" className={viewportClassName}>
|
||||
<VerticalContent className="flex flex-col gap-4 p-4 pr-6 text-text-secondary system-sm-regular leading-6">
|
||||
<div className="space-y-1">
|
||||
<div className={labelClassName}>Article</div>
|
||||
<div className={headingClassName}>Scrollable text region</div>
|
||||
</div>
|
||||
{Array.from({ length: 4 }, (_, groupIndex) => (
|
||||
articleParagraphs.map(paragraph => (
|
||||
<p key={`${groupIndex}-${paragraph}`}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-90 w-full max-w-130 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Long form content" role="region" className="h-full max-h-full max-w-full rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg">
|
||||
<VerticalContent className="flex flex-col gap-4 p-4 pr-6 text-text-secondary system-sm-regular leading-6">
|
||||
<div className="space-y-1">
|
||||
<div className="system-xs-medium-uppercase text-text-tertiary">Article</div>
|
||||
<div className="system-md-semibold text-text-primary">Scrollable text region</div>
|
||||
</div>
|
||||
{Array.from({ length: 4 }, (_, groupIndex) => (
|
||||
articleParagraphs.map(paragraph => (
|
||||
<p key={`${groupIndex}-${paragraph}`}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -196,28 +172,26 @@ export const VerticalTruncation: Story = {
|
||||
title="Constrained content width"
|
||||
description="Use width constraints plus minWidth: 0 on ScrollArea.Content when a vertical-only list should keep vertical scrolling while truncating long labels instead of creating horizontal scroll."
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-48 w-full max-w-80')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Vertical file list" role="region" className={viewportClassName}>
|
||||
<VerticalContent className="flex flex-col gap-0.5 p-2">
|
||||
{fileRows.map(file => (
|
||||
<div
|
||||
key={file}
|
||||
className="flex h-8 w-full min-w-0 items-center gap-2 rounded-lg px-2 text-text-secondary hover:bg-state-base-hover"
|
||||
>
|
||||
<span aria-hidden className="i-ri-file-text-line size-4 shrink-0" />
|
||||
<span className="min-w-0 truncate system-sm-regular" title={file}>
|
||||
{file}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-48 w-full max-w-80 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Vertical file list" role="region" className="h-full max-h-full max-w-full rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg">
|
||||
<VerticalContent className="flex flex-col gap-0.5 p-2">
|
||||
{fileRows.map(file => (
|
||||
<div
|
||||
key={file}
|
||||
className="flex h-8 w-full min-w-0 items-center gap-2 rounded-lg px-2 text-text-secondary hover:bg-state-base-hover"
|
||||
>
|
||||
<span aria-hidden className="i-ri-file-text-line size-4 shrink-0" />
|
||||
<span className="min-w-0 truncate system-sm-regular" title={file}>
|
||||
{file}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -229,24 +203,33 @@ export const ScrollFade: Story = {
|
||||
title="Viewport mask with root focus"
|
||||
description="This mirrors the Base UI scroll-fade example: the viewport owns the mask and the root owns the focus outline so the indicator is never clipped."
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-90 w-full max-w-130')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, scrollFadeRootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Scroll fade article" role="region" className={fadeViewportClassName}>
|
||||
<VerticalContent className="flex flex-col gap-4 px-4 py-3 pr-6 text-text-secondary system-sm-regular leading-6">
|
||||
{Array.from({ length: 5 }, (_, groupIndex) => (
|
||||
articleParagraphs.map(paragraph => (
|
||||
<p key={`${groupIndex}-${paragraph}`}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className={cn(
|
||||
'relative h-90 w-full max-w-130 min-w-0',
|
||||
'has-[>_:first-child:focus-visible]:outline-2 has-[>_:first-child:focus-visible]:outline-offset-0 has-[>_:first-child:focus-visible]:outline-state-accent-solid',
|
||||
)}
|
||||
>
|
||||
<ScrollAreaViewport
|
||||
aria-label="Scroll fade article"
|
||||
role="region"
|
||||
className={cn(
|
||||
'h-full max-h-full max-w-full rounded-xl bg-components-panel-bg outline-none focus-visible:outline-none',
|
||||
'mask-linear-[to_bottom,transparent_0,black_min(40px,var(--scroll-area-overflow-y-start)),black_calc(100%_-_min(40px,var(--scroll-area-overflow-y-end,40px))),transparent_100%] mask-no-repeat',
|
||||
)}
|
||||
>
|
||||
<VerticalContent className="flex flex-col gap-4 px-4 py-3 pr-6 text-text-secondary system-sm-regular leading-6">
|
||||
{Array.from({ length: 5 }, (_, groupIndex) => (
|
||||
articleParagraphs.map(paragraph => (
|
||||
<p key={`${groupIndex}-${paragraph}`}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="opacity-0 data-hovering:opacity-100 data-scrolling:opacity-100 data-scrolling:duration-0">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -259,24 +242,22 @@ export const Horizontal: Story = {
|
||||
description="Horizontal overflow keeps Base UI's content sizing behavior and uses the same viewport focus treatment on the scrollable element."
|
||||
className="mx-auto max-w-190"
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-46 w-full max-w-130')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Horizontal numbered row" role="region" className={viewportClassName}>
|
||||
<ScrollAreaContent className="min-h-full min-w-max p-4 pb-6">
|
||||
<div className="grid grid-cols-[repeat(18,6.25rem)] gap-3">
|
||||
{gridCells.slice(0, 18).map(cell => (
|
||||
<div key={cell} className="flex h-24 items-center justify-center rounded-xl border border-divider-subtle bg-components-panel-bg-alt tabular-nums system-md-semibold text-text-secondary">
|
||||
{cell}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar orientation="horizontal" className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-46 w-full max-w-130 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Horizontal numbered row" role="region" className="h-full max-h-full max-w-full rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg">
|
||||
<ScrollAreaContent className="min-h-full min-w-max p-4 pb-6">
|
||||
<div className="grid grid-cols-[repeat(18,6.25rem)] gap-3">
|
||||
{gridCells.slice(0, 18).map(cell => (
|
||||
<div key={cell} className="flex h-24 items-center justify-center rounded-xl border border-divider-subtle bg-components-panel-bg-alt tabular-nums system-md-semibold text-text-secondary">
|
||||
{cell}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar orientation="horizontal">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -288,28 +269,26 @@ export const BothAxes: Story = {
|
||||
title="Numbered grid"
|
||||
description="This follows the official two-axis example: both scrollbars are rendered and Corner reserves the intersection."
|
||||
>
|
||||
<div className={cn(panelClassName, 'h-85 w-full max-w-140')}>
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full p-1')}>
|
||||
<ScrollAreaViewport aria-label="Numbered grid" role="region" className={viewportClassName}>
|
||||
<ScrollAreaContent className="pt-3 pr-6 pb-6 pl-3">
|
||||
<div className="grid grid-cols-[repeat(10,6.25rem)] grid-rows-[repeat(10,6.25rem)] gap-3">
|
||||
{gridCells.map(cell => (
|
||||
<div key={cell} className="flex items-center justify-center rounded-lg border border-divider-subtle bg-components-panel-bg-alt tabular-nums system-md-semibold text-text-secondary">
|
||||
{cell}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaScrollbar orientation="horizontal" className={scrollbarClassName}>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaCorner />
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-85 w-full max-w-140 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Numbered grid" role="region" className="h-full max-h-full max-w-full rounded-xl border-[0.5px] border-divider-subtle bg-components-panel-bg">
|
||||
<ScrollAreaContent className="pt-3 pr-6 pb-6 pl-3">
|
||||
<div className="grid grid-cols-[repeat(10,6.25rem)] grid-rows-[repeat(10,6.25rem)] gap-3">
|
||||
{gridCells.map(cell => (
|
||||
<div key={cell} className="flex items-center justify-center rounded-lg border border-divider-subtle bg-components-panel-bg-alt tabular-nums system-md-semibold text-text-secondary">
|
||||
{cell}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaScrollbar orientation="horizontal">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaCorner />
|
||||
</ScrollAreaRoot>
|
||||
</StorySection>
|
||||
),
|
||||
}
|
||||
@ -324,54 +303,50 @@ export const AppSidebar: Story = {
|
||||
title="Main navigation list"
|
||||
description="A Dify-like sidebar keeps business UI outside the primitive while preserving the same Root, Viewport, Content, Scrollbar anatomy."
|
||||
>
|
||||
<div className="w-full max-w-70 rounded-2xl border border-divider-subtle bg-background-body p-3 shadow-lg shadow-shadow-shadow-5">
|
||||
<div className="rounded-xl bg-background-default-subtle p-3">
|
||||
<div className="mb-4 flex h-8 items-center gap-2 rounded-lg bg-state-base-active px-2 text-text-accent">
|
||||
<span className="i-ri-apps-fill size-4 shrink-0" aria-hidden />
|
||||
<span className="min-w-0 truncate system-sm-semibold">Explore</span>
|
||||
</div>
|
||||
<div className="mb-1.5 flex items-center justify-between px-2">
|
||||
<span className={labelClassName}>Web apps</span>
|
||||
<span className="system-xs-medium text-text-quaternary">{appRows.length}</span>
|
||||
</div>
|
||||
<div className="h-76 min-h-0">
|
||||
<ScrollAreaRoot className={cn(rootClassName, 'h-full')}>
|
||||
<ScrollAreaViewport aria-label="Web apps" role="region" className="h-full max-h-full max-w-full rounded-lg bg-transparent">
|
||||
<VerticalContent className="space-y-0.5">
|
||||
{appRows.map((row, index) => (
|
||||
<div key={row.name} className="space-y-0.5">
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex h-8 w-full min-w-0 items-center justify-between gap-2 rounded-lg px-2 text-left transition-colors outline-none focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-solid focus-visible:outline-state-accent-solid',
|
||||
row.selected
|
||||
? 'bg-state-base-active text-components-menu-item-text-active'
|
||||
: 'text-components-menu-item-text hover:bg-state-base-hover hover:text-components-menu-item-text-hover',
|
||||
)}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-2">
|
||||
<span className="flex size-5 shrink-0 items-center justify-center rounded-md bg-components-icon-bg-blue-solid text-components-avatar-shape-fill-stop-100">
|
||||
<span aria-hidden className={cn(row.icon, 'size-3.5')} />
|
||||
</span>
|
||||
<span className="min-w-0 truncate system-sm-regular">{row.name}</span>
|
||||
</span>
|
||||
<span className="shrink-0 rounded-md border border-divider-subtle bg-components-panel-bg-alt px-1.5 py-0.5 system-2xs-medium-uppercase text-text-quaternary">
|
||||
{row.meta}
|
||||
</span>
|
||||
</button>
|
||||
{index === pinnedCount - 1 && index !== appRows.length - 1 && (
|
||||
<div className="my-1 h-px bg-divider-subtle" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
<div className="w-full max-w-70 rounded-xl bg-background-default-subtle p-3">
|
||||
<div className="mb-4 flex h-8 items-center gap-2 rounded-lg bg-state-base-active px-2 text-text-accent">
|
||||
<span className="i-ri-apps-fill size-4 shrink-0" aria-hidden />
|
||||
<span className="min-w-0 truncate system-sm-semibold">Explore</span>
|
||||
</div>
|
||||
<div className="mb-1.5 flex items-center justify-between px-2">
|
||||
<span className="system-xs-medium-uppercase text-text-tertiary">Web apps</span>
|
||||
<span className="system-xs-medium text-text-quaternary">{appRows.length}</span>
|
||||
</div>
|
||||
<ScrollAreaRoot className="relative h-76 min-w-0">
|
||||
<ScrollAreaViewport aria-label="Web apps" role="region" className="h-full max-h-full max-w-full rounded-lg bg-transparent">
|
||||
<VerticalContent className="space-y-0.5 pr-3">
|
||||
{appRows.map((row, index) => (
|
||||
<div key={row.name} className="space-y-0.5">
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex h-8 w-full min-w-0 items-center justify-between gap-2 rounded-lg px-2 text-left transition-colors outline-none focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-solid focus-visible:outline-state-accent-solid',
|
||||
row.selected
|
||||
? 'bg-state-base-active text-components-menu-item-text-active'
|
||||
: 'text-components-menu-item-text hover:bg-state-base-hover hover:text-components-menu-item-text-hover',
|
||||
)}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-2">
|
||||
<span className="flex size-5 shrink-0 items-center justify-center rounded-md bg-components-icon-bg-blue-solid text-components-avatar-shape-fill-stop-100">
|
||||
<span aria-hidden className={cn(row.icon, 'size-3.5')} />
|
||||
</span>
|
||||
<span className="min-w-0 truncate system-sm-regular">{row.name}</span>
|
||||
</span>
|
||||
<span className="shrink-0 rounded-md border border-divider-subtle bg-components-panel-bg-alt px-1.5 py-0.5 system-2xs-medium-uppercase text-text-quaternary">
|
||||
{row.meta}
|
||||
</span>
|
||||
</button>
|
||||
{index === pinnedCount - 1 && index !== appRows.length - 1 && (
|
||||
<div className="my-1 h-px bg-divider-subtle" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</VerticalContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
</div>
|
||||
</StorySection>
|
||||
)
|
||||
|
||||
@ -28,15 +28,6 @@ type PricingProps = {
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
const pricingScrollAreaClassNames = {
|
||||
root: 'relative h-full w-full overflow-hidden',
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'min-h-full min-w-[1200px]',
|
||||
verticalScrollbar: 'data-[orientation=vertical]:my-2 data-[orientation=vertical]:me-1',
|
||||
horizontalScrollbar: 'data-[orientation=horizontal]:mx-2 data-[orientation=horizontal]:mb-0.5',
|
||||
corner: 'bg-saas-background',
|
||||
} as const
|
||||
|
||||
const Pricing: FC<PricingProps> = ({
|
||||
onCancel,
|
||||
}) => {
|
||||
@ -65,9 +56,9 @@ const Pricing: FC<PricingProps> = ({
|
||||
<DialogContent
|
||||
className="inset-0 size-full max-h-none max-w-none translate-0 overflow-hidden rounded-none border-none bg-saas-background p-0 shadow-none"
|
||||
>
|
||||
<ScrollAreaRoot className={pricingScrollAreaClassNames.root}>
|
||||
<ScrollAreaViewport className={pricingScrollAreaClassNames.viewport}>
|
||||
<ScrollAreaContent className={pricingScrollAreaClassNames.content}>
|
||||
<ScrollAreaRoot className="relative h-full w-full overflow-hidden">
|
||||
<ScrollAreaViewport className="overscroll-contain">
|
||||
<ScrollAreaContent className="min-h-full min-w-300">
|
||||
<div className="relative grid min-h-full grid-rows-[1fr_auto_auto_1fr] overflow-hidden">
|
||||
<div className="absolute inset-x-0 -top-12 -z-10">
|
||||
<NoiseTop />
|
||||
@ -92,16 +83,13 @@ const Pricing: FC<PricingProps> = ({
|
||||
</div>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className={pricingScrollAreaClassNames.verticalScrollbar}>
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb className="rounded-full" />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaScrollbar
|
||||
orientation="horizontal"
|
||||
className={pricingScrollAreaClassNames.horizontalScrollbar}
|
||||
>
|
||||
<ScrollAreaScrollbar orientation="horizontal">
|
||||
<ScrollAreaThumb className="rounded-full" />
|
||||
</ScrollAreaScrollbar>
|
||||
<ScrollAreaCorner className={pricingScrollAreaClassNames.corner} />
|
||||
<ScrollAreaCorner className="bg-saas-background" />
|
||||
</ScrollAreaRoot>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -23,12 +23,6 @@ import { useGetInstalledApps, useUninstallApp, useUpdateAppPinStatus } from '@/s
|
||||
import Item from './app-nav-item'
|
||||
import NoApps from './no-apps'
|
||||
|
||||
const expandedSidebarScrollAreaClassNames = {
|
||||
content: 'space-y-0.5',
|
||||
scrollbar: 'data-[orientation=vertical]:my-2 data-[orientation=vertical]:-me-3',
|
||||
viewport: 'overscroll-contain',
|
||||
} as const
|
||||
|
||||
const SideBar = () => {
|
||||
const { t } = useTranslation()
|
||||
const pathname = usePathname()
|
||||
@ -116,7 +110,10 @@ const SideBar = () => {
|
||||
<div className="min-h-0 flex-1">
|
||||
<ScrollArea
|
||||
className="h-full"
|
||||
slotClassNames={expandedSidebarScrollAreaClassNames}
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'space-y-0.5 pr-3',
|
||||
}}
|
||||
labelledBy={webAppsLabelId}
|
||||
>
|
||||
{installedAppItems}
|
||||
|
||||
@ -93,7 +93,7 @@ export function ModelSelectorScrollBody({
|
||||
>
|
||||
<ScrollAreaContent className="min-w-0 overflow-x-hidden">{children}</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="z-2 data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar className="z-2">
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -297,7 +297,6 @@ export default function IntegrationsPage({
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'min-h-full',
|
||||
scrollbar: 'data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1',
|
||||
}}
|
||||
>
|
||||
<IntegrationSectionRenderer
|
||||
|
||||
@ -21,7 +21,6 @@ export function IntegrationSectionLayout({
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'min-h-full',
|
||||
scrollbar: 'data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1',
|
||||
}}
|
||||
>
|
||||
<div className={bodyClassName}>
|
||||
|
||||
@ -245,7 +245,7 @@ const ProviderList = ({
|
||||
)}
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -226,7 +226,7 @@ const WebAppsSectionContent = () => {
|
||||
className="overflow-x-hidden"
|
||||
role="region"
|
||||
>
|
||||
<ScrollAreaContent className="w-full max-w-full min-w-0! px-2">
|
||||
<ScrollAreaContent className="w-full max-w-full min-w-0! pr-5 pl-2">
|
||||
{isPending && (
|
||||
<WebAppsSkeleton />
|
||||
)}
|
||||
@ -271,7 +271,7 @@ const WebAppsSectionContent = () => {
|
||||
)}
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -41,7 +41,7 @@ function PluginTaskList({
|
||||
label={t('task.installing', { ns: 'plugin' })}
|
||||
slotClassNames={{
|
||||
viewport: 'max-h-[420px] overscroll-contain',
|
||||
content: 'w-full! max-w-full! min-w-0! overflow-x-hidden!',
|
||||
content: 'w-full! max-w-full! min-w-0! overflow-x-hidden! pr-3',
|
||||
}}
|
||||
>
|
||||
{runningPlugins.length > 0 && (
|
||||
|
||||
@ -164,7 +164,7 @@ const PluginsPanelResults = ({
|
||||
)}
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -180,7 +180,7 @@ const TriggerPluginItem: FC<Props> = ({
|
||||
))}
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -235,7 +235,6 @@ export function AgentSkillDetailDialog({
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain outline-none focus-visible:outline-none mask-linear-[to_bottom,transparent_0,black_min(40px,var(--scroll-area-overflow-y-start)),black_calc(100%_-_min(40px,var(--scroll-area-overflow-y-end,40px))),transparent_100%] mask-no-repeat',
|
||||
content: 'flex min-h-full w-full max-w-full min-w-0 flex-col gap-2 px-6 pt-4 pb-0',
|
||||
scrollbar: 'data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1',
|
||||
}}
|
||||
>
|
||||
{detail.filePreview && (
|
||||
|
||||
@ -41,7 +41,7 @@ export function AgentLogsTable({
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-w-0 flex-col overflow-hidden">
|
||||
<div className="shrink-0">
|
||||
<div className="shrink-0 pr-3">
|
||||
<table aria-hidden="true" className="w-full table-fixed border-collapse">
|
||||
<LogsTableColGroup />
|
||||
<LogsTableHeader labels={tableHeaderLabels} />
|
||||
@ -55,7 +55,7 @@ export function AgentLogsTable({
|
||||
tabIndex={-1}
|
||||
className="overscroll-contain"
|
||||
>
|
||||
<ScrollAreaContent>
|
||||
<ScrollAreaContent className="pr-3">
|
||||
<table className="w-full table-fixed border-collapse">
|
||||
<LogsTableColGroup />
|
||||
<LogsTableHeader labels={tableHeaderLabels} rowClassName="sr-only" />
|
||||
@ -69,7 +69,7 @@ export function AgentLogsTable({
|
||||
</table>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:translate-x-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -139,7 +139,7 @@ export default function RosterPage() {
|
||||
/>
|
||||
</ScrollAreaContent>
|
||||
</ScrollAreaViewport>
|
||||
<ScrollAreaScrollbar className="data-[orientation=vertical]:my-1 data-[orientation=vertical]:me-1">
|
||||
<ScrollAreaScrollbar>
|
||||
<ScrollAreaThumb />
|
||||
</ScrollAreaScrollbar>
|
||||
</ScrollAreaRoot>
|
||||
|
||||
@ -167,7 +167,6 @@ export function GuideCard({ children, actions, contentScrollable = true }: {
|
||||
slotClassNames={{
|
||||
viewport: 'overscroll-contain',
|
||||
content: 'min-h-full pt-0.5 pb-6',
|
||||
scrollbar: 'data-[orientation=vertical]:-me-5 data-[orientation=vertical]:my-1',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user