Преглед изворни кода

Revert "feat(ui): Smooth fading out on scroll, style fixes (#11683)"

This reverts commit e445dc07464d75c893756f6e256c1755d9e2285e.
Adam пре 3 недеља
родитељ
комит
2f76b49df3

+ 2 - 8
packages/app/src/components/settings-general.tsx

@@ -5,7 +5,6 @@ import { Select } from "@opencode-ai/ui/select"
 import { Switch } from "@opencode-ai/ui/switch"
 import { Switch } from "@opencode-ai/ui/switch"
 import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme"
 import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { ScrollFade } from "@opencode-ai/ui/scroll-fade"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { usePlatform } from "@/context/platform"
 import { usePlatform } from "@/context/platform"
 import { useSettings, monoFontFamily } from "@/context/settings"
 import { useSettings, monoFontFamily } from "@/context/settings"
@@ -131,12 +130,7 @@ export const SettingsGeneral: Component = () => {
   const soundOptions = [...SOUND_OPTIONS]
   const soundOptions = [...SOUND_OPTIONS]
 
 
   return (
   return (
-    <ScrollFade
-      direction="vertical"
-      fadeStartSize={0}
-      fadeEndSize={16}
-      class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10"
-    >
+    <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
         <div class="flex flex-col gap-1 pt-6 pb-8">
         <div class="flex flex-col gap-1 pt-6 pb-8">
           <h2 class="text-16-medium text-text-strong">{language.t("settings.tab.general")}</h2>
           <h2 class="text-16-medium text-text-strong">{language.t("settings.tab.general")}</h2>
@@ -417,7 +411,7 @@ export const SettingsGeneral: Component = () => {
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
-    </ScrollFade>
+    </div>
   )
   )
 }
 }
 
 

+ 2 - 8
packages/app/src/components/settings-keybinds.tsx

@@ -5,7 +5,6 @@ import { Icon } from "@opencode-ai/ui/icon"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { TextField } from "@opencode-ai/ui/text-field"
 import { TextField } from "@opencode-ai/ui/text-field"
 import { showToast } from "@opencode-ai/ui/toast"
 import { showToast } from "@opencode-ai/ui/toast"
-import { ScrollFade } from "@opencode-ai/ui/scroll-fade"
 import fuzzysort from "fuzzysort"
 import fuzzysort from "fuzzysort"
 import { formatKeybind, parseKeybind, useCommand } from "@/context/command"
 import { formatKeybind, parseKeybind, useCommand } from "@/context/command"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
@@ -353,12 +352,7 @@ export const SettingsKeybinds: Component = () => {
   })
   })
 
 
   return (
   return (
-    <ScrollFade
-      direction="vertical"
-      fadeStartSize={0}
-      fadeEndSize={16}
-      class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10"
-    >
+    <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
         <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
         <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
           <div class="flex items-center justify-between gap-4">
           <div class="flex items-center justify-between gap-4">
@@ -436,6 +430,6 @@ export const SettingsKeybinds: Component = () => {
           </div>
           </div>
         </Show>
         </Show>
       </div>
       </div>
-    </ScrollFade>
+    </div>
   )
   )
 }
 }

+ 2 - 8
packages/app/src/components/settings-models.tsx

@@ -9,7 +9,6 @@ import { type Component, For, Show } from "solid-js"
 import { useLanguage } from "@/context/language"
 import { useLanguage } from "@/context/language"
 import { useModels } from "@/context/models"
 import { useModels } from "@/context/models"
 import { popularProviders } from "@/hooks/use-providers"
 import { popularProviders } from "@/hooks/use-providers"
-import { ScrollFade } from "@opencode-ai/ui/scroll-fade"
 
 
 type ModelItem = ReturnType<ReturnType<typeof useModels>["list"]>[number]
 type ModelItem = ReturnType<ReturnType<typeof useModels>["list"]>[number]
 
 
@@ -40,12 +39,7 @@ export const SettingsModels: Component = () => {
   })
   })
 
 
   return (
   return (
-    <ScrollFade
-      direction="vertical"
-      fadeStartSize={0}
-      fadeEndSize={16}
-      class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10"
-    >
+    <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
         <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
         <div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
           <h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2>
           <h2 class="text-16-medium text-text-strong">{language.t("settings.models.title")}</h2>
@@ -131,6 +125,6 @@ export const SettingsModels: Component = () => {
           </Show>
           </Show>
         </Show>
         </Show>
       </div>
       </div>
-    </ScrollFade>
+    </div>
   )
   )
 }
 }

