diff --git a/packages/ui/src/components/accordion.css b/packages/ui/src/components/accordion.css index 7bf287fe54..9763298981 100644 --- a/packages/ui/src/components/accordion.css +++ b/packages/ui/src/components/accordion.css @@ -36,7 +36,9 @@ border-radius: var(--radius-md); overflow: clip; color: var(--text-strong); - transition: background-color 0.15s ease; + transition-property: background-color, border-color; + transition-duration: var(--transition-duration); + transition-timing-function: var(--transition-easing); /* text-12-regular */ font-family: var(--font-family-sans); @@ -58,6 +60,16 @@ } } + [data-slot="accordion-arrow"] { + flex-shrink: 0; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-weak); + } + &[data-expanded] { [data-slot="accordion-trigger"] { border-bottom-left-radius: 0; @@ -69,30 +81,29 @@ border-top: none; border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md); + height: auto; } } [data-slot="accordion-content"] { - overflow: hidden; + display: grid; + grid-template-rows: 0fr; + transition-property: grid-template-rows, opacity; + transition-duration: var(--transition-duration); + transition-timing-function: var(--transition-easing); width: 100%; + + > * { + overflow: hidden; + } + + &[data-expanded] { + grid-template-rows: 1fr; + } + + &[data-closed] { + grid-template-rows: 0fr; + } } } } - -@keyframes slideDown { - from { - height: 0; - } - to { - height: var(--kb-accordion-content-height); - } -} - -@keyframes slideUp { - from { - height: var(--kb-accordion-content-height); - } - to { - height: 0; - } -} diff --git a/packages/ui/src/components/accordion.tsx b/packages/ui/src/components/accordion.tsx index 535d38e3d0..e30be95e05 100644 --- a/packages/ui/src/components/accordion.tsx +++ b/packages/ui/src/components/accordion.tsx @@ -1,6 +1,7 @@ import { Accordion as Kobalte } from "@kobalte/core/accordion" -import { splitProps } from "solid-js" +import { Accessor, createContext, splitProps, useContext } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" +import { MorphChevron } from "./morph-chevron" export interface AccordionProps extends ComponentProps {} export interface AccordionItemProps extends ComponentProps {} @@ -8,6 +9,8 @@ export interface AccordionHeaderProps extends ComponentProps {} export interface AccordionContentProps extends ComponentProps {} +const AccordionItemContext = createContext>() + function AccordionRoot(props: AccordionProps) { const [split, rest] = splitProps(props, ["class", "classList"]) return ( @@ -22,17 +25,19 @@ function AccordionRoot(props: AccordionProps) { ) } -function AccordionItem(props: AccordionItemProps) { - const [split, rest] = splitProps(props, ["class", "classList"]) +function AccordionItem(props: AccordionItemProps & { expanded?: boolean }) { + const [split, rest] = splitProps(props, ["class", "classList", "expanded"]) return ( - + split.expanded ?? false}> + + ) } @@ -84,9 +89,25 @@ function AccordionContent(props: ParentProps) { ) } +export interface AccordionArrowProps extends ComponentProps<"div"> { + expanded?: boolean +} + +function AccordionArrow(props: AccordionArrowProps = {}) { + const [local, rest] = splitProps(props, ["expanded"]) + const contextExpanded = useContext(AccordionItemContext) + const isExpanded = () => local.expanded ?? contextExpanded?.() ?? false + return ( +
+ +
+ ) +} + export const Accordion = Object.assign(AccordionRoot, { Item: AccordionItem, Header: AccordionHeader, Trigger: AccordionTrigger, Content: AccordionContent, + Arrow: AccordionArrow, }) diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index bf143cd4fd..c8372d32e8 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -106,10 +106,11 @@ [data-component="user-message"] [data-slot="user-message-text"] { max-height: var(--user-message-collapsed-height, 64px); + transition: max-height 200ms cubic-bezier(0.25, 0, 0.5, 1); } [data-component="user-message"][data-expanded="true"] [data-slot="user-message-text"] { - max-height: none; + max-height: 2000px; } [data-component="user-message"][data-can-expand="true"] [data-slot="user-message-text"] { @@ -141,17 +142,6 @@ background: transparent; cursor: pointer; color: var(--text-weak); - - [data-slot="icon-svg"] { - transition: transform 0.15s ease; - } - } - - [data-component="user-message"][data-expanded="true"] - [data-slot="user-message-text"] - [data-slot="user-message-expand"] - [data-slot="icon-svg"] { - transform: rotate(180deg); } [data-component="user-message"] [data-slot="user-message-text"] [data-slot="user-message-expand"]:hover { @@ -457,6 +447,7 @@ gap: 16px; align-items: center; justify-content: flex-end; + color: var(--icon-base); } [data-slot="session-turn-accordion-content"] { diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index fe53c0939c..fc0a9f7192 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -548,7 +548,7 @@ export function SessionTurn( data-slot="session-turn-collapsible-trigger-content" variant="ghost" size="small" - onClick={props.onStepsExpandedToggle ?? (() => {})} + onClick={props.onStepsExpandedToggle ?? (() => { })} aria-expanded={props.stepsExpanded} > diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 3ed0310ef2..7c8548734d 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -33,8 +33,10 @@ @import "../components/markdown.css" layer(components); @import "../components/message-part.css" layer(components); @import "../components/message-nav.css" layer(components); +@import "../components/morph-chevron.css" layer(components); @import "../components/popover.css" layer(components); @import "../components/progress-circle.css" layer(components); +@import "../components/reasoning-icon.css" layer(components); @import "../components/radio-group.css" layer(components); @import "../components/resize-handle.css" layer(components); @import "../components/select.css" layer(components);