icon

Morainev0.5.0

Utils

Utils

Public helper hooks and low-level utilities for building custom Moraine-based primitives.

View as Markdown

Usage#

Import shared helpers from the dedicated subpath:

import {
createContextProvider,
createMediaQuery,
useControllableValue,
useDisclosureState,
useEventListener,
useEventListenerMap,
useId,
useListVirtualizer,
useLoadingAutoClick,
useSelectableCollectionNavigation,
useTransitionPresence,
} from 'moraine/utils'

Top-level styling helpers such as cn, cva, and extendCN continue to be exported from moraine.

useControllableValue#

Bridge controlled and uncontrolled state with a single accessor/setter pair.

const [value, setValue] = useControllableValue({
value: () => props.value,
defaultValue: () => props.defaultValue,
})

useDisclosureState#

Track open-state metadata for collapsible content, including data-* attrs and measured content height.

const disclosure = useDisclosureState({
open: () => open(),
disabled: () => props.disabled ?? false,
})

useTransitionPresence#

Keep an element mounted until its exit animation or transition finishes.

const presence = useTransitionPresence({
open: () => open(),
mode: 'both',
})

useSelectableCollectionNavigation#

Add keyboard navigation for tabs, radio groups, listboxes, and similar selectable collections.

const { onNavigationKeyDown } = useSelectableCollectionNavigation({
items: () => items(),
getValue: (item) => item.value,
onSelect: (value) => setSelected(value),
})

createMediaQuery#

Create a reactive boolean accessor from a media query.

const isMobile = createMediaQuery('(max-width: 768px)', false)

useListVirtualizer#

Create a virtualRender adapter for List, Select, or MultiSelect based on @tanstack/virtual-core. Configure row measurement with estimateSize, pass virtualRender to the component, and forward scrollToIndex through Select or MultiSelect’s scrollToItem prop when keyboard navigation needs to reveal an off-screen entry.

useId#

Create a stable accessor for explicit or generated element IDs.

const id = useId(() => props.id, 'dialog')

useEventListener and useEventListenerMap#

Attach event listeners with automatic cleanup when the owner scope is disposed.

useEventListener(window, 'keydown', (event) => {
if (event.key === 'Escape') close()
})

useLoadingAutoClick#

Wrap async click handlers and derive a loading state automatically while the returned promise is pending.

const { isLoading, onClick } = useLoadingAutoClick({
loadingAuto: () => true,
onClick: () => props.onSubmit,
})

createContextProvider#

Create a typed Solid context provider and matching consumer hook with optional fallback support.

const [DialogProvider, useDialogContext] = createContextProvider<DialogContextValue>('Dialog')