Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openframe-frontend-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@
"mermaid": "^11.14.0",
"nats.ws": "^1.30.3",
"next-themes": "^0.2.1",
"overlayscrollbars": "^2.16.0",
"overlayscrollbars-react": "^0.5.6",
"react-aria-components": "^1.11.0",
"react-colorful": "^5.7.0",
"react-datepicker": "^8.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useRef, useEffect, useLayoutEffect, useImperativeHandle, forwardRef } from "react"
import { useStickToBottom } from "use-stick-to-bottom"
import { cn } from "../../utils/cn"
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { ChatMessageEnhanced } from "./chat-message-enhanced"
import { ChatMessageListSkeleton } from "./chat-message-skeleton"
import { DotsLoaderIcon } from "../icons-v2-generated"
Expand Down Expand Up @@ -491,13 +492,10 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(

return (
<div className="relative flex-1 min-h-0 flex flex-col">
<div
ref={setScrollRef}
className={cn(
"flex h-full w-full flex-col overflow-y-auto overflow-x-hidden flex-1",
"scrollbar-thin scrollbar-track-transparent scrollbar-thumb-ods-border/30 hover:scrollbar-thumb-ods-text-secondary/30",
className,
)}
<OverlayScrollArea
viewportRef={setScrollRef}
className="flex-1 min-h-0"
contentClassName={cn("flex flex-col overflow-x-hidden", className)}
{...props}
>
<div
Expand Down Expand Up @@ -550,7 +548,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
)
})}
</div>
</div>
</OverlayScrollArea>

