Skip to content

Sai-Myo-Myat/option-select

Repository files navigation

Headless Option Selection

A headless option selection library for JavaScript front-end frameworks like React and Nextjs (and more later). It simplifies building hierarchical selectors, multi-select dropdowns, and tree-style selectors — while giving you full control over UI.

Live demo : option-select-demo

option-select demo option-select's selection-limit demo
Option-Select Demo (hierarchical selectors) Selection Limit Demo

Features

  • Headless: Bring your own UI (works with React, Nextjs).
  • Hierarchical Selection: Handles nested data with parent-child relationships.
  • Selection Limit: Restrict how many root-level items can be selected with a limit prop.
  • ✅ Select all & deselect all functionality
  • ✅ Retrieve selected items on demand or on every selection changes happen
  • ✅ Accept isSelected for default selection, but if an item has subItems and not all subItems have isSelected: true, the parent’s isSelected state will be ignored.

Installation

npm install option-select

or

yarn add option-select

Usage

React Example ( Typescript )

import { useCallback } from "react";
import { useOptionSelect, OptionItem } from "option-select";

interface ItemProps {
  id: string;
  name: string;
  age?: number;
  isSelected?: boolean;
  subItems?: ItemProps[];
}

const items: ItemProps[] = [
    {
      id: "1",
      name: "Parent 1",
      subItems: [
        { id: "1-1", name: "Child 1-1", isSelected: true },
        { id: "1-2", name: "Child 1-2", isSelected: true },
      ],
    },
    {
      id: "2",
      name: "Parent 2",
      subItems: [
        {
          id: "2-1",
          name: "Child 2-1",
          isSelected: true,
          subItems: [{ id: "2-1-1", name: "Child 2-1-1" }],
        },
        {
          id: "2-2",
          name: "Child 2-2",
          subItems: [{ id: "2-2-1", name: "Child 2-2-1" }],
        },
      ],
    },
  ];

const Demo = () => {

    const { getAllItems, selectAll, deselectAll, getSelectedItems } = useOptionSelect(
      {
        items: items,
        getId: (item) => item.id,
        onSelectionChange: handleOnChange,
        limit: 1 // 🔥 Only allow one root-level item to be selected
      }
    );

    const handleOnChange = useCallback((items) => {
        console.log("selected items", items);
    },[])

    // render item and it's sub items Recursively
    function renderItem({ item, isSelected, toggleSelection, subItems }: OptionItem<ItemProps>) {
      return (
        <div key={item.id}>
          <label>
            <input type="checkbox" checked={isSelected} onChange={toggleSelection} />
            {item.name}
          </label>
          {subItems && (
            <div style={{ paddingLeft: 20 }}>
              {subItems.map((subItem) => renderItem(subItem))}
            </div>
          )}
        </div>
      );
    }
    
    return (
      <div>
        <button onClick={selectAll}>Select All</button>
        <button onClick={deselectAll}>Deselect All</button>
        <button onClick={() => console.log("Selected Items:", getSelectedItems())}>
          Get Selected Items
        </button>
        {getAllItems().map((item) => renderItem(item))}
      </div>
    );
}

API

useOptionSelect({items: T[], getId: (item: T) => string, onSelectionChange?: (selectedItems: T[]) => void})

uesOptionSlect hook accept three props

  • items
    • Selection item array, each with isSelected ( default is false ) and subItems (optional) fields.
  • getId
    • Unique id selector function.
  • onSelectionChange (optional)
    • Callback function you want to call on every selection changes.
  • limit (optional)
    • Maximum number of root-level items that can be selected.
      Sub-items are not affected by this limit.

useOptionSelect returns an object with the following methods:

  • getAllItems(): { item: T; isSelected: boolean; toggleSelection: () => void; subItems?: T[] }[]
    • Returns items with it's corresponding sub items wrapped with selection controls (isSelected, toggleSelection).
  • selectAll(): void
    • Selects all items.
  • deselectAll(): void
    • Deselects all items.
  • getSelectedItems(): T[]
    • Returns selected items in the same hierarchical structure as the input.

🤝 Contributing

Contributions are welcome!

  • Fork the repo and create your branch (git checkout -b feature/amazing-feature).

  • Install dependencies with yarn install.

  • Open a Pull Request.

See CONTRIBUTING.md for more details.

License

MIT

About

A headless option selection library for JavaScript front-end frameworks like React and Nextjs (and more later). It simplifies building hierarchical selectors, multi-select dropdowns, and tree-style selectors — while giving you full control over UI.

Topics

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors