combobox-grouping-items-demo

PreviousNext
Docs
pureuiexample

Preview

Loading preview…
registry/pure-ui/examples/combobox/combobox-grouping-items-demo.tsx
"use client";

import {
  Combobox,
  ComboboxCollection,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxGroupLabel,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxPopup,
  ComboboxSeparator,
} from "@/registry/pure-ui/ui/combobox";
import { Fragment, useState } from "react";

type ColorShade = {
  id: string;
  name: string;
  shade: string;
  hex: string;
  rgb: string;
  family:
    | "Gray"
    | "Red"
    | "Orange"
    | "Amber"
    | "Yellow"
    | "Lime"
    | "Green"
    | "Emerald"
    | "Teal"
    | "Cyan"
    | "Sky"
    | "Blue"
    | "Indigo"
    | "Violet"
    | "Purple"
    | "Fuchsia"
    | "Pink"
    | "Rose";
};

type ColorGroup = {
  value: string;
  items: ColorShade[];
};

const colorsData: ColorShade[] = [
  // Gray family
  {
    family: "Gray",
    id: "gray-50",
    name: "Gray 50",
    shade: "50",
    hex: "#fafafa",
    rgb: "250, 250, 250",
  },
  {
    family: "Gray",
    id: "gray-100",
    name: "Gray 100",
    shade: "100",
    hex: "#f4f4f5",
    rgb: "244, 244, 245",
  },
  {
    family: "Gray",
    id: "gray-200",
    name: "Gray 200",
    shade: "200",
    hex: "#e4e4e7",
    rgb: "228, 228, 231",
  },
  {
    family: "Gray",
    id: "gray-300",
    name: "Gray 300",
    shade: "300",
    hex: "#d4d4d8",
    rgb: "212, 212, 216",
  },
  {
    family: "Gray",
    id: "gray-400",
    name: "Gray 400",
    shade: "400",
    hex: "#a1a1aa",
    rgb: "161, 161, 170",
  },
  {
    family: "Gray",
    id: "gray-500",
    name: "Gray 500",
    shade: "500",
    hex: "#71717a",
    rgb: "113, 113, 122",
  },
  {
    family: "Gray",
    id: "gray-600",
    name: "Gray 600",
    shade: "600",
    hex: "#52525b",
    rgb: "82, 82, 91",
  },
  {
    family: "Gray",
    id: "gray-700",
    name: "Gray 700",
    shade: "700",
    hex: "#3f3f46",
    rgb: "63, 63, 70",
  },
  {
    family: "Gray",
    id: "gray-800",
    name: "Gray 800",
    shade: "800",
    hex: "#27272a",
    rgb: "39, 39, 42",
  },
  {
    family: "Gray",
    id: "gray-900",
    name: "Gray 900",
    shade: "900",
    hex: "#18181b",
    rgb: "24, 24, 27",
  },

  // Red family
  {
    family: "Red",
    id: "red-50",
    name: "Red 50",
    shade: "50",
    hex: "#fef2f2",
    rgb: "254, 242, 242",
  },
  {
    family: "Red",
    id: "red-100",
    name: "Red 100",
    shade: "100",
    hex: "#fee2e2",
    rgb: "254, 226, 226",
  },
  {
    family: "Red",
    id: "red-200",
    name: "Red 200",
    shade: "200",
    hex: "#fecaca",
    rgb: "254, 202, 202",
  },
  {
    family: "Red",
    id: "red-300",
    name: "Red 300",
    shade: "300",
    hex: "#fca5a5",
    rgb: "252, 165, 165",
  },
  {
    family: "Red",
    id: "red-400",
    name: "Red 400",
    shade: "400",
    hex: "#f87171",
    rgb: "248, 113, 113",
  },
  {
    family: "Red",
    id: "red-500",
    name: "Red 500",
    shade: "500",
    hex: "#ef4444",
    rgb: "239, 68, 68",
  },
  {
    family: "Red",
    id: "red-600",
    name: "Red 600",
    shade: "600",
    hex: "#dc2626",
    rgb: "220, 38, 38",
  },
  {
    family: "Red",
    id: "red-700",
    name: "Red 700",
    shade: "700",
    hex: "#b91c1c",
    rgb: "185, 28, 28",
  },
  {
    family: "Red",
    id: "red-800",
    name: "Red 800",
    shade: "800",
    hex: "#991b1b",
    rgb: "153, 27, 27",
  },
  {
    family: "Red",
    id: "red-900",
    name: "Red 900",
    shade: "900",
    hex: "#7f1d1d",
    rgb: "127, 29, 29",
  },

  // Blue family
  {
    family: "Blue",
    id: "blue-50",
    name: "Blue 50",
    shade: "50",
    hex: "#eff6ff",
    rgb: "239, 246, 255",
  },
  {
    family: "Blue",
    id: "blue-100",
    name: "Blue 100",
    shade: "100",
    hex: "#dbeafe",
    rgb: "219, 234, 254",
  },
  {
    family: "Blue",
    id: "blue-200",
    name: "Blue 200",
    shade: "200",
    hex: "#bfdbfe",
    rgb: "191, 219, 254",
  },
  {
    family: "Blue",
    id: "blue-300",
    name: "Blue 300",
    shade: "300",
    hex: "#93c5fd",
    rgb: "147, 197, 253",
  },
  {
    family: "Blue",
    id: "blue-400",
    name: "Blue 400",
    shade: "400",
    hex: "#60a5fa",
    rgb: "96, 165, 250",
  },
  {
    family: "Blue",
    id: "blue-500",
    name: "Blue 500",
    shade: "500",
    hex: "#3b82f6",
    rgb: "59, 130, 246",
  },
  {
    family: "Blue",
    id: "blue-600",
    name: "Blue 600",
    shade: "600",
    hex: "#2563eb",
    rgb: "37, 99, 235",
  },
  {
    family: "Blue",
    id: "blue-700",
    name: "Blue 700",
    shade: "700",
    hex: "#1d4ed8",
    rgb: "29, 78, 216",
  },
  {
    family: "Blue",
    id: "blue-800",
    name: "Blue 800",
    shade: "800",
    hex: "#1e40af",
    rgb: "30, 64, 175",
  },
  {
    family: "Blue",
    id: "blue-900",
    name: "Blue 900",
    shade: "900",
    hex: "#1e3a8a",
    rgb: "30, 58, 138",
  },

  // Green family
  {
    family: "Green",
    id: "green-50",
    name: "Green 50",
    shade: "50",
    hex: "#f0fdf4",
    rgb: "240, 253, 244",
  },
  {
    family: "Green",
    id: "green-100",
    name: "Green 100",
    shade: "100",
    hex: "#dcfce7",
    rgb: "220, 252, 231",
  },
  {
    family: "Green",
    id: "green-200",
    name: "Green 200",
    shade: "200",
    hex: "#bbf7d0",
    rgb: "187, 247, 208",
  },
  {
    family: "Green",
    id: "green-300",
    name: "Green 300",
    shade: "300",
    hex: "#86efac",
    rgb: "134, 239, 172",
  },
  {
    family: "Green",
    id: "green-400",
    name: "Green 400",
    shade: "400",
    hex: "#4ade80",
    rgb: "74, 222, 128",
  },
  {
    family: "Green",
    id: "green-500",
    name: "Green 500",
    shade: "500",
    hex: "#22c55e",
    rgb: "34, 197, 94",
  },
  {
    family: "Green",
    id: "green-600",
    name: "Green 600",
    shade: "600",
    hex: "#16a34a",
    rgb: "22, 163, 74",
  },
  {
    family: "Green",
    id: "green-700",
    name: "Green 700",
    shade: "700",
    hex: "#15803d",
    rgb: "21, 128, 61",
  },
  {
    family: "Green",
    id: "green-800",
    name: "Green 800",
    shade: "800",
    hex: "#166534",
    rgb: "22, 101, 52",
  },
  {
    family: "Green",
    id: "green-900",
    name: "Green 900",
    shade: "900",
    hex: "#14532d",
    rgb: "20, 83, 45",
  },

  // Purple family
  {
    family: "Purple",
    id: "purple-50",
    name: "Purple 50",
    shade: "50",
    hex: "#faf5ff",
    rgb: "250, 245, 255",
  },
  {
    family: "Purple",
    id: "purple-100",
    name: "Purple 100",
    shade: "100",
    hex: "#f3e8ff",
    rgb: "243, 232, 255",
  },
  {
    family: "Purple",
    id: "purple-200",
    name: "Purple 200",
    shade: "200",
    hex: "#e9d5ff",
    rgb: "233, 213, 255",
  },
  {
    family: "Purple",
    id: "purple-300",
    name: "Purple 300",
    shade: "300",
    hex: "#d8b4fe",
    rgb: "216, 180, 254",
  },
  {
    family: "Purple",
    id: "purple-400",
    name: "Purple 400",
    shade: "400",
    hex: "#c084fc",
    rgb: "192, 132, 252",
  },
  {
    family: "Purple",
    id: "purple-500",
    name: "Purple 500",
    shade: "500",
    hex: "#a855f7",
    rgb: "168, 85, 247",
  },
  {
    family: "Purple",
    id: "purple-600",
    name: "Purple 600",
    shade: "600",
    hex: "#9333ea",
    rgb: "147, 51, 234",
  },
  {
    family: "Purple",
    id: "purple-700",
    name: "Purple 700",
    shade: "700",
    hex: "#7e22ce",
    rgb: "126, 34, 206",
  },
  {
    family: "Purple",
    id: "purple-800",
    name: "Purple 800",
    shade: "800",
    hex: "#6b21a8",
    rgb: "107, 33, 168",
  },
  {
    family: "Purple",
    id: "purple-900",
    name: "Purple 900",
    shade: "900",
    hex: "#581c87",
    rgb: "88, 28, 135",
  },

  // Amber family
  {
    family: "Amber",
    id: "amber-50",
    name: "Amber 50",
    shade: "50",
    hex: "#fffbeb",
    rgb: "255, 251, 235",
  },
  {
    family: "Amber",
    id: "amber-100",
    name: "Amber 100",
    shade: "100",
    hex: "#fef3c7",
    rgb: "254, 243, 199",
  },
  {
    family: "Amber",
    id: "amber-200",
    name: "Amber 200",
    shade: "200",
    hex: "#fde68a",
    rgb: "253, 230, 138",
  },
  {
    family: "Amber",
    id: "amber-300",
    name: "Amber 300",
    shade: "300",
    hex: "#fcd34d",
    rgb: "252, 211, 77",
  },
  {
    family: "Amber",
    id: "amber-400",
    name: "Amber 400",
    shade: "400",
    hex: "#fbbf24",
    rgb: "251, 191, 36",
  },
  {
    family: "Amber",
    id: "amber-500",
    name: "Amber 500",
    shade: "500",
    hex: "#f59e0b",
    rgb: "245, 158, 11",
  },
  {
    family: "Amber",
    id: "amber-600",
    name: "Amber 600",
    shade: "600",
    hex: "#d97706",
    rgb: "217, 119, 6",
  },
  {
    family: "Amber",
    id: "amber-700",
    name: "Amber 700",
    shade: "700",
    hex: "#b45309",
    rgb: "180, 83, 9",
  },
  {
    family: "Amber",
    id: "amber-800",
    name: "Amber 800",
    shade: "800",
    hex: "#92400e",
    rgb: "146, 64, 14",
  },
  {
    family: "Amber",
    id: "amber-900",
    name: "Amber 900",
    shade: "900",
    hex: "#78350f",
    rgb: "120, 53, 15",
  },

  // Pink family
  {
    family: "Pink",
    id: "pink-50",
    name: "Pink 50",
    shade: "50",
    hex: "#fdf2f8",
    rgb: "253, 242, 248",
  },
  {
    family: "Pink",
    id: "pink-100",
    name: "Pink 100",
    shade: "100",
    hex: "#fce7f3",
    rgb: "252, 231, 243",
  },
  {
    family: "Pink",
    id: "pink-200",
    name: "Pink 200",
    shade: "200",
    hex: "#fbcfe8",
    rgb: "251, 207, 232",
  },
  {
    family: "Pink",
    id: "pink-300",
    name: "Pink 300",
    shade: "300",
    hex: "#f9a8d4",
    rgb: "249, 168, 212",
  },
  {
    family: "Pink",
    id: "pink-400",
    name: "Pink 400",
    shade: "400",
    hex: "#f472b6",
    rgb: "244, 114, 182",
  },
  {
    family: "Pink",
    id: "pink-500",
    name: "Pink 500",
    shade: "500",
    hex: "#ec4899",
    rgb: "236, 72, 153",
  },
  {
    family: "Pink",
    id: "pink-600",
    name: "Pink 600",
    shade: "600",
    hex: "#db2777",
    rgb: "219, 39, 119",
  },
  {
    family: "Pink",
    id: "pink-700",
    name: "Pink 700",
    shade: "700",
    hex: "#be185d",
    rgb: "190, 24, 93",
  },
  {
    family: "Pink",
    id: "pink-800",
    name: "Pink 800",
    shade: "800",
    hex: "#9d174d",
    rgb: "157, 23, 77",
  },
  {
    family: "Pink",
    id: "pink-900",
    name: "Pink 900",
    shade: "900",
    hex: "#831843",
    rgb: "131, 24, 67",
  },

  // Teal family
  {
    family: "Teal",
    id: "teal-50",
    name: "Teal 50",
    shade: "50",
    hex: "#f0fdfa",
    rgb: "240, 253, 250",
  },
  {
    family: "Teal",
    id: "teal-100",
    name: "Teal 100",
    shade: "100",
    hex: "#ccfbf1",
    rgb: "204, 251, 241",
  },
  {
    family: "Teal",
    id: "teal-200",
    name: "Teal 200",
    shade: "200",
    hex: "#99f6e4",
    rgb: "153, 246, 228",
  },
  {
    family: "Teal",
    id: "teal-300",
    name: "Teal 300",
    shade: "300",
    hex: "#5eead4",
    rgb: "94, 234, 212",
  },
  {
    family: "Teal",
    id: "teal-400",
    name: "Teal 400",
    shade: "400",
    hex: "#2dd4bf",
    rgb: "45, 212, 191",
  },
  {
    family: "Teal",
    id: "teal-500",
    name: "Teal 500",
    shade: "500",
    hex: "#14b8a6",
    rgb: "20, 184, 166",
  },
  {
    family: "Teal",
    id: "teal-600",
    name: "Teal 600",
    shade: "600",
    hex: "#0d9488",
    rgb: "13, 148, 136",
  },
  {
    family: "Teal",
    id: "teal-700",
    name: "Teal 700",
    shade: "700",
    hex: "#0f766e",
    rgb: "15, 118, 110",
  },
  {
    family: "Teal",
    id: "teal-800",
    name: "Teal 800",
    shade: "800",
    hex: "#115e59",
    rgb: "17, 94, 89",
  },
  {
    family: "Teal",
    id: "teal-900",
    name: "Teal 900",
    shade: "900",
    hex: "#134e4a",
    rgb: "19, 78, 74",
  },
];