{/* Footer-pinned streaming loader — outside the scroller so it
doesn't jitter as the streaming message grows. Color is set
Expand Down
9 changes: 5 additions & 4 deletions openframe-frontend-core/src/components/chat/chat-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { forwardRef, useRef, useEffect } from "react"
import { cn } from "../../utils/cn"
import { Button } from "../ui/button"
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { ChatPlusIcon, ChatsIcon } from "../icons-v2-generated"
import { Chevron02RightIcon } from "../icons-v2-generated"
import { ChatSidebarSkeleton, DialogListItemSkeleton } from "./chat-sidebar-skeleton"
Expand Down Expand Up @@ -154,12 +155,12 @@ const ChatSidebar = forwardRef<HTMLDivElement, ChatSidebarProps>(
</div>
) : children ? (
/* Custom children content */
<div className="flex-1 overflow-y-auto">
<OverlayScrollArea className="flex-1 min-h-0">
{children}
</div>
</OverlayScrollArea>
) : (
/* Dialogs List */
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto min-h-0">
<OverlayScrollArea viewportRef={scrollContainerRef} className="flex-1 min-h-0">
<div className="flex flex-col">
{dialogs.map((dialog) => (
<DialogListItem
Expand All @@ -183,7 +184,7 @@ const ChatSidebar = forwardRef<HTMLDivElement, ChatSidebarProps>(
</div>
)}
</div>
</div>
</OverlayScrollArea>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as React from 'react'
import { cn } from '../../utils/cn'
import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { ChatTicketItem, type ChatTicketItemData } from './entity-cards/chat-ticket-item'

export interface ChatTicketListProps extends React.HTMLAttributes<HTMLDivElement> {
Expand Down Expand Up @@ -46,15 +47,15 @@ const ChatTicketList = React.forwardRef<HTMLDivElement, ChatTicketListProps>(
!fadeBottom && "border-b rounded-b-md",
)}
>
<div ref={scrollRef} className="overflow-y-auto h-full" onScroll={updateFade}>
<OverlayScrollArea viewportRef={scrollRef} className="h-full" onScroll={updateFade}>
{tickets.map((ticket) => (
<ChatTicketItem
key={ticket.id}
ticket={ticket}
onClick={onTicketClick}
/>
))}
</div>
</OverlayScrollArea>

{/* Scroll-fade overlays — tinted with the page background so edge
tickets fade into the surface behind the list in BOTH themes
Expand Down
10 changes: 6 additions & 4 deletions openframe-frontend-core/src/components/chat/guide-welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react'
import { cn } from '../../utils/cn'
import { MingoIcon } from '../icons'
import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { Skeleton } from '../ui/skeleton'
import { ChatQuickActionRow } from './chat-quick-action-row'
import { EntityIcon } from '../icon-display'
Expand Down Expand Up @@ -139,10 +140,11 @@ export function GuideWelcome({
{/* Greeting + slash-command list share one scroll region; `relative` so
the edge fades can overlay it. */}
<div className="relative flex flex-1 min-h-0 flex-col">
<div
ref={scrollRef}
<OverlayScrollArea
viewportRef={scrollRef}
onScroll={updateScrollFade}
className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
className="flex-1 min-h-0"
contentClassName="flex min-h-full flex-col gap-[var(--spacing-system-m)]"
>
{/* Greeting grows to fill (`flex-1`) so the slash-command list stays
anchored below it — but its content is anchored at the TOP of the
Expand Down Expand Up @@ -185,7 +187,7 @@ export function GuideWelcome({
</div>

{children}
</div>
</OverlayScrollArea>

{/* Edge scroll-fades — visible only when content is hidden beyond them. */}
<ScrollFadeOverlay edge="top" visible={fadeTop} className="h-12" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cn } from '../../utils/cn'
import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
import { Button } from '../ui/button'
import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { Ellipsis01Icon, SearchXmarkIcon } from '../icons-v2-generated'
import { ChatListEmptyState } from './chat-list-empty-state'
import type { DialogItem } from './types/component.types'
Expand Down Expand Up @@ -338,10 +339,11 @@ export function MingoChatHistory({
{/* Scroll region — the search INPUT now lives in the panel header
(Figma 116:51217), so the list is just the grouped rows + fades. */}
<div className="relative flex flex-1 min-h-0 flex-col">
<div
ref={scrollRef}
<OverlayScrollArea
viewportRef={scrollRef}
onScroll={updateFade}
className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
className="flex-1 min-h-0"
contentClassName="flex flex-col gap-[var(--spacing-system-m)]"
>
{noSearchResults ? (
// No search matches — same centred glyph + title + caption layout as
Expand Down Expand Up @@ -374,7 +376,7 @@ export function MingoChatHistory({
))
)}
{hasMore ? <div ref={sentinelRef} className="h-px shrink-0" /> : null}
</div>
</OverlayScrollArea>

{/* Scroll-fade — only while content is hidden in that direction. */}
<ScrollFadeOverlay edge="top" visible={fadeTop} color="var(--color-bg-card)" className="h-12" />
Expand Down
10 changes: 6 additions & 4 deletions openframe-frontend-core/src/components/chat/mingo-welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ChatQuickActionRow } from './chat-quick-action-row'
import { QuickActionChipButton } from './quick-action-chip'
import { Button } from '../ui/button'
import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
import {
CompassIcon,
Expand Down Expand Up @@ -239,10 +240,11 @@ export function MingoWelcome({
input. The wrapper is `relative` so the scroll-fade gradients can
overlay the top/bottom edges. */}
<div className="relative flex flex-1 min-h-0 flex-col">
<div
ref={scrollRef}
<OverlayScrollArea
viewportRef={scrollRef}
onScroll={updateScrollFade}
className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
className="flex-1 min-h-0"
contentClassName="flex min-h-full flex-col gap-[var(--spacing-system-m)]"
>
{/* Greeting — grows to fill (`flex-1`) so it centres vertically,
keeping the grid anchored at the bottom of the scroll area. Default
Expand All @@ -261,7 +263,7 @@ export function MingoWelcome({
<p className="text-h4 text-ods-text-secondary">{subtitle}</p>
</div>
</div>
</div>
</OverlayScrollArea>

{/* Edge scroll-fades — visible only when content is hidden beyond them.
Fade into the panel's dark `ods-bg` surface (the default color), matching
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { useEffect, useRef } from 'react'
import { OverlayScrollArea } from '../../ui/overlay-scroll-area'
import { BellOffIcon } from '../../icons-v2-generated/interface/bell-off-icon'
import { ClockHistoryIcon, ArrowRightUpIcon } from '../../icons-v2-generated'
import { useAppLayoutDrawerContainer } from '../../navigation/app-layout-context'
Expand Down Expand Up @@ -189,9 +190,10 @@ function DrawerScrollList({

const isEmpty = unreadNotifications.length === 0
return (
<div
ref={scrollRef}
className="flex flex-1 flex-col gap-[var(--spacing-system-xs)] overflow-y-auto px-[var(--spacing-system-m)]"
<OverlayScrollArea
viewportRef={scrollRef}
className="flex-1 min-h-0"
contentClassName="flex flex-col gap-[var(--spacing-system-xs)] px-[var(--spacing-system-m)]"
>
{isEmpty && !isLoadingMore ? (
<EmptyState />
Expand All @@ -214,7 +216,7 @@ function DrawerScrollList({
{loadMore && hasMore && <div ref={sentinelRef} className="h-1" aria-hidden="true" />}
</>
)}
</div>
</OverlayScrollArea>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
XmarkIcon,
} from '../../icons-v2-generated'
import { useTrackerClock } from './use-tracker-clock'
import { OverlayScrollArea } from '../../ui/overlay-scroll-area'
import type { TimeTrackerData, TimeTrackerEntry, TimeTrackerStatus } from './types'

interface CustomerAutocompleteOption extends AutocompleteOption {
Expand Down Expand Up @@ -149,11 +150,12 @@ export function TimeTrackerPanel({
const visibleEntries = lastEntries.slice(0, 3)

return (
<div
<OverlayScrollArea
className={cn(
'relative flex max-h-[calc(100vh-6rem)] w-full flex-col gap-[var(--spacing-system-l)] overflow-y-auto rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-m)]',
'max-h-[calc(100vh-6rem)] w-full rounded-md border border-ods-border bg-ods-card',
className,
)}
contentClassName="relative flex flex-col gap-[var(--spacing-system-l)] p-[var(--spacing-system-m)]"
>
<Button
variant="transparent"
Expand Down Expand Up @@ -314,7 +316,7 @@ export function TimeTrackerPanel({
<Button variant="outline" fullWidth onClick={onClose} className="md:hidden">
Close
</Button>
</div>
</OverlayScrollArea>
)
}

Expand Down
18 changes: 13 additions & 5 deletions openframe-frontend-core/src/components/layout/page-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react'
import { cn } from '../../utils/cn'
import type { ActionsMenuGroup } from '../ui/actions-menu'
import { PageActions, type PageActionButton } from '../ui/page-actions'
import { OverlayScrollArea } from '../ui/overlay-scroll-area'
import { BackButton } from './back-button'

// Legacy interface for backward compatibility (layout version)
Expand Down Expand Up @@ -393,7 +394,7 @@ function renderAdvancedPageContainer({

switch (variant) {
case 'detail':
return cn('flex-1 overflow-auto', mobilePadding, contentClassName)
return cn('flex-1 min-h-0', mobilePadding, contentClassName)
case 'list':
return cn('flex flex-col gap-4 md:gap-6', mobilePadding, contentClassName)
case 'form':
Expand All @@ -407,10 +408,17 @@ function renderAdvancedPageContainer({
return (
<div className={getContainerClasses()}>
{renderHeader()}

<div className={getContentClasses()}>
{children}
</div>

{variant === 'detail' ? (
// The only scrolling variant — standardized overlay scrollbar.
<OverlayScrollArea className={getContentClasses()}>
{children}
</OverlayScrollArea>
) : (
<div className={getContentClasses()}>
{children}
</div>
)}
</div>
)
}
Expand Down
11 changes: 8 additions & 3 deletions openframe-frontend-core/src/components/ui/drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { X } from "lucide-react"

import { cn } from "../../utils/cn"
import { useHeaderHeight } from "../../hooks/ui/use-header-height"
import { OverlayScrollArea } from "./overlay-scroll-area"

/** Unified overlay backdrop — dimmed, no blur. Single source of truth for
* every full-screen backdrop (Drawer, AppLayoutDrawer, MobileBurgerMenu,
Expand Down Expand Up @@ -530,12 +531,16 @@ DrawerDescription.displayName = "DrawerDescription"

const DrawerBody = ({
className,
children,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("flex flex-1 flex-col gap-4 overflow-y-auto", className)}
<OverlayScrollArea
className={cn("flex-1 min-h-0", className)}
contentClassName="flex flex-col gap-4"
{...props}
/>
>
{children}
</OverlayScrollArea>
)
DrawerBody.displayName = "DrawerBody"

Expand Down
1 change: 1 addition & 0 deletions openframe-frontend-core/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,4 @@ export * from './simple-markdown-renderer'
export { RichMarkdownRenderer, type RichMarkdownRendererProps } from './rich-markdown-renderer'
export * from './filter-pill-row'

export * from './overlay-scroll-area'
8 changes: 6 additions & 2 deletions openframe-frontend-core/src/components/ui/modal-v2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useEffect, useState } from "react"
import { usePreventScroll } from "@react-aria/overlays"
import { XmarkIcon } from "../icons-v2-generated"
import { cn } from "../../utils/cn"
import { OverlayScrollArea } from "./overlay-scroll-area"

// Duration of the open/close animation in ms — keep in sync with the
// `duration-200` utilities applied to the backdrop and panel below.
Expand Down Expand Up @@ -131,9 +132,12 @@ Modal.displayName = "ModalV2"

const ModalContent = React.forwardRef<HTMLDivElement, ModalContentProps>(
({ children, className }, ref) => (
<div ref={ref} className={cn("flex-1 min-h-0 overflow-y-auto", className)}>
<OverlayScrollArea
viewportRef={ref}
className={cn("flex-1 min-h-0", className)}
>
{children}
</div>
</OverlayScrollArea>
)
)
ModalContent.displayName = "ModalV2Content"
Expand Down
8 changes: 6 additions & 2 deletions openframe-frontend-core/src/components/ui/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from "react"
import { useEffect } from "react"
import { usePreventScroll } from "@react-aria/overlays"
import { cn } from "../../utils/cn"
import { OverlayScrollArea } from "./overlay-scroll-area"

interface ModalProps {
isOpen: boolean
Expand Down Expand Up @@ -82,9 +83,12 @@ Modal.displayName = "Modal"
/** @deprecated Use ModalV2Content from './modal-v2' instead. */
const ModalContent = React.forwardRef<HTMLDivElement, ModalContentProps>(
({ children, className }, ref) => (
<div ref={ref} className={cn("overflow-y-auto min-h-0 flex-1", className)}>
<OverlayScrollArea
viewportRef={ref}
className={cn("min-h-0 flex-1", className)}
>
{children}
</div>
</OverlayScrollArea>
)
)
ModalContent.displayName = "ModalContent"
Expand Down
Loading
Loading