+ 2 - 8
packages/app/src/components/settings-providers.tsx

@@ -12,7 +12,6 @@ import { useGlobalSync } from "@/context/global-sync"
 import { DialogConnectProvider } from "./dialog-connect-provider"
 import { DialogConnectProvider } from "./dialog-connect-provider"
 import { DialogSelectProvider } from "./dialog-select-provider"
 import { DialogSelectProvider } from "./dialog-select-provider"
 import { DialogCustomProvider } from "./dialog-custom-provider"
 import { DialogCustomProvider } from "./dialog-custom-provider"
-import { ScrollFade } from "@opencode-ai/ui/scroll-fade"
 
 
 type ProviderSource = "env" | "api" | "config" | "custom"
 type ProviderSource = "env" | "api" | "config" | "custom"
 type ProviderMeta = { source?: ProviderSource }
 type ProviderMeta = { source?: ProviderSource }
@@ -116,12 +115,7 @@ export const SettingsProviders: Component = () => {
   }
   }
 
 
   return (
   return (
-    <ScrollFade
-      direction="vertical"
-      fadeStartSize={0}
-      fadeEndSize={16}
-      class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10"
-    >
+    <div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
       <div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-raised-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
         <div class="flex flex-col gap-1 pt-6 pb-8 max-w-[720px]">
         <div class="flex flex-col gap-1 pt-6 pb-8 max-w-[720px]">
           <h2 class="text-16-medium text-text-strong">{language.t("settings.providers.title")}</h2>
           <h2 class="text-16-medium text-text-strong">{language.t("settings.providers.title")}</h2>
@@ -267,6 +261,6 @@ export const SettingsProviders: Component = () => {
           </Button>
           </Button>
         </div>
         </div>
       </div>
       </div>
-    </ScrollFade>
+    </div>
   )
   )
 }
 }

+ 33 - 17
packages/ui/src/components/list.css

@@ -1,7 +1,25 @@
+@property --bottom-fade {
+  syntax: "<length>";
+  inherits: false;
+  initial-value: 0px;
+}
+
+@keyframes scroll {
+  0% {
+    --bottom-fade: 20px;
+  }
+  90% {
+    --bottom-fade: 20px;
+  }
+  100% {
+    --bottom-fade: 0;
+  }
+}
+
 [data-component="list"] {
 [data-component="list"] {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
-  gap: 8px;
+  gap: 12px;
   overflow: hidden;
   overflow: hidden;
   padding: 0 12px;
   padding: 0 12px;
 
 
@@ -19,9 +37,7 @@
       flex-shrink: 0;
       flex-shrink: 0;
       background-color: transparent;
       background-color: transparent;
       opacity: 0.5;
       opacity: 0.5;
-      transition-property: opacity;
-      transition-duration: var(--transition-duration);
-      transition-timing-function: var(--transition-easing);
+      transition: opacity 0.15s ease;
 
 
       &:hover:not(:disabled),
       &:hover:not(:disabled),
       &:focus-visible:not(:disabled),
       &:focus-visible:not(:disabled),
@@ -72,9 +88,7 @@
       height: 20px;
       height: 20px;
       background-color: transparent;
       background-color: transparent;
       opacity: 0.5;
       opacity: 0.5;
-      transition-property: opacity;
-      transition-duration: var(--transition-duration);
-      transition-timing-function: var(--transition-easing);
+      transition: opacity 0.15s ease;
 
 
       &:hover:not(:disabled),
       &:hover:not(:disabled),
       &:focus-visible:not(:disabled),
       &:focus-visible:not(:disabled),
@@ -117,6 +131,15 @@
     gap: 12px;
     gap: 12px;
     overflow-y: auto;
     overflow-y: auto;
     overscroll-behavior: contain;
     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"] {
     [data-slot="list-empty-state"] {
       display: flex;
       display: flex;
@@ -192,9 +215,7 @@
           background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent);
           background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent);
           pointer-events: none;
           pointer-events: none;
           opacity: 0;
           opacity: 0;
-          transition-property: opacity;
-          transition-duration: var(--transition-duration);
-          transition-timing-function: var(--transition-easing);
+          transition: opacity 0.15s ease;
         }
         }
 
 
         &[data-stuck="true"]::after {
         &[data-stuck="true"]::after {
@@ -230,22 +251,17 @@
             align-items: center;
             align-items: center;
             justify-content: center;
             justify-content: center;
             flex-shrink: 0;
             flex-shrink: 0;
-            aspect-ratio: 1 / 1;
+            aspect-ratio: 1/1;
             [data-component="icon"] {
             [data-component="icon"] {
               color: var(--icon-strong-base);
               color: var(--icon-strong-base);
             }
             }
           }
           }
