UNPKG

@loke/ui

Version:
157 lines (134 loc) 6.91 kB
--- name: loke-ui type: core library: "@loke/ui" library_version: "1.0.0-rc.1" description: > Entry point and router for @loke/ui headless React UI primitives. Subpath imports only — never import from @loke/ui directly. 19 components across overlays (Dialog, AlertDialog, Popover, Tooltip, DropdownMenu), forms (Checkbox, RadioGroup, Select, Switch, Label), navigation (Accordion, Tabs, Collapsible, Command), and composition (Primitive+Slot, Context+Collection, Overlay Infrastructure, Hooks). All are thin shells over 4 shared infrastructure patterns: DismissableLayer, FocusScope, Presence, useControllableState. --- # @loke/ui Headless, unstyled React UI primitives with granular subpath exports. Every component ships accessible behavior, keyboard navigation, and ARIA semantics. Styling is entirely your responsibility. ## Import Rule **ALWAYS** import from the component subpath. Never from the root package or Radix paths. ```tsx // CORRECT import { Dialog, DialogContent, DialogTitle } from "@loke/ui/dialog"; import { Select, SelectItem, SelectItemText } from "@loke/ui/select"; // WRONG — these all fail import { Dialog } from "@loke/ui"; import { Dialog } from "@radix-ui/react-dialog"; import { Dialog } from "@loke/design-system"; ``` ## Mental Model All 19 components are thin shells over 4 shared infrastructure patterns. Learn these once and every component becomes readable: | Infrastructure | What it does | Components that use it | |---|---|---| | `DismissableLayer` | Stacked dismiss logic, outside-click, escape key | Dialog, Popover, Tooltip, DropdownMenu | | `FocusScope` | Focus trapping, looping, auto-focus on open | Dialog, AlertDialog, Popover (modal) | | `Presence` | Animation state machine — delays unmount for exit transitions | All overlays with `forceMount` | | `useControllableState` | Controlled/uncontrolled dual-mode with dev warnings | All stateful components | ## Sub-Skills Route to the skill that matches what you're building: | Need to | Skill | |---|---| | Build a modal or non-modal dialog | [`dialog`](../dialog/SKILL.md) | | Build a destructive confirmation dialog | [`alert-dialog`](../alert-dialog/SKILL.md) | | Build a floating panel anchored to a trigger | [`popover`](../popover/SKILL.md) | | Add hover/focus tooltips | [`tooltip`](../tooltip/SKILL.md) | | Build a trigger-based dropdown menu | [`dropdown-menu`](../dropdown-menu/SKILL.md) | | Build accessible checkboxes (incl. indeterminate) | [`checkbox`](../checkbox/SKILL.md) | | Build a single-selection radio group | [`radio-group`](../radio-group/SKILL.md) | | Build a listbox-style select for forms | [`select`](../select/SKILL.md) | | Build an on/off toggle switch | [`switch`](../switch/SKILL.md) | | Label a form control accessibly | [`label`](../label/SKILL.md) | | Build expandable section groups | [`accordion`](../accordion/SKILL.md) | | Build a tabbed interface | [`tabs`](../tabs/SKILL.md) | | Build a single collapsible section | [`collapsible`](../collapsible/SKILL.md) | | Build a command palette or searchable list | [`command`](../command/SKILL.md) | | Use `asChild` / delegate element rendering | [`primitive-and-slot`](../primitive-and-slot/SKILL.md) | | Build compound components with scoped context | [`context-and-collection`](../context-and-collection/SKILL.md) | | Build a custom overlay from scratch | [`overlay-infrastructure`](../overlay-infrastructure/SKILL.md) | | Use shared hooks (controllable state, size, etc.) | [`hooks`](../hooks/SKILL.md) | | Unsure which component to use | [`choosing-the-right-component`](../choosing-the-right-component/SKILL.md) | ## Quick Decision Tree ``` Need floating/overlay UI? ├─ Triggered by hover/focus only + non-interactive content Tooltip ├─ Triggered by click + interactive content (forms, buttons) Popover ├─ Triggered by click + list of actions DropdownMenu ├─ Triggered by click + form value selection (static list) Select ├─ Triggered by click + searchable/async option list Popover + Command ├─ Full overlay with title + description ├─ Destructive action confirmation AlertDialog └─ General purpose modal/sheet Dialog └─ Nothing floats, just expand/collapse ├─ Single section Collapsible ├─ Multiple coordinated sections Accordion └─ Tabbed panel switching Tabs Need a form control? ├─ Boolean on/off toggle Switch ├─ Multi-select or tri-state (indeterminate) Checkbox ├─ Single selection from a group RadioGroup ├─ Pick a value, submit in a form Select └─ Any control needs an accessible name Label ``` ## All Subpath Imports ```tsx import { ... } from "@loke/ui/accordion"; import { ... } from "@loke/ui/alert-dialog"; import { ... } from "@loke/ui/checkbox"; import { ... } from "@loke/ui/collapsible"; import { ... } from "@loke/ui/command"; import { ... } from "@loke/ui/context"; import { ... } from "@loke/ui/dialog"; import { ... } from "@loke/ui/dismissable-layer"; import { ... } from "@loke/ui/dropdown-menu"; import { ... } from "@loke/ui/focus-guards"; import { ... } from "@loke/ui/focus-scope"; import { ... } from "@loke/ui/label"; import { ... } from "@loke/ui/popper"; import { ... } from "@loke/ui/popover"; import { ... } from "@loke/ui/portal"; import { ... } from "@loke/ui/presence"; import { ... } from "@loke/ui/primitive"; import { ... } from "@loke/ui/radio-group"; import { ... } from "@loke/ui/select"; import { ... } from "@loke/ui/slot"; import { ... } from "@loke/ui/switch"; import { ... } from "@loke/ui/tabs"; import { ... } from "@loke/ui/tooltip"; import { ... } from "@loke/ui/use-callback-ref"; import { ... } from "@loke/ui/use-controllable-state"; import { ... } from "@loke/ui/use-direction"; import { ... } from "@loke/ui/use-escape-keydown"; import { ... } from "@loke/ui/use-id"; import { ... } from "@loke/ui/use-is-document-hidden"; import { ... } from "@loke/ui/use-is-hydrated"; import { ... } from "@loke/ui/use-layout-effect"; import { ... } from "@loke/ui/use-previous"; import { ... } from "@loke/ui/use-size"; ``` ## Key Patterns **All overlay components share `forceMount` for exit animations:** ```tsx // Without forceMount, the element unmounts before the exit animation runs <DialogPortal forceMount> <DialogOverlay forceMount className="animate-fade-out data-[state=open]:animate-fade-in" /> <DialogContent forceMount className="animate-slide-out data-[state=open]:animate-slide-in"> ... </DialogContent> </DialogPortal> ``` **All stateful components support controlled and uncontrolled mode:** ```tsx // Uncontrolled (internal state) <Dialog defaultOpen={false}>...</Dialog> // Controlled (external state) <Dialog open={open} onOpenChange={setOpen}>...</Dialog> ``` **Do not mix controlled and uncontrolled.** Switching at runtime triggers dev warnings and loses state.