Share & Permissions Popover
moumenlab/share-permissions-popoverFreeComponent
A three-view floating surface that morphs on both axes with a focus trap surviving the swaps.
Live preview
live · rendered by the registry
Rendered by moumenlab on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://lab.moumen.dev/r/share-permissions-popover.jsonSource
1"use client";23import { useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from "react";4import { MotionConfig, motion, useReducedMotion } from "motion/react";56// Share & permissions popover - three views in one floating element.7//8// A popover is a FLOATING element with three different-sized panels inside9// (share list · a person's role picker · link settings), and swapping panels10// has to feel like one surface changing shape, not three popovers taking turns.11//12// · Animated auto-size on BOTH axes. Each view declares its natural width and13// grows its own height; views are absolutely positioned so they keep their14// intrinsic size, and the frame's width AND height are measured px15// (useLayoutEffect + ResizeObserver) that motion eases between - the16// height:auto illusion, on two axes. A fresh open snaps to size instead of17// morphing from the last session's.18// · A focus trap that survives view swaps. Tab cycles the LIVE view's19// controls only; every swap hands focus somewhere sensible (push → the20// current role; pop → the row that opened it; close → the trigger). Pass21// trap={false} to feel Tab walk out.22// · Breadcrumb back-navigation. Escape backs out one layer at a time.23//24// The popover scales in FROM THE TRIGGER (origin under the Share button), stays25// mounted so open/close are interruptible, and slides views directionally:26// push enters from the right, pop from the left, with a short-lived leaving27// snapshot. Animation via motion/react; honours prefers-reduced-motion.28// Requires the lab-theme tokens. Fully Tailwind, no CSS files.2930const EASE = [0.22, 1, 0.36, 1] as const;31const EASE_ICON = [0.2, 0, 0, 1] as const;32const SLIDE = 28; // 1.75rem directional slide distance3334export interface Person {35 id: string;36 name: string;37 email: string;38}3940export interface SharePopoverState {41 open: boolean;42 view: string;43 people: number;44 link: string;45 size: { w: number; h: number } | null;46 lastAction: string | null;47}4849type View = { name: "share" } | { name: "role"; person: Person } | { name: "link" };5051const ROLES = [52 { id: "full", label: "Full access", hint: "Edit, share and manage" },53 { id: "edit", label: "Can edit", hint: "Edit but not share" },54 { id: "comment", label: "Can comment", hint: "Read and comment" },55 { id: "view", label: "Can view", hint: "Read only" },56];5758const LINK_SCOPES = [59// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from moumenlab
All 14 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Caret-Anchored Mention Popovercaret-mention-popover | moumenlab | Components | Free | 1 dep | |
| Command Palette with Argument Chipscommand-palette | moumenlab | Components | Free | 1 dep | |
| Drag-to-Reorder Listdrag-to-reorder-list | moumenlab | Components | Free | 1 dep | |
| File Upload Staging Areafile-upload-staging | moumenlab | Components | Free | 1 dep | |
| Hover-Expand Icon Striphover-expand-icon-strip | moumenlab | Components | Free | 1 dep | |
| Inertial Wheel Listinertial-wheel-list | moumenlab | Components | Free | 1 dep | |
| Morphing Checkout Flowmorphing-checkout | moumenlab | Components | Free | 1 dep | |
| OTP Segmented Inputotp-segmented-input | moumenlab | Components | Free | 1 dep |
