Toolbar Primitive
A container designed for organizing a collection of controls, such as buttons, toggle groups, or dropdown menus.
Installation
Install the component via your command line.
npx expo install @rn-primitives/toolbar
Install @radix-ui/react-toolbar
npx expo install @radix-ui/react-toolbar
Copy/paste the following code for web to ~/components/primitives/toolbar/toolbar.web.tsx
import * as Toolbar from '@radix-ui/react-toolbar';import * as Slot from '~/components/primitives/slot';import { ToggleGroupUtils } from '~/components/primitives/utils';import * as React from 'react';import { Pressable, View, type GestureResponderEvent } from 'react-native';import type { ButtonProps, ButtonRef, LinkProps, LinkRef, RootProps, RootRef, SeparatorProps, SeparatorRef, ToggleGroupProps, ToggleGroupRef, ToggleItemProps, ToggleItemRef,} from './types';
const Root = React.forwardRef<RootRef, RootProps>( ({ asChild, orientation, dir, loop, style, ...props }, ref) => { const Component = asChild ? Slot.View : View; return ( <Toolbar.Root orientation={orientation} dir={dir} loop={loop} asChild> <Component ref={ref} {...props} /> </Toolbar.Root> ); });
Root.displayName = 'RootWebToolbar';
const ToggleGroupContext = React.createContext<ToggleGroupProps | null>(null);
const ToggleGroup = React.forwardRef<ToggleGroupRef, ToggleGroupProps>( ({ asChild, type, value, onValueChange, disabled = false, style, ...viewProps }, ref) => { const Component = asChild ? Slot.View : View; return ( <ToggleGroupContext.Provider value={ { type, value, disabled, onValueChange, } as ToggleGroupProps } > <Toolbar.ToggleGroup type={type as any} value={value as any} onValueChange={onValueChange as any} disabled={disabled} asChild > <Component ref={ref} {...viewProps} /> </Toolbar.ToggleGroup> </ToggleGroupContext.Provider> ); });
ToggleGroup.displayName = 'ToggleGroupWebToolbar';
function useToggleGroupContext() { const context = React.useContext(ToggleGroupContext); if (!context) { throw new Error( 'ToggleGroup compound components cannot be rendered outside the ToggleGroup component' ); } return context;}
const ToggleItem = React.forwardRef<ToggleItemRef, ToggleItemProps>( ( { asChild, value: itemValue, disabled: disabledProp = false, onPress: onPressProp, style, ...props }, ref ) => { const { type, disabled, value, onValueChange } = useToggleGroupContext();
function onPress(ev: GestureResponderEvent) { if (disabled || disabledProp) return; if (type === 'single') { onValueChange(ToggleGroupUtils.getNewSingleValue(value, itemValue)); } if (type === 'multiple') { onValueChange(ToggleGroupUtils.getNewMultipleValue(value, itemValue)); } onPressProp?.(ev); }
const Component = asChild ? Slot.Pressable : Pressable; return ( <Toolbar.ToggleItem value={itemValue} asChild> <Component ref={ref} onPress={onPress} role='button' {...props} /> </Toolbar.ToggleItem> ); });
ToggleItem.displayName = 'ToggleItemWebToolbar';
const Separator = React.forwardRef<SeparatorRef, SeparatorProps>( ({ asChild, style, ...props }, ref) => { const Component = asChild ? Slot.View : View; return <Component ref={ref} {...props} />; });
Separator.displayName = 'SeparatorWebToolbar';
const Link = React.forwardRef<LinkRef, LinkProps>(({ asChild, style, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return ( <Toolbar.Link asChild> <Component ref={ref} {...props} /> </Toolbar.Link> );});
Link.displayName = 'LinkWebToolbar';
const Button = React.forwardRef<ButtonRef, ButtonProps>(({ asChild, style, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return ( <Toolbar.Button asChild> <Component ref={ref} role='button' {...props} /> </Toolbar.Button> );});
export { Button, Link, Root, Separator, ToggleGroup, ToggleItem };
Copy/paste the following code for native to ~/components/primitives/toolbar/toolbar.tsx
import * as Slot from '~/components/primitives/slot';import { ToggleGroupUtils } from '~/components/primitives/utils';import * as React from 'react';import { Pressable, View, type GestureResponderEvent } from 'react-native';import type { ButtonProps, ButtonRef, LinkProps, LinkRef, RootProps, RootRef, SeparatorProps, SeparatorRef, ToggleGroupProps, ToggleGroupRef, ToggleItemProps, ToggleItemRef,} from './types';
const Root = React.forwardRef<RootRef, RootProps>( ({ asChild, orientation: _orientation, dir: _dir, loop: _loop, ...props }, ref) => { const Component = asChild ? Slot.View : View; return <Component ref={ref} role='toolbar' {...props} />; });
Root.displayName = 'RootNativeToolbar';
const ToggleGroupContext = React.createContext<ToggleGroupProps | null>(null);
const ToggleGroup = React.forwardRef<ToggleGroupRef, ToggleGroupProps>( ({ asChild, type, value, onValueChange, disabled = false, ...viewProps }, ref) => { const Component = asChild ? Slot.View : View; return ( <ToggleGroupContext.Provider value={ { type, value, disabled, onValueChange, } as ToggleGroupProps } > <Component ref={ref} role='group' {...viewProps} /> </ToggleGroupContext.Provider> ); });
ToggleGroup.displayName = 'ToggleGroupNativeToolbar';
function useToggleGroupContext() { const context = React.useContext(ToggleGroupContext); if (!context) { throw new Error( 'ToggleGroup compound components cannot be rendered outside the ToggleGroup component' ); } return context;}
const ToggleItem = React.forwardRef<ToggleItemRef, ToggleItemProps>( ( { asChild, value: itemValue, disabled: disabledProp = false, onPress: onPressProp, ...props }, ref ) => { const { type, disabled, value, onValueChange } = useToggleGroupContext();
function onPress(ev: GestureResponderEvent) { if (disabled || disabledProp) return; if (type === 'single') { onValueChange(ToggleGroupUtils.getNewSingleValue(value, itemValue)); } if (type === 'multiple') { onValueChange(ToggleGroupUtils.getNewMultipleValue(value, itemValue)); } onPressProp?.(ev); }
const isChecked = type === 'single' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined; const isSelected = type === 'multiple' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined;
const Component = asChild ? Slot.Pressable : Pressable; return ( <Component ref={ref} aria-disabled={disabled} role={type === 'single' ? 'radio' : 'checkbox'} onPress={onPress} aria-checked={isChecked} aria-selected={isSelected} disabled={(disabled || disabledProp) ?? false} accessibilityState={{ disabled: (disabled || disabledProp) ?? false, checked: isChecked, selected: isSelected, }} {...props} /> ); });
ToggleItem.displayName = 'ToggleItemNativeToolbar';
const Separator = React.forwardRef<SeparatorRef, SeparatorProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.View : View; return <Component role={'separator'} ref={ref} {...props} />;});
Separator.displayName = 'SeparatorNativeToolbar';
const Link = React.forwardRef<LinkRef, LinkProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return <Component ref={ref} role='link' {...props} />;});
Link.displayName = 'LinkNativeToolbar';
const Button = React.forwardRef<ButtonRef, ButtonProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return <Component ref={ref} role='button' {...props} />;});
export { Button, Link, Root, Separator, ToggleGroup, ToggleItem };
Copy/paste the following code for types to ~/components/primitives/toolbar/types.ts
import type { PressableRef, SlottablePressableProps, SlottableViewProps, ViewRef,} from '~/components/primitives/types';
type RootProps = SlottableViewProps & { /** * Platform: WEB ONLY */ orientation?: 'horizontal' | 'vertical'; /** * Platform: WEB ONLY */ dir?: 'ltr' | 'rtl'; /** * Platform: WEB ONLY */ loop?: boolean;};
type SingleToggleGroupProps = { type: 'single'; value: string | undefined; onValueChange: (val: string | undefined) => void;};
type MultipleToggleGroupProps = { type: 'multiple'; value: string[]; onValueChange: (val: string[]) => void;};
type ToggleGroupProps = (SingleToggleGroupProps | MultipleToggleGroupProps) & { disabled?: boolean;} & SlottableViewProps;
type ToggleItemProps = SlottablePressableProps & { value: string;};
type SeparatorProps = SlottableViewProps;type LinkProps = SlottablePressableProps;type ButtonProps = SlottablePressableProps;
type RootRef = ViewRef;type LinkRef = PressableRef;type ButtonRef = PressableRef;type SeparatorRef = ViewRef;type ToggleGroupRef = ViewRef;type ToggleItemRef = PressableRef;
export type { ButtonProps, ButtonRef, LinkProps, LinkRef, RootProps, RootRef, SeparatorProps, SeparatorRef, ToggleGroupProps, ToggleGroupRef, ToggleItemProps, ToggleItemRef,};
Copy/paste the following code for exporting to ~/components/primitives/toolbar/index.ts
export * from './toolbar';export * from './types';
Copy/paste the following code for native to ~/components/primitives/toolbar/index.tsx
import * as Slot from '~/components/primitives/slot';import { ToggleGroupUtils } from '~/components/primitives/utils';import * as React from 'react';import { Pressable, View, type GestureResponderEvent } from 'react-native';import type { ButtonProps, ButtonRef, LinkProps, LinkRef, RootProps, RootRef, SeparatorProps, SeparatorRef, ToggleGroupProps, ToggleGroupRef, ToggleItemProps, ToggleItemRef,} from './types';
const Root = React.forwardRef<RootRef, RootProps>( ({ asChild, orientation: _orientation, dir: _dir, loop: _loop, ...props }, ref) => { const Component = asChild ? Slot.View : View; return <Component ref={ref} role='toolbar' {...props} />; });
Root.displayName = 'RootNativeToolbar';
const ToggleGroupContext = React.createContext<ToggleGroupProps | null>(null);
const ToggleGroup = React.forwardRef<ToggleGroupRef, ToggleGroupProps>( ({ asChild, type, value, onValueChange, disabled = false, ...viewProps }, ref) => { const Component = asChild ? Slot.View : View; return ( <ToggleGroupContext.Provider value={ { type, value, disabled, onValueChange, } as ToggleGroupProps } > <Component ref={ref} role='group' {...viewProps} /> </ToggleGroupContext.Provider> ); });
ToggleGroup.displayName = 'ToggleGroupNativeToolbar';
function useToggleGroupContext() { const context = React.useContext(ToggleGroupContext); if (!context) { throw new Error( 'ToggleGroup compound components cannot be rendered outside the ToggleGroup component' ); } return context;}
const ToggleItem = React.forwardRef<ToggleItemRef, ToggleItemProps>( ( { asChild, value: itemValue, disabled: disabledProp = false, onPress: onPressProp, ...props }, ref ) => { const { type, disabled, value, onValueChange } = useToggleGroupContext();
function onPress(ev: GestureResponderEvent) { if (disabled || disabledProp) return; if (type === 'single') { onValueChange(ToggleGroupUtils.getNewSingleValue(value, itemValue)); } if (type === 'multiple') { onValueChange(ToggleGroupUtils.getNewMultipleValue(value, itemValue)); } onPressProp?.(ev); }
const isChecked = type === 'single' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined; const isSelected = type === 'multiple' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined;
const Component = asChild ? Slot.Pressable : Pressable; return ( <Component ref={ref} aria-disabled={disabled} role={type === 'single' ? 'radio' : 'checkbox'} onPress={onPress} aria-checked={isChecked} aria-selected={isSelected} disabled={(disabled || disabledProp) ?? false} accessibilityState={{ disabled: (disabled || disabledProp) ?? false, checked: isChecked, selected: isSelected, }} {...props} /> ); });
ToggleItem.displayName = 'ToggleItemNativeToolbar';
const Separator = React.forwardRef<SeparatorRef, SeparatorProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.View : View; return <Component role={'separator'} ref={ref} {...props} />;});
Separator.displayName = 'SeparatorNativeToolbar';
const Link = React.forwardRef<LinkRef, LinkProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return <Component ref={ref} role='link' {...props} />;});
Link.displayName = 'LinkNativeToolbar';
const Button = React.forwardRef<ButtonRef, ButtonProps>(({ asChild, ...props }, ref) => { const Component = asChild ? Slot.Pressable : Pressable; return <Component ref={ref} role='button' {...props} />;});
export { Button, Link, Root, Separator, ToggleGroup, ToggleItem };
Copy/paste the following code for types to ~/components/primitives/toolbar/types.ts
import type { PressableRef, SlottablePressableProps, SlottableViewProps, ViewRef,} from '~/components/primitives/types';
type RootProps = SlottableViewProps & { /** * Platform: WEB ONLY */ orientation?: 'horizontal' | 'vertical'; /** * Platform: WEB ONLY */ dir?: 'ltr' | 'rtl'; /** * Platform: WEB ONLY */ loop?: boolean;};
type SingleToggleGroupProps = { type: 'single'; value: string | undefined; onValueChange: (val: string | undefined) => void;};
type MultipleToggleGroupProps = { type: 'multiple'; value: string[]; onValueChange: (val: string[]) => void;};
type ToggleGroupProps = (SingleToggleGroupProps | MultipleToggleGroupProps) & { disabled?: boolean;} & SlottableViewProps;
type ToggleItemProps = SlottablePressableProps & { value: string;};
type SeparatorProps = SlottableViewProps;type LinkProps = SlottablePressableProps;type ButtonProps = SlottablePressableProps;
type RootRef = ViewRef;type LinkRef = PressableRef;type ButtonRef = PressableRef;type SeparatorRef = ViewRef;type ToggleGroupRef = ViewRef;type ToggleItemRef = PressableRef;
export type { ButtonProps, ButtonRef, LinkProps, LinkRef, RootProps, RootRef, SeparatorProps, SeparatorRef, ToggleGroupProps, ToggleGroupRef, ToggleItemProps, ToggleItemRef,};
Usage
import * as React from 'react';import { Text, View } from 'react-native';import * as ToolbarPrimitive from '@rn-primitives/toolbar';
function Example() { const [singleValue, setSingleValue] = React.useState<string>(); const [multipleValue, setMultipleValue] = React.useState<string[]>([]); return ( <ToolbarPrimitive.Root> <ToolbarPrimitive.ToggleGroup type='multiple' value={multipleValue} onValueChange={setMultipleValue}> <ToolbarPrimitive.ToggleItem value='bold'> <Text>Bold</Text> </ToolbarPrimitive.ToggleItem> <ToolbarPrimitive.ToggleItem value='italic'> <Text>Italic</Text> </ToolbarPrimitive.ToggleItem> </ToolbarPrimitive.ToggleGroup> <ToolbarPrimitive.Separator /> <ToolbarPrimitive.ToggleGroup type='single' value={singleValue} onValueChange={setSingleValue}> <ToolbarPrimitive.ToggleItem value='left'> <Text>AlignLeft</Text> </ToolbarPrimitive.ToggleItem> <ToolbarPrimitive.ToggleItem value='center'> <Text>AlignCenter</Text> </ToolbarPrimitive.ToggleItem> </ToolbarPrimitive.ToggleGroup> <ToolbarPrimitive.Separator /> <View> <ToolbarPrimitive.Button> <Text>Button</Text> </ToolbarPrimitive.Button> </View> </ToolbarPrimitive.Root> );}
Props
Root
Extends View
props
Prop | Type | Note |
---|---|---|
asChild | boolean | (optional) |
orientation | ’horizontal’ | ‘vertical’ | Web Only (optional) |
dir | (value: boolean) => void | Web Only (optional) |
loop | boolean | Web Only (optional) |
ToggleGroup
Extends View
props
Prop | Type | Note |
---|---|---|
type | ’single’ | ‘multiple’ | |
value | string | undefined | string[] | |
onValueChange | (val: string | undefined | string[]) => void | |
asChild | boolean | (optional) |
disabled | boolean | (optional) |
ToggleItem
Extends Pressable
props
Prop | Type | Note |
---|---|---|
asChild | boolean | (optional) |
value | string |
Separator
Extends View
props
Prop | Type | Note |
---|---|---|
asChild | boolean | (optional) |
Link
Extends Pressable
props
Prop | Type | Note |
---|---|---|
asChild | boolean | (optional) |