summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/cycle-label.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-02 11:46:25 -0600
committerAdam <[email protected]>2026-02-02 14:24:24 -0600
commit70cf609ce90a7534349c8dd5ed8441cbd32ebba7 (patch)
tree4dbf2be3e1928c3c4414fe3397eaf90d076d0c63 /packages/ui/src/components/cycle-label.tsx
parent2f76b49df3cfd316069a2b5c292fed369acadbde (diff)
downloadopencode-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.tsx135
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 : {}),
- }}
- />
- )
-}