-
-          [name="check"] {
-            color: var(--icon-strong-base);
-          }
-
           [data-slot="list-item-active-icon"] {
           [data-slot="list-item-active-icon"] {
             display: none;
             display: none;
             align-items: center;
             align-items: center;
             justify-content: center;
             justify-content: center;
             flex-shrink: 0;
             flex-shrink: 0;
-            aspect-ratio: 1 / 1;
+            aspect-ratio: 1/1;
             [data-component="icon"] {
             [data-component="icon"] {
               color: var(--icon-strong-base);
               color: var(--icon-strong-base);
             }
             }

+ 2 - 3
packages/ui/src/components/list.tsx

@@ -5,7 +5,6 @@ import { useI18n } from "../context/i18n"
 import { Icon, type IconProps } from "./icon"
 import { Icon, type IconProps } from "./icon"
 import { IconButton } from "./icon-button"
 import { IconButton } from "./icon-button"
 import { TextField } from "./text-field"
 import { TextField } from "./text-field"
-import { ScrollFade } from "./scroll-fade"
 
 
 function findByKey(container: HTMLElement, key: string) {
 function findByKey(container: HTMLElement, key: string) {
   const nodes = container.querySelectorAll<HTMLElement>('[data-slot="list-item"][data-key]')
   const nodes = container.querySelectorAll<HTMLElement>('[data-slot="list-item"][data-key]')
@@ -280,7 +279,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
           {searchAction()}
           {searchAction()}
         </div>
         </div>
       </Show>
       </Show>
-      <ScrollFade ref={setScrollRef} direction="vertical" fadeStartSize={0} fadeEndSize={20} data-slot="list-scroll">
+      <div ref={setScrollRef} data-slot="list-scroll">
         <Show
         <Show
           when={flat().length > 0 || showAdd()}
           when={flat().length > 0 || showAdd()}
           fallback={
           fallback={
@@ -353,7 +352,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
             </div>
             </div>
           </Show>
           </Show>
         </Show>
         </Show>
-      </ScrollFade>
+      </div>
     </div>
     </div>
   )
   )
 }
 }

+ 0 - 82
packages/ui/src/components/scroll-fade.css

@@ -1,82 +0,0 @@
-[data-component="scroll-fade"] {
-  overflow: auto;
-  overscroll-behavior: contain;
-  scrollbar-width: none;
-  box-sizing: border-box;
-  color: inherit;
-  font: inherit;
-  -ms-overflow-style: none;
-
-  &::-webkit-scrollbar {
-    display: none;
-  }
-
-  &[data-direction="horizontal"] {
-    overflow-x: auto;
-    overflow-y: hidden;
-
-    /* Both fades */
-    &[data-fade-start][data-fade-end] {
-      mask-image: linear-gradient(
-        to right,
-        transparent,
-        black var(--scroll-fade-start),
-        black calc(100% - var(--scroll-fade-end)),
-        transparent
-      );
-      -webkit-mask-image: linear-gradient(
-        to right,
-        transparent,
-        black var(--scroll-fade-start),
-        black calc(100% - var(--scroll-fade-end)),
-        transparent
-      );
-    }
-
-    /* Only start fade */
-    &[data-fade-start]:not([data-fade-end]) {
-      mask-image: linear-gradient(to right, transparent, black var(--scroll-fade-start), black 100%);
-      -webkit-mask-image: linear-gradient(to right, transparent, black var(--scroll-fade-start), black 100%);
-    }
-
-    /* Only end fade */
-    &:not([data-fade-start])[data-fade-end] {
-      mask-image: linear-gradient(to right, black 0%, black calc(100% - var(--scroll-fade-end)), transparent);
-      -webkit-mask-image: linear-gradient(to right, black 0%, black calc(100% - var(--scroll-fade-end)), transparent);
-    }
-  }
-
-  &[data-direction="vertical"] {
-    overflow-y: auto;
-    overflow-x: hidden;
-
-    &[data-fade-start][data-fade-end] {
-      mask-image: linear-gradient(
-        to bottom,
-        transparent,
-        black var(--scroll-fade-start),
-        black calc(100% - var(--scroll-fade-end)),
-        transparent
-      );
-      -webkit-mask-image: linear-gradient(
-        to bottom,
-        transparent,
-        black var(--scroll-fade-start),
-        black calc(100% - var(--scroll-fade-end)),
-        transparent
-      );
-    }
-
-    /* Only start fade */
-    &[data-fade-start]:not([data-fade-end]) {
-      mask-image: linear-gradient(to bottom, transparent, black var(--scroll-fade-start), black 100%);
-      -webkit-mask-image: linear-gradient(to bottom, transparent, black var(--scroll-fade-start), black 100%);
-    }
-
-    /* Only end fade */
-    &:not([data-fade-start])[data-fade-end] {
-      mask-image: linear-gradient(to bottom, black 0%, black calc(100% - var(--scroll-fade-end)), transparent);
-      -webkit-mask-image: linear-gradient(to bottom, black 0%, black calc(100% - var(--scroll-fade-end)), transparent);
-    }
-  }
-}

+ 0 - 206
packages/ui/src/components/scroll-fade.tsx

@@ -1,206 +0,0 @@
-import { type JSX, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js"
-
-export interface ScrollFadeProps extends JSX.HTMLAttributes<HTMLDivElement> {
-  direction?: "horizontal" | "vertical"
-  fadeStartSize?: number
-  fadeEndSize?: number
-  trackTransformSelector?: string
-  ref?: (el: HTMLDivElement) => void
-}
-
-export function ScrollFade(props: ScrollFadeProps) {
-  const [local, others] = splitProps(props, [
-    "children",
-    "direction",
-    "fadeStartSize",
-    "fadeEndSize",
-    "trackTransformSelector",
-    "class",
-    "style",
-    "ref",
-  ])
-
-  const direction = () => local.direction ?? "vertical"
-  const fadeStartSize = () => local.fadeStartSize ?? 20
-  const fadeEndSize = () => local.fadeEndSize ?? 20
-
-  const getTransformOffset = (element: Element): number => {
-    const style = getComputedStyle(element)
-    const transform = style.transform
-    if (!transform || transform === "none") return 0
-
-    const match = transform.match(/matrix(?:3d)?\(([^)]+)\)/)
-    if (!match) return 0
-
-    const values = match[1].split(",").map((v) => parseFloat(v.trim()))
-    const isHorizontal = direction() === "horizontal"
-
-    if (transform.startsWith("matrix3d")) {
-      return isHorizontal ? -(values[12] || 0) : -(values[13] || 0)
-    } else {
-      return isHorizontal ? -(values[4] || 0) : -(values[5] || 0)
-    }
-  }
-
-  let containerRef: HTMLDivElement | undefined
-
-  const [fadeStart, setFadeStart] = createSignal(0)
-  const [fadeEnd, setFadeEnd] = createSignal(0)
-  const [isScrollable, setIsScrollable] = createSignal(false)
-
-  let lastScrollPos = 0
-  let lastTransformPos = 0
-  let lastScrollSize = 0
-  let lastClientSize = 0
-
-  const updateFade = () => {
-    if (!containerRef) return
-
-    const isHorizontal = direction() === "horizontal"
-    const scrollPos = isHorizontal ? containerRef.scrollLeft : containerRef.scrollTop
-    const scrollSize = isHorizontal ? containerRef.scrollWidth : containerRef.scrollHeight
-    const clientSize = isHorizontal ? containerRef.clientWidth : containerRef.clientHeight
-
-    let transformPos = 0
-    if (local.trackTransformSelector) {
-      const transformElement = containerRef.querySelector(local.trackTransformSelector)
-      if (transformElement) {
-        transformPos = getTransformOffset(transformElement)
-      }
-    }
-
-    const effectiveScrollPos = Math.max(scrollPos, transformPos)
-
-    if (
-      effectiveScrollPos === lastScrollPos &&
-      transformPos === lastTransformPos &&
-      scrollSize === lastScrollSize &&
-      clientSize === lastClientSize
-    ) {
-      return
-    }
-
-    lastScrollPos = effectiveScrollPos
-    lastTransformPos = transformPos
-    lastScrollSize = scrollSize
-    lastClientSize = clientSize
-
-    const maxScroll = scrollSize - clientSize
-    const canScroll = maxScroll > 1
-
-    setIsScrollable(canScroll)
-
-    if (!canScroll) {
-      setFadeStart(0)
-      setFadeEnd(0)
-      return
-    }
-
-    const progress = maxScroll > 0 ? effectiveScrollPos / maxScroll : 0
-
-    const startProgress = Math.min(progress / 0.1, 1)
-    setFadeStart(startProgress * fadeStartSize())
-
-    const endProgress = progress > 0.9 ? (1 - progress) / 0.1 : 1
-    setFadeEnd(Math.max(0, endProgress) * fadeEndSize())
-  }
-
-  onMount(() => {
-    if (!containerRef) return
-
-    updateFade()
-
-    let rafId: number | undefined
-    let isPolling = false
-    let pollTimeout: ReturnType<typeof setTimeout> | undefined
-
-    const startPolling = () => {
-      if (isPolling) return
-      isPolling = true
-
-      const pollScroll = () => {
-        updateFade()
-        rafId = requestAnimationFrame(pollScroll)
-      }
-      rafId = requestAnimationFrame(pollScroll)
-    }
-
-    const stopPolling = () => {
-      if (!isPolling) return
-      isPolling = false
-      if (rafId !== undefined) {
-        cancelAnimationFrame(rafId)
-        rafId = undefined
-      }
-    }
-
-    const schedulePollingStop = () => {
-      if (pollTimeout !== undefined) clearTimeout(pollTimeout)
-      pollTimeout = setTimeout(stopPolling, 1000)
-    }
-
-    const onActivity = () => {
-      updateFade()
-      if (local.trackTransformSelector) {
-        startPolling()
-        schedulePollingStop()
-      }
-    }
-
-    containerRef.addEventListener("scroll", onActivity, { passive: true })
-
-    const resizeObserver = new ResizeObserver(() => {
-      lastScrollSize = 0
-      lastClientSize = 0
-      onActivity()
-    })
-    resizeObserver.observe(containerRef)
-
-    const mutationObserver = new MutationObserver(() => {
-      lastScrollSize = 0
-      lastClientSize = 0
-      requestAnimationFrame(onActivity)
-    })
-    mutationObserver.observe(containerRef, {
-      childList: true,
-      subtree: true,
-      characterData: true,
-    })
-
-    onCleanup(() => {
-      containerRef?.removeEventListener("scroll", onActivity)
-      resizeObserver.disconnect()
-      mutationObserver.disconnect()
-      stopPolling()
-      if (pollTimeout !== undefined) clearTimeout(pollTimeout)
-    })
-  })
-
-  createEffect(() => {
-    local.children
-    requestAnimationFrame(updateFade)
-  })
-
-  return (
-    <div
-      ref={(el) => {
-        containerRef = el
-        local.ref?.(el)
-      }}
-      data-component="scroll-fade"
-      data-direction={direction()}
-      data-scrollable={isScrollable() || undefined}
-      data-fade-start={fadeStart() > 0 || undefined}
-      data-fade-end={fadeEnd() > 0 || undefined}
-      class={local.class}
-      style={{
-        ...(typeof local.style === "object" ? local.style : {}),
-        "--scroll-fade-start": `${fadeStart()}px`,
-        "--scroll-fade-end": `${fadeEnd()}px`,
-      }}
-      {...others}
-    >
-      {local.children}
-    </div>
-  )
-}

+ 0 - 141
packages/ui/src/components/scroll-reveal.tsx

@@ -1,141 +0,0 @@
-import { type JSX, onCleanup, splitProps } from "solid-js"
-import { ScrollFade, type ScrollFadeProps } from "./scroll-fade"
-
-const SCROLL_SPEED = 60
-const PAUSE_DURATION = 800
-
-type ScrollAnimationState = {
-  rafId: number | null
-  startTime: number
-  running: boolean
-}
-
-const startScrollAnimation = (containerEl: HTMLElement): ScrollAnimationState | null => {
-  containerEl.offsetHeight
-
-  const extraWidth = containerEl.scrollWidth - containerEl.clientWidth
-
-  if (extraWidth <= 0) {
-    return null
-  }
-
-  const scrollDuration = (extraWidth / SCROLL_SPEED) * 1000
-  const totalDuration = PAUSE_DURATION + scrollDuration + PAUSE_DURATION + scrollDuration + PAUSE_DURATION
-
-  const state: ScrollAnimationState = {
-    rafId: null,
-    startTime: performance.now(),
-    running: true,
-  }
-
-  const animate = (currentTime: number) => {
-    if (!state.running) return
-
-    const elapsed = currentTime - state.startTime
-    const progress = (elapsed % totalDuration) / totalDuration
-
-    const pausePercent = PAUSE_DURATION / totalDuration
-    const scrollPercent = scrollDuration / totalDuration
-
-    const pauseEnd1 = pausePercent
-    const scrollEnd1 = pauseEnd1 + scrollPercent
-    const pauseEnd2 = scrollEnd1 + pausePercent
-    const scrollEnd2 = pauseEnd2 + scrollPercent
-
-    let scrollPos = 0
-
-    if (progress < pauseEnd1) {
-      scrollPos = 0
-    } else if (progress < scrollEnd1) {
-      const scrollProgress = (progress - pauseEnd1) / scrollPercent
-      scrollPos = scrollProgress * extraWidth
-    } else if (progress < pauseEnd2) {
-      scrollPos = extraWidth
-    } else if (progress < scrollEnd2) {
-      const scrollProgress = (progress - pauseEnd2) / scrollPercent
-      scrollPos = extraWidth * (1 - scrollProgress)
-    } else {
-      scrollPos = 0
-    }
-
-    containerEl.scrollLeft = scrollPos
-    state.rafId = requestAnimationFrame(animate)
-  }
-
-  state.rafId = requestAnimationFrame(animate)
-  return state
-}
-
-const stopScrollAnimation = (state: ScrollAnimationState | null, containerEl?: HTMLElement) => {
-  if (state) {
-    state.running = false
-    if (state.rafId !== null) {
-      cancelAnimationFrame(state.rafId)
-    }
-  }
-  if (containerEl) {
-    containerEl.scrollLeft = 0
-  }
-}
-
-export interface ScrollRevealProps extends Omit<ScrollFadeProps, "direction"> {
-  hoverDelay?: number
-}
-
-export function ScrollReveal(props: ScrollRevealProps) {
-  const [local, others] = splitProps(props, ["children", "hoverDelay", "ref"])
-
-  const hoverDelay = () => local.hoverDelay ?? 300
-
-  let containerRef: HTMLDivElement | undefined
-  let hoverTimeout: ReturnType<typeof setTimeout> | undefined
-  let scrollAnimationState: ScrollAnimationState | null = null
-
-  const handleMouseEnter: JSX.EventHandler<HTMLDivElement, MouseEvent> = () => {
-    hoverTimeout = setTimeout(() => {
-      if (!containerRef) return
-
-      containerRef.offsetHeight
-
-      const isScrollable = containerRef.scrollWidth > containerRef.clientWidth + 1
-
-      if (isScrollable) {
-        stopScrollAnimation(scrollAnimationState, containerRef)
-        scrollAnimationState = startScrollAnimation(containerRef)
-      }
-    }, hoverDelay())
-  }
-
-  const handleMouseLeave: JSX.EventHandler<HTMLDivElement, MouseEvent> = () => {
-    if (hoverTimeout) {
-      clearTimeout(hoverTimeout)
-      hoverTimeout = undefined
-    }
-    stopScrollAnimation(scrollAnimationState, containerRef)
-    scrollAnimationState = null
-  }
-
-  onCleanup(() => {
-    if (hoverTimeout) {
-      clearTimeout(hoverTimeout)
-    }
-    stopScrollAnimation(scrollAnimationState, containerRef)
-  })
-
-  return (
-    <ScrollFade
-      ref={(el) => {
-        containerRef = el
-        local.ref?.(el)
-      }}
-      fadeStartSize={8}
-      fadeEndSize={8}
-      direction="horizontal"
-      onMouseEnter={handleMouseEnter}
-      onMouseLeave={handleMouseLeave}
-      {...others}
-    >
-      {local.children}
-    </ScrollFade>
-  )
-}

+ 0 - 1
packages/ui/src/styles/index.css

@@ -41,7 +41,6 @@
 @import "../components/select.css" layer(components);
 @import "../components/select.css" layer(components);
 @import "../components/spinner.css" layer(components);
 @import "../components/spinner.css" layer(components);
 @import "../components/switch.css" layer(components);
 @import "../components/switch.css" layer(components);
-@import "../components/scroll-fade.css" layer(components);
 @import "../components/session-review.css" layer(components);
 @import "../components/session-review.css" layer(components);
 @import "../components/session-turn.css" layer(components);
 @import "../components/session-turn.css" layer(components);
 @import "../components/sticky-accordion-header.css" layer(components);
 @import "../components/sticky-accordion-header.css" layer(components);