Minor Changes
-
b56144c: Draggable: collision algorithms, axis constraint, drag bounds, and handle sub-component
- New
collisionAlgorithmprop ('intersect'|'contain'|'center') switches zone hit testing from point-based to rect-vs-rect. The draggable's live rect (computed from its lift-time box offset by pointer delta) is tested against each zone's measured box using the chosen strategy. Falls back to the existing point-in-rect test when unset or when nosourceRectis available. - New
dragAxisprop ('x'|'y'|'both') constrains pointer movement to a single axis during the drag. The ghost and zone targeting respect the clamped position;onDragMovestill receives the raw (unclamped) point. - New
dragBoundsRefprop accepts a ref to a boundaryView. The drag ghost is clamped inside that view's window-coordinate rect on every frame. Pan-transport only (touch on web, native); HTML5 drags are controlled by the browser. - New
<Draggable.Handle>sub-component restricts drag initiation to a sub-area. Multiple handles per draggable are supported; as long as at least one is mounted, the host'sGestureDetectoris suppressed.
Dragzone:
skipRectMeasurefor programmatic hit testingZones that compute hit testing through another mechanism (e.g. arithmetic position in
SortableList) can now setskipRectMeasure={true}. The zone is never measured, never participates in measure sweeps, and always passes the spatial hit test — the consumer'sacceptspredicate is the sole gate.SortableList: Reanimated-powered UI-thread animations
SortableListnow drives item position animations entirely on the UI thread viareact-native-reanimatedshared values anduseAnimatedReaction. Insertion index updates write directly to aSharedValuewithout triggering React re-renders; items read the shared values in worklets and animatetranslateYwithwithTiming. The commit (on drop) snaps items to their new canonical positions via adropVersionshared value bump in auseLayoutEffect— the user never sees an intermediate frame. TheactiveIndex(forrenderItem'sisDraggingflag) stays as React state, so only the dragged item re-renders on lift/drop. - New
-
ee276b3: New
IconButtoncomponent — a purpose-built icon-only button supercedingButton size="icon"IconButtonis a standalone component for icon-only actions. It shares the same 8 visual variants asButton(primary,secondary,ghost,danger,special,inverse,outlineDanger,ghostDanger) and adds theicon/iconBackgroundColor/iconColorAPI fromMenuItemfor coloured icon tiles (iOS Settings style).Key differences from
<Button size="icon">:iconprop takes aComponentType<IconProps>— the icon component itself, not a pre-built elementiconBackgroundColoroptionally wraps the icon in a coloured rounded-square tileiconColoroverrides the variant-derived icon stroke colouraccessibilityLabelis required — an icon-only button needs an accessible name- No
children,leftAdornment, orrightAdornment— the icon IS the content - Sizes:
'sm' | 'md' | 'lg'(24×24, 32×32, 40×40 px squares)
Existing
<Button size="icon">continues to work.ButtonSpinneris now exported frombutton-internalsto power the loading state. -
d8bf5e6: OtpInput: refreshed API with alpha/alphanumeric types, ref handle, and renamed props
BREAKING prop renames (pre-release):
length→numberOfDigitsmask→secureTextEntryonChange→onTextChangeonComplete→onFilledstatus/OTPStatus→OtpInputStatus
New features:
typeprop —'numeric' | 'alpha' | 'alphanumeric'. Controls which characters each slot accepts. ThesanitizeandapplyEditlogic functions now accept atypeparameter.refhandle (OtpInputRef) — exposesfocus(),blur(), andclear()imperatively.autoCompleteprop — forwarded to the hiddenTextInput.stickBlinkMsprop — customise the cursor blink interval.
Internal:
applyEditnow takes a single options object ({ prev, raw, length, anchor, type }) instead of positional arguments. Tests updated accordingly. -
9b9b09c: ReorderableList: remove ghost mode (indicator-only)
ReorderableListis now indicator-mode only. Themodeprop,renderPreviewprop, and all ghost-mode state (previewKeys,ghostKey,flipRects,movedKey) are removed.- Breaking:
modeprop removed —'ghost'is no longer accepted - Breaking:
renderPreviewprop removed - FLIP animation system removed (Animated.View wrappers,
measureInWindowtracking, easing curves) - Items now render in plain
Viewwrappers instead ofAnimated.View ReorderableItemadds a mount-time measure effect so zone rects are populated before a drag can landisPastThresholdhelper removed fromreorderable-list-reorder;insertionPositionno longer acceptsghostHeight
For real-time visual reordering during drag, use the new
SortableListcomponent (rn-motion-ui/sortable-list). - Breaking:
-
43fb467: Rename
DnDList→ReorderableListThe
./dnd-listexport is replaced by./reorderable-list. All associated types and helpers are renamed accordingly:DnDList→ReorderableList— the main container componentDnDItem→ReorderableItem— individual draggable rowsdndReorder→reorderableListReorder— the reorder helper
Import from
rn-motion-ui/reorderable-listinstead ofrn-motion-ui/dnd-list. The old path is removed. -
d6be9ea: New
SortableListcomponentA drag-to-reorder list where items visually reorder in real-time during the drag — the dragged item is dimmed at its preview position while other items animate to close the gap or make room.
Built on the existing gesture primitives (
Draggable,Dragzone,DragManager), so it inherits their transport story and isolation model. Each item computes its visual position as a pure function of(index, activeIndex, insertionIndex)and animatestranslateYto reach it — no rect measurement, no FLIP snapshots, no tree reordering during the drag.- New export:
rn-motion-ui/sortable-list - Requires a fixed
itemHeightprop (every item must share the same height) - Isolates itself inside a
<DragManager isolate>— two lists on the same page are independent - Supports
renderPreviewfor a custom drag ghost - The reorder commits on drop; cancelling reverts items to their original positions
- New export:
-
a37019b: Table: move border and background colours out of the component into configurable
classNamepropsThe Table component previously hardcoded
border-border border-bon rows, headers, cards, and footer, as well asbg-surface-selectedon the selected-row overlay,bg-borderon skeleton pulses, andbg-primary/bg-dangeron row insert/delete buttons. Those are now removed from the component internals and exposed as newclassNameprops:selectedClassName— classes merged onto the selected row/card background overlaydropIndicatorClassName— classes merged onto the column-reorder drop indicatorskeletonClassName— classes merged onto the skeleton pulse bars during loadingemptyClassName— classes merged onto the empty-state wrapper
Stories preserve the classic appearance via a
CLASSIC_TABLEdefaults object spread onto each<Table>instance. Remove or override individual entries to customise. -
1c8a226: ToggleGroup: replace Button children with
itemsprop; addpillvariant; fix bordered outer borderBREAKING:
ToggleGroupno longer accepts Button children. Replacechildrenwith the newitemsprop ({ value: string; label: ReactNode }[]). TheselectedVariant,unselectedVariant, andpressModeprops are removed. Thesizeprop is now'sm' | 'md' | 'lg'(drops'icon').BREAKING:
ButtonandElevatedButtonno longer accept avalueprop. This was only used by the old ToggleGroup pattern and is now removed fromBaseButtonProps.New
pillvariant: abg-muted rounded-fullcontainer with a spring-animated sliding indicator (bg-surface-3/dark:bg-black) that glides behind the selected item's text. RespectsuseReducedMotion().Fixed
borderedvariant: now renders a visible outer border (border border-border rounded-interactive) in addition to the existing inner divider borders.Items are now flat
Pressable+Textsurfaces (no longer Button components), with uniformpx-3horizontal padding.
Patch Changes
-
c7992e0: Button: prune
outline,ghostPrimaryvariants; tighten label + ripple coloursThe
outlineandghostPrimaryvariants are removed. All internal and story usages ofoutlineswitch toghost. Label colour map simplified:primarynow usestext-foreground,secondaryusestext-surface-1,ghostusestext-foreground. Filled-ripple set updated (secondaryadded,primaryremoved) so the white shimmer only fires on opaque dark fills. Spinner colour resolution consolidated.New helpers in
button-scale.ts:buttonRadiusClass()(CSS twin ofbuttonRadius()),STATE_ICON_SIZE, andSTATE_BUTTON_GAP_CLASSNAMEfor proportional state-icon spacing per size. -
c7992e0: Extract shared
CloseButtoncomponent; add close button to MorphingModalA new
CloseButtoncomponent replaces the inlinePressable+CloseLineicon pattern used across AdaptiveModal, FullSheet, and MorphingModal. The component is a simple themed close icon button with consistent hit slop and accessibility label.MorphingModalgains ashowCloseprop that renders aCloseButtonin the top-right corner of the panel.FullSheet'scloseIconprop now accepts any ReactNode (previously just an icon override) — pass a<CloseButton>or any custom element. -
30569fe: Drag overlay: ghost fade-out, HTML5 positioning, Safari fixes
Ghost settle animation: When a drag ends, the ghost now fades out over 200ms instead of disappearing instantly. The overlay caches the last non-null drag and preview so the ghost renders until the fade-out completes.
HTML5 overlay ghost positioning: Under the HTML5 transport, the overlay ghost now anchors horizontally to the source element's left edge (matching the div the user lifted) while following the cursor vertically — instead of using the pan-transport offset calculation.
HTML5 drag image hiding: When a
DragManageroverlay will draw the ghost, the browser's native drag image is replaced with a 1×1 transparent GIF. This prevents a double ghost and stops Safari from snapping the native image back to the lift point when the cursor leaves the window.Safari teleport rejection: Safari fires
dragevents with the grab-point coordinates when the cursor leaves the browser window. These are now detected and rejected (non-zero coordinates that are a teleport back to the lift position), preventing the ghost from snapping back to the source mid-drag.DraggableSessionnow exposes aoverlayHostIdfield so the HTML5 transport can decide whether to hide the browser's drag image. -
35d2f16: Fix exports validation: 4-segment component paths and pre-commit auto-fix
check-exports.mjsderiveExportKeynow handles 4-segment component paths (components/<category>/<Dir>/<file>) in addition to 3-segment, so the primary entry-point file per component is correctly auto-detected and validated.- The pre-commit hook now runs
check-exports.mjsafter lint+typecheck. On failure, it auto-fixes dangling paths with--writeand stages the result. If--writecannot resolve everything, the commit is blocked with a clear error. - Added missing export entries for
./hooks/use-controlledand./hooks/use-press-state.
-
012726a: FileSystem store rename, story fixes for double-rendered children
FileSystem: Internal rename of
s→fileSystemStoreinensureChildrenfor readability.Stories: FileSystem and HoldDraggable stories updated to use
findAllBy*queries (findAllByText,findAllByRole,findAllByTestId) instead offindBy*/getBy*singletons. Components likeHoldContextMenuandDraggablerender children twice (functional copy + offscreen drag-preview ghost), so single-match queries reject. Each call picks the first (functional) copy, which is rendered first in document order. -
c7992e0: FileSystem drop target, RadioCard variant, Draggable Safari fix, Geist fonts
FileSystem: Drop target feedback refactored —
FileSystemDropOutline(a separate overlay node) is removed; the row itself now lights up withbg-infowhen a drag hovers over it. Selected rows mute during drag, and lifting rows usebg-mutedinstead ofbg-surface-hover. The column and list views share the same pattern via arenderBody(isOver)closure.RadioCard: New
variantprop —"radio"(default, shows the ring + dot indicator) and"card"(uses only the animated border and background tint). Settable per-card or at the group level.Drag & drop:
endDragnow handles bogusdragendcoordinates from Chrome (0,0) and Safari (wrong non-zero) by falling back to the store's tracked point frommoveDrag. The web dragzone keeps that point in sync viamoveDragcalls ondragoveranddrop. Dragzone eagerly callsremeasure()on registration so its rect is ready before the first drag begins. The capture-phase click listener ondraggable/holdableelements is now only added whencursorModeis on, fixing a Safari bug where it blocked native drag initiation.Geist fonts: The storybook demo app loads Geist Sans + Geist Mono via
expo-font(native) and@font-face(web).--font-sansand--font-monotokens are overridden in bothstorybook/demo/global.cssandstorybook/web/global.css. -
c7992e0: Relocate Menu + MenuItem to
RowPrimitive/MenuandMenuItemmove frommenus/Menu/andmenus/MenuItem/to the newRowPrimitive/directory. All import paths updated across AdaptiveDropdown, AdaptiveModal, CommandPalette, HoldContextMenu, HoverMenu, and FileSystem. Old files deleted.Menu vertical padding switched from a hardcoded
py-2.5to the newpy-(--menu-vertical-padding)CSS token so the inset stays in sync with the design system. MenuItem row gap reduced fromgap-3togap-2. -
b3d3c80: MorphingModal bottom-sheet sizes to content width; FeedbackWidget and Checkbox layout fixes
- MorphingModal
bottom-sheetplacement now measures content width and animates it alongside height, so the card sizes to its content instead of stretching full-width. The positioning wrapper changed fromitems-stretchtoitems-center. - FeedbackWidget trigger button centres the icon in a
flex-1wrapper, fixing vertical alignment when the button is stretched by its parent. - Checkbox uses
-inset-pxfor the fill background so it doesn't peek past the border-radius on subpixel-snapped edges. - ButtonSpinner (exported from
button-internals) now accepts asizeprop for proportional radius and stroke width.
- MorphingModal
-
35ab5bd:
usePressStatehook: centralise pressed-state bookkeeping;useControlledValue: extract controlled/uncontrolled seamNew
usePressStatehook replaces ad-hocuseState(false)+useCallbackpairs across 13 components (Checkbox, CheckboxCard, Radio, StarRating, Switch, ActionSwap, CloseButton, OverflowActions, Dock, Tabs, ScrollTo, ActionRow, MenuItem). Returns{ pressed, pressHandlers }— spreadpressHandlersontoPressableand usepressedfor animations. Accepts optionalonPressIn/onPressOutforwarding for callers that also need the events.New
useControlledValuehook replaces three duplicated controlled/uncontrolled seams:useCalendar's inlineuseControlled,useDatePicker'suseDisclosure, anduseDateRangePicker'suseDisclosure.Button:
contentStyle→contentClassNameBaseButtonProps.contentStyle(inlineViewStyle) becomescontentClassName(Tailwind string). All button variants (Button, ElevatedButton, GlossyButton, StatefulButton) andButtonGroupupdated. StatefulButton'sSTATE_PAD_SQUEEZEnumeric constant becomesSQUEEZE_PADDING_CLASS(Tailwind classes per size). ButtonGroup'sborderedContentStyle()returns a class string instead of aViewStyleobject.elevated.ts: deriveSURFACE_CLASSNAMEfrom private lookupsSURFACE_CLASSNAMEentries are now built fromSURFACE_BG_CLASSNAME+SURFACE_ELEVATED_SHADOW_CLASSNAMEinstead of hardcoded duplicates.elevated()delegates tosurfaceBackground()andelevatedShadow().RowPrimitive: extract
groupedRowClass; fix sections container + dividersgroupedRowClass()is a shared helper used by bothActionRowGroupandItemRowGroup. Thesectionsvariant divider (h-px bg-border) moves out of the per-group item loops and intoRowGroupContainer, which now wraps its children withmy-2-spaced dividers and useselevated(3)+rounded-cardinstead of the oldrounded-2xl bg-surface-3 p-4.FileSystem: extract
useFileSystemRowInteractionanduseFileSystemDragOptionsTwo new hooks replace duplicated code across all five views (column, list, gallery strip, icons tile, search):
useFileSystemRowInteractionhandles context-menu + hold-prevention bookkeeping;useFileSystemDragOptionsresolves multi-drag payloads. Each view drops ~30 lines of identical inline logic.FileSystem store:
navigationPatchconvergence,recomputeAndSethelperhistorySteprenamed tonavigationPatch, whichnavigateTonow delegates to instead of duplicating the patch. ArecomputeAndSethelper cuts repetition acrossapplySortKey,toggleSortColumn, and_setItems.nextFileTypeFilterstakes anextIdfactory instead of a pre-generated id.OtpInput: flexbox centring replaces
lineHeighttrickEach slot's digit animates inside a
MotiViewwithitems-center justify-centerinstead of aMotiTextwith a hardcodedlineHeight: 44, so the digit stays vertically centred regardless of slot size.ReorderableList: smoother reorder animations
Reorder durations increased from 200→300ms; pushed-row easing switched from
lineartoEasing.bezier(0.16, 1, 0.3, 1). Story examples gaintransition-all duration-300 ease-out.Button stories: fix icon colours for
secondaryvarianticonColorFornow resolvessecondarytosurface-1. Story examples usingsecondaryvariant icons switch fromcolors.foregroundtocolors['surface-1']. -
28db38b: RowPrimitive: split row-group into per-component files, add sections variant, and enrich adornments
ActionRowGroupandItemRowGroupextracted fromrow-group.tsxinto their own files (action-row-group.tsx,item-row-group.tsx).RowGroupContainerstays inrow-group.tsxas the shared internal shell.- New
sectionsvariant: renders rows in a padded surface-3 container with horizontal rule dividers between items; the grouped variant now correctly adjusts corner radii per position. ItemRowAdornmenticon objects now accepticonColorandiconBackgroundColor— the latter draws a tinted circular badge behind the icon.- Left adornments now default to
muted-foregroundinstead offoreground.
Button system: vertical press animations and toggle-group signalling
- New
pressModevalues —scaleX,scaleXFirst,scaleXLast— animate buttons in vertical groups: first nudges up, last nudges down, middle collapse horizontally. ButtonGroupapplies the appropriate vertical press mode and softens inner-border dividers to/50opacity, removing the-ml-px/-mt-pxoverlap hack.BaseButtonPropsgains an opaquevalueprop for container components (ToggleGroup) to track selection.
Drag and drop: built-in multi-drag ghost and HTML5 custom previews
MultiDragManagernow provides a default ghost ("N items" chip with a dot) when norenderPreviewis passed.DraggableandHoldDraggablekeep their preview element in the DOM at all times — on-screen for pan transports, off-screen for HTML5 — so the HTML5 transport can callsetDragImagewith the custom ghost instead of the browser's default element screenshot.useDraggableHtml5gainssetDragImageFromRef— clones the preview DOM node, appends it to<body>, callssetDragImage, and removes it on the next microtask.
FileSystem: row enter/exit animations, multi-move, and stacked ghost
FileSystemAnimatedRowanduseFileSystemRowAnimationadd smooth enter/exit transitions when entries are added or removed from a column.- Drag-scope group ghost replaced with a stacked-icon deck (up to 3 items, folder glyphs or file-type icons) plus a count label.
handleMovein the demo now processes all sources in a multi-drag instead of just the first.- Column row loses
rounded-md(the animated wrapper now handles it) andextraData={selectedPaths}drives FlatList re-renders for selection changes.
MorphingModal: bottom-sheet placement
- New
bottom-sheetplacement: full-width panel with rounded top corners only, no max-width constraint,justify-endalignment, and a longer enter distance (80px). Press scale is disabled.
HoldContextMenu: right-click opens menu without firing afterHold
openMenuFromContextMenuis a separate callback for the DOMcontextmenuevent — it opens the menu but skips the consumer'safterHoldaction so a right-click does not also toggle multi-select.
Typography: per-weight font-family tokens
- The
Textcomponent now resolves eachfont×weightpair to a single per-weight family class (font-sans-bold,font-mono-medium, etc.) via cva compound variants. This is required on native wherefontWeightalone cannot select between different.ttffiles. - Theme tokens expanded from 3 generic font stacks to 12 per-weight tokens (
--font-sans-normalthrough--font-mono-bold). Geist @font-face declarations in both web and demo updated to match: each weight gets its own font-family. --radius-menutoken added to the theme.
New components: ToggleGroup and ReorderableList
ToggleGroup— form component for mutually-exclusive or multi-select toggle buttons.ReorderableList— gesture-driven reorderable list with drag handles,ReorderableItem, andReorderableListreorder helper. Both new components are registered in the package exports map.
-
c7992e0: Token refresh: tighter sizing, smaller radius, new typography + background tokens
Interactive surface heights reduced further (sm: 24, md: 32, lg: 40) and lg horizontal padding increased to 22px. Corner radius tightened:
--radius-interactive10→6px,--radius-menu16→6px.New tokens:
--color-background(theme-flipping white/black),--font-sans,--font-serif,--font-mono(typography family stack), and--menu-vertical-padding(4px). Thebackgroundcolour is also added to theThemeTokenunion and its OKLCH lookup tables souseThemeColor('background')resolves correctly in both schemes. TheTextcomponent gains afontprop ("sans" | "serif" | "mono") that maps to the corresponding CSS utility class.