diff options
| author | Adam <[email protected]> | 2026-02-02 11:46:25 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-02-02 14:24:24 -0600 |
| commit | 70cf609ce90a7534349c8dd5ed8441cbd32ebba7 (patch) | |
| tree | 4dbf2be3e1928c3c4414fe3397eaf90d076d0c63 /packages/ui/src/components/cycle-label.tsx | |
| parent | 2f76b49df3cfd316069a2b5c292fed369acadbde (diff) | |
| download | opencode-70cf609ce90a7534349c8dd5ed8441cbd32ebba7.tar.gz opencode-70cf609ce90a7534349c8dd5ed8441cbd32ebba7.zip | |
Revert "feat(ui): Select, dropdown, popover styles & transitions (#11675)"
This reverts commit 377bf7ff21a4f05807c38675ac70cd08fe67b516.
Diffstat (limited to 'packages/ui/src/components/cycle-label.tsx')
| -rw-r--r-- | packages/ui/src/components/cycle-label.tsx | 135 |
1 files changed, 0 insertions, 135 deletions
diff --git a/packages/ui/src/components/cycle-label.tsx b/packages/ui/src/components/cycle-label.tsx deleted file mode 100644 index dc12bd75c..000000000 --- a/packages/ui/src/components/cycle-label.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import "./cycle-label.css" -import { createEffect, createSignal, JSX, on } from "solid-js" - -export interface CycleLabelProps extends JSX.HTMLAttributes<HTMLSpanElement> { - value: string - onValueChange?: (value: string) => void - duration?: number | ((value: string) => number) - stagger?: number - opacity?: [number, number] - blur?: [number, number] - skewX?: number - onAnimationStart?: () => void - onAnimationEnd?: () => void -} - -const segmenter = - typeof Intl !== "undefined" && Intl.Segmenter ? new Intl.Segmenter("en", { granularity: "grapheme" }) : null - -const getChars = (text: string): string[] => - segmenter ? Array.from(segmenter.segment(text), (s) => s.segment) : text.split("") - -const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) - -export function CycleLabel(props: CycleLabelProps) { - const getDuration = (text: string) => { - const d = - props.duration ?? - Number(getComputedStyle(document.documentElement).getPropertyValue("--transition-duration")) ?? - 200 - return typeof d === "function" ? d(text) : d - } - const stagger = () => props?.stagger ?? 30 - const opacity = () => props?.opacity ?? [0, 1] - const blur = () => props?.blur ?? [0, 0] - const skewX = () => props?.skewX ?? 10 - - let containerRef: HTMLSpanElement | undefined - let isAnimating = false - const [currentText, setCurrentText] = createSignal(props.value) - - const setChars = (el: HTMLElement, text: string, state: "enter" | "exit" | "pre" = "enter") => { - el.innerHTML = "" - const chars = getChars(text) - chars.forEach((char, i) => { - const span = document.createElement("span") - span.textContent = char === " " ? "\u00A0" : char - span.className = `cycle-char ${state}` - span.style.setProperty("--i", String(i)) - el.appendChild(span) - }) - } - - const animateToText = async (newText: string) => { - if (!containerRef || isAnimating) return - if (newText === currentText()) return - - isAnimating = true - props.onAnimationStart?.() - - const dur = getDuration(newText) - const stag = stagger() - - containerRef.style.width = containerRef.offsetWidth + "px" - - const oldChars = containerRef.querySelectorAll(".cycle-char") - oldChars.forEach((c) => c.classList.replace("enter", "exit")) - - const clone = containerRef.cloneNode(false) as HTMLElement - Object.assign(clone.style, { - position: "absolute", - visibility: "hidden", - width: "auto", - transition: "none", - }) - setChars(clone, newText) - document.body.appendChild(clone) - const nextWidth = clone.offsetWidth - clone.remove() - - const exitTime = oldChars.length * stag + dur - await wait(exitTime * 0.3) - - containerRef.style.width = nextWidth + "px" - - const widthDur = 200 - await wait(widthDur * 0.3) - - setChars(containerRef, newText, "pre") - containerRef.offsetWidth - - Array.from(containerRef.children).forEach((c) => (c.className = "cycle-char enter")) - setCurrentText(newText) - props.onValueChange?.(newText) - - const enterTime = getChars(newText).length * stag + dur - await wait(enterTime) - - containerRef.style.width = "" - isAnimating = false - props.onAnimationEnd?.() - } - - createEffect( - on( - () => props.value, - (newValue) => { - if (newValue !== currentText()) { - animateToText(newValue) - } - }, - ), - ) - - const initRef = (el: HTMLSpanElement) => { - containerRef = el - setChars(el, props.value) - } - - return ( - <span - ref={initRef} - class={`cycle-label ${props.class ?? ""}`} - style={{ - "--c-duration": `${getDuration(currentText())}ms`, - "--c-stagger": `${stagger()}ms`, - "--c-opacity-start": opacity()[0], - "--c-opacity-end": opacity()[1], - "--c-blur-start": `${blur()[0]}px`, - "--c-blur-end": `${blur()[1]}px`, - "--c-skew": `${skewX()}deg`, - ...(typeof props.style === "object" ? props.style : {}), - }} - /> - ) -} |
