npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@atom_design/menu

v1.0.0

Published

A React Native dropdown menu component with search, radio, checkbox, and chips support. Part of the Atom Design System.

Readme

@atom_design/menu

A React Native dropdown menu component with search, radio, checkbox, and chips support. Part of the Atom Design System.

npm version license

Features

  • 🔍 Searchable - Built-in search functionality
  • 🎯 Radio Select - Single selection with radio buttons
  • ☑️ Checkbox - Multi-select with "Select All" option
  • 🏷️ Chips - Visual chip tags for selections
  • 🔢 Input Mode - Two input fields with footer actions
  • 🎨 Customizable - Style all elements with custom props
  • Accessible - Full screen reader support
  • 💪 TypeScript - Full type definitions included

📦 Installation

npm install @atom_design/menu
# or
yarn add @atom_design/menu

Peer Dependencies

npm install prop-types react-native-vector-icons

🚀 Basic Usage

import React, { useState } from 'react';
import { View } from 'react-native';
import Menu from '@atom_design/menu';

const App = () => {
  const [selected, setSelected] = useState('');

  const options = [
    { label: 'Apple', value: 'apple' },
    { label: 'Banana', value: 'banana' },
    { label: 'Cherry', value: 'cherry' },
  ];

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Menu
        label="Select Fruit"
        type="searchRadio"
        options={options}
        onSelect={setSelected}
      />
    </View>
  );
};

export default App;

🧩 Menu Types

Search + Radio (Single Select)

<Menu
  label="Country"
  type="searchRadio"
  options={countries}
  onSelect={(value) => setCountry(value)}
/>

Search + Checkbox (Multi-Select)

<Menu
  label="Skills"
  type="searchCheckbox"
  options={skills}
  showCheckAll={true}
  checkAllLabel="Select All Skills"
  onSelect={(values) => setSelectedSkills(values)}
/>

Search + Chips Checkbox

<Menu
  label="Tags"
  type="searchChipsCheckbox"
  options={tagOptions}
  chips={selectedChips}
  onSelect={(values) => setSelectedValues(values)}
  onAddChip={(chip) => setChips([...chips, chip])}
  onRemoveChip={(chip) => setChips(chips.filter(c => c !== chip))}
/>

Input with Footer

<Menu
  label="Price Range"
  type="inputFooter"
  inputLabels={['Min Price', 'Max Price']}
  inputPlaceholders={['₹0', '₹10,000']}
  applyButtonText="Apply Filter"
  cancelButtonText="Reset"
  onApply={(data) => console.log(data.input1, data.input2)}
  onCancel={() => console.log('Cancelled')}
/>

📋 Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | label | string | - | Label above trigger | | triggerText | string | - | Trigger button text | | type | string | 'searchRadio' | Menu type | | options | Option[] | [] | Array of options | | chips | string[] | [] | Chips for chips mode | | value | string \| array | - | Controlled value | | onSelect | function | - | Selection callback | | onAddChip | function | - | Add chip callback | | onRemoveChip | function | - | Remove chip callback | | onApply | function | - | Apply callback (inputFooter) | | onCancel | function | - | Cancel callback | | placeholder | string | 'Search...' | Search placeholder | | disabled | boolean | false | Disable menu | | searchable | boolean | true | Show search bar | | showCheckAll | boolean | true | Show "Select All" | | checkAllLabel | string | 'Select All' | "Select All" text | | applyButtonText | string | 'Apply' | Apply button text | | cancelButtonText | string | 'Cancel' | Cancel button text | | inputLabels | [string, string] | ['Min Value', 'Max Value'] | Input labels | | inputPlaceholders | [string, string] | ['Enter min', 'Enter max'] | Input placeholders | | containerStyle | ViewStyle | - | Container style | | triggerStyle | ViewStyle | - | Trigger style | | menuStyle | ViewStyle | - | Menu popup style | | testID | string | - | Test ID |


📁 Option Structure

interface MenuOption {
  label: string;        // Display text
  value: string | number; // Value returned on selection
}

🧪 Test Screen Example

import React, { useState } from 'react';
import { SafeAreaView, ScrollView, Text, StyleSheet, View } from 'react-native';
import Menu from '@atom_design/menu';