function groupColors(colors: ColorShade[]): ColorGroup[] {
  const groups: Record<string, ColorShade[]> = {};
  for (const color of colors) {
    if (!groups[color.family]) {
      groups[color.family] = [];
    }
    // biome-ignore lint/style/noNonNullAssertion: will never be null
    groups[color.family]!.push(color);
  }

  const order: Array<ColorGroup["value"]> = [
    "Gray",
    "Red",
    "Blue",
    "Green",
    "Purple",
    "Amber",
    "Pink",
    "Teal",
  ];
  return order.map((value) => ({ items: groups[value] ?? [], value }));
}

const groupedColors: ColorGroup[] = groupColors(colorsData);

export function ComboboxGroupingItemsDemo() {
  const [selectedColor, setSelectedColor] = useState<ColorShade | null>(null);

  return (
    <div className="flex flex-col gap-4 max-w-sm w-full">
      <Combobox
        items={groupedColors}
        value={selectedColor}
        onValueChange={(color: ColorShade | null) => {
          setSelectedColor(color);
        }}
        itemToStringLabel={(color: ColorShade) => color.name}
      >
        <div className="flex flex-col items-start gap-2 ">
          <ComboboxInput
            aria-label="Search colors"
            placeholder="Search color palette..."
            className="w-full"
          />
        </div>
        <ComboboxPopup>
          <ComboboxEmpty>No colors found.</ComboboxEmpty>
          <ComboboxList>
            {(group: ColorGroup) => (
              <Fragment key={group.value}>
                <ComboboxGroup items={group.items}>
                  <ComboboxGroupLabel>{group.value}</ComboboxGroupLabel>
                  <ComboboxCollection>
                    {(color: ColorShade) => (
                      <ComboboxItem key={color.id} value={color}>
                        <div className="flex items-center justify-between gap-3 w-full">
                          <div className="flex items-center gap-3 min-w-0 flex-1">
                            <div
                              className="h-6 w-6 rounded border shrink-0 shadow-sm"
                              style={{ backgroundColor: color.hex }}
                              aria-hidden="true"
                            />
                            <div className="flex flex-col min-w-0">
                              <span className="font-medium truncate">
                                {color.name}
                              </span>
                              <span className="text-xs opacity-60 truncate font-mono">
                                {color.hex}
                              </span>
                            </div>
                          </div>
                          <span className="text-xs opacity-50 font-medium shrink-0">
                            {color.shade}
                          </span>
                        </div>
                      </ComboboxItem>
                    )}
                  </ComboboxCollection>
                </ComboboxGroup>
                {group.value !== "Teal" && <ComboboxSeparator />}
              </Fragment>
            )}
          </ComboboxList>
        </ComboboxPopup>
      </Combobox>

      {selectedColor && (
        <div className="rounded-lg border p-4 space-y-3">
          <div className="flex items-center gap-3">
            <div
              className="h-12 w-12 rounded-lg border shadow-sm shrink-0"
              style={{ backgroundColor: selectedColor.hex }}
              aria-hidden="true"
            />
            <div className="flex flex-col min-w-0">
              <p className="font-semibold text-sm">{selectedColor.name}</p>
              <p className="text-xs text-muted-foreground">
                {selectedColor.family} family
              </p>
            </div>
          </div>
          <div className="grid grid-cols-2 gap-2 text-xs">
            <div className="space-y-1">
              <p className="text-muted-foreground">HEX</p>
              <p className="font-mono font-medium">{selectedColor.hex}</p>
            </div>
            <div className="space-y-1">
              <p className="text-muted-foreground">RGB</p>
              <p className="font-mono font-medium">{selectedColor.rgb}</p>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

Installation

npx shadcn@latest add @pureui/combobox-grouping-items-demo

Usage

import { ComboboxGroupingItemsDemo } from "@/components/combobox-grouping-items-demo"
<ComboboxGroupingItemsDemo />