feat: update list styles, add fade in, out

This commit is contained in:
Aaron Iker 2025-12-21 23:23:44 +01:00
parent 0da901a188
commit 670863adae
2 changed files with 140 additions and 88 deletions

View file

@ -1,14 +1,43 @@
@property --bottom-fade {
syntax: "<length>";
inherits: false;
initial-value: 16px;
}
@property --top-fade-opacity {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
@keyframes scroll {
0% {
--bottom-fade: 16px;
--top-fade-opacity: 0;
}
10% {
--top-fade-opacity: 1;
}
90% {
--bottom-fade: 16px;
}
100% {
--bottom-fade: 0;
--top-fade-opacity: 1;
}
}
[data-component="list"] {
display: flex;
flex-direction: column;
gap: 20px;
gap: 12px;
overflow: hidden;
padding: 0 12px;
[data-slot="list-search"] {
display: flex;
height: 40px;
flex-shrink: 0;
padding: 4px 10px 4px 16px;
padding: 8px 8px 8px 12px;
align-items: center;
gap: 12px;
align-self: stretch;
@ -19,11 +48,17 @@
[data-slot="list-search-container"] {
display: flex;
align-items: center;
gap: 16px;
gap: 8px;
flex: 1 0 0;
max-height: 20px;
[data-slot="list-search-input"] {
width: 100%;
&[data-slot="input-input"] {
line-height: 20px;
max-height: 20px;
}
}
}
}
@ -31,88 +66,68 @@
[data-slot="list-scroll"] {
display: flex;
flex-direction: column;
gap: 20px;
gap: 12px;
overflow-y: auto;
overscroll-behavior: contain;
mask: linear-gradient(to bottom, #ffff calc(100% - var(--bottom-fade)), #0000);
animation: scroll;
animation-timeline: --scroll;
scroll-timeline: --scroll y;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
[data-slot="list-empty-state"] {
display: flex;
padding: 32px 0px;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
align-self: stretch;
[data-slot="list-message"] {
[data-slot="list-empty-state"] {
display: flex;
padding: 32px 0px;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2px;
color: var(--text-weak);
text-align: center;
/* text-14-regular */
font-family: var(--font-family-sans);
font-size: 14px;
font-style: normal;
font-weight: var(--font-weight-regular);
line-height: var(--line-height-large); /* 142.857% */
letter-spacing: var(--letter-spacing-normal);
}
[data-slot="list-filter"] {
color: var(--text-strong);
}
}
[data-slot="list-group"] {
position: relative;
display: flex;
flex-direction: column;
[data-slot="list-header"] {
display: flex;
z-index: 10;
height: 28px;
padding: 0 10px;
justify-content: space-between;
align-items: center;
gap: 8px;
align-self: stretch;
background: var(--surface-raised-stronger-non-alpha);
position: sticky;
top: 0;
color: var(--text-base);
[data-slot="list-message"] {
display: flex;
justify-content: center;
align-items: center;
gap: 2px;
color: var(--text-weak);
text-align: center;
/* text-14-medium */
font-family: var(--font-family-sans);
font-size: 14px;
font-style: normal;
font-weight: var(--font-weight-medium);
line-height: var(--line-height-large); /* 142.857% */
letter-spacing: var(--letter-spacing-normal);
/* text-14-regular */
font-family: var(--font-family-sans);
font-size: 14px;
font-style: normal;
font-weight: var(--font-weight-regular);
line-height: var(--line-height-large); /* 142.857% */
letter-spacing: var(--letter-spacing-normal);
}
[data-slot="list-filter"] {
color: var(--text-strong);
}
}
[data-slot="list-items"] {
[data-slot="list-group"] {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
[data-slot="list-item"] {
[data-slot="list-header"] {
display: flex;
width: 100%;
height: 28px;
padding: 4px 10px;
z-index: 10;
height: 32px;
padding: 0 12px 8px 12px;
justify-content: space-between;
align-items: center;
color: var(--text-strong);
scroll-margin-top: 28px;
align-self: stretch;
background: var(--surface-raised-stronger-non-alpha);
position: sticky;
top: 0;
color: var(--text-base);
/* text-14-medium */
font-family: var(--font-family-sans);
@ -122,30 +137,66 @@
line-height: var(--line-height-large); /* 142.857% */
letter-spacing: var(--letter-spacing-normal);
[data-slot="list-item-selected-icon"] {
color: var(--icon-strong-base);
}
[data-slot="list-item-active-icon"] {
display: none;
color: var(--icon-strong-base);
&::after {
content: "";
position: absolute;
top: 100%;
left: 0;
right: 0;
height: 16px;
background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent);
pointer-events: none;
opacity: var(--top-fade-opacity);
}
}
&[data-active="true"] {
border-radius: var(--radius-md);
background: var(--surface-raised-base-hover);
[data-slot="list-items"] {
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
[data-slot="list-item"] {
display: flex;
width: 100%;
padding: 6px 8px 6px 4px;
align-items: center;
color: var(--text-strong);
scroll-margin-top: 32px;
/* text-14-medium */
font-family: var(--font-family-sans);
font-size: 14px;
font-style: normal;
font-weight: var(--font-weight-medium);
line-height: var(--line-height-large); /* 142.857% */
letter-spacing: var(--letter-spacing-normal);
[data-slot="list-item-selected-icon"] {
color: var(--icon-strong-base);
}
[data-slot="list-item-active-icon"] {
display: block;
display: none;
color: var(--icon-strong-base);
}
[data-slot="list-item-extra-icon"] {
display: block !important;
color: var(--icon-strong-base) !important;
&[data-active="true"] {
border-radius: var(--radius-md);
background: var(--surface-raised-base-hover);
[data-slot="list-item-active-icon"] {
display: block;
}
[data-slot="list-item-extra-icon"] {
display: block !important;
color: var(--icon-strong-base) !important;
}
}
&:active {
background: var(--surface-raised-base-active);
}
&:focus-visible {
outline: none;
}
}
&:active {
background: var(--surface-raised-base-active);
}
&:focus-visible {
outline: none;
}
}
}

View file

@ -1,7 +1,7 @@
import { createEffect, on, Show, For, type JSX, createSignal } from "solid-js"
import { type FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks"
import { createEffect, createSignal, For, type JSX, on, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks"
import { Icon, IconProps } from "./icon"
import { Icon, type IconProps } from "./icon"
import { IconButton } from "./icon-button"
import { TextField } from "./text-field"
@ -160,6 +160,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
data-active={props.key(item) === active()}
data-selected={item === props.current}
onClick={() => handleSelect(item, i())}
type="button"
onMouseMove={() => {
setStore("mouseActive", true)
setActive(props.key(item))