const options = [
  { label: 'Apple', value: 'apple' },
  { label: 'Banana', value: 'banana' },
  { label: 'Cherry', value: 'cherry' },
  { label: 'Orange', value: 'orange' },
  { label: 'Mango', value: 'mango' },
];

const MenuTestScreen = () => {
  const [radioValue, setRadioValue] = useState('');
  const [checkboxValues, setCheckboxValues] = useState([]);
  const [chips, setChips] = useState(['Apple', 'Banana']);
  const [chipsValues, setChipsValues] = useState(['apple', 'banana']);
  const [inputResult, setInputResult] = useState(null);

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.content}>
        <Text style={styles.header}>@atom_design/menu</Text>

        {/* Radio Select */}
        <Menu
          label="Single Select (Radio)"
          triggerText={radioValue || 'Select a fruit'}
          type="searchRadio"
          options={options}
          onSelect={setRadioValue}
        />

        {/* Checkbox Multi-Select */}
        <View style={styles.spacing} />
        <Menu
          label="Multi Select (Checkbox)"
          triggerText={
            checkboxValues.length > 0
              ? `${checkboxValues.length} selected`
              : 'Select fruits'
          }
          type="searchCheckbox"
          options={options}
          showCheckAll={true}
          onSelect={setCheckboxValues}
        />

        {/* Chips Checkbox */}
        <View style={styles.spacing} />
        <Menu
          label="Chips Select"
          triggerText="Select with chips"
          type="searchChipsCheckbox"
          options={options}
          chips={chips}
          value={chipsValues}
          onSelect={setChipsValues}
          onAddChip={(chip) => {
            if (!chips.includes(chip)) {
              setChips([...chips, chip]);
            }
          }}
          onRemoveChip={(chip) => setChips(chips.filter(c => c !== chip))}
        />

        {/* Input Footer */}
        <View style={styles.spacing} />
        <Menu
          label="Quantity Range"
          triggerText={
            inputResult
              ? `${inputResult.input1} - ${inputResult.input2}`
              : 'Set range'
          }
          type="inputFooter"
          inputLabels={['Minimum', 'Maximum']}
          inputPlaceholders={['0', '100']}
          applyButtonText="Apply"
          cancelButtonText="Cancel"
          onApply={setInputResult}
          onCancel={() => setInputResult(null)}
        />

        {/* Disabled */}
        <View style={styles.spacing} />
        <Menu
          label="Disabled Menu"
          triggerText="Cannot open"
          type="searchRadio"
          options={options}
          disabled
        />

        {/* Results */}
        <View style={styles.results}>
          <Text style={styles.resultsTitle}>Current Values:</Text>
          <Text>Radio: {radioValue || 'None'}</Text>
          <Text>Checkbox: {checkboxValues.join(', ') || 'None'}</Text>
          <Text>Chips: {chips.join(', ') || 'None'}</Text>
          <Text>
            Range: {inputResult ? `${inputResult.input1} - ${inputResult.input2}` : 'None'}
          </Text>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  content: {
    padding: 20,
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 24,
    color: '#333',
  },
  spacing: {
    height: 16,
  },
  results: {
    marginTop: 32,
    padding: 16,
    backgroundColor: '#fff',
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#eee',
  },
  resultsTitle: {
    fontSize: 16,
    fontWeight: '600',
    marginBottom: 12,
    color: '#333',
  },
});

export default MenuTestScreen;

📝 TypeScript

Full TypeScript support included:

import Menu, { MenuOption, MenuProps } from '@atom_design/menu';

const options: MenuOption[] = [
  { label: 'Option 1', value: 'opt1' },
  { label: 'Option 2', value: 'opt2' },
];

<Menu
  type="searchCheckbox"
  options={options}
  onSelect={(values) => console.log(values as string[])}
/>

♿ Accessibility

The component includes full accessibility support:

  • Proper accessibilityRole on all interactive elements
  • accessibilityState for checked/expanded states
  • Descriptive labels for screen readers
  • Keyboard navigation support

👤 Author

Atom Design Team


📄 License

MIT © Atom Design