diff --git a/packages/dify-ui/docs/selection.md b/packages/dify-ui/docs/selection.md index a33934ae7f9..03262b01910 100644 --- a/packages/dify-ui/docs/selection.md +++ b/packages/dify-ui/docs/selection.md @@ -6,9 +6,7 @@ domain value type through its public API. ## Primitive choice - [`RadioGroup`] selects one persistent field value from a visible set of options. Every `Radio` or - `RadioItem` belongs to a group; do not render a standalone radio. To animate a custom selected surface, place - `RadioGroupIndicator` first inside a positioned, isolated group and give its items relative - positioning. Keyboard focus and reduced-motion preferences select the new surface immediately. + `RadioItem` belongs to a group; do not render a standalone radio. - `SegmentedControl` selects one mode, filter, or view. It follows radio-group semantics: an active item cannot be toggled off, `Tab` enters on the selected item, and arrow keys move and select. The primitive owns the track, focus ring and selection motion. Preserve the existing density, diff --git a/packages/dify-ui/src/radio-group/index.tsx b/packages/dify-ui/src/radio-group/index.tsx index d2a7ca8512a..11861ce8a1f 100644 --- a/packages/dify-ui/src/radio-group/index.tsx +++ b/packages/dify-ui/src/radio-group/index.tsx @@ -89,8 +89,6 @@ function RadioSkeleton({ className, ...props }: RadioSkeletonProps) { ) } -export { RadioGroupIndicator } from './indicator' - export { Radio, RadioControl, RadioGroup, RadioItem, RadioSkeleton } export type { diff --git a/packages/dify-ui/src/radio-group/indicator.tsx b/packages/dify-ui/src/radio-group/indicator.tsx deleted file mode 100644 index aae429e61bb..00000000000 --- a/packages/dify-ui/src/radio-group/indicator.tsx +++ /dev/null @@ -1,93 +0,0 @@ -'use client' - -import * as React from 'react' - -export function RadioGroupIndicator() { - const ref = React.useRef(null) - - React.useLayoutEffect(() => { - const thumb = ref.current! - const group = thumb.parentElement! - const getSelected = () => group.querySelector('[role="radio"][data-checked]') - const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)') - let selected = getSelected() - let slide: Animation | undefined - let hideFill: Animation | undefined - - function stop() { - if (slide) { - slide.onfinish = null - slide.cancel() - } - hideFill?.cancel() - slide = undefined - thumb.hidden = true - } - - function update() { - const next = getSelected() - if (next === selected) return - const from = (slide ? thumb : selected)?.getBoundingClientRect() - selected = next - stop() - if (!next || !from?.width || reducedMotion.matches || next.matches(':focus-visible')) return - - const to = next.getBoundingClientRect() - if (!to.width || !to.height) return - - // Paint the item's actual surface during the slide. Its layout and focus ring stay in place. - const surface = getComputedStyle(next) - Object.assign(thumb.style, { - width: surface.width, - height: surface.height, - backgroundColor: surface.backgroundColor, - border: surface.border, - borderRadius: surface.borderRadius, - boxShadow: surface.getPropertyValue('--tw-shadow'), - }) - thumb.hidden = false - const origin = thumb.getBoundingClientRect() - const scaleX = origin.width / Number.parseFloat(surface.width) - const scaleY = origin.height / Number.parseFloat(surface.height) - const transform = (rect: DOMRect) => - `translate(${(rect.x - origin.x) / scaleX}px, ${(rect.y - origin.y) / scaleY}px) scale(${rect.width / origin.width}, ${rect.height / origin.height})` - - const timing: KeyframeAnimationOptions = { - duration: 180, - easing: 'cubic-bezier(0.2, 0, 0, 1)', - fill: 'both', - } - const clear = { - backgroundColor: 'transparent', - borderColor: 'transparent', - '--tw-shadow': '0 0 #0000', - } - hideFill = next.animate([clear, clear], timing) - slide = thumb.animate([{ transform: transform(from) }, { transform: transform(to) }], timing) - slide.onfinish = stop - } - - const observer = new MutationObserver(update) - observer.observe(group, { - attributes: true, - attributeFilter: ['data-checked'], - childList: true, - subtree: true, - }) - reducedMotion.addEventListener('change', stop) - return () => { - observer.disconnect() - reducedMotion.removeEventListener('change', stop) - stop() - } - }, []) - - return ( -