Adaptive Actions
nexvyn/adaptive-actionsFreeComponent
A responsive action toolbar that measures its available inline size and moves overflowed items into an accessible dropdown menu, with priority and pinned flags to control what stays visible.
Live preview
live · rendered by the registry
Rendered by nexvyn on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.nexvyn.dev/r/adaptive-actions.jsonSource
1'use client'23import {4 createContext,5 forwardRef,6 useCallback,7 useContext,8 useEffect,9 useId,10 useLayoutEffect,11 useMemo,12 useRef,13 useState,14 type HTMLAttributes,15 type ReactNode,16} from 'react'17import { createPortal } from 'react-dom'18import { AnimatePresence, motion, useReducedMotion } from 'motion/react'19import { cn } from '@/lib/utils'2021export interface ActionItem {22 /** Stable unique identifier for the action. */23 id: string24 /** Accessible label — rendered as visible text or `aria-label` for icon-only. */25 label: string26 /** Icon rendered inside the button (decorative, `aria-hidden`). */27 icon?: ReactNode28 /** Disables this action. */29 disabled?: boolean30 /** Marks action as destructive (applies destructive color tokens). */31 destructive?: boolean32 /** Priority determines overflow order. Higher priority items overflow last. Default 0. */33 priority?: number34 /** When true, this item never overflows into the menu regardless of space. */35 pinned?: boolean36 /** Invoked on activation (click / Enter / Space). */37 onSelect?: () => void38}3940export interface AdaptiveActionsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {41 /** The actions to render. */42 actions: ActionItem[]43 /** Accessible label for the toolbar. Defaults to 'Actions'. */44 label?: string45 /** Localized label for the overflow trigger button. Defaults to 'More actions'. */46 moreLabel?: string47 /** Render a custom trigger for the overflow menu. Receives the count of hidden items. */48 renderMoreTrigger?: (count: number) => ReactNode49 /** Maximum visible items before overflow — independent of inline measurement. Optional. */50 maxVisible?: number51}5253interface AdaptiveActionsContextValue {54 rovingIndex: number55 setRovingIndex: (i: number) => void56 itemCount: number57}5859const AdaptiveActionsContext = createContext<AdaptiveActionsContextValue | null>(null)6061function useAdaptiveActionsCtx() {62 const ctx = useContext(AdaptiveActionsContext)63 if (!ctx) throw new Error('AdaptiveActions compound child used outside AdaptiveActions')64 return ctx65}6667const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect6869interface OverflowMenuProps {70 actions: ActionItem[]71 triggerId: string72 contentId: string73 moreLabel: string74 renderMoreTrigger?: (count: number) => ReactNode75 triggerTabIndex: number76}7778function OverflowMenu({79 actions,80 triggerId,81 contentId,82 moreLabel,83 renderMoreTrigger,84// … truncated
What it pulls in
npm packages
Files it writes
More from nexvyn
All 40 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | nexvyn | Components | Free | 1 dep | |
| Action Buttonaction-button | nexvyn | Components | Free | 3 deps · 4 files | |
| AI Inputai-input | nexvyn | Components | Free | 1 dep · 2 files | |
| Badgebadge | nexvyn | Components | Free | 2 deps · 2 files | |
| Bars Themebars-theme | nexvyn | Components | Free | 1 dep · 3 files | |
| Bounce Sidebarbounce-sidebar | nexvyn | Components | Free | 1 dep · 2 files | |
| Breadcrumbsbreadcrumbs | nexvyn | Components | Free | 1 dep · 2 files | |
| Buttonbutton | nexvyn | Components | Free | 3 deps · 3 files |
