mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
Feat/select component (#1313)
* chore(root): menu and select components in progress * chore(root): merged with main * feat(menu): stories created * feat(dropdown): menu migrated to menu component * feat(select): base implementation in progress * feat(select): listbox implemented * feat(select): scroll overflow and auto scroll added * feat(select): colors & variants improved, inside placement complete, outside pending * feat(scroll-shadow): new component in progress * feat(select): scroll shadow integrated * fix(select): popover implementation, more scroll shadow stories added * feat(listbox): custom styles example added to stories * chore(listbox): overflow hidden removed * feat(select): variants and styles completed * chore(select): description story added * feat(select): more stories added * fix(select): dynamic collections inference, styles improved, more stories added * fix(select): auto scroll to selected item, scroll padding added * chore(select): scroll behavior changed to nearest * feat(select): custom item story added * fix(select): autoscroll fixed * feat(select): multi select support added * feat(select): more examples added, clean-package modified to remove dev packages * chore(modal): useImperativeHandle removed * feat(select): render value story improved * feat(docs): listbox & scroll shadow docs done * feat(docs): select documentation in progress * fix: select aria label (#1425) * feat(docs): more examples added * feat(docs): select multiple added, storybook favicon changed * fix: added value to aria label of select (#1431) * feat(select): more examples added, storybook favicon changed * fix(popover): dialog role removed * feat(select): api added, async loading exampled added * fix: fixed list box on click not working with sr click (#1439) * feat(select): async items loading support added and documented * feat(root): input styles updated to be as the select ones * chore(root): listbox and scroll shadow readme updated * feat(select): ts examples added, banner updated * fix(popover): voiceover closes when focusing an item fixed * chore(select): focus scope removed * fix(popover): free solo popover added to use without a trigger * feat(select): blog post added * chore(docs): search meta generated, multi controlled onchange example fixed * chore(root): changeset added --------- Co-authored-by: Jakob Guddas <github@jguddas.de>
This commit is contained in:
parent
ea2868e58e
commit
baec55029d
26
.changeset/odd-lamps-glow.md
Normal file
26
.changeset/odd-lamps-glow.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
"@nextui-org/use-data-scroll-overflow": minor
|
||||
"@nextui-org/use-aria-multiselect": minor
|
||||
"@nextui-org/use-infinite-scroll": minor
|
||||
"@nextui-org/scroll-shadow": minor
|
||||
"@nextui-org/dropdown": minor
|
||||
"@nextui-org/listbox": minor
|
||||
"@nextui-org/popover": minor
|
||||
"@nextui-org/select": minor
|
||||
"@nextui-org/input": minor
|
||||
"@nextui-org/react": minor
|
||||
"@nextui-org/theme": minor
|
||||
"@nextui-org/react-rsc-utils": patch
|
||||
"@nextui-org/shared-icons": patch
|
||||
"@nextui-org/accordion": patch
|
||||
"@nextui-org/snippet": patch
|
||||
"@nextui-org/modal": patch
|
||||
"@nextui-org/menu": patch
|
||||
"@nextui-org/tabs": patch
|
||||
---
|
||||
|
||||
New components:
|
||||
|
||||
- Select
|
||||
- Listbox
|
||||
- ScrollShadow
|
||||
113
apps/docs/app/examples/select/async-items-loading/page.tsx
Normal file
113
apps/docs/app/examples/select/async-items-loading/page.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
/* eslint-disable no-console */
|
||||
"use client";
|
||||
|
||||
import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useInfiniteScroll} from "@nextui-org/use-infinite-scroll";
|
||||
|
||||
type Pokemon = {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
type UsePokemonListProps = {
|
||||
/** Delay to wait before fetching more items */
|
||||
fetchDelay?: number;
|
||||
};
|
||||
|
||||
function usePokemonList({fetchDelay = 0}: UsePokemonListProps = {}) {
|
||||
const [items, setItems] = useState<Pokemon[]>([]);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const limit = 10; // Number of items per page, adjust as necessary
|
||||
|
||||
const loadPokemon = async (currentOffset: number) => {
|
||||
const controller = new AbortController();
|
||||
const {signal} = controller;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
if (offset > 0) {
|
||||
// Delay to simulate network latency
|
||||
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
||||
}
|
||||
|
||||
let res = await fetch(
|
||||
`https://pokeapi.co/api/v2/pokemon?offset=${currentOffset}&limit=${limit}`,
|
||||
{signal},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
let json = await res.json();
|
||||
|
||||
setHasMore(json.next !== null);
|
||||
// Append new results to existing ones
|
||||
setItems((prevItems) => [...prevItems, ...json.results]);
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
if (error.name === "AbortError") {
|
||||
console.log("Fetch aborted");
|
||||
} else {
|
||||
console.error("There was an error with the fetch operation:", error);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadPokemon(offset);
|
||||
}, []);
|
||||
|
||||
const onLoadMore = () => {
|
||||
const newOffset = offset + limit;
|
||||
|
||||
setOffset(newOffset);
|
||||
loadPokemon(newOffset);
|
||||
};
|
||||
|
||||
return {
|
||||
items,
|
||||
hasMore,
|
||||
isLoading,
|
||||
onLoadMore,
|
||||
};
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const {items, hasMore, isLoading, onLoadMore} = usePokemonList({fetchDelay: 1500});
|
||||
|
||||
const [, scrollerRef] = useInfiniteScroll({
|
||||
hasMore,
|
||||
isEnabled: isOpen,
|
||||
shouldUseLoader: false, // We don't want to show the loader at the bottom of the list
|
||||
onLoadMore,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<Select
|
||||
className="max-w-xs"
|
||||
isLoading={isLoading}
|
||||
items={items}
|
||||
label="Pick a Pokemon"
|
||||
placeholder="Select a Pokemon"
|
||||
scrollRef={scrollerRef}
|
||||
selectionMode="single"
|
||||
onOpenChange={setIsOpen}
|
||||
>
|
||||
{(item) => (
|
||||
<SelectItem key={item.name} className="capitalize">
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -105,7 +105,7 @@ const users = [
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
|
||||
@ -105,7 +105,7 @@ const users = [
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
|
||||
@ -15,7 +15,7 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isMounted && (
|
||||
<motion.div
|
||||
<motion.article
|
||||
animate={{opacity: 1, y: 0}}
|
||||
exit={{opacity: 0, y: 5}}
|
||||
initial={{opacity: 0, y: 5}}
|
||||
@ -24,7 +24,7 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
<Card
|
||||
isBlurred
|
||||
as={NextLink}
|
||||
className="p-2 border-transparent text-start bg-white/5 dark:bg-default-400/10 backdrop-blur-lg backdrop-saturate-[1.8]"
|
||||
className="p-2 h-full border-transparent text-start bg-white/5 dark:bg-default-400/10 backdrop-blur-lg backdrop-saturate-[1.8]"
|
||||
href={post.url}
|
||||
isPressable={!!post.url}
|
||||
>
|
||||
@ -40,8 +40,8 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
</Link>
|
||||
</CardHeader>
|
||||
<CardBody className="pt-0 px-2 pb-1">
|
||||
<Image className="mb-3" src={post.image} />
|
||||
<p className="font-normal px-1 text-default-600">{post.description}</p>
|
||||
<Image className="mb-4" src={post.image} />
|
||||
<p className="font-normal w-full text-default-600">{post.description}</p>
|
||||
</CardBody>
|
||||
<CardFooter className="flex justify-between items-center">
|
||||
<time className="block text-small text-default-500" dateTime={post.date}>
|
||||
@ -50,7 +50,7 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
<Avatar size="sm" src={post.author?.avatar} />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</motion.article>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
@ -58,7 +58,7 @@ const BlogPostCard = (post: BlogPost) => {
|
||||
|
||||
export const BlogPostList = ({posts}: {posts: BlogPost[]}) => {
|
||||
return (
|
||||
<div className="mt-10 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="mt-10 grid gap-4 grid-cols-[repeat(auto-fill,minmax(300px,1fr))]">
|
||||
{posts.map((post, idx) => (
|
||||
<BlogPostCard key={idx} {...post} />
|
||||
))}
|
||||
|
||||
@ -33,9 +33,11 @@ interface CodeDemoProps extends UseCodeDemoProps, WindowResizerProps {
|
||||
enableResize?: boolean;
|
||||
showTabs?: boolean;
|
||||
showPreview?: boolean;
|
||||
hideWindowActions?: boolean;
|
||||
showOpenInCodeSandbox?: boolean;
|
||||
isPreviewCentered?: boolean;
|
||||
resizeEnabled?: boolean;
|
||||
typescriptStrict?: boolean;
|
||||
displayMode?: "always" | "visible";
|
||||
isGradientBox?: boolean;
|
||||
gradientColor?: GradientBoxProps["color"];
|
||||
@ -52,8 +54,11 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
|
||||
showPreview = true,
|
||||
asIframe = false,
|
||||
resizeEnabled = true,
|
||||
hideWindowActions = false,
|
||||
showSandpackPreview = false,
|
||||
isPreviewCentered = false,
|
||||
// when false .js files will be used
|
||||
typescriptStrict = false,
|
||||
showOpenInCodeSandbox,
|
||||
isGradientBox = false,
|
||||
defaultExpanded = false,
|
||||
@ -96,6 +101,7 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
|
||||
|
||||
const content = asIframe ? (
|
||||
<WindowResizer
|
||||
hideWindowActions={hideWindowActions}
|
||||
iframeHeight={previewHeight}
|
||||
iframeInitialWidth={iframeInitialWidth}
|
||||
iframeSrc={iframeSrc}
|
||||
@ -121,6 +127,7 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
|
||||
isGradientBox,
|
||||
gradientColor,
|
||||
previewHeight,
|
||||
hideWindowActions,
|
||||
asIframe,
|
||||
showPreview,
|
||||
isInView,
|
||||
@ -138,6 +145,7 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
|
||||
showEditor={showEditor}
|
||||
showOpenInCodeSandbox={showOpenInCodeSandbox || showPreview}
|
||||
showPreview={showSandpackPreview}
|
||||
typescriptStrict={typescriptStrict}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ const resizer = tv({
|
||||
|
||||
export interface WindowResizerProps {
|
||||
resizeEnabled?: boolean;
|
||||
hideWindowActions?: boolean;
|
||||
iframeHeight?: string | number;
|
||||
iframeMinWidth?: number;
|
||||
iframeSrc?: string;
|
||||
@ -70,6 +71,7 @@ const WindowResizer: React.FC<WindowResizerProps> = (props) => {
|
||||
iframeSrc,
|
||||
iframeTitle,
|
||||
resizeEnabled,
|
||||
hideWindowActions = false,
|
||||
iframeHeight: height = "420px",
|
||||
iframeInitialWidth,
|
||||
iframeMinWidth: minWidth = MIN_WIDTH,
|
||||
@ -120,7 +122,7 @@ const WindowResizer: React.FC<WindowResizerProps> = (props) => {
|
||||
width: isMobile ? "100%" : browserWidth,
|
||||
}}
|
||||
>
|
||||
<WindowActions className="bg-default-100 dark:bg-default-50" />
|
||||
{!hideWindowActions && <WindowActions className="bg-default-100 dark:bg-default-50" />}
|
||||
<motion.iframe ref={iframeRef} className={iframe()} src={iframeSrc} title={iframeTitle} />
|
||||
</motion.div>
|
||||
{resizeEnabled && (
|
||||
|
||||
@ -223,10 +223,13 @@ export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], sl
|
||||
as={NextLink}
|
||||
className="hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
||||
color="secondary"
|
||||
href="/blog/nextui-v2"
|
||||
href="/blog/v2.1.0"
|
||||
variant="dot"
|
||||
>
|
||||
Introducing NextUI v2.0
|
||||
New components v2.1.0
|
||||
<span aria-label="party emoji" role="img">
|
||||
🎉
|
||||
</span>
|
||||
</Chip>
|
||||
</NavbarItem>
|
||||
</ul>
|
||||
|
||||
@ -26,6 +26,7 @@ export const Sandpack: FC<SandpackProps> = ({
|
||||
files: filesProp,
|
||||
template,
|
||||
highlightedLines,
|
||||
typescriptStrict = false,
|
||||
showPreview = false,
|
||||
showEditor = true,
|
||||
defaultExpanded = false,
|
||||
@ -41,6 +42,7 @@ export const Sandpack: FC<SandpackProps> = ({
|
||||
useSandpack({
|
||||
files: filesProp,
|
||||
template,
|
||||
typescriptStrict,
|
||||
highlightedLines,
|
||||
});
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import {useLocalStorage} from "@/hooks/use-local-storage";
|
||||
|
||||
export interface UseSandpackProps {
|
||||
files?: SandpackFiles;
|
||||
typescriptStrict?: boolean;
|
||||
template?: SandpackPredefinedTemplate;
|
||||
highlightedLines?: HighlightedLines;
|
||||
}
|
||||
@ -19,6 +20,7 @@ const importAllReact = 'import * as React from "react";';
|
||||
|
||||
export const useSandpack = ({
|
||||
files = {},
|
||||
typescriptStrict = false,
|
||||
template = "vite-react",
|
||||
highlightedLines,
|
||||
}: UseSandpackProps) => {
|
||||
@ -57,6 +59,9 @@ export const useSandpack = ({
|
||||
if (key.includes("App") && !key.includes(mimeType)) {
|
||||
return acc;
|
||||
}
|
||||
if (typescriptStrict && key.includes("js")) {
|
||||
return acc;
|
||||
}
|
||||
// @ts-ignore
|
||||
acc[key] = files[key];
|
||||
|
||||
@ -76,6 +81,11 @@ export const useSandpack = ({
|
||||
const aName = getFileName(a);
|
||||
const bName = getFileName(b);
|
||||
|
||||
// if bName includes "App" should be first
|
||||
if (bName.includes("App")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (aFile?.includes(bName)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -164,8 +164,7 @@
|
||||
"key": "chip",
|
||||
"title": "Chip",
|
||||
"keywords": "chip, tag, label, small actionable entity",
|
||||
"path": "/docs/components/chip.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/chip.mdx"
|
||||
},
|
||||
{
|
||||
"key": "circular-progress",
|
||||
@ -183,8 +182,7 @@
|
||||
"key": "divider",
|
||||
"title": "Divider",
|
||||
"keywords": "divider, boundary, separator, section divider",
|
||||
"path": "/docs/components/divider.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/divider.mdx"
|
||||
},
|
||||
{
|
||||
"key": "dropdown",
|
||||
@ -208,8 +206,7 @@
|
||||
"key": "kbd",
|
||||
"title": "Kbd",
|
||||
"keywords": "keyboard input, shortcut, keys, user input display",
|
||||
"path": "/docs/components/kbd.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/kbd.mdx"
|
||||
},
|
||||
{
|
||||
"key": "link",
|
||||
@ -217,6 +214,13 @@
|
||||
"keywords": "link, navigation, href, web page connection",
|
||||
"path": "/docs/components/link.mdx"
|
||||
},
|
||||
{
|
||||
"key": "listbox",
|
||||
"title": "Listbox",
|
||||
"keywords": "listbox, selection, option list, multiple choice",
|
||||
"path": "/docs/components/listbox.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "modal",
|
||||
"title": "Modal",
|
||||
@ -253,18 +257,30 @@
|
||||
"keywords": "radio group, selection set, option selection, exclusive choices",
|
||||
"path": "/docs/components/radio-group.mdx"
|
||||
},
|
||||
{
|
||||
"key": "select",
|
||||
"title": "Select",
|
||||
"keywords": "select, selection, option list, multiple choice",
|
||||
"path": "/docs/components/select.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
"key": "skeleton",
|
||||
"title": "Skeleton",
|
||||
"keywords": "skeleton, loading state, placeholder, content preview",
|
||||
"path": "/docs/components/skeleton.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/skeleton.mdx"
|
||||
},
|
||||
{
|
||||
"key": "snippet",
|
||||
"title": "Snippet",
|
||||
"keywords": "snippet, code block, programming, code example",
|
||||
"path": "/docs/components/snippet.mdx",
|
||||
"path": "/docs/components/snippet.mdx"
|
||||
},
|
||||
{
|
||||
"key": "scroll-shadow",
|
||||
"title": "Scroll Shadow",
|
||||
"keywords": "scroll shadow, scroll indicator, scroll bar, scroll position",
|
||||
"path": "/docs/components/scroll-shadow.mdx",
|
||||
"newPost": true
|
||||
},
|
||||
{
|
||||
@ -295,8 +311,7 @@
|
||||
"key": "tabs",
|
||||
"title": "Tabs",
|
||||
"keywords": "tabs, section navigation, categorized content, tabbed interface",
|
||||
"path": "/docs/components/tabs.mdx",
|
||||
"newPost": true
|
||||
"path": "/docs/components/tabs.mdx"
|
||||
},
|
||||
{
|
||||
"key": "textarea",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -310,3 +310,16 @@ file helps streamline the process of transforming design ideas into functioning
|
||||
To start using **NextUI v2.0**, head over to our [installation guide](/docs/guide/installation).
|
||||
|
||||
We can't wait to see the amazing things you'll build with **NextUI v2.0**!
|
||||
|
||||
---
|
||||
|
||||
## Community
|
||||
|
||||
We're excited to see the community adopt NextUI, raise issues, and provide feedback.
|
||||
Whether it's a feature request, bug report, or a project to showcase, please get involved!
|
||||
|
||||
<Community />
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD) to learn how you can contribute to this project.
|
||||
|
||||
167
apps/docs/content/blog/v2.1.0.mdx
Normal file
167
apps/docs/content/blog/v2.1.0.mdx
Normal file
@ -0,0 +1,167 @@
|
||||
---
|
||||
title: "New components v2.1.0 🎉"
|
||||
description: "NextUI v2.1.0 is here with new components, Select, Listbox and ScrollShadow."
|
||||
date: "2023-08-22"
|
||||
image: "/blog/v2.1.0.jpg"
|
||||
tags: ["nextui", "select", "listbox", "scroll-shadow", "multi-select"]
|
||||
author:
|
||||
name: "Junior Garcia"
|
||||
username: "@jrgarciadev"
|
||||
link: "https://twitter.com/jrgarciadev"
|
||||
avatar: "/avatars/junior-garcia.jpeg"
|
||||
---
|
||||
|
||||
import {selectContent} from "@/content/components/select";
|
||||
import {listboxContent} from "@/content/components/listbox";
|
||||
import {scrollShadowContent} from "@/content/components/scroll-shadow";
|
||||
|
||||
|
||||
<img
|
||||
src="/blog/v2.1.0.jpg"
|
||||
width={700}
|
||||
height={350}
|
||||
alt="NextUI v2"
|
||||
className="w-full border border-transparent dark:border-default-200/50 object-fit rounded-xl shadow-lg"
|
||||
/>
|
||||
|
||||
We are thrilled to announce the latest update to NextUI, version **2.1.0**! This release introduces some game-changing
|
||||
additions that many of you have eagerly been waiting for.
|
||||
|
||||
First on the list is the highly-anticipated **Select** component. Fully customizable and beautifully designed, supports both single and
|
||||
multi-select modes and is accessible out of the box.
|
||||
|
||||
But that's not all. We're also rolling out two more incredible components **Listbox** and **ScrollShadow**. The new
|
||||
**Listbox** allows you to make list manipulations more efficient and visually appealing. Meanwhile, the
|
||||
**ScrollShadow** component adds an elegant shadow effect to scrollable areas, enhancing the UI aesthetics while
|
||||
also improving usability.
|
||||
|
||||
## Select
|
||||
|
||||
Creating a select component that is both accessible and customizable is a challenging task. We've spent a lot of time
|
||||
researching and testing different approaches to come up with a solution that works for everyone. The result is a
|
||||
component that is easy to use, fully accessible, and highly customizable.
|
||||
|
||||
The new **Select** component includes:
|
||||
|
||||
- Support for selecting a single option.
|
||||
- Support for selecting multiple options.
|
||||
- Support for disabled options.
|
||||
- Support for sections.
|
||||
- Labeling support for accessibility.
|
||||
- Exposed to assistive technology as a button with a listbox popup using ARIA (combined with [Listbox](/docs/components/listbox)).
|
||||
- Support for description and error message help text linked to the input via ARIA.
|
||||
- Support for mouse, touch, and keyboard interactions.
|
||||
- Tab stop focus management.
|
||||
- Asyncronous options loading.
|
||||
- Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly.
|
||||
- Typeahead to allow selecting options by typing text, even without opening the listbox.
|
||||
- Browser autofill integration via a hidden native `<select>` element.
|
||||
- Support for mobile form navigation via software keyboard.
|
||||
- Mobile screen reader listbox dismissal support.
|
||||
- And much more...
|
||||
|
||||
### Single Select
|
||||
|
||||
The single select component is used to select a single option from a list of options. It is a combination of a button
|
||||
and a listbox. The button displays the currently selected option and the listbox displays the available options.
|
||||
|
||||
<CodeDemo title="Usage" files={selectContent.usage} />
|
||||
|
||||
### Multiple Select
|
||||
|
||||
The multiple select component can be used to select multiple options from a list of options.
|
||||
|
||||
You only need to pass the `selectionMode="multiple"` prop to the `Select` component.
|
||||
|
||||
<CodeDemo title="Multiple Selection" files={selectContent.multiple} />
|
||||
|
||||
### Multiple Variants
|
||||
|
||||
The select component comes with multiple variants.
|
||||
|
||||
<CodeDemo title="Variants" files={selectContent.variants} />
|
||||
|
||||
|
||||
### Chips Support
|
||||
|
||||
The select component is flexible and allows you to render any component as an option and as a selected option.
|
||||
|
||||
<CodeDemo title="Multiple Selection with Chips" files={selectContent.multipleWithChips} />
|
||||
|
||||
|
||||
### Customizable
|
||||
|
||||
The select component is highly customizable, you can customize the selected option, the options, the listbox,
|
||||
the popover and the scrollable area.
|
||||
|
||||
<CodeDemo title="Custom Styles" files={selectContent.customStyles} />
|
||||
|
||||
|
||||
Go to the [Select](/docs/components/select) component page to learn more about sizes, colors, and more.
|
||||
|
||||
|
||||
## Listbox
|
||||
|
||||
The listbox component allows you to make list manipulations more efficient and visually appealing.
|
||||
|
||||
The new **Listbox** component includes:
|
||||
|
||||
- Support for single, multiple, or no selection.
|
||||
- Exposed to assistive technology as a `listbox` using ARIA.
|
||||
- Support for disabled items.
|
||||
- Support for sections.
|
||||
- Labeling support for accessibility.
|
||||
- Support for mouse, touch, and keyboard interactions.
|
||||
- Tab stop focus management.
|
||||
- Keyboard navigation support including arrow keys, home/end, page up/down, select all, and clear.
|
||||
- Automatic scrolling support during keyboard navigation.
|
||||
- Typeahead to allow focusing options by typing text.
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
<CodeDemo title="Usage" files={listboxContent.usage} />
|
||||
|
||||
### Custom Styles
|
||||
|
||||
The Listbox components offers multiple customization options.
|
||||
|
||||
<CodeDemo title="Custom Styles" files={listboxContent.customStyles} />
|
||||
|
||||
> **Note**: In the above example, we've utilized the [Boxicons](https://boxicons.com/) icons collection.
|
||||
|
||||
Go to the [Listbox](/docs/components/listbox) component page to learn more about it.
|
||||
|
||||
|
||||
## ScrollShadow
|
||||
|
||||
The ScrollShadow component gives a nice shadow effect to scrollable areas. These shadows are handled by using
|
||||
the CSS `mask-image` property, which makes the shadows adapt to the background color.
|
||||
|
||||
### Usage
|
||||
|
||||
<CodeDemo title="Usage" files={scrollShadowContent.usage} />
|
||||
|
||||
You can hide the scrollbars, customize the shadows size, change the orientation, and more.
|
||||
|
||||
Go to the [ScrollShadow](/docs/components/scroll-shadow) component page to learn more about it.
|
||||
|
||||
|
||||
<Spacer y={6}/>
|
||||
|
||||
We hope you enjoy these new components and the new features. We're excited to see what you build with them!
|
||||
|
||||
Thanks for reading and happy coding! 🚀
|
||||
|
||||
---
|
||||
|
||||
## Community
|
||||
|
||||
We're excited to see the community adopt NextUI, raise issues, and provide feedback.
|
||||
Whether it's a feature request, bug report, or a project to showcase, please get involved!
|
||||
|
||||
<Community />
|
||||
|
||||
## Contributing
|
||||
|
||||
PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.MD) to learn how you can contribute to this project.
|
||||
@ -49,6 +49,7 @@ export default function App() {
|
||||
"data-[hover=true]:text-foreground",
|
||||
"data-[hover=true]:bg-default-100",
|
||||
"dark:data-[hover=true]:bg-default-50",
|
||||
"data-[selectable=true]:focus:bg-default-50",
|
||||
"data-[pressed=true]:opacity-70",
|
||||
"data-[focus-visible=true]:ring-default-500",
|
||||
],
|
||||
|
||||
@ -138,6 +138,7 @@ export default function App() {
|
||||
<DropdownItem
|
||||
key="edit"
|
||||
shortcut="⌘⇧E"
|
||||
showDivider
|
||||
description="Allows you to edit the file"
|
||||
startContent={<EditDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
@ -145,7 +146,6 @@ export default function App() {
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
showDivider
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
shortcut="⌘⇧D"
|
||||
|
||||
@ -142,7 +142,6 @@ export default function App() {
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
showDivider
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
shortcut="⌘⇧D"
|
||||
|
||||
@ -19,7 +19,7 @@ export default function App() {
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
aria-label="Single selection actions"
|
||||
aria-label="Multiple selection example"
|
||||
variant="flat"
|
||||
closeOnSelect={false}
|
||||
disallowEmptySelection
|
||||
|
||||
@ -14,7 +14,7 @@ export default function App() {
|
||||
<DropdownItem key="new" shortcut="⌘N">New file</DropdownItem>
|
||||
<DropdownItem key="copy" shortcut="⌘C">Copy link</DropdownItem>
|
||||
<DropdownItem key="edit" shortcut="⌘⇧E">Edit file</DropdownItem>
|
||||
<DropdownItem key="delete" showDivider shortcut="⌘⇧D" className="text-danger" color="danger">
|
||||
<DropdownItem key="delete" shortcut="⌘⇧D" className="text-danger" color="danger">
|
||||
Delete file
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
|
||||
@ -19,7 +19,7 @@ export default function App() {
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
aria-label="Single selection actions"
|
||||
aria-label="Single selection example"
|
||||
variant="flat"
|
||||
disallowEmptySelection
|
||||
selectionMode="single"
|
||||
|
||||
@ -6,7 +6,7 @@ import sizes from "./sizes";
|
||||
import colors from "./colors";
|
||||
import variants from "./variants";
|
||||
import radius from "./radius";
|
||||
import labelPositions from "./label-positions";
|
||||
import labelPlacements from "./label-placements";
|
||||
import description from "./description";
|
||||
import password from "./password";
|
||||
import clearButton from "./clear-button";
|
||||
@ -26,7 +26,7 @@ export const inputContent = {
|
||||
colors,
|
||||
variants,
|
||||
radius,
|
||||
labelPositions,
|
||||
labelPlacements,
|
||||
description,
|
||||
password,
|
||||
clearButton,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const App = `import {Input} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
const positions = [
|
||||
const placements = [
|
||||
"inside",
|
||||
"outside",
|
||||
"outside-left",
|
||||
@ -12,13 +12,13 @@ export default function App() {
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="text-default-500 text-small">Without placeholder</h3>
|
||||
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
{positions.map((position) => (
|
||||
{placements.map((placement) => (
|
||||
<Input
|
||||
key={position}
|
||||
key={placement}
|
||||
type="email"
|
||||
label="Email"
|
||||
labelPlacement={position}
|
||||
description={position}
|
||||
labelPlacement={placement}
|
||||
description={placement}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -26,14 +26,14 @@ export default function App() {
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="text-default-500 text-small">With placeholder</h3>
|
||||
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
{positions.map((position) => (
|
||||
{placements.map((placement) => (
|
||||
<Input
|
||||
key={position}
|
||||
key={placement}
|
||||
type="email"
|
||||
label="Email"
|
||||
labelPlacement={position}
|
||||
labelPlacement={placement}
|
||||
placeholder="Enter your email"
|
||||
description={position}
|
||||
description={placement}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -1,7 +1,6 @@
|
||||
const App = `import {Input} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
|
||||
const variants = ["flat", "bordered", "underlined", "faded"];
|
||||
|
||||
return (
|
||||
|
||||
284
apps/docs/content/components/listbox/custom-styles.ts
Normal file
284
apps/docs/content/components/listbox/custom-styles.ts
Normal file
@ -0,0 +1,284 @@
|
||||
const BugIcon = `export const BugIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M16.895,6.519l2.813-2.812l-1.414-1.414l-2.846,2.846c-0.233-0.166-0.473-0.321-0.723-0.454 c-1.723-0.91-3.726-0.911-5.45,0c-0.25,0.132-0.488,0.287-0.722,0.453L5.707,2.293L4.293,3.707l2.813,2.812 C6.53,7.242,6.08,8.079,5.756,9H2v2h2.307C4.242,11.495,4.2,11.997,4.2,12.5c0,0.507,0.042,1.013,0.107,1.511H2v2h2.753 c0.013,0.039,0.021,0.08,0.034,0.118c0.188,0.555,0.421,1.093,0.695,1.6c0.044,0.081,0.095,0.155,0.141,0.234l-2.33,2.33 l1.414,1.414l2.11-2.111c0.235,0.254,0.478,0.498,0.736,0.716c0.418,0.354,0.867,0.657,1.332,0.903 c0.479,0.253,0.982,0.449,1.496,0.58C10.911,21.931,11.455,22,12,22s1.089-0.069,1.618-0.204c0.514-0.131,1.017-0.327,1.496-0.58 c0.465-0.246,0.914-0.55,1.333-0.904c0.258-0.218,0.5-0.462,0.734-0.716l2.111,2.111l1.414-1.414l-2.33-2.33 c0.047-0.08,0.098-0.155,0.142-0.236c0.273-0.505,0.507-1.043,0.694-1.599c0.013-0.039,0.021-0.079,0.034-0.118H22v-2h-2.308 c0.065-0.499,0.107-1.004,0.107-1.511c0-0.503-0.042-1.005-0.106-1.5H22V9h-3.756C17.92,8.079,17.47,7.242,16.895,6.519z M8.681,7.748c0.445-0.558,0.96-0.993,1.528-1.294c1.141-0.603,2.442-0.602,3.581,0c0.569,0.301,1.084,0.736,1.53,1.295 c0.299,0.373,0.54,0.8,0.753,1.251H7.927C8.141,8.549,8.381,8.121,8.681,7.748z M17.8,12.5c0,0.522-0.042,1.044-0.126,1.553 c-0.079,0.49-0.199,0.973-0.355,1.436c-0.151,0.449-0.34,0.882-0.559,1.288c-0.217,0.399-0.463,0.772-0.733,1.11 c-0.267,0.333-0.56,0.636-0.869,0.898c-0.31,0.261-0.639,0.484-0.979,0.664s-0.695,0.317-1.057,0.41 c-0.04,0.01-0.082,0.014-0.122,0.023V14h-2v5.881c-0.04-0.009-0.082-0.013-0.122-0.023c-0.361-0.093-0.717-0.23-1.057-0.41 s-0.669-0.403-0.978-0.664c-0.311-0.263-0.604-0.565-0.871-0.899c-0.27-0.337-0.516-0.71-0.731-1.108 c-0.22-0.407-0.408-0.84-0.56-1.289c-0.156-0.463-0.276-0.946-0.356-1.438C6.242,13.544,6.2,13.022,6.2,12.5 c0-0.505,0.041-1.009,0.119-1.5h11.361C17.759,11.491,17.8,11.995,17.8,12.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
`;
|
||||
|
||||
const PullRequestIcon = `export const PullRequestIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M19.01 15.163V7.997C19.005 6.391 17.933 4 15 4V2l-4 3 4 3V6c1.829 0 2.001 1.539 2.01 2v7.163c-1.44.434-2.5 1.757-2.5 3.337 0 1.93 1.57 3.5 3.5 3.5s3.5-1.57 3.5-3.5c0-1.58-1.06-2.903-2.5-3.337zm-1 4.837c-.827 0-1.5-.673-1.5-1.5s.673-1.5 1.5-1.5 1.5.673 1.5 1.5-.673 1.5-1.5 1.5zM9.5 5.5C9.5 3.57 7.93 2 6 2S2.5 3.57 2.5 5.5c0 1.58 1.06 2.903 2.5 3.337v6.326c-1.44.434-2.5 1.757-2.5 3.337C2.5 20.43 4.07 22 6 22s3.5-1.57 3.5-3.5c0-1.58-1.06-2.903-2.5-3.337V8.837C8.44 8.403 9.5 7.08 9.5 5.5zm-5 0C4.5 4.673 5.173 4 6 4s1.5.673 1.5 1.5S6.827 7 6 7s-1.5-.673-1.5-1.5zm3 13c0 .827-.673 1.5-1.5 1.5s-1.5-.673-1.5-1.5S5.173 17 6 17s1.5.673 1.5 1.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const ChatIcon = `export const ChatIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M5 18v3.766l1.515-.909L11.277 18H16c1.103 0 2-.897 2-2V8c0-1.103-.897-2-2-2H4c-1.103 0-2 .897-2 2v8c0 1.103.897 2 2 2h1zM4 8h12v8h-5.277L7 18.234V16H4V8z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M20 2H8c-1.103 0-2 .897-2 2h12c1.103 0 2 .897 2 2v8c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const PlayCircleIcon = `export const PlayCircleIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="m9 17 8-5-8-5z" fill="currentColor" />
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const LayoutIcon = `export const LayoutIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M19 3H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2V5c0-1.103-.897-2-2-2zm0 2 .001 4H5V5h14zM5 11h8v8H5v-8zm10 8v-8h4.001l.001 8H15z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const TagIcon = `export const TagIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M11.707 2.293A.997.997 0 0 0 11 2H6a.997.997 0 0 0-.707.293l-3 3A.996.996 0 0 0 2 6v5c0 .266.105.52.293.707l10 10a.997.997 0 0 0 1.414 0l8-8a.999.999 0 0 0 0-1.414l-10-10zM13 19.586l-9-9V6.414L6.414 4h4.172l9 9L13 19.586z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<circle cx="8.353" cy="8.353" fill="currentColor" r="1.647" />
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const UsersIcon = `export const UsersIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M16.604 11.048a5.67 5.67 0 0 0 .751-3.44c-.179-1.784-1.175-3.361-2.803-4.44l-1.105 1.666c1.119.742 1.8 1.799 1.918 2.974a3.693 3.693 0 0 1-1.072 2.986l-1.192 1.192 1.618.475C18.951 13.701 19 17.957 19 18h2c0-1.789-.956-5.285-4.396-6.952z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M9.5 12c2.206 0 4-1.794 4-4s-1.794-4-4-4-4 1.794-4 4 1.794 4 4 4zm0-6c1.103 0 2 .897 2 2s-.897 2-2 2-2-.897-2-2 .897-2 2-2zm1.5 7H8c-3.309 0-6 2.691-6 6v1h2v-1c0-2.206 1.794-4 4-4h3c2.206 0 4 1.794 4 4v1h2v-1c0-3.309-2.691-6-6-6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const WatchersIcon = `export const WatchersIcons = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="m21.977 13.783-2-9A1.002 1.002 0 0 0 19 4h-3v2h2.198l.961 4.326A4.467 4.467 0 0 0 17.5 10c-1.953 0-3.603 1.258-4.224 3h-2.553c-.621-1.742-2.271-3-4.224-3-.587 0-1.145.121-1.659.326L5.802 6H8V4H5a1 1 0 0 0-.976.783l-2 9 .047.011A4.552 4.552 0 0 0 2 14.5C2 16.981 4.019 19 6.5 19c2.31 0 4.197-1.756 4.449-4h2.102c.252 2.244 2.139 4 4.449 4 2.481 0 4.5-2.019 4.5-4.5 0-.242-.034-.475-.071-.706l.048-.011zM6.5 17C5.122 17 4 15.878 4 14.5S5.122 12 6.5 12 9 13.122 9 14.5 7.878 17 6.5 17zm11 0c-1.379 0-2.5-1.122-2.5-2.5s1.121-2.5 2.5-2.5 2.5 1.122 2.5 2.5-1.121 2.5-2.5 2.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const BookIcon = `export const BookIcon = (props) => (
|
||||
<svg height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path
|
||||
d="M6 22h15v-2H6.012C5.55 19.988 5 19.805 5 19s.55-.988 1.012-1H21V4c0-1.103-.897-2-2-2H6c-1.206 0-3 .799-3 3v14c0 2.201 1.794 3 3 3zM5 8V5c0-.805.55-.988 1-1h13v12H5V8z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M8 6h9v2H8z" fill="currentColor" />
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const ChevronRightIcon = `export const ChevronRightIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path d="m9 18 6-6-6-6" />
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const IconWrapper = `import {cn} from "@nextui-org/react";
|
||||
|
||||
const IconWrapper = ({children, className}) => (
|
||||
<div className={cn(className, "flex items-center rounded-small justify-center w-7 h-7")}>
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const ItemCounter = `const ItemCounter = ({number}) => (
|
||||
<div className="flex items-center gap-1 text-default-400">
|
||||
<span className="text-small">{number}</span>
|
||||
<ChevronRightIcon className="text-xl" />
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {IconWrapper} from "./IconWrapper";
|
||||
import {ItemCounter} from "./ItemCounter";
|
||||
import {BugIcon} from "./BugIcon";
|
||||
import {PullRequestIcon} from "./PullRequestIcon";
|
||||
import {ChatIcon} from "./ChatIcon";
|
||||
import {PlayCircleIcon} from "./PlayCircleIcon";
|
||||
import {LayoutIcon} from "./LayoutIcon";
|
||||
import {TagIcon} from "./TagIcon";
|
||||
import {UsersIcon} from "./UsersIcon";
|
||||
import {WatchersIcon} from "./WatchersIcon";
|
||||
import {BookIcon} from "./BookIcon";
|
||||
import {ChevronRightIcon} from "./ChevronRightIcon";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Listbox
|
||||
aria-label="User Menu"
|
||||
onAction={(key) => alert(key)}
|
||||
className="p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 max-w-[300px] overflow-visible shadow-small rounded-medium"
|
||||
itemClasses={{
|
||||
base: "px-3 first:rounded-t-medium last:rounded-b-medium rounded-none gap-3 h-12 data-[hover=true]:bg-default-100/80",
|
||||
}}
|
||||
>
|
||||
<ListboxItem
|
||||
key="issues"
|
||||
endContent={<ItemCounter number={13} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-success/10 text-success">
|
||||
<BugIcon className="text-lg " />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Issues
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="pull_requests"
|
||||
endContent={<ItemCounter number={6} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-primary/10 text-primary">
|
||||
<PullRequestIcon className="text-lg " />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Pull Requests
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="discussions"
|
||||
endContent={<ItemCounter number={293} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-secondary/10 text-secondary">
|
||||
<ChatIcon className="text-lg " />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Discussions
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="actions"
|
||||
endContent={<ItemCounter number={2} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-warning/10 text-warning">
|
||||
<PlayCircleIcon className="text-lg " />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Actions
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="projects"
|
||||
endContent={<ItemCounter number={4} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-default/50 text-foreground">
|
||||
<LayoutIcon className="text-lg " />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Projects
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="releases"
|
||||
className="group h-auto py-3"
|
||||
endContent={<ItemCounter number={399} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-primary/10 text-primary">
|
||||
<TagIcon className="text-lg" />
|
||||
</IconWrapper>
|
||||
}
|
||||
textValue="Releases"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span>Releases</span>
|
||||
<div className="px-2 py-1 rounded-small bg-default-100 group-data-[hover=true]:bg-default-200">
|
||||
<span className="text-tiny text-default-600">@nextui-org/react@2.0.10</span>
|
||||
<div className="flex gap-2 text-tiny">
|
||||
<span className="text-default-500">49 minutes ago</span>
|
||||
<span className="text-success">Latest</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="contributors"
|
||||
endContent={<ItemCounter number={79} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-warning/10 text-warning">
|
||||
<UsersIcon />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Contributors
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="watchers"
|
||||
endContent={<ItemCounter number={82} />}
|
||||
startContent={
|
||||
<IconWrapper className="bg-default/50 text-foreground">
|
||||
<WatchersIcons />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
Watchers
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="license"
|
||||
endContent={<span className="text-small text-default-400">MIT</span>}
|
||||
startContent={
|
||||
<IconWrapper className="bg-danger/10 text-danger dark:text-danger-500">
|
||||
<BookIcon />
|
||||
</IconWrapper>
|
||||
}
|
||||
>
|
||||
License
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/IconWrapper.jsx": IconWrapper,
|
||||
"/ItemCounter.jsx": ItemCounter,
|
||||
"/BugIcon.jsx": BugIcon,
|
||||
"/PullRequestIcon.jsx": PullRequestIcon,
|
||||
"/ChatIcon.jsx": ChatIcon,
|
||||
"/PlayCircleIcon.jsx": PlayCircleIcon,
|
||||
"/LayoutIcon.jsx": LayoutIcon,
|
||||
"/TagIcon.jsx": TagIcon,
|
||||
"/UsersIcon.jsx": UsersIcon,
|
||||
"/WatchersIcons.jsx": WatchersIcon,
|
||||
"/BookIcon.jsx": BookIcon,
|
||||
"/ChevronRightIcon.jsx": ChevronRightIcon,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
169
apps/docs/content/components/listbox/description.ts
Normal file
169
apps/docs/content/components/listbox/description.ts
Normal file
@ -0,0 +1,169 @@
|
||||
const AddNoteIcon = `export const AddNoteIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M7.37 22h9.25a4.87 4.87 0 0 0 4.87-4.87V8.37a4.87 4.87 0 0 0-4.87-4.87H7.37A4.87 4.87 0 0 0 2.5 8.37v8.75c0 2.7 2.18 4.88 4.87 4.88Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M8.29 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM15.71 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM12 14.75h-1.69V13c0-.41-.34-.75-.75-.75s-.75.34-.75.75v1.75H7c-.41 0-.75.34-.75.75s.34.75.75.75h1.81V18c0 .41.34.75.75.75s.75-.34.75-.75v-1.75H12c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const CopyDocumentIcon = `export const CopyDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.5 13.15h-2.17c-1.78 0-3.23-1.44-3.23-3.23V7.75c0-.41-.33-.75-.75-.75H6.18C3.87 7 2 8.5 2 11.18v6.64C2 20.5 3.87 22 6.18 22h5.89c2.31 0 4.18-1.5 4.18-4.18V13.9c0-.42-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M17.82 2H11.93C9.67 2 7.84 3.44 7.76 6.01c.06 0 .11-.01.17-.01h5.89C16.13 6 18 7.5 18 10.18V16.83c0 .06-.01.11-.01.16 2.23-.07 4.01-1.55 4.01-4.16V6.18C22 3.5 20.13 2 17.82 2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11.98 7.15c-.31-.31-.84-.1-.84.33v2.62c0 1.1.93 2 2.07 2 .71.01 1.7.01 2.55.01.43 0 .65-.5.35-.8-1.09-1.09-3.03-3.04-4.13-4.16Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const EditDocumentIcon = `export const EditDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.48 3H7.52C4.07 3 2 5.06 2 8.52v7.95C2 19.94 4.07 22 7.52 22h7.95c3.46 0 5.52-2.06 5.52-5.52V8.52C21 5.06 18.93 3 15.48 3Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M21.02 2.98c-1.79-1.8-3.54-1.84-5.38 0L14.51 4.1c-.1.1-.13.24-.09.37.7 2.45 2.66 4.41 5.11 5.11.03.01.08.01.11.01.1 0 .2-.04.27-.11l1.11-1.12c.91-.91 1.36-1.78 1.36-2.67 0-.9-.45-1.79-1.36-2.71ZM17.86 10.42c-.27-.13-.53-.26-.77-.41-.2-.12-.4-.25-.59-.39-.16-.1-.34-.25-.52-.4-.02-.01-.08-.06-.16-.14-.31-.25-.64-.59-.95-.96-.02-.02-.08-.08-.13-.17-.1-.11-.25-.3-.38-.51-.11-.14-.24-.34-.36-.55-.15-.25-.28-.5-.4-.76-.13-.28-.23-.54-.32-.79L7.9 10.72c-.35.35-.69 1.01-.76 1.5l-.43 2.98c-.09.63.08 1.22.47 1.61.33.33.78.5 1.28.5.11 0 .22-.01.33-.02l2.97-.42c.49-.07 1.15-.4 1.5-.76l5.38-5.38c-.25-.08-.5-.19-.78-.31Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const DeleteDocumentIcon = `export const DeleteDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M21.07 5.23c-1.61-.16-3.22-.28-4.84-.37v-.01l-.22-1.3c-.15-.92-.37-2.3-2.71-2.3h-2.62c-2.33 0-2.55 1.32-2.71 2.29l-.21 1.28c-.93.06-1.86.12-2.79.21l-2.04.2c-.42.04-.72.41-.68.82.04.41.4.71.82.67l2.04-.2c5.24-.52 10.52-.32 15.82.21h.08c.38 0 .71-.29.75-.68a.766.766 0 0 0-.69-.82Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.23 8.14c-.24-.25-.57-.39-.91-.39H5.68c-.34 0-.68.14-.91.39-.23.25-.36.59-.34.94l.62 10.26c.11 1.52.25 3.42 3.74 3.42h6.42c3.49 0 3.63-1.89 3.74-3.42l.62-10.25c.02-.36-.11-.7-.34-.95Z"
|
||||
fill="currentColor"
|
||||
opacity={0.399}
|
||||
/>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M9.58 17a.75.75 0 0 1 .75-.75h3.33a.75.75 0 0 1 0 1.5h-3.33a.75.75 0 0 1-.75-.75ZM8.75 13a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 0 1.5h-5a.75.75 0 0 1-.75-.75Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem, cn} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
import {AddNoteIcon} from "./AddNoteIcon.jsx";
|
||||
import {CopyDocumentIcon} from "./CopyDocumentIcon.jsx";
|
||||
import {EditDocumentIcon} from "./EditDocumentIcon.jsx";
|
||||
import {DeleteDocumentIcon} from "./DeleteDocumentIcon.jsx";
|
||||
|
||||
export default function App() {
|
||||
const iconClasses = "text-xl text-default-500 pointer-events-none flex-shrink-0";
|
||||
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox variant="flat" aria-label="Listbox menu with descriptions">
|
||||
<ListboxItem
|
||||
key="new"
|
||||
description="Create a new file"
|
||||
startContent={<AddNoteIcon className={iconClasses} />}
|
||||
>
|
||||
New file
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="copy"
|
||||
description="Copy the file link"
|
||||
startContent={<CopyDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Copy link
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="edit"
|
||||
showDivider
|
||||
description="Allows you to edit the file"
|
||||
startContent={<EditDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Edit file
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="delete"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
description="Permanently delete the file"
|
||||
startContent={<DeleteDocumentIcon className={cn(iconClasses, "text-danger")} />}
|
||||
>
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
"/AddNoteIcon.jsx": AddNoteIcon,
|
||||
"/CopyDocumentIcon.jsx": CopyDocumentIcon,
|
||||
"/EditDocumentIcon.jsx": EditDocumentIcon,
|
||||
"/DeleteDocumentIcon.jsx": DeleteDocumentIcon,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
36
apps/docs/content/components/listbox/disabled-keys.ts
Normal file
36
apps/docs/content/components/listbox/disabled-keys.ts
Normal file
@ -0,0 +1,36 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
aria-label="Example with disabled actions"
|
||||
disabledKeys={["edit", "delete"]}
|
||||
onAction={(key) => alert(key)}
|
||||
>
|
||||
<ListboxItem key="new">New file</ListboxItem>
|
||||
<ListboxItem key="copy">Copy link</ListboxItem>
|
||||
<ListboxItem key="edit">Edit file</ListboxItem>
|
||||
<ListboxItem key="delete" className="text-danger" color="danger">
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
58
apps/docs/content/components/listbox/dynamic.ts
Normal file
58
apps/docs/content/components/listbox/dynamic.ts
Normal file
@ -0,0 +1,58 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
const items = [
|
||||
{
|
||||
key: "new",
|
||||
label: "New file",
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
label: "Copy link",
|
||||
},
|
||||
{
|
||||
key: "edit",
|
||||
label: "Edit file",
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
label: "Delete file",
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
items={items}
|
||||
aria-label="Dynamic Actions"
|
||||
onAction={(key) => alert(key)}
|
||||
>
|
||||
{(item) => (
|
||||
<ListboxItem
|
||||
key={item.key}
|
||||
color={item.key === "delete" ? "danger" : "default"}
|
||||
className={item.key === "delete" ? "text-danger" : ""}
|
||||
>
|
||||
{item.label}
|
||||
</ListboxItem>
|
||||
)}
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
165
apps/docs/content/components/listbox/icons.ts
Normal file
165
apps/docs/content/components/listbox/icons.ts
Normal file
@ -0,0 +1,165 @@
|
||||
const AddNoteIcon = `export const AddNoteIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M7.37 22h9.25a4.87 4.87 0 0 0 4.87-4.87V8.37a4.87 4.87 0 0 0-4.87-4.87H7.37A4.87 4.87 0 0 0 2.5 8.37v8.75c0 2.7 2.18 4.88 4.87 4.88Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M8.29 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM15.71 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM12 14.75h-1.69V13c0-.41-.34-.75-.75-.75s-.75.34-.75.75v1.75H7c-.41 0-.75.34-.75.75s.34.75.75.75h1.81V18c0 .41.34.75.75.75s.75-.34.75-.75v-1.75H12c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const CopyDocumentIcon = `export const CopyDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.5 13.15h-2.17c-1.78 0-3.23-1.44-3.23-3.23V7.75c0-.41-.33-.75-.75-.75H6.18C3.87 7 2 8.5 2 11.18v6.64C2 20.5 3.87 22 6.18 22h5.89c2.31 0 4.18-1.5 4.18-4.18V13.9c0-.42-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M17.82 2H11.93C9.67 2 7.84 3.44 7.76 6.01c.06 0 .11-.01.17-.01h5.89C16.13 6 18 7.5 18 10.18V16.83c0 .06-.01.11-.01.16 2.23-.07 4.01-1.55 4.01-4.16V6.18C22 3.5 20.13 2 17.82 2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11.98 7.15c-.31-.31-.84-.1-.84.33v2.62c0 1.1.93 2 2.07 2 .71.01 1.7.01 2.55.01.43 0 .65-.5.35-.8-1.09-1.09-3.03-3.04-4.13-4.16Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const EditDocumentIcon = `export const EditDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.48 3H7.52C4.07 3 2 5.06 2 8.52v7.95C2 19.94 4.07 22 7.52 22h7.95c3.46 0 5.52-2.06 5.52-5.52V8.52C21 5.06 18.93 3 15.48 3Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M21.02 2.98c-1.79-1.8-3.54-1.84-5.38 0L14.51 4.1c-.1.1-.13.24-.09.37.7 2.45 2.66 4.41 5.11 5.11.03.01.08.01.11.01.1 0 .2-.04.27-.11l1.11-1.12c.91-.91 1.36-1.78 1.36-2.67 0-.9-.45-1.79-1.36-2.71ZM17.86 10.42c-.27-.13-.53-.26-.77-.41-.2-.12-.4-.25-.59-.39-.16-.1-.34-.25-.52-.4-.02-.01-.08-.06-.16-.14-.31-.25-.64-.59-.95-.96-.02-.02-.08-.08-.13-.17-.1-.11-.25-.3-.38-.51-.11-.14-.24-.34-.36-.55-.15-.25-.28-.5-.4-.76-.13-.28-.23-.54-.32-.79L7.9 10.72c-.35.35-.69 1.01-.76 1.5l-.43 2.98c-.09.63.08 1.22.47 1.61.33.33.78.5 1.28.5.11 0 .22-.01.33-.02l2.97-.42c.49-.07 1.15-.4 1.5-.76l5.38-5.38c-.25-.08-.5-.19-.78-.31Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const DeleteDocumentIcon = `export const DeleteDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M21.07 5.23c-1.61-.16-3.22-.28-4.84-.37v-.01l-.22-1.3c-.15-.92-.37-2.3-2.71-2.3h-2.62c-2.33 0-2.55 1.32-2.71 2.29l-.21 1.28c-.93.06-1.86.12-2.79.21l-2.04.2c-.42.04-.72.41-.68.82.04.41.4.71.82.67l2.04-.2c5.24-.52 10.52-.32 15.82.21h.08c.38 0 .71-.29.75-.68a.766.766 0 0 0-.69-.82Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.23 8.14c-.24-.25-.57-.39-.91-.39H5.68c-.34 0-.68.14-.91.39-.23.25-.36.59-.34.94l.62 10.26c.11 1.52.25 3.42 3.74 3.42h6.42c3.49 0 3.63-1.89 3.74-3.42l.62-10.25c.02-.36-.11-.7-.34-.95Z"
|
||||
fill="currentColor"
|
||||
opacity={0.399}
|
||||
/>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M9.58 17a.75.75 0 0 1 .75-.75h3.33a.75.75 0 0 1 0 1.5h-3.33a.75.75 0 0 1-.75-.75ZM8.75 13a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 0 1.5h-5a.75.75 0 0 1-.75-.75Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem, cn} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
import {AddNoteIcon} from "./AddNoteIcon.jsx";
|
||||
import {CopyDocumentIcon} from "./CopyDocumentIcon.jsx";
|
||||
import {EditDocumentIcon} from "./EditDocumentIcon.jsx";
|
||||
import {DeleteDocumentIcon} from "./DeleteDocumentIcon.jsx";
|
||||
|
||||
export default function App() {
|
||||
const iconClasses = "text-xl text-default-500 pointer-events-none flex-shrink-0";
|
||||
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox variant="faded" aria-label="Listbox menu with icons">
|
||||
<ListboxItem
|
||||
key="new"
|
||||
startContent={<AddNoteIcon className={iconClasses} />}
|
||||
>
|
||||
New file
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="copy"
|
||||
startContent={<CopyDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Copy link
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="edit"
|
||||
showDivider
|
||||
startContent={<EditDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Edit file
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="delete"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
startContent={<DeleteDocumentIcon className={cn(iconClasses, "text-danger")} />}
|
||||
>
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
"/AddNoteIcon.jsx": AddNoteIcon,
|
||||
"/CopyDocumentIcon.jsx": CopyDocumentIcon,
|
||||
"/EditDocumentIcon.jsx": EditDocumentIcon,
|
||||
"/DeleteDocumentIcon.jsx": DeleteDocumentIcon,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
23
apps/docs/content/components/listbox/index.ts
Normal file
23
apps/docs/content/components/listbox/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import usage from "./usage";
|
||||
import dynamic from "./dynamic";
|
||||
import disabledKeys from "./disabled-keys";
|
||||
import variants from "./variants";
|
||||
import singleSelection from "./single-selection";
|
||||
import multipleSelection from "./multiple-selection";
|
||||
import icons from "./icons";
|
||||
import description from "./description";
|
||||
import sections from "./sections";
|
||||
import customStyles from "./custom-styles";
|
||||
|
||||
export const listboxContent = {
|
||||
usage,
|
||||
dynamic,
|
||||
disabledKeys,
|
||||
variants,
|
||||
singleSelection,
|
||||
multipleSelection,
|
||||
icons,
|
||||
description,
|
||||
sections,
|
||||
customStyles,
|
||||
};
|
||||
48
apps/docs/content/components/listbox/multiple-selection.ts
Normal file
48
apps/docs/content/components/listbox/multiple-selection.ts
Normal file
@ -0,0 +1,48 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
const [selectedKeys, setSelectedKeys] = React.useState(new Set(["text"]));
|
||||
|
||||
const selectedValue = React.useMemo(
|
||||
() => Array.from(selectedKeys).join(", "),
|
||||
[selectedKeys]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
aria-label="Multiple selection example"
|
||||
variant="flat"
|
||||
disallowEmptySelection
|
||||
selectionMode="multiple"
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
>
|
||||
<ListboxItem key="text">Text</ListboxItem>
|
||||
<ListboxItem key="number">Number</ListboxItem>
|
||||
<ListboxItem key="date">Date</ListboxItem>
|
||||
<ListboxItem key="single_date">Single Date</ListboxItem>
|
||||
<ListboxItem key="iteration">Iteration</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
<p className="text-small text-default-500">Selected value: {selectedValue}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
172
apps/docs/content/components/listbox/sections.ts
Normal file
172
apps/docs/content/components/listbox/sections.ts
Normal file
@ -0,0 +1,172 @@
|
||||
const AddNoteIcon = `export const AddNoteIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M7.37 22h9.25a4.87 4.87 0 0 0 4.87-4.87V8.37a4.87 4.87 0 0 0-4.87-4.87H7.37A4.87 4.87 0 0 0 2.5 8.37v8.75c0 2.7 2.18 4.88 4.87 4.88Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M8.29 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM15.71 6.29c-.42 0-.75-.34-.75-.75V2.75a.749.749 0 1 1 1.5 0v2.78c0 .42-.33.76-.75.76ZM12 14.75h-1.69V13c0-.41-.34-.75-.75-.75s-.75.34-.75.75v1.75H7c-.41 0-.75.34-.75.75s.34.75.75.75h1.81V18c0 .41.34.75.75.75s.75-.34.75-.75v-1.75H12c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const CopyDocumentIcon = `export const CopyDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.5 13.15h-2.17c-1.78 0-3.23-1.44-3.23-3.23V7.75c0-.41-.33-.75-.75-.75H6.18C3.87 7 2 8.5 2 11.18v6.64C2 20.5 3.87 22 6.18 22h5.89c2.31 0 4.18-1.5 4.18-4.18V13.9c0-.42-.34-.75-.75-.75Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M17.82 2H11.93C9.67 2 7.84 3.44 7.76 6.01c.06 0 .11-.01.17-.01h5.89C16.13 6 18 7.5 18 10.18V16.83c0 .06-.01.11-.01.16 2.23-.07 4.01-1.55 4.01-4.16V6.18C22 3.5 20.13 2 17.82 2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11.98 7.15c-.31-.31-.84-.1-.84.33v2.62c0 1.1.93 2 2.07 2 .71.01 1.7.01 2.55.01.43 0 .65-.5.35-.8-1.09-1.09-3.03-3.04-4.13-4.16Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const EditDocumentIcon = `export const EditDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.48 3H7.52C4.07 3 2 5.06 2 8.52v7.95C2 19.94 4.07 22 7.52 22h7.95c3.46 0 5.52-2.06 5.52-5.52V8.52C21 5.06 18.93 3 15.48 3Z"
|
||||
fill="currentColor"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<path
|
||||
d="M21.02 2.98c-1.79-1.8-3.54-1.84-5.38 0L14.51 4.1c-.1.1-.13.24-.09.37.7 2.45 2.66 4.41 5.11 5.11.03.01.08.01.11.01.1 0 .2-.04.27-.11l1.11-1.12c.91-.91 1.36-1.78 1.36-2.67 0-.9-.45-1.79-1.36-2.71ZM17.86 10.42c-.27-.13-.53-.26-.77-.41-.2-.12-.4-.25-.59-.39-.16-.1-.34-.25-.52-.4-.02-.01-.08-.06-.16-.14-.31-.25-.64-.59-.95-.96-.02-.02-.08-.08-.13-.17-.1-.11-.25-.3-.38-.51-.11-.14-.24-.34-.36-.55-.15-.25-.28-.5-.4-.76-.13-.28-.23-.54-.32-.79L7.9 10.72c-.35.35-.69 1.01-.76 1.5l-.43 2.98c-.09.63.08 1.22.47 1.61.33.33.78.5 1.28.5.11 0 .22-.01.33-.02l2.97-.42c.49-.07 1.15-.4 1.5-.76l5.38-5.38c-.25-.08-.5-.19-.78-.31Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const DeleteDocumentIcon = `export const DeleteDocumentIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M21.07 5.23c-1.61-.16-3.22-.28-4.84-.37v-.01l-.22-1.3c-.15-.92-.37-2.3-2.71-2.3h-2.62c-2.33 0-2.55 1.32-2.71 2.29l-.21 1.28c-.93.06-1.86.12-2.79.21l-2.04.2c-.42.04-.72.41-.68.82.04.41.4.71.82.67l2.04-.2c5.24-.52 10.52-.32 15.82.21h.08c.38 0 .71-.29.75-.68a.766.766 0 0 0-.69-.82Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.23 8.14c-.24-.25-.57-.39-.91-.39H5.68c-.34 0-.68.14-.91.39-.23.25-.36.59-.34.94l.62 10.26c.11 1.52.25 3.42 3.74 3.42h6.42c3.49 0 3.63-1.89 3.74-3.42l.62-10.25c.02-.36-.11-.7-.34-.95Z"
|
||||
fill="currentColor"
|
||||
opacity={0.399}
|
||||
/>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M9.58 17a.75.75 0 0 1 .75-.75h3.33a.75.75 0 0 1 0 1.5h-3.33a.75.75 0 0 1-.75-.75ZM8.75 13a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 0 1.5h-5a.75.75 0 0 1-.75-.75Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem, ListboxSection, cn} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
import {AddNoteIcon} from "./AddNoteIcon.jsx";
|
||||
import {CopyDocumentIcon} from "./CopyDocumentIcon.jsx";
|
||||
import {EditDocumentIcon} from "./EditDocumentIcon.jsx";
|
||||
import {DeleteDocumentIcon} from "./DeleteDocumentIcon.jsx";
|
||||
|
||||
export default function App() {
|
||||
const iconClasses = "text-xl text-default-500 pointer-events-none flex-shrink-0";
|
||||
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox variant="flat" aria-label="Listbox menu with sections">
|
||||
<ListboxSection title="Actions" showDivider>
|
||||
<ListboxItem
|
||||
key="new"
|
||||
description="Create a new file"
|
||||
startContent={<AddNoteIcon className={iconClasses} />}
|
||||
>
|
||||
New file
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="copy"
|
||||
description="Copy the file link"
|
||||
startContent={<CopyDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Copy link
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="edit"
|
||||
description="Allows you to edit the file"
|
||||
startContent={<EditDocumentIcon className={iconClasses} />}
|
||||
>
|
||||
Edit file
|
||||
</ListboxItem>
|
||||
</ListboxSection>
|
||||
<ListboxSection title="Danger zone">
|
||||
<ListboxItem
|
||||
key="delete"
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
description="Permanently delete the file"
|
||||
startContent={<DeleteDocumentIcon className={cn(iconClasses, "text-danger")} />}
|
||||
>
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</ListboxSection>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
"/AddNoteIcon.jsx": AddNoteIcon,
|
||||
"/CopyDocumentIcon.jsx": CopyDocumentIcon,
|
||||
"/EditDocumentIcon.jsx": EditDocumentIcon,
|
||||
"/DeleteDocumentIcon.jsx": DeleteDocumentIcon,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
48
apps/docs/content/components/listbox/single-selection.ts
Normal file
48
apps/docs/content/components/listbox/single-selection.ts
Normal file
@ -0,0 +1,48 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
const [selectedKeys, setSelectedKeys] = React.useState(new Set(["text"]));
|
||||
|
||||
const selectedValue = React.useMemo(
|
||||
() => Array.from(selectedKeys).join(", "),
|
||||
[selectedKeys]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
aria-label="Single selection example"
|
||||
variant="flat"
|
||||
disallowEmptySelection
|
||||
selectionMode="single"
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
>
|
||||
<ListboxItem key="text">Text</ListboxItem>
|
||||
<ListboxItem key="number">Number</ListboxItem>
|
||||
<ListboxItem key="date">Date</ListboxItem>
|
||||
<ListboxItem key="single_date">Single Date</ListboxItem>
|
||||
<ListboxItem key="iteration">Iteration</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
<p className="text-small text-default-500">Selected value: {selectedValue}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
35
apps/docs/content/components/listbox/usage.ts
Normal file
35
apps/docs/content/components/listbox/usage.ts
Normal file
@ -0,0 +1,35 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
aria-label="Actions"
|
||||
onAction={(key) => alert(key)}
|
||||
>
|
||||
<ListboxItem key="new">New file</ListboxItem>
|
||||
<ListboxItem key="copy">Copy link</ListboxItem>
|
||||
<ListboxItem key="edit">Edit file</ListboxItem>
|
||||
<ListboxItem key="delete" className="text-danger" color="danger">
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
72
apps/docs/content/components/listbox/variants.ts
Normal file
72
apps/docs/content/components/listbox/variants.ts
Normal file
@ -0,0 +1,72 @@
|
||||
const ListboxWrapper = `const ListboxWrapper = ({children}) => (
|
||||
<div className="w-full max-w-[260px] border-small px-1 py-2 rounded-small border-default-200 dark:border-default-100">
|
||||
{children}
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {Listbox, ListboxItem, RadioGroup, Radio} from "@nextui-org/react";
|
||||
import {ListboxWrapper} from "./ListboxWrapper";
|
||||
|
||||
export default function App() {
|
||||
const [selectedVariant, setSelectedVariant] = React.useState("solid")
|
||||
const [selectedColor, setSelectedColor] = React.useState("default")
|
||||
|
||||
const variants = ["solid", "bordered", "light", "flat", "faded", "shadow"];
|
||||
const colors = ["default", "primary", "secondary", "success", "warning", "danger"];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<ListboxWrapper>
|
||||
<Listbox
|
||||
aria-label="Listbox Variants"
|
||||
color={selectedColor}
|
||||
variant={selectedVariant}
|
||||
>
|
||||
<ListboxItem key="new">New file</ListboxItem>
|
||||
<ListboxItem key="copy">Copy link</ListboxItem>
|
||||
<ListboxItem key="edit">Edit file</ListboxItem>
|
||||
<ListboxItem key="delete" className="text-danger" color="danger">
|
||||
Delete file
|
||||
</ListboxItem>
|
||||
</Listbox>
|
||||
</ListboxWrapper>
|
||||
<div className="flex flex-col gap-2">
|
||||
<RadioGroup
|
||||
label="Select listbox item variant"
|
||||
orientation="horizontal"
|
||||
color={selectedVariant}
|
||||
defaultValue="solid"
|
||||
onValueChange={setSelectedVariant}
|
||||
>
|
||||
{variants.map((variant) => (
|
||||
<Radio key={variant} value={variant} className="capitalize">
|
||||
{variant}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
label="Select listbox item color"
|
||||
orientation="horizontal"
|
||||
color={selectedColor}
|
||||
defaultValue="default"
|
||||
onValueChange={setSelectedColor}
|
||||
>
|
||||
{colors.map((color) => (
|
||||
<Radio key={color} value={color} className="capitalize">
|
||||
{color}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/ListboxWrapper.jsx": ListboxWrapper,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@ -5,7 +5,7 @@ export default function App() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
<p className="text-default-500">Selected Page: {currentPage}</p>
|
||||
<p className="text-small text-default-500">Selected Page: {currentPage}</p>
|
||||
<Pagination
|
||||
total={10}
|
||||
color="secondary"
|
||||
|
||||
54
apps/docs/content/components/scroll-shadow/custom-size.ts
Normal file
54
apps/docs/content/components/scroll-shadow/custom-size.ts
Normal file
@ -0,0 +1,54 @@
|
||||
const Content = `export const Content = () => (
|
||||
<div>
|
||||
<p>
|
||||
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
|
||||
</p>
|
||||
<p>
|
||||
Sunt ad dolore quis aute consequat. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
<p>
|
||||
Est velit labore esse esse cupidatat. Velit id elit consequat minim. Mollit enim excepteur ea laboris adipisicing aliqua proident occaecat do do adipisicing adipisicing ut fugiat. Consequat pariatur ullamco aute sunt esse. Irure excepteur eu non eiusmod. Commodo commodo et ad ipsum elit esse pariatur sit adipisicing sunt excepteur enim.
|
||||
</p>
|
||||
<p>
|
||||
Incididunt duis commodo mollit esse veniam non exercitation dolore occaecat ea nostrud laboris. Adipisicing occaecat fugiat fugiat irure fugiat in magna non consectetur proident fugiat. Commodo magna et aliqua elit sint cupidatat. Sint aute ullamco enim cillum anim ex. Est eiusmod commodo occaecat consequat laboris est do duis. Enim incididunt non culpa velit quis aute in elit magna ullamco in consequat ex proident.
|
||||
</p>
|
||||
<p>
|
||||
Dolore incididunt mollit fugiat pariatur cupidatat ipsum laborum cillum. Commodo consequat velit cupidatat duis ex nisi non aliquip ad ea pariatur do culpa. Eiusmod proident adipisicing tempor tempor qui pariatur voluptate dolor do ea commodo. Veniam voluptate cupidatat ex nisi do ullamco in quis elit.
|
||||
</p>
|
||||
<p>
|
||||
Cillum proident veniam cupidatat pariatur laborum tempor cupidatat anim eiusmod id nostrud pariatur tempor reprehenderit. Do esse ullamco laboris sunt proident est ea exercitation cupidatat. Do Lorem eiusmod aliqua culpa ullamco consectetur veniam voluptate cillum. Dolor consequat cillum tempor laboris mollit laborum reprehenderit reprehenderit veniam aliqua deserunt cupidatat consequat id.
|
||||
</p>
|
||||
<p>
|
||||
Est id tempor excepteur enim labore sint aliquip consequat duis minim tempor proident. Dolor incididunt aliquip minim elit ea. Exercitation non officia eu id.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum ipsum consequat incididunt do aliquip pariatur nostrud. Qui ut sint culpa labore Lorem. Magna deserunt aliquip aute duis consectetur magna amet anim. Magna fugiat est nostrud veniam. Officia duis ea sunt aliqua.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum minim officia aute anim minim aute aliquip aute non in non. Ipsum aliquip proident ut dolore eiusmod ad fugiat fugiat ut ex. Ea velit Lorem ut et commodo nulla voluptate veniam ea et aliqua esse id. Pariatur dolor et adipisicing ea mollit. Ipsum non irure proident ipsum dolore aliquip adipisicing laborum irure dolor nostrud occaecat exercitation.
|
||||
</p>
|
||||
<p>
|
||||
Culpa qui reprehenderit nostrud aliqua reprehenderit et ullamco proident nisi commodo non ut. Ipsum quis irure nisi sint do qui velit nisi. Sunt voluptate eu reprehenderit tempor consequat eiusmod Lorem irure velit duis Lorem laboris ipsum cupidatat. Pariatur excepteur tempor veniam cillum et nulla ipsum veniam ad ipsum ad aute. Est officia duis pariatur ad eiusmod id voluptate.
|
||||
</p>
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {ScrollShadow} from "@nextui-org/react";
|
||||
import {Content} from "./Content";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ScrollShadow size={100} className="w-[300px] h-[400px]">
|
||||
<Content />
|
||||
</ScrollShadow>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/Content.jsx": Content,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
54
apps/docs/content/components/scroll-shadow/hide-scrollbar.ts
Normal file
54
apps/docs/content/components/scroll-shadow/hide-scrollbar.ts
Normal file
@ -0,0 +1,54 @@
|
||||
const Content = `export const Content = () => (
|
||||
<div>
|
||||
<p>
|
||||
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
|
||||
</p>
|
||||
<p>
|
||||
Sunt ad dolore quis aute consequat. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
<p>
|
||||
Est velit labore esse esse cupidatat. Velit id elit consequat minim. Mollit enim excepteur ea laboris adipisicing aliqua proident occaecat do do adipisicing adipisicing ut fugiat. Consequat pariatur ullamco aute sunt esse. Irure excepteur eu non eiusmod. Commodo commodo et ad ipsum elit esse pariatur sit adipisicing sunt excepteur enim.
|
||||
</p>
|
||||
<p>
|
||||
Incididunt duis commodo mollit esse veniam non exercitation dolore occaecat ea nostrud laboris. Adipisicing occaecat fugiat fugiat irure fugiat in magna non consectetur proident fugiat. Commodo magna et aliqua elit sint cupidatat. Sint aute ullamco enim cillum anim ex. Est eiusmod commodo occaecat consequat laboris est do duis. Enim incididunt non culpa velit quis aute in elit magna ullamco in consequat ex proident.
|
||||
</p>
|
||||
<p>
|
||||
Dolore incididunt mollit fugiat pariatur cupidatat ipsum laborum cillum. Commodo consequat velit cupidatat duis ex nisi non aliquip ad ea pariatur do culpa. Eiusmod proident adipisicing tempor tempor qui pariatur voluptate dolor do ea commodo. Veniam voluptate cupidatat ex nisi do ullamco in quis elit.
|
||||
</p>
|
||||
<p>
|
||||
Cillum proident veniam cupidatat pariatur laborum tempor cupidatat anim eiusmod id nostrud pariatur tempor reprehenderit. Do esse ullamco laboris sunt proident est ea exercitation cupidatat. Do Lorem eiusmod aliqua culpa ullamco consectetur veniam voluptate cillum. Dolor consequat cillum tempor laboris mollit laborum reprehenderit reprehenderit veniam aliqua deserunt cupidatat consequat id.
|
||||
</p>
|
||||
<p>
|
||||
Est id tempor excepteur enim labore sint aliquip consequat duis minim tempor proident. Dolor incididunt aliquip minim elit ea. Exercitation non officia eu id.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum ipsum consequat incididunt do aliquip pariatur nostrud. Qui ut sint culpa labore Lorem. Magna deserunt aliquip aute duis consectetur magna amet anim. Magna fugiat est nostrud veniam. Officia duis ea sunt aliqua.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum minim officia aute anim minim aute aliquip aute non in non. Ipsum aliquip proident ut dolore eiusmod ad fugiat fugiat ut ex. Ea velit Lorem ut et commodo nulla voluptate veniam ea et aliqua esse id. Pariatur dolor et adipisicing ea mollit. Ipsum non irure proident ipsum dolore aliquip adipisicing laborum irure dolor nostrud occaecat exercitation.
|
||||
</p>
|
||||
<p>
|
||||
Culpa qui reprehenderit nostrud aliqua reprehenderit et ullamco proident nisi commodo non ut. Ipsum quis irure nisi sint do qui velit nisi. Sunt voluptate eu reprehenderit tempor consequat eiusmod Lorem irure velit duis Lorem laboris ipsum cupidatat. Pariatur excepteur tempor veniam cillum et nulla ipsum veniam ad ipsum ad aute. Est officia duis pariatur ad eiusmod id voluptate.
|
||||
</p>
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {ScrollShadow} from "@nextui-org/react";
|
||||
import {Content} from "./Content";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ScrollShadow hideScrollBar className="w-[300px] h-[400px]">
|
||||
<Content />
|
||||
</ScrollShadow>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/Content.jsx": Content,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
54
apps/docs/content/components/scroll-shadow/horizontal.ts
Normal file
54
apps/docs/content/components/scroll-shadow/horizontal.ts
Normal file
@ -0,0 +1,54 @@
|
||||
const Content = `export const Content = ({className}) => (
|
||||
<div className={className}>
|
||||
<p>
|
||||
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
|
||||
</p>
|
||||
<p>
|
||||
Sunt ad dolore quis aute consequat. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
<p>
|
||||
Est velit labore esse esse cupidatat. Velit id elit consequat minim. Mollit enim excepteur ea laboris adipisicing aliqua proident occaecat do do adipisicing adipisicing ut fugiat. Consequat pariatur ullamco aute sunt esse. Irure excepteur eu non eiusmod. Commodo commodo et ad ipsum elit esse pariatur sit adipisicing sunt excepteur enim.
|
||||
</p>
|
||||
<p>
|
||||
Incididunt duis commodo mollit esse veniam non exercitation dolore occaecat ea nostrud laboris. Adipisicing occaecat fugiat fugiat irure fugiat in magna non consectetur proident fugiat. Commodo magna et aliqua elit sint cupidatat. Sint aute ullamco enim cillum anim ex. Est eiusmod commodo occaecat consequat laboris est do duis. Enim incididunt non culpa velit quis aute in elit magna ullamco in consequat ex proident.
|
||||
</p>
|
||||
<p>
|
||||
Dolore incididunt mollit fugiat pariatur cupidatat ipsum laborum cillum. Commodo consequat velit cupidatat duis ex nisi non aliquip ad ea pariatur do culpa. Eiusmod proident adipisicing tempor tempor qui pariatur voluptate dolor do ea commodo. Veniam voluptate cupidatat ex nisi do ullamco in quis elit.
|
||||
</p>
|
||||
<p>
|
||||
Cillum proident veniam cupidatat pariatur laborum tempor cupidatat anim eiusmod id nostrud pariatur tempor reprehenderit. Do esse ullamco laboris sunt proident est ea exercitation cupidatat. Do Lorem eiusmod aliqua culpa ullamco consectetur veniam voluptate cillum. Dolor consequat cillum tempor laboris mollit laborum reprehenderit reprehenderit veniam aliqua deserunt cupidatat consequat id.
|
||||
</p>
|
||||
<p>
|
||||
Est id tempor excepteur enim labore sint aliquip consequat duis minim tempor proident. Dolor incididunt aliquip minim elit ea. Exercitation non officia eu id.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum ipsum consequat incididunt do aliquip pariatur nostrud. Qui ut sint culpa labore Lorem. Magna deserunt aliquip aute duis consectetur magna amet anim. Magna fugiat est nostrud veniam. Officia duis ea sunt aliqua.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum minim officia aute anim minim aute aliquip aute non in non. Ipsum aliquip proident ut dolore eiusmod ad fugiat fugiat ut ex. Ea velit Lorem ut et commodo nulla voluptate veniam ea et aliqua esse id. Pariatur dolor et adipisicing ea mollit. Ipsum non irure proident ipsum dolore aliquip adipisicing laborum irure dolor nostrud occaecat exercitation.
|
||||
</p>
|
||||
<p>
|
||||
Culpa qui reprehenderit nostrud aliqua reprehenderit et ullamco proident nisi commodo non ut. Ipsum quis irure nisi sint do qui velit nisi. Sunt voluptate eu reprehenderit tempor consequat eiusmod Lorem irure velit duis Lorem laboris ipsum cupidatat. Pariatur excepteur tempor veniam cillum et nulla ipsum veniam ad ipsum ad aute. Est officia duis pariatur ad eiusmod id voluptate.
|
||||
</p>
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {ScrollShadow} from "@nextui-org/react";
|
||||
import {Content} from "./Content";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ScrollShadow orientation="horizontal" className="max-w-[400px] max-h-[300px]">
|
||||
<Content className="w-[800px]" />
|
||||
</ScrollShadow>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/Content.jsx": Content,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
13
apps/docs/content/components/scroll-shadow/index.ts
Normal file
13
apps/docs/content/components/scroll-shadow/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import usage from "./usage";
|
||||
import hideScrollbar from "./hide-scrollbar";
|
||||
import customSize from "./custom-size";
|
||||
import horizontal from "./horizontal";
|
||||
import offset from "./offset";
|
||||
|
||||
export const scrollShadowContent = {
|
||||
usage,
|
||||
hideScrollbar,
|
||||
customSize,
|
||||
horizontal,
|
||||
offset,
|
||||
};
|
||||
59
apps/docs/content/components/scroll-shadow/offset.ts
Normal file
59
apps/docs/content/components/scroll-shadow/offset.ts
Normal file
@ -0,0 +1,59 @@
|
||||
const Content = `export const Content = ({className}) => (
|
||||
<div className={className}>
|
||||
<p>
|
||||
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
|
||||
</p>
|
||||
<p>
|
||||
Sunt ad dolore quis aute consequat. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
<p>
|
||||
Est velit labore esse esse cupidatat. Velit id elit consequat minim. Mollit enim excepteur ea laboris adipisicing aliqua proident occaecat do do adipisicing adipisicing ut fugiat. Consequat pariatur ullamco aute sunt esse. Irure excepteur eu non eiusmod. Commodo commodo et ad ipsum elit esse pariatur sit adipisicing sunt excepteur enim.
|
||||
</p>
|
||||
<p>
|
||||
Incididunt duis commodo mollit esse veniam non exercitation dolore occaecat ea nostrud laboris. Adipisicing occaecat fugiat fugiat irure fugiat in magna non consectetur proident fugiat. Commodo magna et aliqua elit sint cupidatat. Sint aute ullamco enim cillum anim ex. Est eiusmod commodo occaecat consequat laboris est do duis. Enim incididunt non culpa velit quis aute in elit magna ullamco in consequat ex proident.
|
||||
</p>
|
||||
<p>
|
||||
Dolore incididunt mollit fugiat pariatur cupidatat ipsum laborum cillum. Commodo consequat velit cupidatat duis ex nisi non aliquip ad ea pariatur do culpa. Eiusmod proident adipisicing tempor tempor qui pariatur voluptate dolor do ea commodo. Veniam voluptate cupidatat ex nisi do ullamco in quis elit.
|
||||
</p>
|
||||
<p>
|
||||
Cillum proident veniam cupidatat pariatur laborum tempor cupidatat anim eiusmod id nostrud pariatur tempor reprehenderit. Do esse ullamco laboris sunt proident est ea exercitation cupidatat. Do Lorem eiusmod aliqua culpa ullamco consectetur veniam voluptate cillum. Dolor consequat cillum tempor laboris mollit laborum reprehenderit reprehenderit veniam aliqua deserunt cupidatat consequat id.
|
||||
</p>
|
||||
<p>
|
||||
Est id tempor excepteur enim labore sint aliquip consequat duis minim tempor proident. Dolor incididunt aliquip minim elit ea. Exercitation non officia eu id.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum ipsum consequat incididunt do aliquip pariatur nostrud. Qui ut sint culpa labore Lorem. Magna deserunt aliquip aute duis consectetur magna amet anim. Magna fugiat est nostrud veniam. Officia duis ea sunt aliqua.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum minim officia aute anim minim aute aliquip aute non in non. Ipsum aliquip proident ut dolore eiusmod ad fugiat fugiat ut ex. Ea velit Lorem ut et commodo nulla voluptate veniam ea et aliqua esse id. Pariatur dolor et adipisicing ea mollit. Ipsum non irure proident ipsum dolore aliquip adipisicing laborum irure dolor nostrud occaecat exercitation.
|
||||
</p>
|
||||
<p>
|
||||
Culpa qui reprehenderit nostrud aliqua reprehenderit et ullamco proident nisi commodo non ut. Ipsum quis irure nisi sint do qui velit nisi. Sunt voluptate eu reprehenderit tempor consequat eiusmod Lorem irure velit duis Lorem laboris ipsum cupidatat. Pariatur excepteur tempor veniam cillum et nulla ipsum veniam ad ipsum ad aute. Est officia duis pariatur ad eiusmod id voluptate.
|
||||
</p>
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {ScrollShadow} from "@nextui-org/react";
|
||||
import {Content} from "./Content";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ScrollShadow
|
||||
hideScrollBar
|
||||
offset={100}
|
||||
orientation="horizontal"
|
||||
className="max-w-[400px] max-h-[300px]"
|
||||
>
|
||||
<Content className="w-[800px]" />
|
||||
</ScrollShadow>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/Content.jsx": Content,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
54
apps/docs/content/components/scroll-shadow/usage.ts
Normal file
54
apps/docs/content/components/scroll-shadow/usage.ts
Normal file
@ -0,0 +1,54 @@
|
||||
const Content = `export const Content = () => (
|
||||
<div>
|
||||
<p>
|
||||
Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis.
|
||||
</p>
|
||||
<p>
|
||||
Sunt ad dolore quis aute consequat. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
|
||||
</p>
|
||||
<p>
|
||||
Est velit labore esse esse cupidatat. Velit id elit consequat minim. Mollit enim excepteur ea laboris adipisicing aliqua proident occaecat do do adipisicing adipisicing ut fugiat. Consequat pariatur ullamco aute sunt esse. Irure excepteur eu non eiusmod. Commodo commodo et ad ipsum elit esse pariatur sit adipisicing sunt excepteur enim.
|
||||
</p>
|
||||
<p>
|
||||
Incididunt duis commodo mollit esse veniam non exercitation dolore occaecat ea nostrud laboris. Adipisicing occaecat fugiat fugiat irure fugiat in magna non consectetur proident fugiat. Commodo magna et aliqua elit sint cupidatat. Sint aute ullamco enim cillum anim ex. Est eiusmod commodo occaecat consequat laboris est do duis. Enim incididunt non culpa velit quis aute in elit magna ullamco in consequat ex proident.
|
||||
</p>
|
||||
<p>
|
||||
Dolore incididunt mollit fugiat pariatur cupidatat ipsum laborum cillum. Commodo consequat velit cupidatat duis ex nisi non aliquip ad ea pariatur do culpa. Eiusmod proident adipisicing tempor tempor qui pariatur voluptate dolor do ea commodo. Veniam voluptate cupidatat ex nisi do ullamco in quis elit.
|
||||
</p>
|
||||
<p>
|
||||
Cillum proident veniam cupidatat pariatur laborum tempor cupidatat anim eiusmod id nostrud pariatur tempor reprehenderit. Do esse ullamco laboris sunt proident est ea exercitation cupidatat. Do Lorem eiusmod aliqua culpa ullamco consectetur veniam voluptate cillum. Dolor consequat cillum tempor laboris mollit laborum reprehenderit reprehenderit veniam aliqua deserunt cupidatat consequat id.
|
||||
</p>
|
||||
<p>
|
||||
Est id tempor excepteur enim labore sint aliquip consequat duis minim tempor proident. Dolor incididunt aliquip minim elit ea. Exercitation non officia eu id.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum ipsum consequat incididunt do aliquip pariatur nostrud. Qui ut sint culpa labore Lorem. Magna deserunt aliquip aute duis consectetur magna amet anim. Magna fugiat est nostrud veniam. Officia duis ea sunt aliqua.
|
||||
</p>
|
||||
<p>
|
||||
Ipsum minim officia aute anim minim aute aliquip aute non in non. Ipsum aliquip proident ut dolore eiusmod ad fugiat fugiat ut ex. Ea velit Lorem ut et commodo nulla voluptate veniam ea et aliqua esse id. Pariatur dolor et adipisicing ea mollit. Ipsum non irure proident ipsum dolore aliquip adipisicing laborum irure dolor nostrud occaecat exercitation.
|
||||
</p>
|
||||
<p>
|
||||
Culpa qui reprehenderit nostrud aliqua reprehenderit et ullamco proident nisi commodo non ut. Ipsum quis irure nisi sint do qui velit nisi. Sunt voluptate eu reprehenderit tempor consequat eiusmod Lorem irure velit duis Lorem laboris ipsum cupidatat. Pariatur excepteur tempor veniam cillum et nulla ipsum veniam ad ipsum ad aute. Est officia duis pariatur ad eiusmod id voluptate.
|
||||
</p>
|
||||
</div>
|
||||
);`;
|
||||
|
||||
const App = `import {ScrollShadow} from "@nextui-org/react";
|
||||
import {Content} from "./Content";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ScrollShadow className="w-[300px] h-[400px]">
|
||||
<Content />
|
||||
</ScrollShadow>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/Content.jsx": Content,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
187
apps/docs/content/components/select/async-loading-items.ts
Normal file
187
apps/docs/content/components/select/async-loading-items.ts
Normal file
@ -0,0 +1,187 @@
|
||||
const usePokemonListTs = `export type Pokemon = {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type UsePokemonListProps = {
|
||||
/** Delay to wait before fetching more items */
|
||||
fetchDelay?: number;
|
||||
};
|
||||
|
||||
export function usePokemonList({fetchDelay = 0}: UsePokemonListProps = {}) {
|
||||
const [items, setItems] = React.useState<Pokemon[]>([]);
|
||||
const [hasMore, setHasMore] = React.useState(true);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [offset, setOffset] = React.useState(0);
|
||||
const limit = 10; // Number of items per page, adjust as necessary
|
||||
|
||||
const loadPokemon = async (currentOffset: number) => {
|
||||
const controller = new AbortController();
|
||||
const {signal} = controller;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
if (offset > 0) {
|
||||
// Delay to simulate network latency
|
||||
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
||||
}
|
||||
|
||||
let res = await fetch(
|
||||
\`https://pokeapi.co/api/v2/pokemon?offset=\${currentOffset}&limit=\${limit}\`,
|
||||
{signal},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
let json = await res.json();
|
||||
|
||||
setHasMore(json.next !== null);
|
||||
// Append new results to existing ones
|
||||
setItems((prevItems) => [...prevItems, ...json.results]);
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
console.log("Fetch aborted");
|
||||
} else {
|
||||
console.error("There was an error with the fetch operation:", error);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
loadPokemon(offset);
|
||||
}, []);
|
||||
|
||||
const onLoadMore = () => {
|
||||
const newOffset = offset + limit;
|
||||
|
||||
setOffset(newOffset);
|
||||
loadPokemon(newOffset);
|
||||
};
|
||||
|
||||
return {
|
||||
items,
|
||||
hasMore,
|
||||
isLoading,
|
||||
onLoadMore,
|
||||
};
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const usePokemonList = `export function usePokemonList({fetchDelay = 0} = {}) {
|
||||
const [items, setItems] = React.useState([]);
|
||||
const [hasMore, setHasMore] = React.useState(true);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [offset, setOffset] = React.useState(0);
|
||||
const limit = 10; // Number of items per page, adjust as necessary
|
||||
|
||||
const loadPokemon = async (currentOffset) => {
|
||||
const controller = new AbortController();
|
||||
const {signal} = controller;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
if (offset > 0) {
|
||||
// Delay to simulate network latency
|
||||
await new Promise((resolve) => setTimeout(resolve, fetchDelay));
|
||||
}
|
||||
|
||||
let res = await fetch(
|
||||
\`https://pokeapi.co/api/v2/pokemon?offset=\${currentOffset}&limit=\${limit}\`,
|
||||
{signal},
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
let json = await res.json();
|
||||
|
||||
setHasMore(json.next !== null);
|
||||
// Append new results to existing ones
|
||||
setItems((prevItems) => [...prevItems, ...json.results]);
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
console.log("Fetch aborted");
|
||||
} else {
|
||||
console.error("There was an error with the fetch operation:", error);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
loadPokemon(offset);
|
||||
}, []);
|
||||
|
||||
const onLoadMore = () => {
|
||||
const newOffset = offset + limit;
|
||||
|
||||
setOffset(newOffset);
|
||||
loadPokemon(newOffset);
|
||||
};
|
||||
|
||||
return {
|
||||
items,
|
||||
hasMore,
|
||||
isLoading,
|
||||
onLoadMore,
|
||||
};
|
||||
};`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {useInfiniteScroll} from "@nextui-org/use-infinity-scroll";
|
||||
import {usePokemonList} from "./usePokemonList";
|
||||
|
||||
export default function App() {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const {items, hasMore, isLoading, onLoadMore} = usePokemonList({fetchDelay: 1500});
|
||||
|
||||
const [, scrollerRef] = useInfiniteScroll({
|
||||
hasMore,
|
||||
isEnabled: isOpen,
|
||||
shouldUseLoader: false, // We don't want to show the loader at the bottom of the list
|
||||
onLoadMore,
|
||||
});
|
||||
|
||||
return (
|
||||
<Select
|
||||
className="max-w-xs"
|
||||
isLoading={isLoading}
|
||||
items={items}
|
||||
label="Pick a Pokemon"
|
||||
placeholder="Select a Pokemon"
|
||||
scrollRef={scrollerRef}
|
||||
selectionMode="single"
|
||||
onOpenChange={setIsOpen}
|
||||
>
|
||||
{(item) => (
|
||||
<SelectItem key={item.name} className="capitalize">
|
||||
{item.name}
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/usePokemonList.js": usePokemonList,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": App,
|
||||
"/usePokemonList.ts": usePokemonListTs,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
71
apps/docs/content/components/select/colors.ts
Normal file
71
apps/docs/content/components/select/colors.ts
Normal file
@ -0,0 +1,71 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const colors = [
|
||||
"default",
|
||||
"primary",
|
||||
"secondary",
|
||||
"success",
|
||||
"warning",
|
||||
"danger",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row flex-wrap gap-4">
|
||||
{colors.map((color) => (
|
||||
<Select
|
||||
key={color}
|
||||
color={color}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
238
apps/docs/content/components/select/custom-items.ts
Normal file
238
apps/docs/content/components/select/custom-items.ts
Normal file
@ -0,0 +1,238 @@
|
||||
const data = `export const users = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tony Reichert",
|
||||
role: "CEO",
|
||||
team: "Management",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/1.png",
|
||||
email: "tony.reichert@example.com",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Zoey Lang",
|
||||
role: "Tech Lead",
|
||||
team: "Development",
|
||||
status: "paused",
|
||||
age: "25",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/1.png",
|
||||
email: "zoey.lang@example.com",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Jane Fisher",
|
||||
role: "Sr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/2.png",
|
||||
email: "jane.fisher@example.com",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "William Howard",
|
||||
role: "C.M.",
|
||||
team: "Marketing",
|
||||
status: "vacation",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/2.png",
|
||||
email: "william.howard@example.com",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Kristen Copper",
|
||||
role: "S. Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "24",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/3.png",
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/3.png",
|
||||
email: "brian.kim@example.com",
|
||||
status: "Active",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Michael Hunt",
|
||||
role: "Designer",
|
||||
team: "Design",
|
||||
status: "paused",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/4.png",
|
||||
email: "michael.hunt@example.com",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Samantha Brooks",
|
||||
role: "HR Manager",
|
||||
team: "HR",
|
||||
status: "active",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/4.png",
|
||||
email: "samantha.brooks@example.com",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Frank Harrison",
|
||||
role: "F. Manager",
|
||||
team: "Finance",
|
||||
status: "vacation",
|
||||
age: "33",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/5.png",
|
||||
email: "frank.harrison@example.com",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Emma Adams",
|
||||
role: "Ops Manager",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "35",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/5.png",
|
||||
email: "emma.adams@example.com",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Brandon Stevens",
|
||||
role: "Jr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/7.png",
|
||||
email: "brandon.stevens@example.com",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Megan Richards",
|
||||
role: "P. Manager",
|
||||
team: "Product",
|
||||
status: "paused",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/7.png",
|
||||
email: "megan.richards@example.com",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Oliver Scott",
|
||||
role: "S. Manager",
|
||||
team: "Security",
|
||||
status: "active",
|
||||
age: "37",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/8.png",
|
||||
email: "oliver.scott@example.com",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Grace Allen",
|
||||
role: "M. Specialist",
|
||||
team: "Marketing",
|
||||
status: "active",
|
||||
age: "30",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/8.png",
|
||||
email: "grace.allen@example.com",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Noah Carter",
|
||||
role: "IT Specialist",
|
||||
team: "I. Technology",
|
||||
status: "paused",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/9.png",
|
||||
email: "noah.carter@example.com",
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "Ava Perez",
|
||||
role: "Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/9.png",
|
||||
email: "ava.perez@example.com",
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: "Liam Johnson",
|
||||
role: "Data Analyst",
|
||||
team: "Analysis",
|
||||
status: "active",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/11.png",
|
||||
email: "liam.johnson@example.com",
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: "Sophia Taylor",
|
||||
role: "QA Analyst",
|
||||
team: "Testing",
|
||||
status: "active",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/11.png",
|
||||
email: "sophia.taylor@example.com",
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: "Lucas Harris",
|
||||
role: "Administrator",
|
||||
team: "Information Technology",
|
||||
status: "paused",
|
||||
age: "32",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/12.png",
|
||||
email: "lucas.harris@example.com",
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: "Mia Robinson",
|
||||
role: "Coordinator",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "26",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/12.png",
|
||||
email: "mia.robinson@example.com",
|
||||
},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem, Avatar} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
placeholder="Select a user"
|
||||
labelPlacement="outside"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
320
apps/docs/content/components/select/custom-render-value.ts
Normal file
320
apps/docs/content/components/select/custom-render-value.ts
Normal file
@ -0,0 +1,320 @@
|
||||
const data = `export const users = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tony Reichert",
|
||||
role: "CEO",
|
||||
team: "Management",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/1.png",
|
||||
email: "tony.reichert@example.com",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Zoey Lang",
|
||||
role: "Tech Lead",
|
||||
team: "Development",
|
||||
status: "paused",
|
||||
age: "25",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/1.png",
|
||||
email: "zoey.lang@example.com",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Jane Fisher",
|
||||
role: "Sr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/2.png",
|
||||
email: "jane.fisher@example.com",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "William Howard",
|
||||
role: "C.M.",
|
||||
team: "Marketing",
|
||||
status: "vacation",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/2.png",
|
||||
email: "william.howard@example.com",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Kristen Copper",
|
||||
role: "S. Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "24",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/3.png",
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/3.png",
|
||||
email: "brian.kim@example.com",
|
||||
status: "Active",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Michael Hunt",
|
||||
role: "Designer",
|
||||
team: "Design",
|
||||
status: "paused",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/4.png",
|
||||
email: "michael.hunt@example.com",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Samantha Brooks",
|
||||
role: "HR Manager",
|
||||
team: "HR",
|
||||
status: "active",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/4.png",
|
||||
email: "samantha.brooks@example.com",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Frank Harrison",
|
||||
role: "F. Manager",
|
||||
team: "Finance",
|
||||
status: "vacation",
|
||||
age: "33",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/5.png",
|
||||
email: "frank.harrison@example.com",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Emma Adams",
|
||||
role: "Ops Manager",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "35",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/5.png",
|
||||
email: "emma.adams@example.com",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Brandon Stevens",
|
||||
role: "Jr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/7.png",
|
||||
email: "brandon.stevens@example.com",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Megan Richards",
|
||||
role: "P. Manager",
|
||||
team: "Product",
|
||||
status: "paused",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/7.png",
|
||||
email: "megan.richards@example.com",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Oliver Scott",
|
||||
role: "S. Manager",
|
||||
team: "Security",
|
||||
status: "active",
|
||||
age: "37",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/8.png",
|
||||
email: "oliver.scott@example.com",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Grace Allen",
|
||||
role: "M. Specialist",
|
||||
team: "Marketing",
|
||||
status: "active",
|
||||
age: "30",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/8.png",
|
||||
email: "grace.allen@example.com",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Noah Carter",
|
||||
role: "IT Specialist",
|
||||
team: "I. Technology",
|
||||
status: "paused",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/9.png",
|
||||
email: "noah.carter@example.com",
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "Ava Perez",
|
||||
role: "Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/9.png",
|
||||
email: "ava.perez@example.com",
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: "Liam Johnson",
|
||||
role: "Data Analyst",
|
||||
team: "Analysis",
|
||||
status: "active",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/11.png",
|
||||
email: "liam.johnson@example.com",
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: "Sophia Taylor",
|
||||
role: "QA Analyst",
|
||||
team: "Testing",
|
||||
status: "active",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/11.png",
|
||||
email: "sophia.taylor@example.com",
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: "Lucas Harris",
|
||||
role: "Administrator",
|
||||
team: "Information Technology",
|
||||
status: "paused",
|
||||
age: "32",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/12.png",
|
||||
email: "lucas.harris@example.com",
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: "Mia Robinson",
|
||||
role: "Coordinator",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "26",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/12.png",
|
||||
email: "mia.robinson@example.com",
|
||||
},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem, Avatar} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
placeholder="Select a user"
|
||||
labelPlacement="outside"
|
||||
classNames={{
|
||||
base: "max-w-xs",
|
||||
trigger: "h-12",
|
||||
}}
|
||||
renderValue={(items) => {
|
||||
return items.map((item) => (
|
||||
<div key={item.key} className="flex items-center gap-2">
|
||||
<Avatar
|
||||
alt={item.data.name}
|
||||
className="flex-shrink-0"
|
||||
size="sm"
|
||||
src={item.data.avatar}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span>{item.data.name}</span>
|
||||
<span className="text-default-500 text-tiny">({item.data.email})</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}}
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Avatar, SelectedItems} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
type User = {
|
||||
id: number;
|
||||
name: string;
|
||||
role: string;
|
||||
team: string;
|
||||
status: string;
|
||||
age: string;
|
||||
avatar: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
placeholder="Select a user"
|
||||
labelPlacement="outside"
|
||||
classNames={{
|
||||
base: "max-w-xs",
|
||||
trigger: "h-12",
|
||||
}}
|
||||
renderValue={(items: SelectedItems<User>) => {
|
||||
return items.map((item) => (
|
||||
<div key={item.key} className="flex items-center gap-2">
|
||||
<Avatar
|
||||
alt={item.data.name}
|
||||
className="flex-shrink-0"
|
||||
size="sm"
|
||||
src={item.data.avatar}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span>{item.data.name}</span>
|
||||
<span className="text-default-500 text-tiny">({item.data.email})</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}}
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.ts": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
55
apps/docs/content/components/select/custom-sections-style.ts
Normal file
55
apps/docs/content/components/select/custom-sections-style.ts
Normal file
@ -0,0 +1,55 @@
|
||||
const App = `import {Select, SelectItem, SelectSection} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
const headingClasses = "flex w-full sticky top-1 z-20 py-1.5 px-2 bg-default-100 shadow-small rounded-small";
|
||||
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
scrollShadowProps={{
|
||||
isEnabled: false,
|
||||
}}
|
||||
>
|
||||
<SelectSection
|
||||
title="Mammals"
|
||||
classNames={{
|
||||
heading: headingClasses,
|
||||
}}
|
||||
>
|
||||
<SelectItem key="Lion">Lion</SelectItem>
|
||||
<SelectItem key="Tiger">Tiger</SelectItem>
|
||||
<SelectItem key="Elephant">Elephant</SelectItem>
|
||||
<SelectItem key="Kangaroo">Kangaroo</SelectItem>
|
||||
<SelectItem key="Panda">Panda</SelectItem>
|
||||
<SelectItem key="Giraffe">Giraffe</SelectItem>
|
||||
<SelectItem key="Zebra">Zebra</SelectItem>
|
||||
<SelectItem key="Cheetah">Cheetah</SelectItem>
|
||||
</SelectSection>
|
||||
<SelectSection
|
||||
title="Birds"
|
||||
classNames={{
|
||||
heading: headingClasses,
|
||||
}}
|
||||
>
|
||||
<SelectItem key="Eagle">Eagle</SelectItem>
|
||||
<SelectItem key="Parrot">Parrot</SelectItem>
|
||||
<SelectItem key="Penguin">Penguin</SelectItem>
|
||||
<SelectItem key="Ostrich">Ostrich</SelectItem>
|
||||
<SelectItem key="Peacock">Peacock</SelectItem>
|
||||
<SelectItem key="Swan">Swan</SelectItem>
|
||||
<SelectItem key="Falcon">Falcon</SelectItem>
|
||||
<SelectItem key="Flamingo">Flamingo</SelectItem>
|
||||
</SelectSection>
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
81
apps/docs/content/components/select/custom-selector-icon.ts
Normal file
81
apps/docs/content/components/select/custom-selector-icon.ts
Normal file
@ -0,0 +1,81 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const SelectorIcon = `export const SelectorIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path d="M0 0h24v24H0z" fill="none" stroke="none" />
|
||||
<path d="M8 9l4 -4l4 4" />
|
||||
<path d="M16 15l-4 4l-4 -4" />
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {SelectorIcon} from "./SelectorIcon";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
labelPlacement="outside"
|
||||
className="max-w-xs"
|
||||
disableSelectorIconRotation
|
||||
selectorIcon={<SelectorIcon />}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
"/SelectorIcon.jsx": SelectorIcon,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
279
apps/docs/content/components/select/custom-styles.ts
Normal file
279
apps/docs/content/components/select/custom-styles.ts
Normal file
@ -0,0 +1,279 @@
|
||||
const data = `export const users = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tony Reichert",
|
||||
role: "CEO",
|
||||
team: "Management",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/1.png",
|
||||
email: "tony.reichert@example.com",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Zoey Lang",
|
||||
role: "Tech Lead",
|
||||
team: "Development",
|
||||
status: "paused",
|
||||
age: "25",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/1.png",
|
||||
email: "zoey.lang@example.com",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Jane Fisher",
|
||||
role: "Sr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/2.png",
|
||||
email: "jane.fisher@example.com",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "William Howard",
|
||||
role: "C.M.",
|
||||
team: "Marketing",
|
||||
status: "vacation",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/2.png",
|
||||
email: "william.howard@example.com",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Kristen Copper",
|
||||
role: "S. Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "24",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/3.png",
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/3.png",
|
||||
email: "brian.kim@example.com",
|
||||
status: "Active",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Michael Hunt",
|
||||
role: "Designer",
|
||||
team: "Design",
|
||||
status: "paused",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/4.png",
|
||||
email: "michael.hunt@example.com",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Samantha Brooks",
|
||||
role: "HR Manager",
|
||||
team: "HR",
|
||||
status: "active",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/4.png",
|
||||
email: "samantha.brooks@example.com",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Frank Harrison",
|
||||
role: "F. Manager",
|
||||
team: "Finance",
|
||||
status: "vacation",
|
||||
age: "33",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/5.png",
|
||||
email: "frank.harrison@example.com",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Emma Adams",
|
||||
role: "Ops Manager",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "35",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/5.png",
|
||||
email: "emma.adams@example.com",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Brandon Stevens",
|
||||
role: "Jr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/7.png",
|
||||
email: "brandon.stevens@example.com",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Megan Richards",
|
||||
role: "P. Manager",
|
||||
team: "Product",
|
||||
status: "paused",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/7.png",
|
||||
email: "megan.richards@example.com",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Oliver Scott",
|
||||
role: "S. Manager",
|
||||
team: "Security",
|
||||
status: "active",
|
||||
age: "37",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/8.png",
|
||||
email: "oliver.scott@example.com",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Grace Allen",
|
||||
role: "M. Specialist",
|
||||
team: "Marketing",
|
||||
status: "active",
|
||||
age: "30",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/8.png",
|
||||
email: "grace.allen@example.com",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Noah Carter",
|
||||
role: "IT Specialist",
|
||||
team: "I. Technology",
|
||||
status: "paused",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/9.png",
|
||||
email: "noah.carter@example.com",
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "Ava Perez",
|
||||
role: "Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/9.png",
|
||||
email: "ava.perez@example.com",
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: "Liam Johnson",
|
||||
role: "Data Analyst",
|
||||
team: "Analysis",
|
||||
status: "active",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/11.png",
|
||||
email: "liam.johnson@example.com",
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: "Sophia Taylor",
|
||||
role: "QA Analyst",
|
||||
team: "Testing",
|
||||
status: "active",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/11.png",
|
||||
email: "sophia.taylor@example.com",
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: "Lucas Harris",
|
||||
role: "Administrator",
|
||||
team: "Information Technology",
|
||||
status: "paused",
|
||||
age: "32",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/12.png",
|
||||
email: "lucas.harris@example.com",
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: "Mia Robinson",
|
||||
role: "Coordinator",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "26",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/12.png",
|
||||
email: "mia.robinson@example.com",
|
||||
},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem, Avatar} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
className="max-w-xs"
|
||||
variant="bordered"
|
||||
classNames={{
|
||||
label: "group-data-[filled=true]:-translate-y-5",
|
||||
trigger: "min-h-unit-16",
|
||||
listboxWrapper: "max-h-[400px]",
|
||||
}}
|
||||
listboxProps={{
|
||||
itemClasses: {
|
||||
base: [
|
||||
"rounded-md",
|
||||
"text-default-500",
|
||||
"transition-opacity",
|
||||
"data-[hover=true]:text-foreground",
|
||||
"data-[hover=true]:bg-default-100",
|
||||
"dark:data-[hover=true]:bg-default-50",
|
||||
"data-[selectable=true]:focus:bg-default-50",
|
||||
"data-[pressed=true]:opacity-70",
|
||||
"data-[focus-visible=true]:ring-default-500",
|
||||
],
|
||||
},
|
||||
}}
|
||||
popoverProps={{
|
||||
classNames: {
|
||||
base: "p-0 border-small border-divider bg-background",
|
||||
arrow: "bg-default-200",
|
||||
},
|
||||
}}
|
||||
renderValue={(items) => {
|
||||
return items.map((item) => (
|
||||
<div key={item.key} className="flex items-center gap-2">
|
||||
<Avatar
|
||||
alt={item.data.name}
|
||||
className="flex-shrink-0"
|
||||
size="sm"
|
||||
src={item.data.avatar}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span>{item.data.name}</span>
|
||||
<span className="text-default-500 text-tiny">({item.data.email})</span>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
}}
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
57
apps/docs/content/components/select/description.ts
Normal file
57
apps/docs/content/components/select/description.ts
Normal file
@ -0,0 +1,57 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
description="The second most popular pet in the world"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
56
apps/docs/content/components/select/disabled-items.ts
Normal file
56
apps/docs/content/components/select/disabled-items.ts
Normal file
@ -0,0 +1,56 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
disabledKeys={["zebra", "tiger", "lion", "elephant", "crocodile", "whale"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
57
apps/docs/content/components/select/disabled.ts
Normal file
57
apps/docs/content/components/select/disabled.ts
Normal file
@ -0,0 +1,57 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
isDisabled
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
52
apps/docs/content/components/select/dynamic.ts
Normal file
52
apps/docs/content/components/select/dynamic.ts
Normal file
@ -0,0 +1,52 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={animals}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{(animal) => <SelectItem key={animal.value}>{animal.label}</SelectItem>}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
67
apps/docs/content/components/select/error-message.ts
Normal file
67
apps/docs/content/components/select/error-message.ts
Normal file
@ -0,0 +1,67 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState(new Set([]));
|
||||
const [touched, setTouched] = React.useState(false);
|
||||
|
||||
const isValid = value.has("cat");
|
||||
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
placeholder="Select an animal"
|
||||
description="The second most popular pet in the world"
|
||||
errorMessage={isValid || !touched ? "" : "You must select a cat"}
|
||||
validationState={isValid || !touched ? "valid" : "invalid"}
|
||||
selectedKeys={value}
|
||||
className="max-w-xs"
|
||||
onSelectionChange={setValue}
|
||||
onClose={() => setTouched(true)}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
61
apps/docs/content/components/select/index.ts
Normal file
61
apps/docs/content/components/select/index.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import usage from "./usage";
|
||||
import dynamic from "./dynamic";
|
||||
import disabled from "./disabled";
|
||||
import disabledItems from "./disabled-items";
|
||||
import required from "./required";
|
||||
import sizes from "./sizes";
|
||||
import colors from "./colors";
|
||||
import variants from "./variants";
|
||||
import radius from "./radius";
|
||||
import labelPlacements from "./label-placements";
|
||||
import startContent from "./start-content";
|
||||
import itemStartContent from "./item-start-content";
|
||||
import asyncLoadingItems from "./async-loading-items";
|
||||
import withoutScrollShadow from "./without-scroll-shadow";
|
||||
import description from "./description";
|
||||
import errorMessage from "./error-message";
|
||||
import singleControlled from "./single-controlled";
|
||||
import singleControlledOnChange from "./single-controlled-onchange";
|
||||
import openState from "./open-state";
|
||||
import customItems from "./custom-items";
|
||||
import customRenderValue from "./custom-render-value";
|
||||
import sections from "./sections";
|
||||
import customSectionsStyle from "./custom-sections-style";
|
||||
import multiple from "./multiple";
|
||||
import multipleControlled from "./multiple-controlled";
|
||||
import multipleControlledOnChange from "./multiple-controlled-onchange";
|
||||
import multipleWithChips from "./multiple-chips";
|
||||
import customSelectorIcon from "./custom-selector-icon";
|
||||
import customStyles from "./custom-styles";
|
||||
|
||||
export const selectContent = {
|
||||
usage,
|
||||
dynamic,
|
||||
disabled,
|
||||
disabledItems,
|
||||
required,
|
||||
sizes,
|
||||
colors,
|
||||
variants,
|
||||
radius,
|
||||
labelPlacements,
|
||||
asyncLoadingItems,
|
||||
description,
|
||||
startContent,
|
||||
itemStartContent,
|
||||
withoutScrollShadow,
|
||||
errorMessage,
|
||||
singleControlled,
|
||||
singleControlledOnChange,
|
||||
openState,
|
||||
customItems,
|
||||
customRenderValue,
|
||||
sections,
|
||||
customSectionsStyle,
|
||||
multiple,
|
||||
multipleControlled,
|
||||
multipleControlledOnChange,
|
||||
multipleWithChips,
|
||||
customSelectorIcon,
|
||||
customStyles,
|
||||
};
|
||||
75
apps/docs/content/components/select/item-start-content.ts
Normal file
75
apps/docs/content/components/select/item-start-content.ts
Normal file
@ -0,0 +1,75 @@
|
||||
const App = `import {Select, SelectItem, Avatar} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
className="max-w-xs"
|
||||
label="Select country"
|
||||
>
|
||||
<SelectItem
|
||||
key="argentina"
|
||||
startContent={<Avatar alt="Argentina" className="w-6 h-6" src="https://flagcdn.com/ar.svg" />}
|
||||
>
|
||||
Argentina
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="venezuela"
|
||||
startContent={<Avatar alt="Venezuela" className="w-6 h-6" src="https://flagcdn.com/ve.svg" />}
|
||||
>
|
||||
Venezuela
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="brazil"
|
||||
startContent={<Avatar alt="Brazil" className="w-6 h-6" src="https://flagcdn.com/br.svg" />}
|
||||
>
|
||||
Brazil
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="switzerland"
|
||||
startContent={
|
||||
<Avatar alt="Switzerland" className="w-6 h-6" src="https://flagcdn.com/ch.svg" />
|
||||
}
|
||||
>
|
||||
Switzerland
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="germany"
|
||||
startContent={<Avatar alt="Germany" className="w-6 h-6" src="https://flagcdn.com/de.svg" />}
|
||||
>
|
||||
Germany
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="spain"
|
||||
startContent={<Avatar alt="Spain" className="w-6 h-6" src="https://flagcdn.com/es.svg" />}
|
||||
>
|
||||
Spain
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="france"
|
||||
startContent={<Avatar alt="France" className="w-6 h-6" src="https://flagcdn.com/fr.svg" />}
|
||||
>
|
||||
France
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="italy"
|
||||
startContent={<Avatar alt="Italy" className="w-6 h-6" src="https://flagcdn.com/it.svg" />}
|
||||
>
|
||||
Italy
|
||||
</SelectItem>
|
||||
<SelectItem
|
||||
key="mexico"
|
||||
startContent={<Avatar alt="Mexico" className="w-6 h-6" src="https://flagcdn.com/mx.svg" />}
|
||||
>
|
||||
Mexico
|
||||
</SelectItem>
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
89
apps/docs/content/components/select/label-placements.ts
Normal file
89
apps/docs/content/components/select/label-placements.ts
Normal file
@ -0,0 +1,89 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const placements = [
|
||||
"inside",
|
||||
"outside",
|
||||
"outside-left",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="text-default-500 text-small">Without placeholder</h3>
|
||||
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
{placements.map((placement) => (
|
||||
<Select
|
||||
labelPlacement={placement}
|
||||
label="Favorite Animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="text-default-500 text-small">With placeholder</h3>
|
||||
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
{placements.map((placement) => (
|
||||
<Select
|
||||
labelPlacement={placement}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
312
apps/docs/content/components/select/multiple-chips.ts
Normal file
312
apps/docs/content/components/select/multiple-chips.ts
Normal file
@ -0,0 +1,312 @@
|
||||
const data = `export const users = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Tony Reichert",
|
||||
role: "CEO",
|
||||
team: "Management",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/1.png",
|
||||
email: "tony.reichert@example.com",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Zoey Lang",
|
||||
role: "Tech Lead",
|
||||
team: "Development",
|
||||
status: "paused",
|
||||
age: "25",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/1.png",
|
||||
email: "zoey.lang@example.com",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Jane Fisher",
|
||||
role: "Sr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/2.png",
|
||||
email: "jane.fisher@example.com",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "William Howard",
|
||||
role: "C.M.",
|
||||
team: "Marketing",
|
||||
status: "vacation",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/2.png",
|
||||
email: "william.howard@example.com",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Kristen Copper",
|
||||
role: "S. Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "24",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/3.png",
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/3.png",
|
||||
email: "brian.kim@example.com",
|
||||
status: "Active",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Michael Hunt",
|
||||
role: "Designer",
|
||||
team: "Design",
|
||||
status: "paused",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/4.png",
|
||||
email: "michael.hunt@example.com",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "Samantha Brooks",
|
||||
role: "HR Manager",
|
||||
team: "HR",
|
||||
status: "active",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/4.png",
|
||||
email: "samantha.brooks@example.com",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Frank Harrison",
|
||||
role: "F. Manager",
|
||||
team: "Finance",
|
||||
status: "vacation",
|
||||
age: "33",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/5.png",
|
||||
email: "frank.harrison@example.com",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "Emma Adams",
|
||||
role: "Ops Manager",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "35",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/5.png",
|
||||
email: "emma.adams@example.com",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Brandon Stevens",
|
||||
role: "Jr. Dev",
|
||||
team: "Development",
|
||||
status: "active",
|
||||
age: "22",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/7.png",
|
||||
email: "brandon.stevens@example.com",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Megan Richards",
|
||||
role: "P. Manager",
|
||||
team: "Product",
|
||||
status: "paused",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/7.png",
|
||||
email: "megan.richards@example.com",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Oliver Scott",
|
||||
role: "S. Manager",
|
||||
team: "Security",
|
||||
status: "active",
|
||||
age: "37",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/8.png",
|
||||
email: "oliver.scott@example.com",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Grace Allen",
|
||||
role: "M. Specialist",
|
||||
team: "Marketing",
|
||||
status: "active",
|
||||
age: "30",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/8.png",
|
||||
email: "grace.allen@example.com",
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Noah Carter",
|
||||
role: "IT Specialist",
|
||||
team: "I. Technology",
|
||||
status: "paused",
|
||||
age: "31",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/9.png",
|
||||
email: "noah.carter@example.com",
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "Ava Perez",
|
||||
role: "Manager",
|
||||
team: "Sales",
|
||||
status: "active",
|
||||
age: "29",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/9.png",
|
||||
email: "ava.perez@example.com",
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: "Liam Johnson",
|
||||
role: "Data Analyst",
|
||||
team: "Analysis",
|
||||
status: "active",
|
||||
age: "28",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/11.png",
|
||||
email: "liam.johnson@example.com",
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: "Sophia Taylor",
|
||||
role: "QA Analyst",
|
||||
team: "Testing",
|
||||
status: "active",
|
||||
age: "27",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/11.png",
|
||||
email: "sophia.taylor@example.com",
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: "Lucas Harris",
|
||||
role: "Administrator",
|
||||
team: "Information Technology",
|
||||
status: "paused",
|
||||
age: "32",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/male/12.png",
|
||||
email: "lucas.harris@example.com",
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: "Mia Robinson",
|
||||
role: "Coordinator",
|
||||
team: "Operations",
|
||||
status: "active",
|
||||
age: "26",
|
||||
avatar: "https://d2u8k2ocievbld.cloudfront.net/memojis/female/12.png",
|
||||
email: "mia.robinson@example.com",
|
||||
},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem, Avatar, Chip} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
variant="bordered"
|
||||
isMultiline={true}
|
||||
selectionMode="multiple"
|
||||
placeholder="Select a user"
|
||||
labelPlacement="outside"
|
||||
classNames={{
|
||||
base: "max-w-xs",
|
||||
trigger: "min-h-unit-12 py-2",
|
||||
}}
|
||||
renderValue={(items) => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{items.map((item) => (
|
||||
<Chip key={item.key}>{item.data.name}</Chip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Avatar, Chip, SelectedItems} from "@nextui-org/react";
|
||||
import {users} from "./data";
|
||||
|
||||
type User = {
|
||||
id: number;
|
||||
name: string;
|
||||
role: string;
|
||||
team: string;
|
||||
status: string;
|
||||
age: string;
|
||||
avatar: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
items={users}
|
||||
label="Assigned to"
|
||||
variant="bordered"
|
||||
isMultiline={true}
|
||||
selectionMode="multiple"
|
||||
placeholder="Select a user"
|
||||
labelPlacement="outside"
|
||||
classNames={{
|
||||
base: "max-w-xs",
|
||||
trigger: "min-h-unit-12 py-2",
|
||||
}}
|
||||
renderValue={(items: SelectedItems<User>) => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{items.map((item) => (
|
||||
<Chip key={item.key}>{item.data.name}</Chip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(user) => (
|
||||
<SelectItem key={user.id} textValue={user.name}>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
|
||||
<div className="flex flex-col">
|
||||
<span className="text-small">{user.name}</span>
|
||||
<span className="text-tiny text-default-400">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
@ -0,0 +1,104 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [values, setValues] = React.useState(new Set([]));
|
||||
|
||||
const handleSelectionChange = (e) => {
|
||||
setValues(new Set(e.target.value.split(",")));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
selectionMode="multiple"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={values}
|
||||
className="max-w-xs"
|
||||
onChange={handleSelectionChange}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {Array.from(values).join(", ")}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Selection} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [values, setValues] = React.useState<Selection>(new Set([]));
|
||||
|
||||
const handleSelectionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setValues(new Set(e.target.value.split(",")));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
selectionMode="multiple"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={values}
|
||||
className="max-w-xs"
|
||||
onChange={handleSelectionChange}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {Array.from(values).join(", ")}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.ts": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
96
apps/docs/content/components/select/multiple-controlled.ts
Normal file
96
apps/docs/content/components/select/multiple-controlled.ts
Normal file
@ -0,0 +1,96 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [values, setValues] = React.useState(new Set(["cat", "dog"]));
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
selectionMode="multiple"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={values}
|
||||
className="max-w-xs"
|
||||
onSelectionChange={setValues}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {Array.from(values).join(", ")}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Selection} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [values, setValues] = React.useState<Selection>(new Set(["cat", "dog"]));
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
selectionMode="multiple"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={values}
|
||||
className="max-w-xs"
|
||||
onSelectionChange={setValues}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {Array.from(values).join(", ")}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.ts": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
56
apps/docs/content/components/select/multiple.ts
Normal file
56
apps/docs/content/components/select/multiple.ts
Normal file
@ -0,0 +1,56 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
selectionMode="multiple"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
64
apps/docs/content/components/select/open-state.ts
Normal file
64
apps/docs/content/components/select/open-state.ts
Normal file
@ -0,0 +1,64 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem, Button} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs items-center gap-2">
|
||||
<Select
|
||||
isOpen={isOpen}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<Button onPress={() => setIsOpen(!isOpen)}>
|
||||
{isOpen ? "Close" : "Open"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
70
apps/docs/content/components/select/radius.ts
Normal file
70
apps/docs/content/components/select/radius.ts
Normal file
@ -0,0 +1,70 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const radius = [
|
||||
"full",
|
||||
"lg",
|
||||
"md",
|
||||
"sm",
|
||||
"none",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row flex-wrap gap-4">
|
||||
{radius.map((r) => (
|
||||
<Select
|
||||
key={r}
|
||||
radius={r}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-[45%]"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
57
apps/docs/content/components/select/required.ts
Normal file
57
apps/docs/content/components/select/required.ts
Normal file
@ -0,0 +1,57 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
isRequired
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
40
apps/docs/content/components/select/sections.ts
Normal file
40
apps/docs/content/components/select/sections.ts
Normal file
@ -0,0 +1,40 @@
|
||||
const App = `import {Select, SelectItem, SelectSection} from "@nextui-org/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
<SelectSection showDivider title="Mammals">
|
||||
<SelectItem key="Lion">Lion</SelectItem>
|
||||
<SelectItem key="Tiger">Tiger</SelectItem>
|
||||
<SelectItem key="Elephant">Elephant</SelectItem>
|
||||
<SelectItem key="Kangaroo">Kangaroo</SelectItem>
|
||||
<SelectItem key="Panda">Panda</SelectItem>
|
||||
<SelectItem key="Giraffe">Giraffe</SelectItem>
|
||||
<SelectItem key="Zebra">Zebra</SelectItem>
|
||||
<SelectItem key="Cheetah">Cheetah</SelectItem>
|
||||
</SelectSection>
|
||||
<SelectSection title="Birds">
|
||||
<SelectItem key="Eagle">Eagle</SelectItem>
|
||||
<SelectItem key="Parrot">Parrot</SelectItem>
|
||||
<SelectItem key="Penguin">Penguin</SelectItem>
|
||||
<SelectItem key="Ostrich">Ostrich</SelectItem>
|
||||
<SelectItem key="Peacock">Peacock</SelectItem>
|
||||
<SelectItem key="Swan">Swan</SelectItem>
|
||||
<SelectItem key="Falcon">Falcon</SelectItem>
|
||||
<SelectItem key="Flamingo">Flamingo</SelectItem>
|
||||
</SelectSection>
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@ -0,0 +1,104 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState(new Set([]));
|
||||
|
||||
const handleSelectionChange = (e) => {
|
||||
setValue(new Set([e.target.value]));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={value}
|
||||
className="max-w-xs"
|
||||
onChange={handleSelectionChange}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {value}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Selection} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState<Selection>(new Set([]));
|
||||
|
||||
const handleSelectionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setValue(new Set([e.target.value]));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={value}
|
||||
className="max-w-xs"
|
||||
onChange={handleSelectionChange}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {value}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
96
apps/docs/content/components/select/single-controlled.ts
Normal file
96
apps/docs/content/components/select/single-controlled.ts
Normal file
@ -0,0 +1,96 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const AppTs = `import {Select, SelectItem, Selection} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState<Selection>(new Set([]));
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={value}
|
||||
className="max-w-xs"
|
||||
onSelectionChange={setValue}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-default-500 text-small">Selected: {value}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const [value, setValue] = React.useState(new Set([]));
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
variant="bordered"
|
||||
placeholder="Select an animal"
|
||||
selectedKeys={value}
|
||||
className="max-w-xs"
|
||||
onSelectionChange={setValue}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<p className="text-small text-default-500">Selected: {value}</p>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
const reactTs = {
|
||||
"/App.tsx": AppTs,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
...reactTs,
|
||||
};
|
||||
75
apps/docs/content/components/select/sizes.ts
Normal file
75
apps/docs/content/components/select/sizes.ts
Normal file
@ -0,0 +1,75 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const sizes = ["sm", "md", "lg"];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-4">
|
||||
{sizes.map((size) => (
|
||||
<div key={size} className="flex w-full flex-wrap md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
<Select
|
||||
size={size}
|
||||
label="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
size={size}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
93
apps/docs/content/components/select/start-content.ts
Normal file
93
apps/docs/content/components/select/start-content.ts
Normal file
@ -0,0 +1,93 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const PetIcon = `export const PetIcon = (props) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M19.0803 15.7203C18.4903 12.1903 15.1003 9.32031 11.5203 9.32031C7.63028 9.32031 4.21028 12.4703 3.88028 16.3503C3.75028 17.8503 4.23028 19.2703 5.22028 20.3403C6.20028 21.4103 7.58028 22.0003 9.08028 22.0003H13.7603C15.4503 22.0003 16.9303 21.3403 17.9403 20.1503C18.9503 18.9603 19.3503 17.3803 19.0803 15.7203Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M10.2796 7.86C11.8978 7.86 13.2096 6.54819 13.2096 4.93C13.2096 3.31181 11.8978 2 10.2796 2C8.66141 2 7.34961 3.31181 7.34961 4.93C7.34961 6.54819 8.66141 7.86 10.2796 7.86Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M16.94 9.02844C18.2876 9.02844 19.38 7.93601 19.38 6.58844C19.38 5.24086 18.2876 4.14844 16.94 4.14844C15.5924 4.14844 14.5 5.24086 14.5 6.58844C14.5 7.93601 15.5924 9.02844 16.94 9.02844Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M20.5496 12.9313C21.6266 12.9313 22.4996 12.0582 22.4996 10.9812C22.4996 9.90429 21.6266 9.03125 20.5496 9.03125C19.4727 9.03125 18.5996 9.90429 18.5996 10.9812C18.5996 12.0582 19.4727 12.9313 20.5496 12.9313Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M3.94 10.9816C5.28757 10.9816 6.38 9.88914 6.38 8.54156C6.38 7.19399 5.28757 6.10156 3.94 6.10156C2.59243 6.10156 1.5 7.19399 1.5 8.54156C1.5 9.88914 2.59243 10.9816 3.94 10.9816Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {PetIcon} from "./PetIcon";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
startContent={<PetIcon />}
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/PetIcon.jsx": PetIcon,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
67
apps/docs/content/components/select/usage.ts
Normal file
67
apps/docs/content/components/select/usage.ts
Normal file
@ -0,0 +1,67 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<Select
|
||||
label="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
75
apps/docs/content/components/select/variants.ts
Normal file
75
apps/docs/content/components/select/variants.ts
Normal file
@ -0,0 +1,75 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
const variants = ["flat", "bordered", "underlined", "faded"];
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-4">
|
||||
{variants.map((variant) => (
|
||||
<div key={variant} className="flex w-full flex-wrap md:flex-nowrap mb-6 md:mb-0 gap-4">
|
||||
<Select
|
||||
variant={variant}
|
||||
label="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
variant={variant}
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
className="max-w-xs"
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
59
apps/docs/content/components/select/without-scroll-shadow.ts
Normal file
59
apps/docs/content/components/select/without-scroll-shadow.ts
Normal file
@ -0,0 +1,59 @@
|
||||
const data = `export const animals = [
|
||||
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
|
||||
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
|
||||
{label: "Elephant", value: "elephant", description: "The largest land animal"},
|
||||
{label: "Lion", value: "lion", description: "The king of the jungle"},
|
||||
{label: "Tiger", value: "tiger", description: "The largest cat species"},
|
||||
{label: "Giraffe", value: "giraffe", description: "The tallest land animal"},
|
||||
{
|
||||
label: "Dolphin",
|
||||
value: "dolphin",
|
||||
description: "A widely distributed and diverse group of aquatic mammals",
|
||||
},
|
||||
{label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"},
|
||||
{label: "Zebra", value: "zebra", description: "A several species of African equids"},
|
||||
{
|
||||
label: "Shark",
|
||||
value: "shark",
|
||||
description: "A group of elasmobranch fish characterized by a cartilaginous skeleton",
|
||||
},
|
||||
{
|
||||
label: "Whale",
|
||||
value: "whale",
|
||||
description: "Diverse group of fully aquatic placental marine mammals",
|
||||
},
|
||||
{label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"},
|
||||
{label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"},
|
||||
];`;
|
||||
|
||||
const App = `import {Select, SelectItem} from "@nextui-org/react";
|
||||
import {animals} from "./data";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Select
|
||||
label="Favorite Animal"
|
||||
placeholder="Select an animal"
|
||||
defaultSelectedKeys={["cat"]}
|
||||
className="max-w-xs"
|
||||
scrollShadowProps={{
|
||||
isEnabled: false
|
||||
}}
|
||||
>
|
||||
{animals.map((animal) => (
|
||||
<SelectItem key={animal.value} value={animal.value}>
|
||||
{animal.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}`;
|
||||
|
||||
const react = {
|
||||
"/App.jsx": App,
|
||||
"/data.js": data,
|
||||
};
|
||||
|
||||
export default {
|
||||
...react,
|
||||
};
|
||||
@ -8,7 +8,7 @@ export default function App() {
|
||||
<Switch isSelected={isSelected} onValueChange={setIsSelected}>
|
||||
Airplane mode
|
||||
</Switch>
|
||||
<p className="text-default-500">Selected: {isSelected ? "true" : "false"}</p>
|
||||
<p className="text-small text-default-500">Selected: {isSelected ? "true" : "false"}</p>
|
||||
</div>
|
||||
)
|
||||
}`;
|
||||
|
||||
@ -67,7 +67,7 @@ const users = [
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
|
||||
@ -67,7 +67,7 @@ const users = [
|
||||
email: "kristen.cooper@example.com",
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
id: 6,
|
||||
name: "Brian Kim",
|
||||
role: "P. Manager",
|
||||
team: "Management",
|
||||
|
||||
@ -143,7 +143,7 @@ Here's an example of how to customize the accordion styles:
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`AccordionItem` has the following attributes on the `root` element:
|
||||
`AccordionItem` has the following attributes on the `base` element:
|
||||
|
||||
- **data-open**:
|
||||
Whether the accordion item is open.
|
||||
@ -164,7 +164,7 @@ Here's an example of how to customize the accordion styles:
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Keyboard event support for <kbd>Space</kbd>, <kbd>Enter</kbd>, <kbd>Arrow Up</kbd>, <kbd>Arrow Down</kbd> and <kbd>Home</kbd> / <kbd>End</kbd> keys.
|
||||
- Keyboard event support for <Kbd>Space</Kbd>, <Kbd>Enter</Kbd>, <Kbd>Arrow Up</Kbd>, <Kbd>Arrow Down</Kbd> and <Kbd>Home</Kbd> / <Kbd>End</Kbd> keys.
|
||||
- Keyboard focus management and cross browser normalization.
|
||||
- `aria-expanded` attribute for the accordion item.
|
||||
- `aria-disabled` attribute for the accordion item.
|
||||
|
||||
@ -140,7 +140,7 @@ You can customize any part of the avatar by using the `classNames` prop, each `s
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Avatar` has the following attributes on the `root` element:
|
||||
`Avatar` has the following attributes on the `base` element:
|
||||
|
||||
- **data-hover**:
|
||||
When the avatar is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
|
||||
@ -112,7 +112,7 @@ A common use case for the `ButtonGroup` component is to display a group of two b
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Button` has the following attributes on the `root` element:
|
||||
`Button` has the following attributes on the `base` element:
|
||||
|
||||
- **data-hover**:
|
||||
When the button is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
@ -132,7 +132,7 @@ A common use case for the `ButtonGroup` component is to display a group of two b
|
||||
## Accessibility
|
||||
|
||||
- Button has role of `button`.
|
||||
- Keyboard event support for <kbd>Space</kbd> and <kbd>Enter</kbd> keys.
|
||||
- Keyboard event support for <Kbd>Space</Kbd> and <Kbd>Enter</Kbd> keys.
|
||||
- Mouse and touch event handling, and press state management.
|
||||
- Keyboard focus management and cross browser normalization.
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ You can use `Image` component as the cover of the card by taking it out of the `
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Card` has the following attributes on the `root` element:
|
||||
`Card` has the following attributes on the `base` element:
|
||||
|
||||
- **data-hover**:
|
||||
When the card is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
|
||||
@ -90,7 +90,7 @@ In case you need to customize the checkbox even further, you can use the `useChe
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Checkbox` has the following attributes on the `root` element:
|
||||
`Checkbox` has the following attributes on the `base` element:
|
||||
|
||||
- **data-selected**:
|
||||
When the checkbox is checked. Based on `isSelected` prop.
|
||||
@ -120,7 +120,7 @@ In case you need to customize the checkbox even further, you can use the `useChe
|
||||
- Built with a native HTML `<input>` element.
|
||||
- Full support for browser features like form autofill.
|
||||
- Keyboard focus management and cross browser normalization.
|
||||
- Keyboard event support for <kbd>Tab</kbd> and <kbd>Space</kbd> keys.
|
||||
- Keyboard event support for <Kbd>Tab</Kbd> and <Kbd>Space</Kbd> keys.
|
||||
- Labeling support for assistive technology.
|
||||
- Indeterminate state support.
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ You can customize the `CircularProgress` component by passing custom Tailwind CS
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`CircularProgress` has the following attributes on the `root` element:
|
||||
`CircularProgress` has the following attributes on the `base` element:
|
||||
|
||||
- **data-indeterminate**:
|
||||
Indicates whether the progress is indeterminate.
|
||||
|
||||
@ -32,7 +32,7 @@ Divider is a component that separates content in a page.
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Divider` has the following attributes on the `root` element:
|
||||
`Divider` has the following attributes on the `base` element:
|
||||
|
||||
- **data-orientation**:
|
||||
The orientation of the divider. Based on `orientation` prop.
|
||||
|
||||
@ -97,6 +97,8 @@ You can set the `selectionMode` property as `multiple` to allow the user to sele
|
||||
files={dropdownContent.multipleSelection}
|
||||
/>
|
||||
|
||||
> **Note**: To allow empty selection, you can set the `disallowEmptySelection` property as `false`.
|
||||
|
||||
### With Shortcut
|
||||
|
||||
You can use the `shortcut` prop to add a shortcut to the dropdown item.
|
||||
@ -189,18 +191,18 @@ you to customize each item individually.
|
||||
|
||||
| Key | Description |
|
||||
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <kbd>Space</kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu and focuses the first item. When focus is on an item, activates the focused item. |
|
||||
| <kbd>Enter</kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu and focuses the first item. When focus is on an item, activates the focused item. |
|
||||
| <kbd>ArrowDown</kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu. When focus is on an item, moves focus to the next item. |
|
||||
| <kbd>ArrowUp</kbd> | When focus is on an item, moves focus to the previous item. |
|
||||
| <kbd>Esc</kbd> | Closes the dropdown menu and moves focus to `DropdownTrigger`. |
|
||||
| <kbd>A-Z</kbd> or <kbd>a-z</kbd> | When the menu is open, moves focus to the next menu item with a label that starts with the typed character if such an menu item exists. |
|
||||
| <Kbd>Space</Kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu and focuses the first item. When focus is on an item, activates the focused item. |
|
||||
| <Kbd>Enter</Kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu and focuses the first item. When focus is on an item, activates the focused item. |
|
||||
| <Kbd>ArrowDown</Kbd> | When focus is on `DropdownTrigger`, opens the dropdown menu. When focus is on an item, moves focus to the next item. |
|
||||
| <Kbd>ArrowUp</Kbd> | When focus is on an item, moves focus to the previous item. |
|
||||
| <Kbd>Esc</Kbd> | Closes the dropdown menu and moves focus to `DropdownTrigger`. |
|
||||
| <Kbd>A-Z</Kbd> or <Kbd>a-z</Kbd> | When the menu is open, moves focus to the next menu item with a label that starts with the typed character if such an menu item exists. |
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`DropdownItem` has the following attributes on the `root` element:
|
||||
`DropdownItem` has the following attributes on the `base` element:
|
||||
|
||||
- **data-disabled**:
|
||||
When the dropdown item is disabled. Based on dropdown `disabledKeys` prop.
|
||||
@ -219,7 +221,7 @@ you to customize each item individually.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Exposed to assistive technology as a button with a menu using ARIA.
|
||||
- Exposed to assistive technology as a `button` with a `menu` using ARIA.
|
||||
- Support for single, multiple, or no selection.
|
||||
- Support for disabled items.
|
||||
- Support for sections.
|
||||
@ -320,6 +322,7 @@ you to customize each item individually.
|
||||
| startContent | `ReactNode` | The start content of the dropdown item. | - |
|
||||
| endContent | `ReactNode` | The end content of the dropdown item. This is positioned after the shortcut and the selected icon. | - |
|
||||
| selectedIcon | [SelectedIconProps](#dropdown-item-selected-icon-props) | Custom icon to render when the item is selected. | - |
|
||||
| showDivider | `boolean` | Whether to show a divider below the item. | `false` |
|
||||
| isDisabled | `boolean` | Whether the dropdown item should be disabled. (**Deprecated**) pass **disabledKeys** to `DropdownMenu` instead. | `false` |
|
||||
| isSelected | `boolean` | Whether the dropdown item should be selected. (**Deprecated**) pass **selectedKeys** to `DropdownMenu` instead. | `false` |
|
||||
| isReadOnly | `boolean` | Whether the dropdown item press events should be ignored. | `false` |
|
||||
|
||||
@ -63,16 +63,10 @@ the end of the label and the input will be required.
|
||||
|
||||
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
|
||||
|
||||
<CodeDemo title="Label Positions" files={inputContent.labelPositions} />
|
||||
<CodeDemo title="Label Placements" files={inputContent.labelPlacements} />
|
||||
|
||||
> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.
|
||||
|
||||
### With Description
|
||||
|
||||
You can add a description to the input by passing the `description` property.
|
||||
|
||||
<CodeDemo title="With Description" files={inputContent.description} />
|
||||
|
||||
### Password Input
|
||||
|
||||
You can use the `type` property to change the input type to `password`.
|
||||
@ -92,6 +86,12 @@ You can use the `startContent` and `endContent` properties to add content to the
|
||||
|
||||
<CodeDemo title="Start and End Content" files={inputContent.startEndContent} />
|
||||
|
||||
### With Description
|
||||
|
||||
You can add a description to the input by passing the `description` property.
|
||||
|
||||
<CodeDemo title="With Description" files={inputContent.description} />
|
||||
|
||||
### With Error Message
|
||||
|
||||
You can combine the `validationState="invalid"` and `errorMessage` properties to show an invalid input.
|
||||
@ -145,7 +145,7 @@ In case you need to customize the input even further, you can use the `useInput`
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Input` has the following attributes on the `root` element:
|
||||
`Input` has the following attributes on the `base` element:
|
||||
|
||||
- **data-invalid**:
|
||||
When the input is invalid. Based on `validationState` prop.
|
||||
@ -157,6 +157,8 @@ In case you need to customize the input even further, you can use the `useInput`
|
||||
When the input is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
- **data-focus**:
|
||||
When the input is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-focus-within**:
|
||||
When the input is being focused or any of its children. Based on [useFocusWithin](https://react-spectrum.adobe.com/react-aria/useFocusWithin.html).
|
||||
- **data-focus-visible**:
|
||||
When the input is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-disabled**:
|
||||
|
||||
@ -85,7 +85,7 @@ In case you need to customize the link even further, you can use the `useLink` h
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Link` has the following attributes on the `root` element:
|
||||
`Link` has the following attributes on the `base` element:
|
||||
|
||||
- **data-focus**:
|
||||
When the link is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html)
|
||||
@ -101,7 +101,7 @@ In case you need to customize the link even further, you can use the `useLink` h
|
||||
- Support for mouse, touch, and keyboard interactions.
|
||||
- Support for navigation links via `<a>` elements or custom element types via ARIA.
|
||||
- Support for disabled links.
|
||||
- Keyboard users may activate links using the <kbd>Enter</kbd> key.
|
||||
- Keyboard users may activate links using the <Kbd>Enter</Kbd> key.
|
||||
|
||||
<Spacer y={4} />{" "}
|
||||
|
||||
|
||||
292
apps/docs/content/docs/components/listbox.mdx
Normal file
292
apps/docs/content/docs/components/listbox.mdx
Normal file
@ -0,0 +1,292 @@
|
||||
---
|
||||
title: "Listbox"
|
||||
description: "A listbox displays a list of options and allows a user to select one or more of them."
|
||||
---
|
||||
|
||||
import {listboxContent} from "@/content/components/listbox";
|
||||
|
||||
# Listbox
|
||||
|
||||
A listbox displays a list of options and allows a user to select one or more of them.
|
||||
|
||||
<ComponentLinks component="listbox" reactAriaHook="useListBox" />
|
||||
|
||||
---
|
||||
|
||||
<CarbonAd />
|
||||
|
||||
## Import
|
||||
|
||||
NextUI exports 3 listbox-related components:
|
||||
|
||||
- **Listbox**: The main component, which is a wrapper for the other components.
|
||||
- **ListboxSection**: The component that contains a group of listbox items.
|
||||
- **ListboxItem**: The component that represents a listbox item.
|
||||
|
||||
<ImportTabs
|
||||
commands={{
|
||||
main: `import {
|
||||
Listbox,
|
||||
ListboxSection,
|
||||
ListboxItem
|
||||
} from "@nextui-org/react";`,
|
||||
individual: `import {
|
||||
Listbox,
|
||||
ListboxSection,
|
||||
ListboxItem
|
||||
} from "@nextui-org/listbox";`,
|
||||
}}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeDemo title="Usage" files={listboxContent.usage} />
|
||||
|
||||
### Dynamic items
|
||||
|
||||
Listbox follows the [Collection Components API](https://react-spectrum.adobe.com/react-stately/collections.html), accepting both static and dynamic collections.
|
||||
|
||||
- **Static**: The usage example above shows the static implementation, which can be used when the full list of options is known ahead of time.
|
||||
- **Dynamic**: The example below can be used when the options come from an external data source such as an API call, or update over time.
|
||||
|
||||
<CodeDemo title="Dynamic items" files={listboxContent.dynamic} />
|
||||
|
||||
### Disabled Keys
|
||||
|
||||
Listbox items can be disabled using the `disabledKeys` prop to the `Listbox` component.
|
||||
|
||||
<CodeDemo title="Disabled Keys" highlightedLines="10" files={listboxContent.disabledKeys} />
|
||||
|
||||
> **Note**: It's important to have a unique key for each item, otherwise the disabled keys will not work.
|
||||
|
||||
### Variants
|
||||
|
||||
You can use the `variant` in the `Listbox` component to change the `hover` style of the listbox items.
|
||||
|
||||
<CodeDemo title="Variants" highlightedLines="18" files={listboxContent.variants} />
|
||||
|
||||
### Single Selection
|
||||
|
||||
You can set the `selectionMode` property as `single` to allow the user to select only one item at a time.
|
||||
|
||||
<CodeDemo
|
||||
title="Single Selection"
|
||||
highlightedLines="20-22"
|
||||
files={listboxContent.singleSelection}
|
||||
/>
|
||||
|
||||
### Multiple Selection
|
||||
|
||||
You can set the `selectionMode` property as `multiple` to allow the user to select multiple items at a time.
|
||||
|
||||
<CodeDemo
|
||||
title="Multiple Selection"
|
||||
highlightedLines="20-22"
|
||||
files={listboxContent.multipleSelection}
|
||||
/>
|
||||
|
||||
> **Note**: To allow empty selection, you can set the `disallowEmptySelection` property as `false`.
|
||||
|
||||
### With Icons
|
||||
|
||||
It is possible to add icons to the listbox items using the `startContent` / `endContent` props.
|
||||
|
||||
<CodeDemo title="With Icons" highlightedLines="23,30,38" files={listboxContent.icons} />
|
||||
|
||||
> **Note**: Note: If you use `currentColor` as the icon color, the icon will have the same color as the item text.
|
||||
|
||||
### With Description
|
||||
|
||||
You can use the `description` prop to add a description to the listbox item.
|
||||
|
||||
<CodeDemo
|
||||
title="With Description"
|
||||
highlightedLines="17,24,32,41"
|
||||
files={listboxContent.description}
|
||||
/>
|
||||
|
||||
### With Sections
|
||||
|
||||
You can use the `ListboxSection` component to group listbox items.
|
||||
|
||||
<CodeDemo title="With Sections" highlightedLines="15,38" files={listboxContent.sections} />
|
||||
|
||||
> **Note**: Sections without a `title` must provide an `aria-label` for accessibility.
|
||||
|
||||
## Slots
|
||||
|
||||
Listbox has 2 components with slots the `ListboxItem` and `ListboxSection` components.
|
||||
|
||||
### ListboxItem
|
||||
|
||||
- **base**: The main slot for the listbox item. It wraps all the other slots.
|
||||
- **wrapper**: The `title` and `description` wrapper.
|
||||
- **title**: The title of the listbox item.
|
||||
- **description**: The description of the listbox item.
|
||||
- **selectedIcon**: The selected icon slot. This is only visible when the item is selected.
|
||||
|
||||
### ListboxSection
|
||||
|
||||
- **base**: The main slot for the listbox section. It wraps all the other slots.
|
||||
- **heading**: The title that is render on top of the section group.
|
||||
- **group**: The group of listbox items.
|
||||
- **divider**: The divider that is render between the groups. This is only visible when `showDivider` is `true`.
|
||||
|
||||
### Customizing the listbox
|
||||
|
||||
You can customize the `Listbox` items style by using the `itemClasses` prop and passing custom Tailwind CSS classes.
|
||||
|
||||
<CodeDemo title="Custom Styles" files={listboxContent.customStyles} />
|
||||
|
||||
> **Note**: In the above example, we've utilized the [Boxicons](https://boxicons.com/) icons collection.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
### Keyboard Interactions
|
||||
|
||||
| Key | Description |
|
||||
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| <Kbd>Home</Kbd> | Moves focus to the first item. |
|
||||
| <Kbd>End</Kbd> | Moves focus to the last item. |
|
||||
| <Kbd>ArrowDown</Kbd> | When focus is on an item, moves focus to the next item. |
|
||||
| <Kbd>ArrowUp</Kbd> | When focus is on an item, moves focus to the previous item. |
|
||||
| <Kbd>Enter</Kbd> or <Kbd>Space</Kbd> | When focus is on an item, selects the item. |
|
||||
| <Kbd>A-Z</Kbd> or <Kbd>a-z</Kbd> | Moves focus to the next menu item with a label that starts with the typed character if such an menu item exists. |
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`ListboxItem` has the following attributes on the `base` element:
|
||||
|
||||
- **data-disabled**:
|
||||
When the listbox item is disabled. Based on listbox `disabledKeys` prop.
|
||||
- **data-selected**:
|
||||
When the listbox item is selected. Based on listbox `selectedKeys` prop.
|
||||
- **data-selectable**:
|
||||
When the listbox item is selectable. Based on listbox `selectionMode` prop.
|
||||
- **data-hover**:
|
||||
When the listbox item is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
- **data-pressed**:
|
||||
When the listbox item is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html)
|
||||
- **data-focus**:
|
||||
When the listbox item is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-focus-visible**:
|
||||
When the listbox item is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Exposed to assistive technology as a `listbox` using ARIA.
|
||||
- Support for single, multiple, or no selection.
|
||||
- Support for disabled items.
|
||||
- Support for sections.
|
||||
- Labeling support for accessibility.
|
||||
- Support for mouse, touch, and keyboard interactions.
|
||||
- Tab stop focus management.
|
||||
- Keyboard navigation support including arrow keys, home/end, page up/down, select all, and clear.
|
||||
- Automatic scrolling support during keyboard navigation.
|
||||
- Typeahead to allow focusing options by typing text.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## API
|
||||
|
||||
### Listbox Props
|
||||
|
||||
| Attribute | Type | Description | Default |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | --------- |
|
||||
| children\* | `ReactNode[]` | The children to render. Usually a list of `ListboxItem` or `ListboxSection` | - |
|
||||
| items | [`Iterable<T>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) | Item objects in the collection. (dynamic) | - |
|
||||
| variant | `solid` \| `bordered` \| `light` \| `flat` \| `faded` \| `shadow` | The listbox items appearance style. | `solid` |
|
||||
| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The listbox items color theme. | `default` |
|
||||
| selectionMode | `none` \| `single` \| `multiple` | The type of selection that is allowed in the collection. | - |
|
||||
| selectedKeys | `React.Key[]` | The currently selected keys in the collection (controlled). | - |
|
||||
| disabledKeys | `React.Key[]` | The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | - |
|
||||
| defaultSelectedKeys | `all` \| `React.Key[]` | The initial selected keys in the collection (uncontrolled). | - |
|
||||
| disallowEmptySelection | `boolean` | Whether the collection allows empty selection. | `false` |
|
||||
| autoFocus | `boolean` \| `first` \| `last` | Where the focus should be set. | `false` |
|
||||
| shouldFocusWrap | `boolean` | Whether keyboard navigation is circular. | `false` |
|
||||
| disableAnimation | `boolean` | Whether to disable the animation of the listbox items. | `false` |
|
||||
| itemClasses | `Record<"base"| "wrapper"| "title"| "description"| "selectedIcon", string>` | Allows to set custom class names for the listbox item slots. | - |
|
||||
|
||||
### Listbox Events
|
||||
|
||||
| Attribute | Type | Description |
|
||||
| ----------------- | ----------------------------- | -------------------------------------------------- |
|
||||
| onAction | `(key: React.Key) => void` | Handler that is called when an item is selected. |
|
||||
| onSelectionChange | `(keys: React.Key[]) => void` | Handler that is called when the selection changes. |
|
||||
|
||||
---
|
||||
|
||||
### ListboxSection Props
|
||||
|
||||
| Attribute | Type | Description | Default |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | ------- |
|
||||
| children\* | `ReactNode` | The contents of the listbox section. Usually a list of `ListboxItem` components. (static) | - |
|
||||
| title | `string` | The title of the listbox section. | - |
|
||||
| items | [`Iterable<T>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) | Item objects in the collection. (dynamic) | - |
|
||||
| showDivider | `boolean` | Whether to show the divider between the groups. | `false` |
|
||||
| DividerProps | [DividerProps](/docs/components/divider) | The divider component props. | - |
|
||||
| classNames | `Record<"base"| "heading"| "group"| "divider", string>` | Allows to set custom class names for the listbox section slots. | - |
|
||||
| itemClasses | `Record<"base"| "wrapper"| "title"| "description"| "shortcut" | "selectedIcon", string>` | Allows to set custom class names for the listbox item slots. | - |
|
||||
|
||||
---
|
||||
|
||||
### ListboxItem Props
|
||||
|
||||
| Attribute | Type | Description | Default |
|
||||
| ------------ | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| children\* | `ReactNode` | The contents of the listbox item. | - |
|
||||
| key | `React.Key` | The unique key for the listbox item. | - |
|
||||
| title | `string` \| `ReactNode` | The title of the listbox item. | - |
|
||||
| textValue | `string` | A string representation of the item's contents, used for features like typeahead. | - |
|
||||
| description | `string` \| `ReactNode` | The description of the listbox item. | - |
|
||||
| shortcut | `string` \| `ReactNode` | The listbox item keyboard shortcut. | - |
|
||||
| startContent | `ReactNode` | The start content of the listbox item. | - |
|
||||
| endContent | `ReactNode` | The end content of the listbox item. This is positioned after the shortcut and the selected icon. | - |
|
||||
| selectedIcon | [SelectedIconProps](#listbox-item-selected-icon-props) | Custom icon to render when the item is selected. | - |
|
||||
| showDivider | `boolean` | Whether to show a divider below the item. | `false` |
|
||||
| isDisabled | `boolean` | Whether the listbox item should be disabled. (**Deprecated**) pass **disabledKeys** to `Listbox` instead. | `false` |
|
||||
| isSelected | `boolean` | Whether the listbox item should be selected. (**Deprecated**) pass **selectedKeys** to `Listbox` instead. | `false` |
|
||||
| isReadOnly | `boolean` | Whether the listbox item press events should be ignored. | `false` |
|
||||
| classNames | `Record<"base"| "wrapper"| "title"| "description"| "shortcut" | "selectedIcon", string>` | Allows to set custom class names for the listbox item slots. | - |
|
||||
|
||||
### ListboxItem Events
|
||||
|
||||
| Attribute | Type | Description |
|
||||
| ------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
||||
| onAction | `() => void` | Handler that is called when the listbox item is selected. (**Deprecated**) pass to `Listbox` instead. |
|
||||
| onPress | `(e: PressEvent) => void` | Handler called when the press is released over the target. |
|
||||
| onPressStart | `(e: PressEvent) => void` | Handler called when a press interaction starts. |
|
||||
| onPressEnd | `(e: PressEvent) => void` | Handler called when a press interaction ends, either over the target or when the pointer leaves the target. |
|
||||
| onPressChange | `(isPressed: boolean) => void` | Handler called when the press state changes. |
|
||||
| onPressUp | `(e: PressEvent) => void` | Handler called when a press is released over the target, regardless of whether it started on the target or not. |
|
||||
| onKeyDown | `(e: KeyboardEvent) => void` | Handler called when a key is pressed. |
|
||||
| onKeyUp | `(e: KeyboardEvent) => void` | Handler called when a key is released. |
|
||||
| onClick | `MouseEventHandler` | The native button click event handler (**Deprecated**) use **onPress** instead. |
|
||||
|
||||
---
|
||||
|
||||
### Types
|
||||
|
||||
#### Listbox Item Selected Icon Props
|
||||
|
||||
```ts
|
||||
export type ListboxItemSelectedIconProps = {
|
||||
/**
|
||||
* The current icon, usually an checkmark icon.
|
||||
*/
|
||||
icon?: ReactNode;
|
||||
/**
|
||||
* The current selected status.
|
||||
*/
|
||||
isSelected?: boolean;
|
||||
/**
|
||||
* The current disabled status.
|
||||
* @default false
|
||||
*/
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
type selectedIcon?: ReactNode | ((props: ListboxItemSelectedIconProps) => ReactNode) | null;
|
||||
```
|
||||
@ -141,7 +141,7 @@ You can customize the `Modal` component by passing custom Tailwind CSS classes t
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Modal` has the following attributes on the `root` element:
|
||||
`Modal` has the following attributes on the `base` element:
|
||||
|
||||
- **data-open**:
|
||||
When the modal is open. Based on modal state.
|
||||
@ -153,7 +153,7 @@ You can customize the `Modal` component by passing custom Tailwind CSS classes t
|
||||
## Accessibility
|
||||
|
||||
- Content outside the modal is hidden from assistive technologies while it is open.
|
||||
- The modal optionally closes when interacting outside, or pressing the <kbd>Esc</kbd> key.
|
||||
- The modal optionally closes when interacting outside, or pressing the <Kbd>Esc</Kbd> key.
|
||||
- Focus is moved into the modal on mount, and restored to the trigger element on unmount.
|
||||
- While open, focus is contained within the modal, preventing the user from tabbing outside.
|
||||
- Scrolling the page behind the modal is prevented while it is open, including in mobile browsers.
|
||||
|
||||
@ -210,7 +210,7 @@ When the `NavbarItem` is active, it will have a `data-active` attribute. You can
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Navbar` has the following attributes on the `root` element:
|
||||
`Navbar` has the following attributes on the `base` element:
|
||||
|
||||
- **data-menu-open**:
|
||||
Indicates if the navbar menu is open.
|
||||
@ -222,12 +222,12 @@ When the `NavbarItem` is active, it will have a `data-active` attribute. You can
|
||||
- **data-justify**:
|
||||
The justify content of the navbar content. It takes into account the correct space distribution.
|
||||
|
||||
`NavbarItem` has the following attributes on the `root` element:
|
||||
`NavbarItem` has the following attributes on the `base` element:
|
||||
|
||||
- **data-active**:
|
||||
Indicates if the navbar item is active. It is used when the `isActive` prop is `true`.
|
||||
|
||||
`NavbarMenuToggle` has the following attributes on the `root` element:
|
||||
`NavbarMenuToggle` has the following attributes on the `base` element:
|
||||
|
||||
- **data-open**:
|
||||
Indicates if the navbar menu is open. It is used when the `isMenuOpen` prop is `true`.
|
||||
|
||||
@ -135,7 +135,7 @@ your own implementation.
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Pagination` has the following attributes on the `root` element:
|
||||
`Pagination` has the following attributes on the `base` element:
|
||||
|
||||
- **data-controls**:
|
||||
Indicates whether the pagination has controls. Based on `showControls` prop.
|
||||
|
||||
@ -124,7 +124,7 @@ You can customize the `Popover` component by passing custom Tailwind CSS classes
|
||||
|
||||
- The trigger and popover are automatically associated semantically via ARIA.
|
||||
- Content outside the popover is hidden from assistive technologies while it is open.
|
||||
- The popover closes when interacting outside, or pressing the <kbd>Escape</kbd> key.
|
||||
- The popover closes when interacting outside, or pressing the <Kbd>Escape</Kbd> key.
|
||||
- Focus is moved into the popover on mount, and restored to the trigger element on unmount.
|
||||
- The popover is positioned relative to the trigger element, and automatically flips and adjusts to avoid overlapping with the edge of the browser window.
|
||||
- Scrolling is prevented outside the popover to avoid unintentionally repositioning or closing it.
|
||||
|
||||
@ -86,7 +86,7 @@ You can customize the `Progress` component by passing custom Tailwind CSS classe
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`CircularProgress` has the following attributes on the `root` element:
|
||||
`CircularProgress` has the following attributes on the `base` element:
|
||||
|
||||
- **data-indeterminate**:
|
||||
Indicates whether the progress is indeterminate.
|
||||
|
||||
@ -87,12 +87,12 @@ In case you need to customize the radio group even further, you can use the `use
|
||||
|
||||
## Data Attributes
|
||||
|
||||
- RadioGroup has the following attributes on the `root` element:
|
||||
- RadioGroup has the following attributes on the `base` element:
|
||||
|
||||
- **data-orientation**:
|
||||
The orientation of the radio group. Based on `orientation` prop.
|
||||
|
||||
- Radio has the following attributes on the `root` element:
|
||||
- Radio has the following attributes on the `base` element:
|
||||
|
||||
- **data-selected**:
|
||||
When the radio is checked. Based on `isSelected` prop.
|
||||
|
||||
68
apps/docs/content/docs/components/scroll-shadow.mdx
Normal file
68
apps/docs/content/docs/components/scroll-shadow.mdx
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: "Scroll Shadow"
|
||||
description: "Applies top and bottom shadows when content overflows on scroll."
|
||||
---
|
||||
|
||||
import {scrollShadowContent} from "@/content/components/scroll-shadow";
|
||||
|
||||
# Scroll Shadow
|
||||
|
||||
Applies top and bottom shadows when content overflows on scroll.
|
||||
|
||||
<ComponentLinks component="scroll-shadow" />
|
||||
|
||||
---
|
||||
|
||||
<CarbonAd />
|
||||
|
||||
## Import
|
||||
|
||||
<ImportTabs
|
||||
commands={{
|
||||
main: 'import {ScrollShadow} from "@nextui-org/react";',
|
||||
individual: 'import {ScrollShadow} from "@nextui-org/scroll-shadow";',
|
||||
}}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeDemo title="Usage" files={scrollShadowContent.usage} />
|
||||
|
||||
### Hide Scrollbar
|
||||
|
||||
You can use the `hideScrollBar` property to hide vertical and horizontal scrollbars.
|
||||
|
||||
<CodeDemo title="Hide Scrollbar" files={scrollShadowContent.hideScrollbar} />
|
||||
|
||||
### Custom Shadow Size
|
||||
|
||||
By default, the shadow size is `40` in pixels, but you can change it using the `size` property.
|
||||
|
||||
<CodeDemo title="Custom Shadow Size" files={scrollShadowContent.customSize} />
|
||||
|
||||
### Horizontal Orientation
|
||||
|
||||
In case you need to apply the shadow on the horizontal scroll, you can set the `orientation` property to `horizontal`.
|
||||
|
||||
<CodeDemo title="Horizontal Orientation" files={scrollShadowContent.horizontal} />
|
||||
|
||||
### Shadow Offset
|
||||
|
||||
By default the shadow offset is `0` in pixels, but you can change it using the `offset` property. This allows you to apply the shadow on
|
||||
a specific position.
|
||||
|
||||
<CodeDemo title="Shadow Offset" files={scrollShadowContent.offset} />
|
||||
|
||||
<Spacer y={4} />{" "}
|
||||
|
||||
## API
|
||||
|
||||
### ShadowScroll Props
|
||||
|
||||
| Attribute | Type | Description | Default |
|
||||
| ------------- | -------------------------- | ----------------------------------------------- | ---------- |
|
||||
| size | `number` | The shadow size in pixels. | `40` |
|
||||
| offset | `number` | The scroll offset to show the shadow in pixels. | `0` |
|
||||
| hideScrollBar | `boolean` | Whether to hide the scrollbar or not. | `false` |
|
||||
| orientation | `horizontal` \| `vertical` | The scroll orientation. | `vertical` |
|
||||
| isEnabled | `boolean` | Whether the shadow is enabled or not. | `true` |
|
||||
427
apps/docs/content/docs/components/select.mdx
Normal file
427
apps/docs/content/docs/components/select.mdx
Normal file
@ -0,0 +1,427 @@
|
||||
---
|
||||
title: "Select"
|
||||
description: "A select displays a collapsible list of options and allows a user to select one or more of them."
|
||||
---
|
||||
|
||||
import {selectContent} from "@/content/components/select";
|
||||
|
||||
# Select
|
||||
|
||||
A select displays a collapsible list of options and allows a user to select one or more of them.
|
||||
|
||||
<ComponentLinks component="select" reactAriaHook="useSelect" />
|
||||
|
||||
---
|
||||
|
||||
<CarbonAd />
|
||||
|
||||
## Import
|
||||
|
||||
NextUI exports 3 select-related components:
|
||||
|
||||
- **Select**: The main component, which is a wrapper for the other components.
|
||||
- **SelectSection**: The component that contains a group of select items.
|
||||
- **SelectItem**: The component that represents a select item.
|
||||
|
||||
<ImportTabs
|
||||
commands={{
|
||||
main: 'import {Select, SelectSection, SelectItem} from "@nextui-org/react";',
|
||||
individual: 'import {Select, SelectSection, SelectItem} from "@nextui-org/select";',
|
||||
}}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeDemo title="Usage" files={selectContent.usage} />
|
||||
|
||||
### Dynamic items
|
||||
|
||||
Select follows the [Collection Components API](https://react-spectrum.adobe.com/react-stately/collections.html), accepting both static and dynamic collections.
|
||||
|
||||
- **Static**: The usage example above shows the static implementation, which can be used when the full list of options is known ahead of time.
|
||||
- **Dynamic**: The example below can be used when the options come from an external data source such as an API call, or update over time.
|
||||
|
||||
<CodeDemo title="Dynamic items" highlightedLines="8" files={selectContent.dynamic} />
|
||||
|
||||
### Disabled
|
||||
|
||||
<CodeDemo title="Disabled" highlightedLines="8" files={selectContent.disabled} />
|
||||
|
||||
### Disabled Items
|
||||
|
||||
You can disable specific items by using the `disabledKeys` property.
|
||||
|
||||
<CodeDemo title="Disabled Items" highlightedLines="8" files={selectContent.disabledItems} />
|
||||
|
||||
### Required
|
||||
|
||||
If you pass the `isRequired` property to the select, it will have a `danger` asterisk at
|
||||
the end of the label and the select will be required.
|
||||
|
||||
<CodeDemo title="Required" highlightedLines="8" files={selectContent.required} />
|
||||
|
||||
### Sizes
|
||||
|
||||
<CodeDemo title="Sizes" highlightedLines="13,26" files={selectContent.sizes} />
|
||||
|
||||
### Colors
|
||||
|
||||
<CodeDemo title="Colors" files={selectContent.colors} />
|
||||
|
||||
### Variants
|
||||
|
||||
<CodeDemo title="Variants" files={selectContent.variants} />
|
||||
|
||||
### Radius
|
||||
|
||||
<CodeDemo title="Radius" files={selectContent.radius} />
|
||||
|
||||
### Label Placements
|
||||
|
||||
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
|
||||
|
||||
<CodeDemo title="Label Placements" highlightedLines="20,37" files={selectContent.labelPlacements} />
|
||||
|
||||
> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.
|
||||
|
||||
### Start Content
|
||||
|
||||
You can use the `startContent` and `endContent` properties to add content to the start and end of
|
||||
the select.
|
||||
|
||||
<CodeDemo title="Start Content" highlightedLines="9" files={selectContent.startContent} />
|
||||
|
||||
### Item Start Content
|
||||
|
||||
Since the `Select` component uses the [Listbox](/docs/components/listbox) component under the hood, you can
|
||||
use the `startContent` and `endContent` properties of the `SelectItem` component to add content to the start
|
||||
and end of the select item.
|
||||
|
||||
<CodeDemo title="Item Start Content" files={selectContent.itemStartContent} />
|
||||
|
||||
### Custom Selector Icon
|
||||
|
||||
By default the select uses a `crevron-down` icon as the selector icon which rotates when the select is open. You can
|
||||
customize this icon by passing a custom one to the `selectorIcon` property.
|
||||
|
||||
<CodeDemo title="Custom Selector Icon" files={selectContent.customSelectorIcon} />
|
||||
|
||||
> **Note**: Use the `disableSelectionIconRotation` property to disable the rotation of the icon.
|
||||
|
||||
### Without Scroll Shadow
|
||||
|
||||
Select component uses the [ScrollShadow](/docs/components/scroll-shadow) under the hood to show a shadow when the select content is scrollable.
|
||||
You can disable this shadow by passing using the `scrollShadowProps` property.
|
||||
|
||||
<CodeDemo title="Without Scroll Shadow" files={selectContent.withoutScrollShadow} />
|
||||
|
||||
> **Note**: You can also use the `showScrollIndicators` property to disable the scroll indicators.
|
||||
|
||||
### With Description
|
||||
|
||||
You can add a description to the select by passing the `description` property.
|
||||
|
||||
<CodeDemo title="With Description" files={selectContent.description} />
|
||||
|
||||
### With Error Message
|
||||
|
||||
You can combine the `validationState="invalid"` and `errorMessage` properties to show an invalid select.
|
||||
|
||||
<CodeDemo title="With Error Message" files={selectContent.errorMessage} />
|
||||
|
||||
### Controlled
|
||||
|
||||
You can use the `selectedKeys` and `onSelectionChange` / `onChange` properties to control the select value.
|
||||
|
||||
Using `onSelectionChange`:
|
||||
|
||||
<CodeDemo title="Controlled with onSelectionChange" files={selectContent.singleControlled} />
|
||||
|
||||
Using `onChange`:
|
||||
|
||||
<CodeDemo title="Controlled with onChange" files={selectContent.singleControlledOnChange} />
|
||||
|
||||
### Controlling the open state
|
||||
|
||||
You can control the open state of the select by using the `isOpen` and `onOpenChange` / `onClose` properties.
|
||||
|
||||
<CodeDemo title="Controlling the open state" files={selectContent.openState} />
|
||||
|
||||
### Custom Items
|
||||
|
||||
You can customize the select items by modifying the `SelectItem` children.
|
||||
|
||||
<CodeDemo title="Custom Items" files={selectContent.customItems} />
|
||||
|
||||
### Custom Render Value
|
||||
|
||||
By default the select will render the selected item's text value, but you can customize this by passing a `renderValue` function.
|
||||
|
||||
<CodeDemo title="Custom Render Value" files={selectContent.customRenderValue} />
|
||||
|
||||
The `renderValue` function receives the selected items as a parameter and must return a
|
||||
`ReactNode`. Check the [Render Value Function](#render-value-function) section for more details.
|
||||
|
||||
### With Sections
|
||||
|
||||
You can use the `SelectSection` component to group select items.
|
||||
|
||||
<CodeDemo title="With Sections" files={selectContent.sections} />
|
||||
|
||||
### Custom Sections Style
|
||||
|
||||
You can customize the sections style by using the `classNames` property of the `SelectSection` component.
|
||||
|
||||
<CodeDemo title="Custom Sections Style" files={selectContent.customSectionsStyle} />
|
||||
|
||||
### Asyncronous Loading
|
||||
|
||||
Select supports asyncronous loading, in the example below we are using a custom hook to fetch the [Pokemon API](https://pokeapi.co/api/v2/pokemon) data in combination with the `useInfinityScroll` hook to load more data when the user reaches the end of the list.
|
||||
|
||||
The `isLoading` prop is used to show a loading indicator intead of the selector icon when the data is being fetched.
|
||||
|
||||
<PackageManagers
|
||||
commands={{
|
||||
npm: "npm install @nextui-org/use-infinity-scroll",
|
||||
yarn: "yarn add @nextui-org/use-infinity-scroll",
|
||||
pnpm: "pnpm add @nextui-org/use-infinity-scroll",
|
||||
}}
|
||||
/>
|
||||
|
||||
```jsx
|
||||
import {useInfinityScroll} from "@nextui-org/use-infinity-scroll";
|
||||
```
|
||||
|
||||
<Spacer y={2} />
|
||||
|
||||
<CodeDemo
|
||||
asIframe
|
||||
typescriptStrict={true}
|
||||
title="Asyncronous Loading"
|
||||
hideWindowActions={true}
|
||||
resizeEnabled={false}
|
||||
displayMode="always"
|
||||
files={selectContent.asyncLoadingItems}
|
||||
previewHeight="400px"
|
||||
iframeSrc="/examples/select/async-items-loading"
|
||||
/>
|
||||
|
||||
### Multiple Select
|
||||
|
||||
You can use the `selectionMode="multiple"` property to allow multiple selection.
|
||||
|
||||
<CodeDemo title="Multiple Selection" files={selectContent.multiple} />
|
||||
|
||||
### Multiple Select Controlled
|
||||
|
||||
You can use the same properties as the single select to control the multiple select, `selectedKeys` and `onSelectionChange` / `onChange`.
|
||||
|
||||
Using `onSelectionChange`:
|
||||
|
||||
<CodeDemo
|
||||
title="Multiple Selection Controlled with onSelectionChange"
|
||||
files={selectContent.multipleControlled}
|
||||
/>
|
||||
|
||||
Using `onChange`:
|
||||
|
||||
<CodeDemo
|
||||
title="Multiple Selection Controlled with onChange"
|
||||
files={selectContent.multipleControlledOnChange}
|
||||
/>
|
||||
|
||||
### Mutliple With Chips
|
||||
|
||||
You can render any component as the select value by using the `renderValue` property. In this example we are
|
||||
using the [Chip](/docs/components/chip) component to render the selected items.
|
||||
|
||||
<CodeDemo title="Multiple Selection with Chips" files={selectContent.multipleWithChips} />
|
||||
|
||||
> **Note**: Make sure to pass the `isMultiline` property to the `Select` component to allow the chips to wrap.
|
||||
|
||||
The `renderValue` function receives the selected items as a parameter and must return a
|
||||
`ReactNode`. Check the [Render Value Function](#render-value-function) section for more details.
|
||||
|
||||
### Customizing the select
|
||||
|
||||
You can customize any slot of the select by using the `classNames` property. Select
|
||||
component also provides the [popoverProps](/docs/components/popover#api) and [listboxProps](/docs/components/listbox#api) properties to customize
|
||||
the popover and listbox components.
|
||||
|
||||
<CodeDemo title="Custom Styles" files={selectContent.customStyles} />
|
||||
|
||||
## Slots
|
||||
|
||||
- **base**: The main wrapper of the select. This wraps the rest of the slots.
|
||||
- **label**: The label of the select.
|
||||
- **trigger**: The trigger of the select. This wraps the label the inner wrapper and the selector icon.
|
||||
- **innerWrapper**: The wrapper of the select content. This wraps the start/end content and the select value.
|
||||
- **selectorIcon**: The selector icon of the select. This is the icon that rotates when the select is open (`data-open`).
|
||||
- **value**: The select value. This is also the slot that wraps the `renderValue` function result.
|
||||
- **listboxWrapper**: The wrapper of the listbox. This wraps the listbox component, this slot is used on top of the scroll shadow component.
|
||||
- **listbox**: The listbox component. This is the component that wraps the select items.
|
||||
- **popover**: The popover component. This is the component that wraps the listbox component.
|
||||
- **helperWrapper**: The wrapper of the helper text. This wraps the helper text and the error message.
|
||||
- **description**: The description of the select.
|
||||
- **errorMessage**: The error message of the select.
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Select` has the following attributes on the `base` element:
|
||||
|
||||
- **data-filled**:
|
||||
Indicates if the select has a value, is focused, has start/end content or is open.
|
||||
|
||||
`Select` has the following attributes on the `trigger` element:
|
||||
|
||||
- **data-open**:
|
||||
Indicates if the select is open.
|
||||
- **data-disabled**:
|
||||
When the select trigger is disabled. Based on select `isDisabled` prop.
|
||||
- **data-focus**:
|
||||
When the select trigger is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-focus-visible**:
|
||||
When the select trigger is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-pressed**:
|
||||
When the select trigger is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html)
|
||||
- **data-hover**:
|
||||
When the select trigger is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
|
||||
`Select` has the following attributes on the `selectorIcon` element:
|
||||
|
||||
- **data-open**:
|
||||
Indicates if the select is open.
|
||||
|
||||
`SelectItem` has the following attributes on the `base` element:
|
||||
|
||||
- **data-disabled**:
|
||||
When the select item is disabled. Based on select `disabledKeys` prop.
|
||||
- **data-selected**:
|
||||
When the select item is selected. Based on select `selectedKeys` prop.
|
||||
- **data-hover**:
|
||||
When the select item is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html)
|
||||
- **data-pressed**:
|
||||
When the select item is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html)
|
||||
- **data-focus**:
|
||||
When the select item is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
- **data-focus-visible**:
|
||||
When the select item is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Exposed to assistive technology as a button with a listbox popup using ARIA (combined with [Listbox](/docs/components/listbox)).
|
||||
- Support for selecting a single option.
|
||||
- Support for selecting multiple options.
|
||||
- Support for disabled options.
|
||||
- Support for sections.
|
||||
- Labeling support for accessibility.
|
||||
- Support for description and error message help text linked to the input via ARIA.
|
||||
- Support for mouse, touch, and keyboard interactions.
|
||||
- Tab stop focus management.
|
||||
- Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly.
|
||||
- Typeahead to allow selecting options by typing text, even without opening the listbox.
|
||||
- Browser autofill integration via a hidden native `<select>` element.
|
||||
- Support for mobile form navigation via software keyboard.
|
||||
- Mobile screen reader listbox dismissal support.
|
||||
|
||||
<Spacer y={4} />
|
||||
|
||||
## API
|
||||
|
||||
### Select Props
|
||||
|
||||
| Attribute | Type | Description | Default |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------ |
|
||||
| children\* | `ReactNode[]` | The children to render. Usually a list of `SelectItem` and `SelectSection` elements. | - |
|
||||
| items | [`Iterable<T>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) | Item objects in the select. (dynamic) | - |
|
||||
| selectionMode | `single` \| `multiple` | The type of selection that is allowed in the collection. | - |
|
||||
| selectedKeys | `all` \| `React.Key[]` | The currently selected keys in the collection (controlled). | - |
|
||||
| disabledKeys | `all` \| `React.Key[]` | The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | - |
|
||||
| defaultSelectedKeys | `all` \| `React.Key[]` | The initial selected keys in the collection (uncontrolled). | - |
|
||||
| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the select. | `flat` |
|
||||
| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the select. | `default` |
|
||||
| size | `sm` \| `md` \| `lg` | The size of the select. | `md` |
|
||||
| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the select. | - |
|
||||
| placeholder | `string` | The placeholder of the select. | `Select an option` |
|
||||
| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` |
|
||||
| validationState | `valid` \| `invalid` | Whether the select should display its "valid" or "invalid" visual styling. | - |
|
||||
| label | `ReactNode` | The content to display as the label. | - |
|
||||
| description | `ReactNode` | A description for the select. Provides a hint such as specific requirements for what to choose. | - |
|
||||
| errorMessage | `ReactNode` | An error message for the select. | - |
|
||||
| startContent | `ReactNode` | Element to be rendered in the left side of the select. | - |
|
||||
| endContent | `ReactNode` | Element to be rendered in the right side of the select. | - |
|
||||
| selectorIcon | `ReactNode` | Element to be rendered as the selector icon. | - |
|
||||
| scrollRef | `React.RefObject<HTMLElement>` | A ref to the scrollable element. | - |
|
||||
| spinnerRef | `React.RefObject<HTMLElement>` | A ref to the spinner element. | - |
|
||||
| fullWidth | `boolean` | Whether the select should take up the width of its parent. | `true` |
|
||||
| isOpen | `boolean` | Whether the select is open by default (controlled). | - |
|
||||
| defaultOpen | `boolean` | Whether the select is open by default (uncontrolled). | - |
|
||||
| isRequired | `boolean` | Whether user select is required on the select before form submission. | `false` |
|
||||
| isDisabled | `boolean` | Whether the select is disabled. | `false` |
|
||||
| isMultiline | `boolean` | Whether the select should allow multiple lines of text. | `false` |
|
||||
| showScrollIndicators | `boolean` | Whether the select should show scroll indicators when the listbox is scrollable. | `true` |
|
||||
| autoFocus | `boolean` | Whether the select should be focused on the first mount. | `false` |
|
||||
| disallowEmptySelection | `boolean` | Whether the collection allows empty selection. | `false` |
|
||||
| disableAnimation | `boolean` | Whether the select should be animated. | `true` |
|
||||
| disableSelectionIconRotation | `boolean` | Whether the select should disable the rotation of the selector icon. | `false` |
|
||||
| popoverProps | [PopoverProps](/docs/components/popover#api) | Props to be passed to the popover component. | - |
|
||||
| listboxProps | [ListboxProps](/docs/components/listbox#api) | Props to be passed to the listbox component. | - |
|
||||
| scrollShadowProps | [ScrollShadowProps](/docs/components/scroll-shadow#api) | Props to be passed to the scroll shadow component. | - |
|
||||
| classNames | `Record<"base"| "label"| "trigger"| "innerWrapper"| "selectorIcon" | "value" | "listboxWrapper"| "listbox" | "popover" | "helperWrapper" | "description" | "errorMessage", string>` | Allows to set custom class names for the dropdown item slots. | - |
|
||||
|
||||
### Select Events
|
||||
|
||||
| Attribute | Type | Description |
|
||||
| ----------------- | --------------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| onClose | `() => void` | Callback fired when the select popover is closed. |
|
||||
| onOpenChange | `(isOpen: boolean) => void` | Callback fired when the select popover is opened or closed. |
|
||||
| onSelectionChange | `(keys: React.Key[]) => void` | Callback fired when the selected keys change. |
|
||||
| onChange | `React.ChangeEvent<HTMLSelectElement>` | Native select change event, fired when the selected value changes. |
|
||||
| renderValue | [RenderValueFunction](#render-value-function) | Function to render the value of the select. It renders the selected item by default. |
|
||||
|
||||
---
|
||||
|
||||
### SelectItem Props
|
||||
|
||||
Check the [ListboxItem](/docs/components/listbox#listboxitem-props) props.
|
||||
|
||||
### SelectItem Events
|
||||
|
||||
Check the [ListboxItem](/docs/components/listbox#listboxitem-events) events.
|
||||
|
||||
### SelectSection Props
|
||||
|
||||
Check the [ListboxSection](/docs/components/listbox#listboxsection-props) props.
|
||||
|
||||
---
|
||||
|
||||
### Types
|
||||
|
||||
#### Render Value Function
|
||||
|
||||
The `T` type is the type of the data passed to the select `items`.
|
||||
|
||||
```tsx
|
||||
export type SelectedItemProps<T> = {
|
||||
/** A unique key for the item. */
|
||||
key?: Key;
|
||||
/** The props passed to the item. */
|
||||
props?: Record<string, any>;
|
||||
/** The item data. */
|
||||
data?: T | null;
|
||||
/** An accessibility label for this item. */
|
||||
"aria-label"?: string;
|
||||
/** The rendered contents of this item (e.g. JSX). */
|
||||
rendered?: ReactNode;
|
||||
/** A string value for this item, used for features like typeahead. */
|
||||
textValue?: string;
|
||||
/** The type of item this item represents. */
|
||||
type?: string;
|
||||
};
|
||||
|
||||
type SelectedItems<T> = Array<SelectedItemProps<T>>;
|
||||
|
||||
renderValue: (items: SelectedItems<T>) => ReactNode;
|
||||
```
|
||||
@ -50,7 +50,7 @@ You can use the `isLoaded` prop to stop the skeleton animation and show the chil
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Skeleton` has the following attributes on the `root` element:
|
||||
`Skeleton` has the following attributes on the `base` element:
|
||||
|
||||
- **data-loaded**:
|
||||
Indicates the loaded state of the skeleton. Based on the `isLoaded` prop.
|
||||
|
||||
@ -87,7 +87,7 @@ In case you need to customize the switch even further, you can use the `useSwitc
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Switch` has the following attributes on the `root` element:
|
||||
`Switch` has the following attributes on the `base` element:
|
||||
|
||||
- **data-selected**:
|
||||
When the switch is checked. Based on `isSelected` prop.
|
||||
@ -111,7 +111,7 @@ In case you need to customize the switch even further, you can use the `useSwitc
|
||||
- Built with a native HTML `<input>` element.
|
||||
- Full support for browser features like form autofill.
|
||||
- Keyboard focus management and cross browser normalization.
|
||||
- Keyboard event support for <kbd>Tab</kbd> and <kbd>Space</kbd> keys.
|
||||
- Keyboard event support for <Kbd>Tab</Kbd> and <Kbd>Space</Kbd> keys.
|
||||
- Labeling support for assistive technology.
|
||||
- Exposed as a switch to assistive technology via ARIA
|
||||
|
||||
|
||||
@ -96,7 +96,7 @@ You can customize the `Tabs` component by passing custom Tailwind CSS classes to
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Tab` has the following attributes on the `root` element:
|
||||
`Tab` has the following attributes on the `base` element:
|
||||
|
||||
- **data-selected**:
|
||||
When the tab is selected.
|
||||
|
||||
@ -86,7 +86,7 @@ You can use the `value` and `onValueChange` properties to control the input valu
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Textarea` has the following attributes on the `root` element:
|
||||
`Textarea` has the following attributes on the `base` element:
|
||||
|
||||
- **data-invalid**:
|
||||
When the textarea is invalid. Based on `validationState` prop.
|
||||
|
||||
@ -86,7 +86,7 @@ You can customize the `Tooltip` component by passing custom Tailwind CSS classes
|
||||
|
||||
## Data Attributes
|
||||
|
||||
`Tooltip` has the following attributes on the `root` element:
|
||||
`Tooltip` has the following attributes on the `base` element:
|
||||
|
||||
- **data-open**:
|
||||
When the tooltip is open. Based on tooltip state.
|
||||
|
||||
BIN
apps/docs/public/blog/v2.1.0.jpg
Normal file
BIN
apps/docs/public/blog/v2.1.0.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
@ -1,4 +1,5 @@
|
||||
{
|
||||
"remove": ["devDependencies"],
|
||||
"replace": {
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user