feat(docs): installations and frameworks guides added

This commit is contained in:
Junior Garcia 2023-05-13 00:59:47 -03:00
parent 0df8b26697
commit 48667d9444
61 changed files with 1455 additions and 564 deletions

View File

@ -6,12 +6,12 @@ const blockquote = tv({
variants: {
color: {
neutral: "border-neutral-100 bg-neutral-50",
primary: "border-primary-200 bg-primary-50",
secondary: "border-secondary-200 bg-secondary-50",
success: "border-success-200 bg-success-50",
warning: "border-warning-200 bg-warning-50",
danger: "border-danger-200 bg-danger-50",
neutral: "border-neutral-100 bg-neutral-50/20",
primary: "border-primary-100 bg-primary-50/20",
secondary: "border-secondary-100 bg-secondary-50/20",
success: "border-success-100 bg-success-50/20",
warning: "border-warning-100 bg-warning-50/20",
danger: "border-danger-100 bg-danger-50/20",
},
},
defaultVariants: {

View File

@ -5,36 +5,88 @@ import BaseHighlight, {Language, PrismTheme, defaultProps} from "prism-react-ren
import defaultTheme from "@/libs/prism-theme";
interface CodeblockProps {
codeString: string;
language: Language;
codeString: string;
metastring?: string;
theme?: PrismTheme;
showLines?: boolean;
className?: string;
children?: (props: any) => React.ReactNode;
}
const RE = /{([\d,-]+)}/;
const calculateLinesToHighlight = (meta?: string) => {
if (!meta) {
return () => false;
}
if (!RE.test(meta)) {
return () => false;
}
// @ts-ignore
const lineNumbers = RE.exec(meta)[1]
.split(`,`)
.map((v) => v.split(`-`).map((x) => parseInt(x, 10)));
return (index: number) => {
const lineNumber = index + 1;
const inRange = lineNumbers.some(([start, end]) =>
end ? lineNumber >= start && lineNumber <= end : lineNumber === start,
);
return inRange;
};
};
export function Codeblock({
codeString,
language,
showLines,
theme: themeProp,
metastring,
className: classNameProp,
...props
}: CodeblockProps) {
const theme = themeProp || defaultTheme;
const shouldHighlightLine = calculateLinesToHighlight(metastring);
const isMultiLine = codeString.split("\n").length > 2;
return (
<BaseHighlight {...defaultProps} code={codeString} language={language} theme={theme} {...props}>
{({className, style, tokens, getLineProps, getTokenProps}) => (
<div data-language={language}>
<pre className={className} style={style}>
<div className="w-full" data-language={language}>
<pre
className={clsx(className, classNameProp, "flex max-w-full", {
"flex-col": isMultiLine,
})}
style={style}
>
{tokens.map((line, i) => {
const lineProps = getLineProps({line, key: i});
return (
<div key={i} {...lineProps} className={clsx(lineProps.className, "px-2")}>
<div
key={i}
{...lineProps}
className={clsx(
lineProps.className,
"px-4 relative [&>span]:relative [&>span]:z-10",
{
"px-2": showLines,
},
{
"before:content-[''] before:w-full before:h-full before:absolute before:z-0 before:left-0 before:bg-gradient-to-r before:from-white/10 before:to-code-background before:border-l-2 border-l-white/80 dark:before:border-l-white/50": shouldHighlightLine(
i,
),
},
)}
>
{showLines && (
<span className="select-none text-sm mr-6 opacity-30">{i + 1}</span>
<span className="select-none text-xs mr-6 opacity-30">{i + 1}</span>
)}
{line.map((token, key) => (
<span key={key} {...getTokenProps({token, key})} />
<span key={key} {...getTokenProps({token, key})} className={className} />
))}
</div>
);

View File

@ -1,6 +1,6 @@
import {Button, Link} from "@nextui-org/react";
import {GithubIcon, NpmIcon, AdobeIcon} from "@/components/icons";
import {GithubIcon, NpmIcon, AdobeIcon, StorybookIcon} from "@/components/icons";
import {COMPONENT_PATH, COMPONENT_THEME_PATH} from "@/libs/github/constants";
export interface ComponentLinksProps {
@ -49,6 +49,18 @@ export const ComponentLinks = ({component, reactAria}: ComponentLinksProps) => {
{`@nextui-org/${component}`}
</Button>
<Button
isExternal
as={Link}
className="text-neutral-700 font-normal"
href={`https://storiesv2.nextui.org/?path=/story/components-${component}`}
size="sm"
startIcon={<StorybookIcon className="text-lg text-[#ff4785]" />}
variant="flat"
>
Storybook
</Button>
{reactAria && (
<Button
isExternal

View File

@ -0,0 +1,42 @@
import {NewNextJSIcon, ViteIcon, RemixIcon, AstroIcon} from "@/components/icons";
import {FeaturesGrid} from "@/components/marketing";
const frameworks = [
{
title: "Next.js",
description: "Full-featured React framework with great developer experience.",
icon: <NewNextJSIcon height={40} width={40} />,
href: "/docs/frameworks/nextjs",
},
{
title: "Vite",
description: "Fast and modern development server and build tool.",
icon: <ViteIcon height={40} width={40} />,
href: "/docs/frameworks/vite",
},
{
title: "Remix",
description: "Full stack framework focused on web fundamentals and modern UX.",
icon: <RemixIcon className="text-foreground" height={40} width={40} />,
href: "/docs/frameworks/remix",
},
{
title: "Astro",
description: "The all-in-one web framework designed for speed.",
icon: <AstroIcon className="text-foreground" height={40} width={40} />,
href: "/docs/frameworks/astro",
},
];
export const Frameworks = () => {
return (
<FeaturesGrid
classNames={{
base: "mt-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4",
iconWrapper: "bg-neutral-300/20",
body: "py-0",
}}
features={frameworks}
/>
);
};

View File

@ -2,3 +2,4 @@ export * from "./component-links";
export * from "./package-managers";
export * from "./blockquote";
export * from "./community";
export * from "./frameworks";

View File

@ -35,7 +35,8 @@ export const PackageManagers = ({commands}: PackageManagersProps) => {
<Tabs
aria-label="NextUI installation commands"
classNames={{
base: "mt-4",
base: "group mt-4",
tabList: "h-10",
}}
variant="underlined"
>

View File

@ -12,9 +12,9 @@ export interface DocsTocProps {
const paddingLeftByLevel: Record<string, string> = {
1: "pl-0",
2: "pl-4",
3: "pl-8",
4: "pl-12",
2: "pl-3",
3: "pl-6",
4: "pl-9",
};
export const DocsToc: FC<DocsTocProps> = ({headings}) => {

View File

@ -175,6 +175,7 @@ export const AdobeIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em"
const YarnIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg
aria-hidden="true"
fill="currentColor"
focusable="false"
height={height}
@ -192,6 +193,7 @@ const YarnIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...pro
const PnpmIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg
aria-hidden="true"
fill="currentColor"
focusable="false"
height={height}
@ -208,6 +210,171 @@ const PnpmIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...pro
);
};
const AstroIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg aria-hidden="true" fill="none" focusable="false" height={height} width={width} {...props}>
<path
clipRule="evenodd"
d="M16.986 23.22c-3.498 0-6.986 1.694-6.986 1.694s5.924-16.1 5.937-16.131c.181-.476.457-.783.844-.783h7.11c.386 0 .673.307.843.783l5.936 16.131s-2.843-1.695-6.985-1.695l-2.637-8.14c-.1-.395-.387-.662-.713-.662-.325 0-.614.268-.712.661l-2.637 8.141zm-.572 6.477h.001-.001zM15.3 24.378c-.532 1.751-.16 4.168 1.115 5.319l.017-.061a1.42 1.42 0 00.03-.116c.16-.73.762-1.195 1.524-1.173.741.021 1.135.392 1.25 1.22.042.307.046.62.05.933l.001.098c.01.707.195 1.361.585 1.952a3.4 3.4 0 001.515 1.279l-.018-.06a4.332 4.332 0 00-.03-.1c-.488-1.476-.137-2.49 1.16-3.356l.398-.261c.293-.19.585-.38.867-.586 1.022-.747 1.665-1.732 1.817-3.007a3.404 3.404 0 00-.162-1.547c-.076.045-.148.09-.218.134-.15.094-.29.182-.437.253-1.897.921-3.902 1.035-5.944.73-1.322-.197-2.599-.547-3.52-1.651z"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
};
const NewNextJSIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg aria-hidden="true" fill="none" focusable="false" height={height} width={width} {...props}>
<mask
height="26"
id="nextjs-white_svg__a"
maskUnits="userSpaceOnUse"
style={{
maskType: "alpha",
}}
width="26"
x="7"
y="7"
>
<path
d="M20 33c7.18 0 13-5.82 13-13S27.18 7 20 7 7 12.82 7 20s5.82 13 13 13z"
fill="#000"
/>
</mask>
<g mask="url(#nextjs-white_svg__a)">
<path
d="M20 32.567c6.94 0 12.567-5.627 12.567-12.567S26.94 7.433 20 7.433 7.433 13.06 7.433 20 13.06 32.567 20 32.567z"
fill="#000"
stroke="#fff"
strokeWidth="0.867"
/>
<path
d="M28.596 29.753L16.987 14.8H14.8v10.396h1.75v-8.174l10.672 13.789c.482-.322.94-.676 1.374-1.058z"
fill="url(#nextjs-white_svg__b)"
/>
<path d="M25.344 14.8h-1.733v10.4h1.733V14.8z" fill="url(#nextjs-white_svg__c)" />
</g>
<defs>
<linearGradient
gradientUnits="userSpaceOnUse"
id="nextjs-white_svg__b"
x1="22.744"
x2="27.872"
y1="23.828"
y2="30.183"
>
<stop stopColor="#fff" />
<stop offset="1" stopColor="#fff" stopOpacity="0" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
id="nextjs-white_svg__c"
x1="24.478"
x2="24.449"
y1="14.8"
y2="22.438"
>
<stop stopColor="#fff" />
<stop offset="1" stopColor="#fff" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
);
};
const RemixIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={height}
viewBox="0 0 40 40"
width={width}
{...props}
>
<path
d="M29.397 26.388c.213 2.733.213 4.014.213 5.412h-6.322c0-.305.005-.583.01-.866.018-.878.036-1.794-.107-3.643-.188-2.708-1.354-3.31-3.497-3.31H9.75v-4.926h10.244c2.708 0 4.063-.823 4.063-3.005 0-1.917-1.355-3.08-4.063-3.08H9.75V8.15h11.373c6.13 0 9.177 2.896 9.177 7.521 0 3.46-2.144 5.716-5.04 6.092 2.445.489 3.874 1.88 4.137 4.625z"
fill="currentColor"
/>
<path
d="M9.75 31.8v-3.672h6.685c1.116 0 1.359.828 1.359 1.322v2.35H9.75z"
fill="currentColor"
/>
</svg>
);
};
const ViteIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={height}
viewBox="0 0 40 40"
width={width}
{...props}
>
<path
d="M31.881 11.61L20.886 31.563a.593.593 0 01-1.04.004L8.633 11.611c-.251-.446.125-.987.624-.897l11.007 1.997a.59.59 0 00.212 0l10.777-1.994c.497-.092.875.445.628.893z"
fill="url(#vite_svg__paint0_linear_41_6732)"
/>
<path
d="M25.506 8.096l-8.137 1.618a.302.302 0 00-.241.28l-.5 8.578a.3.3 0 00.365.314l2.265-.531c.212-.05.404.14.36.356l-.673 3.345a.3.3 0 00.38.35l1.399-.43a.3.3 0 01.38.35l-1.07 5.255c-.067.328.364.507.543.226l.12-.189 6.63-13.428c.111-.225-.08-.481-.323-.433l-2.332.456a.301.301 0 01-.344-.381l1.522-5.355a.301.301 0 00-.345-.381z"
fill="url(#vite_svg__paint1_linear_41_6732)"
/>
<defs>
<linearGradient
gradientUnits="userSpaceOnUse"
id="vite_svg__paint0_linear_41_6732"
x1="8.359"
x2="22.306"
y1="10.001"
y2="28.665"
>
<stop stopColor="#41D1FF" />
<stop offset="1" stopColor="#BD34FE" />
</linearGradient>
<linearGradient
gradientUnits="userSpaceOnUse"
id="vite_svg__paint1_linear_41_6732"
x1="19.631"
x2="22.178"
y1="8.535"
y2="25.757"
>
<stop stopColor="#FFEA83" />
<stop offset="0.083" stopColor="#FFDD35" />
<stop offset="1" stopColor="#FFA800" />
</linearGradient>
</defs>
</svg>
);
};
const StorybookIcon: React.FC<IconSvgProps> = ({width = "1em", height = "1em", ...props}) => {
return (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height={height}
viewBox="0 0 512 512"
width={width}
{...props}
>
<g>
<path
d="M356.5,5.2L353.9,63c-0.1,3.2,3.7,5.2,6.3,3.2l22.6-17.1L401.9,64c2.5,1.7,5.8,0,6-3l-2.2-58.8l28.4-2.2 c14.7-1,27.3,10.8,27.3,25.6v460.8c0,14.7-12.3,26.3-26.9,25.6L91.1,496.6c-13.3-0.6-24.1-11.3-24.5-24.7l-16-422.3 c-0.8-14.2,9.9-26.3,24.1-27.1L356.2,4.7L356.5,5.2z M291,198.4c0,10,67.4,5.1,76.6-1.7c0-68.2-36.7-104.3-103.6-104.3 c-67.2,0-104.5,36.8-104.5,91.6c0,94.9,128,96.6,128,148.4c0,15-6.8,23.5-22.4,23.5c-20.5,0-28.8-10.4-27.7-46.1 c0-7.7-77.8-10.3-80.4,0c-5.7,86,47.6,110.9,108.7,110.9c59.6,0,106.1-31.7,106.1-89.1c0-101.7-130.1-99-130.1-149.3 c0-20.7,15.4-23.4,24.1-23.4c9.7,0,26.7,1.5,25.4,39.8L291,198.4z"
fill="currentColor"
/>
</g>
</svg>
);
};
export {
TwitterIcon,
DiscordIcon,
@ -220,4 +387,9 @@ export {
NpmSmallIcon,
PnpmIcon,
YarnIcon,
AstroIcon,
RemixIcon,
ViteIcon,
NewNextJSIcon,
StorybookIcon,
};

View File

@ -70,12 +70,15 @@ export const A11yOtb = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const ref = useRef<any>(null);
const isInView = useInView(ref, {
margin: "-300px",
});
const isMobile = useIsMobile();
const [isDropdownOpen, setIsDropdownOpen] = useState(() => !isMobile);
const isInView = useInView(ref, {
margin: isMobile ? "0px" : "-300px",
});
return (
<section className={sectionWrapper({class: "z-20 mt-16 lg:mt-44"})}>
<div className="flex flex-col gap-8">
@ -139,14 +142,16 @@ export const A11yOtb = () => {
<InfoBoldIcon className="rotate-180" />
</Button>
</Tooltip>
{isInView && ref.current && (
{ref.current && (
<Dropdown
className="shadow-xl"
defaultOpen={!isMobile}
closeOnSelect={true}
isOpen={isDropdownOpen && isInView}
placement="bottom"
portalContainer={ref.current}
shouldBlockScroll={isMobile}
shouldFlip={isMobile}
onOpenChange={(open) => setIsDropdownOpen(open)}
>
<DropdownTrigger>
<Button color="success" variant="flat">

View File

@ -14,14 +14,14 @@ const bannerSuggestions = [
description:
"Make beautiful, modern, and fast websites/applications regardless of your design experience.",
icon: <NoteLinearIcon className="text-pink-500" />,
href: "/docs/guide/getting-started",
href: "/docs/guide/installation",
},
{
title: "NextUI + Next.js",
description:
"NextUI is totally compatible with Next.js you just need to customize the _app.jsx entry file to load the provider.",
icon: <NextJsIcon className="text-pink-500" />,
href: "/docs/guide/nextui-plus-nextjs",
href: "/docs/frameworks/nextjs",
},
];

View File

@ -10,9 +10,11 @@
// import Block from "../templates/example-block";
import {clsx} from "@nextui-org/shared-utils";
import * as Components from "@nextui-org/react";
import {Language} from "prism-react-renderer";
import * as DocsComponents from "@/components/docs/components";
import {VirtualAnchor} from "@/components";
import {Codeblock} from "@/components/docs/components/codeblock";
import {VirtualAnchor, virtualAnchorEncode} from "@/components";
const Table: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
@ -24,7 +26,17 @@ const Table: React.FC<{children?: React.ReactNode}> = ({children}) => {
const Thead: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
<thead className="[&>tr]:h-12 [&>tr>th]:bg-neutral-300 [&>tr>th]:text-neutral-400 [&>tr>th]:text-sm [&>tr>th]:text-left [&>tr>th]:px-4">
<thead
className={clsx(
"[&>tr]:h-12 [&>tr>th]:bg-neutral-100/50",
"[&>tr>th]:align-middle",
"[&>tr>th]:py-0",
"[&>tr>th]:text-neutral-500 [&>tr>th]:text-xs",
"[&>tr>th]:text-left [&>tr>th]:pl-2",
"[&>tr>th:first-child]:rounded-l-lg",
"[&>tr>th:last-child]:rounded-r-lg",
)}
>
{children}
</thead>
);
@ -56,11 +68,15 @@ const LinkedHeading: React.FC<LinkedHeadingProps> = ({as, linked = true, classNa
const level = linkedLevels[as] || 1;
const id = virtualAnchorEncode(props.children as string);
return (
<Component
className={clsx({"linked-heading": linked}, linked ? {} : className)}
data-id={id}
data-level={level}
data-name={props.children}
id={id}
{...props}
>
{linked ? <VirtualAnchor>{props.children}</VirtualAnchor> : <>{props.children}</>}
@ -76,9 +92,18 @@ const List: React.FC<{children?: React.ReactNode}> = ({children}) => {
);
};
const Code = ({children, className}: {children?: React.ReactNode; className?: string}) => {
const isMultiline =
Array.isArray(children) && !!children.find((child) => String(child).includes("\n\n"));
const Code = ({
className,
children,
metastring,
}: {
children?: React.ReactNode;
metastring?: string;
className?: string;
}) => {
const isMultiLine = (children as string).split("\n").length > 2;
const language = (className?.replace(/language-/, "") ?? "jsx") as Language;
const codeString = String(children).trim();
return (
<Components.Snippet
@ -87,17 +112,18 @@ const Code = ({children, className}: {children?: React.ReactNode; className?: st
hideSymbol
classNames={{
base: clsx(
"bg-code-background text-code-foreground",
"px-0 bg-code-background text-code-foreground",
{
"items-start": isMultiline,
"items-start": isMultiLine,
},
className,
),
pre: "font-light text-sm",
copyButton: "text-lg text-code-foreground/50 -mr-2",
pre: "font-light w-full text-sm",
copyButton: "text-lg text-code-foreground/50 mr-2",
}}
codeString={codeString}
>
<div>{children}</div>
<Codeblock codeString={codeString} language={language} metastring={metastring} />
</Components.Snippet>
);
};
@ -112,6 +138,14 @@ const Link = ({href, children}: {href?: string; children?: React.ReactNode}) =>
);
};
const InlineCode = ({children}: {children?: React.ReactNode}) => {
return (
<Components.Code className="font-normal bg-transparent px-0 py-0 text-code-mdx">
{children}
</Components.Code>
);
};
export const MDXComponents = ({
/**
* NextUI components
@ -146,8 +180,7 @@ export const MDXComponents = ({
blockquote: (props: Omit<React.HTMLAttributes<HTMLElement>, "color">) => (
<DocsComponents.Blockquote {...props} />
),
inlineCode: (props: Omit<React.HTMLAttributes<HTMLElement>, "color">) => (
<Components.Code className="font-normal bg-transparent px-0 py-0 text-code-mdx" {...props} />
),
inlineCode: InlineCode,
InlineCode,
// Block,
} as unknown) as Record<string, React.ReactNode>;

View File

@ -8,11 +8,9 @@ url: https://nextui.org/docs/components/avatar
The Avatar component is used to represent a user, and displays the profile picture, initials or fallback icon.
<ComponentLinks
component="avatar"
/>
<ComponentLinks component="avatar" />
## Import
## Import
NextUI exports 3 avatar-related components:
@ -21,7 +19,7 @@ NextUI exports 3 avatar-related components:
- **AvatarIcon**: The default icon used as fallback when the image fails to load.
```jsx
import { Avatar, AvatarGroup, AvatarIcon } from "@nextui-org/react";
import {Avatar, AvatarGroup, AvatarIcon} from "@nextui-org/react";
```
## Usage
@ -72,17 +70,17 @@ import { Avatar, AvatarGroup, AvatarIcon } from "@nextui-org/react";
## Props
| Attribute | Type | Description | Default |
| ------------ | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------- |
| color | "neutral" \| "primary" \| "secondary" \| "success" \| "warning" \| "danger" | Sets the avatar background color. | "neutral" |
| radius | "none" \| "base" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "full" | Sets the avatar border radius. | "full" |
| size | "xs" \| "sm" \| "md" \| "lg" \| "xl" | Sets the avatar size. | "md" |
| name | string | Displays the initials if the image is not provided or fails to load. | - |
| src | string | The source URL of the image to be displayed. | - |
| isBordered | boolean | If `true`, adds a border around the avatar. | false |
| isDisabled | boolean | If `true`, disables the avatar and applies a disabled styling. | false |
| isFocusable | boolean | If `true`, makes the avatar focusable for keyboard navigation. | false |
| icon | node | Displays a custom icon inside the avatar. | - |
| classNames | object | Allows to set custom class names for the avatar base, icon, or text. | - |
| showFallback | boolean | If `true`, shows the fallback icon or initials when the image fails to load. | false |
| fallback | node | A custom fallback component to display when the image fails to load. | - |
| Attribute | Type | Description | Default |
| ------------ | ------------------------------------------------------------ | ---------------------------------------------------------------------------- | --------- |
| src | `string` | The source URL of the image to be displayed. | - |
| color | `neutral` `primary` `secondary` `success` `warning` `danger` | Sets the avatar background color. | `neutral` |
| radius | `none` `base` `sm` `md` `lg` `xl` `2xl` `3xl` `full` | Sets the avatar border radius. | `full` |
| size | `xs` `sm` `md` `lg` `xl` | Sets the avatar size. | `md` |
| name | `string` | Displays the initials if the image is not provided or fails to load. | - |
| icon | `ReactNode` | Displays a custom icon inside the avatar. | - |
| isBordered | `boolean` | If `true`, adds a border around the avatar. | `false` |
| isDisabled | `boolean` | If `true`, disables the avatar and applies a disabled styling. | `false` |
| isFocusable | `boolean` | If `true`, makes the avatar focusable for keyboard navigation. | `false` |
| showFallback | `boolean` | If `true`, shows the fallback icon or initials when the image fails to load. | `false` |
| fallback | `ReactNode` | A custom fallback component to display when the image fails to load. | - |
| classNames | `base` `img` `fallback` `name` `icon` | Allows to set custom class names for the avatar slots. | - |

View File

@ -0,0 +1,96 @@
---
title: Astro
description: How to use NextUI with Astro
url: https://nextui.org/docs/frameworks/astro
---
# Astro
Requirements:
- [React 18](https://reactjs.org/) or later
- [Tailwind CSS 3](https://tailwindcss.com/docs/guides/astro) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
------
## Installation
In your Astro project, run one of the following command to install NextUI:
<PackageManagers
commands={{
npm: 'npm i @nextui-org/react framer-motion',
yarn: 'yarn add @nextui-org/react framer-motion',
pnpm: 'pnpm add @nextui-org/react framer-motion',
}}
/>
<Spacer y={4}/>
### Tailwind CSS Setup
NextUI is built on top of Tailwind CSS, so you need to install Tailwind CSS first. You can follow the official
[installation guide](https://tailwindcss.com/docs/guides/astro) to install Tailwind CSS. Then you need to add
the following code to your `tailwind.config.cjs` file:
```js {8,13-14}
// tailwind.config.cjs
const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [nextui()]
}
```
### Usage
Now you can import NextUI components and use them in your Astro project:
```markdown
---
import Layout from '../layouts/Layout.astro';
import { Button } from '@nextui-org/react';
---
<Layout title="Welcome to Astro.">
<main>
<Button color="primary" client:visible>My button</Button>
</main>
</Layout>
```
Note that you have to add `client:visible` to the component to make it visible only on the client side. Otherwise
some functionalities of NextUI components may not work properly.
### Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
<Blockquote
color="warning"
>
Version 2 is only compatible with React 18 or later. If you are using React 17 or earlier, please use <Link href="https://v1.nextui.org/docs/getting-started" isExternal>version 1 of NextUI</Link>.
</Blockquote>

View File

@ -0,0 +1,156 @@
---
title: Next.js
description: How to use NextUI with Next.js
url: https://nextui.org/docs/frameworks/nextjs
---
# Next.js
Requirements:
- [Next.js 12](https://nextjs.org/) or later
- [React 18](https://reactjs.org/) or later
- [Tailwind CSS 3](https://tailwindcss.com/docs/guides/nextjs) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
------
## Installation
In your Next.js project, run one of the following command to install NextUI:
<PackageManagers
commands={{
npm: 'npm i @nextui-org/react framer-motion',
yarn: 'yarn add @nextui-org/react framer-motion',
pnpm: 'pnpm add @nextui-org/react framer-motion',
}}
/>
<Spacer y={4}/>
### Tailwind CSS Setup
NextUI is built on top of Tailwind CSS, so you need to install Tailwind CSS first. You can follow the official
[installation guide](https://tailwindcss.com/docs/guides/nextjs) to install Tailwind CSS. Then you need to add
the following code to your `tailwind.config.js` file:
```js {8,13-14}
// tailwind.config.js
const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [nextui()]
}
```
After installing NextUI, you need to set up the `NextUIProvider` at the `root` of your application.
### Pages Directory Setup
Go to pages`/_app.js` or `pages/_app.tsx` (create it if it doesn't exist) and wrap the Component with the `NextUIProvider`:
```jsx
// pages/_app.js
import { NextUIProvider } from '@nextui-org/react'
function MyApp({ Component, pageProps }) {
return (
<NextUIProvider>
<Component {...pageProps} />
</NextUIProvider>
)
}
export default MyApp;
```
### App directory Setup
Next.js 13 introduces a new `app/` directory structure. By default it uses Server Components.
As NextUI components use React hooks, we added the `use client;` at build time, so you can import them
directly in your React Server Components (RSC).
Go to your `app/providers.tsx` or `app/providers.jsx` (create it if it doesn't exist) and wrap the Component with the `NextUIProvider`:
```jsx
// app/providers.tsx
'use client'
import { NextUIProvider } from '@nextui-org/react'
export function Providers({children}: { children: React.ReactNode }) {
return (
<NextUIProvider>
{children}
</NextUIProvider>
)
}
```
Now, Go to your `root` layout page and wrap it with the `NextUIProvider`:
```jsx
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({children}: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
```
Now you can import any NextUI component directly in your Server Components without needing to use
the `use client;` directive:
```jsx
// app/page.tsx
import { Button } from '@nextui-org/react'
export default function Page() {
return (
<div>
<Button>Click me</Button>
</div>
)
}
```
### Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
<Blockquote
color="warning"
>
Version 2 is only compatible with React 18 or later. If you are using React 17 or earlier, please use <Link href="https://v1.nextui.org/docs/getting-started" isExternal>version 1 of NextUI</Link>.
</Blockquote>

View File

@ -0,0 +1,114 @@
---
title: Remix
description: How to use NextUI with Remix
url: https://nextui.org/docs/frameworks/remix
---
# Remix
Requirements:
- [React 18](https://reactjs.org/) or later
- [Tailwind CSS 3](https://tailwindcss.com/docs/guides/remix) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
------
## Installation
In your Remix project, run one of the following command to install NextUI:
<PackageManagers
commands={{
npm: 'npm i @nextui-org/react framer-motion',
yarn: 'yarn add @nextui-org/react framer-motion',
pnpm: 'pnpm add @nextui-org/react framer-motion',
}}
/>
<Spacer y={4}/>
### Tailwind CSS Setup
NextUI is built on top of Tailwind CSS, so you need to install Tailwind CSS first. You can follow the official
[installation guide](https://tailwindcss.com/docs/guides/remix) to install Tailwind CSS. Then you need to add
the following code to your `tailwind.config.js` file:
```ts {9,14-15}
// tailwind.config.ts
const { nextui } = require("@nextui-org/react");
import type { Config } from 'tailwindcss'
export default {
content: [
// ...
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [nextui()]
} satisfies Config
```
### Provider Setup
After installing NextUI, you need to set up the `NextUIProvider` at the `root` of your application.
Go to the src directory and inside `root.tsx`, wrap `NextUIProvider` around App:
```tsx {9,19,24}
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
import { NextUIProvider } from "@nextui-org/react";
export default function App() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<NextUIProvider>
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</NextUIProvider>
</body>
</html>
);
}
```
### Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
<Blockquote
color="warning"
>
Version 2 is only compatible with React 18 or later. If you are using React 17 or earlier, please use <Link href="https://v1.nextui.org/docs/getting-started" isExternal>version 1 of NextUI</Link>.
</Blockquote>

View File

@ -0,0 +1,102 @@
---
title: Vite
description: How to use NextUI with Vite
url: https://nextui.org/docs/frameworks/vite
---
# Vite
Requirements:
- [Vite 2](https://vitejs.dev/) or later
- [React 18](https://reactjs.org/) or later
- [Tailwind CSS 3](https://tailwindcss.com/docs/guides/vite#react) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
------
## Installation
In your Vite React project, run one of the following command to install NextUI:
<PackageManagers
commands={{
npm: 'npm i @nextui-org/react framer-motion',
yarn: 'yarn add @nextui-org/react framer-motion',
pnpm: 'pnpm add @nextui-org/react framer-motion',
}}
/>
<Spacer y={4}/>
After installing NextUI, you need do the following steps to complete the installation:
### Tailwind CSS Setup
NextUI is built on top of Tailwind CSS, so you need to install Tailwind CSS first. You can follow the official
[installation guide](https://tailwindcss.com/docs/guides/vite#react) to install Tailwind CSS. Then you need to add
the following code to your `tailwind.config.js` file:
```js {8,13-14}
// tailwind.config.js
const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [nextui()]
}
```
### Provider Setup
After installing NextUI, you need to set up the `NextUIProvider` at the `root` of your application.
Go to the src directory and inside `main.jsx` or `main.tsx`, wrap `NextUIProvider` around App:
```jsx
// main.tsx or main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { NextUIProvider } from '@nextui-org/react'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<NextUIProvider>
<App />
</NextUIProvider>
</React.StrictMode>,
)
```
### Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
<Blockquote
color="warning"
>
Version 2 is only compatible with React 18 or later. If you are using React 17 or earlier, please use <Link href="https://v1.nextui.org/docs/getting-started" isExternal>version 1 of NextUI</Link>.
</Blockquote>

View File

@ -1,7 +1,7 @@
---
title: Installation
description: Get started with NextUI in the official documentation, and learn more about all our features!
url: https://nextui.org/docs/getting-started
url: https://nextui.org/docs/guide/installation
---
# Installation
@ -12,7 +12,7 @@ Requirements:
- [Tailwind CSS 3](https://tailwindcss.com/) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
<Divider />
------
<Spacer y={4}/>
@ -31,16 +31,16 @@ To install NextUI, run one of the following commands in your terminal:
After installing NextUI, you need do the following steps to complete the installation:
### 1. Setup Tailwind CSS
## Tailwind CSS Setup
NextUI is built on top of Tailwind CSS, so you need to install Tailwind CSS first. You can follow the official
[installation guide](https://tailwindcss.com/docs/installation) to install Tailwind CSS. Then you need to add
the following code to your `tailwind.config.js` file:
```js
// tailwind.config.js
const {nextui} = require("@nextui-org/react");
```js {8,13-14}
// tailwind.config.js
const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
@ -56,7 +56,7 @@ module.exports = {
}
```
### 2. Setup Provider
## Provider Setup
To ensure proper functioning of React Aria, NextUI utilizes the [React Aria SSRProvider](https://react-spectrum.adobe.com/react-aria/SSRProvider.html) and [Overlay provider](https://react-spectrum.adobe.com/react-aria/useOverlayTrigger.html#example) internally.
Therefore, it is essential to incorporate the `NextUIProvider` at the `root` of your application.
@ -77,6 +77,20 @@ function App() {
}
```
## Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
<Blockquote
color="warning"
>
@ -86,3 +100,5 @@ function App() {
## Framework Guides
NextUI UI is compatible with your preferred framework. We have compiled comprehensive, step-by-step tutorials for the following frameworks:
<Frameworks />

View File

@ -1,7 +1,7 @@
---
title: Introduction
description: NextUI is a UI library for React that helps you build beautiful and accessible user interfaces. Created on top of Tailwind CSS and React Aria.
url: https://nextui.org/docs/introduction
url: https://nextui.org/docs/guide/introduction
---
# Introduction
@ -16,6 +16,8 @@ NextUI is a UI library for React that helps you build beautiful and accessible u
NextUI's primary goal is to streamline the development process, offering a beautiful and adaptable system design for an enhanced user experience.
------
## FAQ
### Is NextUI a Vercel related project?
@ -32,6 +34,12 @@ style overrides, etc.
a complete solution for building accessible and customizable user interfaces. Since NextUI is built upon TailwindCSS,
you can utilize all TailwindCSS classes within your NextUI components, ensuring optimal compiled CSS size.
### How NextUI deals with TailwindCSS classes conflicts?
We created a TailwindCSS utility library called [tailwind-variants](https://www.tailwind-variants.org/) that automatically
handle TailwindCSS class conflicts. This ensures your custom classes will consistently override the default ones, eliminating any duplication.
### Does NextUI use runtime CSS?
No, NextUI version 2 employs TailwindCSS JIT, which generates CSS at build time, eliminating the need for
@ -47,7 +55,7 @@ NextUI is specifically designed for React and is built on top of React Aria. As
tailored for React applications. However, you can still use the CSS part of NextUI with other frameworks or
libraries, but you will lose the benefits of React Aria and pre-built accessible components that come with NextUI.
<Divider />
------
## Community

View File

@ -0,0 +1,158 @@
---
title: Upgrade to v2
description: Upgrade from NextUI v1 to v2
url: https://nextui.org/docs/guide/upgrade-to-v2
---
# Upgrade to v2
## Installation
Requirements:
- [React 18](https://reactjs.org/) or later
- [Tailwind CSS 3](https://tailwindcss.com/) or later
- [Framer Motion 4](https://www.framer.com/motion/) or later
-----
### Upgrade React version
NextUI v2 requires React 18 or later. To upgrade React, run the following command:
<PackageManagers
commands={{
npm: 'npm i react@latest react-dom@latest',
yarn: 'yarn add react@latest react-dom@latest',
pnpm: 'pnpm add react@latest react-dom@latest',
}}
/>
### Install Framer motion
In v2, NextUI now requires `framer-motion` as a dependency. To install both, use the following command:
<PackageManagers
commands={{
npm: 'npm i framer-motion',
yarn: 'yarn add framer-motion',
pnpm: 'pnpm add framer-motion',
}}
/>
### TailwindCSS Setup
NextUI v2 now uses Tailwind CSS. Add the NextUI plugin to your `tailwind.config.js` file:
```js {9, 13-14}
// tailwind.config.js
const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [nextui()]
}
```
### Provider Setup
The provider setup is essentially the same as v1.
### Setup pnpm (optional)
If you are using pnpm, you need to add the following code to your `.npmrc` file:
```bash
# .npmrc
strict-peer-dependencies=false
enable-pre-post-scripts=true
public-hoist-pattern[]=*@nextui-org/theme*
```
After modfiying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.
## Next.js upgrade steps
Make sure to follow the previous steps since they are required to upgrade to v2.
### Pages Directory Setup
Go to pages`/_app.js` or `pages/_app.tsx` (create it if it doesn't exist) and wrap the Component with the `NextUIProvider`:
```jsx
// pages/_app.js
import { NextUIProvider } from '@nextui-org/react'
function MyApp({ Component, pageProps }) {
return (
<NextUIProvider>
<Component {...pageProps} />
</NextUIProvider>
)
}
export default MyApp;
```
<Blockquote
color="neutral"
>
Make sure to remove all properties from the previous <InlineCode>NextUIProvider</InlineCode> component. Since now the <InlineCode>theme</InlineCode> is handled by the <InlineCode>nextui</InlineCode> Tailwind plugin.
</Blockquote>
### App Directory Setup
Go to your `app/providers.tsx` or `app/providers.jsx` (create it if it doesn't exist) and wrap the Component with the `NextUIProvider`:
```jsx
// app/providers.tsx
'use client'
import { NextUIProvider } from '@nextui-org/react'
export function Providers({children}: { children: React.ReactNode }) {
return (
<NextUIProvider>
{children}
</NextUIProvider>
)
}
```
Now, Go to your `root` layout page and wrap it with the `NextUIProvider`:
```jsx
// app/layout.tsx
import { Providers } from "./providers";
export default function RootLayout({children}: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
```
<Blockquote
color="neutral"
>
Please visit the <Link href="https://github.com/nextui-org/nextui/releases/tag/v2.0.0" showAnchorIcon isExternal>Release Notes</Link> for more information about the new features and breaking changes.
</Blockquote>

View File

@ -1,8 +0,0 @@
---
title: Container
description: A component for fixing an elements width to the current breakpoint.
url: https://nextui.org/docs/layout/container
---
# Container

View File

@ -1,7 +0,0 @@
---
title: Grid
description: The layout grid adapts to screen size and orientation, ensuring consistency across layouts.
url: https://nextui.org/docs/layout/grid
---
# Grid

View File

@ -1,8 +0,0 @@
---
title: Spacer
description: Provide empty space.
url: https://nextui.org/docs/layout/spacer
---
# Spacer

View File

@ -17,6 +17,44 @@
"title": "Installation",
"keywords": "getting started, installation, start, nextui",
"path": "/docs/guide/installation.mdx"
},
{
"key": "upgrade-to-v2",
"title": "Upgrade to v2",
"keywords": "upgrade, v2, nextui",
"path": "/docs/guide/upgrade-to-v2.mdx"
}
]
},
{
"key": "frameworks",
"title": "Frameworks",
"defaultOpen": true,
"keywords": "frameworks, nextjs + nextui, vite + nextui, remix + nextui, astro + nextui",
"routes": [
{
"key": "nextjs",
"title": "Next.js",
"keywords": "nextjs, nextui, next.js",
"path": "/docs/frameworks/nextjs.mdx"
},
{
"key": "vite",
"title": "Vite",
"keywords": "vite, nextui",
"path": "/docs/frameworks/vite.mdx"
},
{
"key": "remix",
"title": "Remix",
"keywords": "remix, nextui",
"path": "/docs/frameworks/remix.mdx"
},
{
"key": "astro",
"title": "Astro",
"keywords": "astro, nextui",
"path": "/docs/frameworks/astro.mdx"
}
]
},
@ -59,32 +97,6 @@
}
]
},
{
"key": "layout",
"title": "Layout",
"keywords": "layout, container, grid, spacer, flex",
"defaultOpen": true,
"routes": [
{
"key": "container",
"title": "Container",
"keywords": "cont, container, flex, media query",
"path": "/docs/layout/container.mdx"
},
{
"key": "grid",
"title": "Grid",
"keywords": "grid, container, media query",
"path": "/docs/layout/grid.mdx"
},
{
"key": "spacer",
"title": "Spacer",
"keywords": "space, spacer, gap",
"path": "/docs/layout/spacer.mdx"
}
]
},
{
"key": "components",
"title": "Components",

View File

@ -52,11 +52,11 @@ export const DocsLayout: FC<DocsLayoutProps> = ({
<Head {...meta} />
<Navbar />
<main className="container mx-auto max-w-7xl min-h-[calc(100dvh_-_64px_-_108px)] px-6 mb-12">
<div className="flex">
<div className="hidden relative lg:block lg:w-[30%] mt-8 pr-4">
<div className="flex xl:gap-12">
<div className="hidden relative lg:block lg:w-[30%] mt-8 pr-8">
<DocsSidebar routes={routes} slug={slug} tag={tag} />
</div>
<div className="w-full xl:px-12 mt-10">
<div className="w-full mt-10">
<div className="w-full prose prose-neutral">{children}</div>
<FooterNav nextRoute={nextRoute} prevRoute={prevRoute} tag={tag} />
<footer>
@ -92,7 +92,7 @@ export const DocsLayout: FC<DocsLayoutProps> = ({
<div
aria-hidden="true"
className="fixed hidden dark:md:block dark:opacity-70 -top-[60%] -right-[45%] z-[-10] rotate-12"
className="fixed hidden dark:md:block dark:opacity-70 -top-[80%] -right-[60%] 2xl:-top-[60%] 2xl:-right-[45%] z-[-10] rotate-12"
>
<Image
removeWrapper

View File

@ -1,7 +1,6 @@
import React from "react";
import NextHead from "next/head";
import {useTheme} from "next-themes";
import capitalize from "lodash/capitalize";
import {TWITTER_USER_NAME, SITE_URL} from "@/libs/constants";
@ -30,7 +29,7 @@ export const Head: React.FC<HeaderProps> = ({
const {theme} = useTheme();
const isDark = theme === "dark";
let pageTitle = title ? `${capitalize(title)} | ` : "";
let pageTitle = title ? `${title} | ` : "";
pageTitle += "NextUI - Beautiful, fast and modern React UI Library";

View File

@ -78,7 +78,7 @@ export const Navbar = () => {
<NextLink href="/">
<NextUILogo auto />
</NextLink>
{isMounted && ref.current && (
{isMounted && ref.current ? (
<Dropdown placement="bottom-start" portalContainer={ref.current}>
<DropdownTrigger>
<Button
@ -100,6 +100,8 @@ export const Navbar = () => {
<DropdownItem key="v1">v1.0.0</DropdownItem>
</DropdownMenu>
</Dropdown>
) : (
<div className="w-[66px]" />
)}
</NavbarBrand>
<div className="hidden lg:flex gap-4 ml-10 justify-start">

View File

@ -4,7 +4,8 @@ const codeTheme: PrismTheme = {
plain: {
backgroundColor: "hsl(var(--nextui-code-background))",
color: "#F4F4F4",
fontWeight: "500",
fontWeight: "400",
fontSize: "14px",
fontStyle: "normal",
textRendering: "geometricPrecision",
},
@ -24,13 +25,7 @@ const codeTheme: PrismTheme = {
{
types: ["punctuation"],
style: {
color: "#17c964",
},
},
{
types: ["property"],
style: {
color: "#17c964",
color: "#a2e9c1",
},
},
{
@ -51,6 +46,12 @@ const codeTheme: PrismTheme = {
color: "#E5C07B",
},
},
{
types: ["property", "function"],
style: {
color: "#61AFEF",
},
},
{
types: ["tag-id", "selector", "atrule-id"],
style: {
@ -60,7 +61,7 @@ const codeTheme: PrismTheme = {
{
types: ["attr-name"],
style: {
color: "#c4841d",
color: "#f9c97c",
},
},
{
@ -70,7 +71,6 @@ const codeTheme: PrismTheme = {
"entity",
"url",
"attr-value",
"keyword",
"control",
"directive",
"unit",
@ -93,7 +93,7 @@ const codeTheme: PrismTheme = {
{
types: ["language-javascript", "script"],
style: {
color: "#17c964",
color: "#a2e9c1",
},
},
{
@ -115,7 +115,7 @@ const codeTheme: PrismTheme = {
},
},
{
types: ["important", "primitive"],
types: ["important", "primitive", "keyword"],
style: {
color: "#c678dd",
},

View File

@ -99,12 +99,8 @@ export const getStaticProps: GetStaticProps = async ({params}) => {
doc = content.toString();
meta = data;
}
const mdxSource = await serialize(doc, {
mdxOptions: {
remarkPlugins: [require("remark-autolink-headings"), require("remark-slug")],
rehypePlugins: [require("@mapbox/rehype-prism")],
},
});
const mdxSource = await serialize(doc);
const routes = manifest.routes.map((route: any) => {
if (route.icon) {

View File

@ -7,13 +7,16 @@
@apply text-code-foreground;
}
.code-block .token.tag,
.code-block .token.selector,
.code-block .token.selector .class,
.code-block .token.function {
@apply text-code-syntax1;
}
.code-block .token.tag {
@apply text-code-tag
}
.code-block .token.script.language-javascript {
@apply text-code-foreground;
}

View File

@ -19,35 +19,35 @@ module.exports = {
extend: {
colors: {
"code-foreground": "#F4F4F4",
"code-syntax1": "#ADD7FF",
"code-syntax2": "#5DE4C7",
"code-syntax1": "#61AFEF",
"code-syntax2": "#98C379",
"code-syntax3": "#c678dd",
"code-syntax4": "#91B4D5",
"code-syntax5": "#5DE4C7",
"code-syntax6": "#91B4D5",
"code-removed": commonColors.red[300],
"code-string": "#5DE4C7",
"code-syntax4": "#d4d4d8",
"code-syntax5": "#E5C07B",
"code-syntax6": "#F5A524",
"code-removed": '#f871a0',
"code-string": "#98C379",
"code-class": "#91B4D5",
"code-punctuation": commonColors.green[200],
"code-number": "#91B4D5",
"code-added": commonColors.green[300],
"code-line-number": twColors.zinc[300],
"code-faded-line": twColors.zinc[500],
"code-comment": twColors.zinc[500],
"code-punctuation": '#a2e9c1',
"code-number": "#E5C07B",
"code-added": '#74dfa2',
"code-line-number": '#d4d4d8',
"code-faded-line": '#71717a',
"code-comment": '#71717a',
"code-keyword": "#c678dd",
"code-function": "#ADD7FF",
"code-function": "#61AFEF",
"code-tag": "#E06C75",
"code-attr-name": "#91B4D5",
"code-language-javascript": "#91B4D5",
"code-highlighted-word1-bg": commonColors.purple[500],
"code-highlighted-word1-bg-active": commonColors.purple[600],
"code-highlighted-word1-text": commonColors.purple[800],
"code-highlighted-word2-bg": commonColors.red[100],
"code-highlighted-word2-bg-active": commonColors.red[500],
"code-highlighted-word2-text": commonColors.red[200],
"code-highlighted-word3-bg": commonColors.green[300],
"code-highlighted-word3-bg-active": commonColors.green[300],
"code-highlighted-word3-text": commonColors.green[100],
"code-highlighted-word1-bg": '#7828c8',
"code-highlighted-word1-bg-active": '#6020a0',
"code-highlighted-word1-text": '#301050',
"code-highlighted-word2-bg": '#fdd0df',
"code-highlighted-word2-bg-active": '#f31260',
"code-highlighted-word2-text": '#faa0bf',
"code-highlighted-word3-bg": '#74dfa2',
"code-highlighted-word3-bg-active": '#74dfa2',
"code-highlighted-word3-text": '#d1f4e0',
},
boxShadow: {
highlighted: `${commonColors.purple[500]} 1px 0 0, ${commonColors.purple[500]} -1px 0 0`,
@ -180,21 +180,17 @@ module.exports = {
lineHeight: theme("fontSize.sm")[1].lineHeight,
},
thead: {
color: theme("colors.slate.700"),
borderBottomColor: theme("colors.slate.200"),
border: 'none',
},
"thead th": {
paddingTop: 0,
fontWeight: theme("fontWeight.semibold"),
},
"tbody tr": {
borderBottomColor: theme("colors.slate.100"),
border: 'none',
},
"tbody tr:last-child": {
borderBottomWidth: "1px",
},
"tbody code": {
fontSize: theme("fontSize.xs")[0],
border: 'none',
},
"figure figcaption": {
textAlign: "center",
@ -314,7 +310,7 @@ module.exports = {
},
dark: {
"code-background": "#111111",
"code-mdx": "#c678dd",
"code-mdx": "#06B7DB",
},
},
}),

View File

@ -15,7 +15,7 @@ export function getHeadings(): Heading[] {
return Array.from(headings).map((h) => {
return {
id: h.id,
id: h.getAttribute("data-id"),
text: h.getAttribute("data-name"),
level: h.getAttribute("data-level"),
};

View File

@ -1,26 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock

View File

@ -1,70 +0,0 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

View File

@ -1,44 +0,0 @@
{
"name": "with-tailwindcss",
"version": "0.1.0",
"private": true,
"dependencies": {
"@nextui-org/react": "^2.0.0-beta.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^13.5.0",
"react": "^18.0.0",
"react-dom": "^17.0.2,
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"autoprefixer": "^10.4.5",
"postcss": "^8.4.12",
"tailwindcss": "^3.0.24"
}
}

View File

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -1,31 +0,0 @@
import './App.css';
import { Container, Link, Button } from '@nextui-org/react';
function App() {
return (
<Container className="flex flex-col h-screen justify-center items-center">
<h1 className="text-7xl mb-10 font-bold">
Welcome to&nbsp;
<Link href="https://tailwindcss.com/" rel="noreferer" target="_blank">
tailwindcss
</Link>
&nbsp;+&nbsp;
<Link
href="https://nextui.org?utm_source=with-tailwindcss-example"
target="_blank"
>
NextUI
</Link>
</h1>
<Button
href="https://nextui.org?utm_source=with-tailwindcss-example"
target="_blank"
as="a"
>
Go to NextUI
</Button>
</Container>
);
}
export default App;

View File

@ -1,8 +0,0 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -1,16 +0,0 @@
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@ -1,14 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { NextUIProvider } from '@nextui-org/react';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<NextUIProvider>
<App />
</NextUIProvider>
</React.StrictMode>
);

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,13 +0,0 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@ -1,7 +0,0 @@
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {}
},
plugins: []
};

View File

@ -50,6 +50,7 @@ export function useDropdown(props: UseDropdownProps) {
closeOnSelect = true,
classNames: classNamesProp,
disableAnimation = false,
onClose,
className,
...otherProps
} = props;
@ -64,7 +65,17 @@ export function useDropdown(props: UseDropdownProps) {
const triggerId = useId();
const menuId = useId();
const state = useMenuTriggerState({trigger, isOpen, defaultOpen, onOpenChange});
const state = useMenuTriggerState({
trigger,
isOpen,
defaultOpen,
onOpenChange: (isOpen) => {
onOpenChange?.(isOpen);
if (!isOpen) {
onClose?.();
}
},
});
const {menuTriggerProps, menuProps} = useMenuTrigger(
{type, trigger, isDisabled},

View File

@ -494,7 +494,7 @@ const CustomTriggerTemplate: ComponentStory<any> = ({variant, ...args}) => {
<DropdownTrigger>
<Avatar
isBordered
as="button"
isFocusable
className="transition-transform"
color="secondary"
size="lg"
@ -526,7 +526,7 @@ const CustomTriggerTemplate: ComponentStory<any> = ({variant, ...args}) => {
<Dropdown {...args} placement="bottom-start">
<DropdownTrigger>
<User
as="button"
isFocusable
avatarProps={{
isBordered: true,
color: "primary",

View File

@ -0,0 +1,105 @@
import {RefObject, useLayoutEffect} from "react";
import {
AriaPopoverProps,
useOverlay,
PopoverAria,
usePreventScroll,
useOverlayPosition,
} from "@react-aria/overlays";
import {OverlayPlacement, toReactAriaPlacement, ariaHideOutside} from "@nextui-org/aria-utils";
import {OverlayTriggerState} from "@react-stately/overlays";
import {mergeProps} from "@react-aria/utils";
export interface Props {
/**
* Whether the element should render an arrow.
* @default false
*/
showArrow?: boolean;
/**
* The placement of the element with respect to its anchor element.
* @default 'top'
*/
placement?: OverlayPlacement;
/**
* A ref for the scrollable region within the overlay.
* @default popoverRef
*/
scrollRef?: RefObject<HTMLElement>;
}
export type ReactAriaPopoverProps = Props & Omit<AriaPopoverProps, "placement">;
/**
* Provides the behavior and accessibility implementation for a popover component.
* A popover is an overlay element positioned relative to a trigger.
*/
export function useReactAriaPopover(
props: ReactAriaPopoverProps,
state: OverlayTriggerState,
): PopoverAria {
const {
triggerRef,
popoverRef,
showArrow,
offset = 7,
crossOffset = 0,
scrollRef,
shouldFlip,
placement: placementProp = "top",
containerPadding,
isNonModal: isNonModalProp,
isKeyboardDismissDisabled,
...otherProps
} = props;
const isNonModal = isNonModalProp || true;
const {overlayProps, underlayProps} = useOverlay(
{
isOpen: state.isOpen,
onClose: state.close,
shouldCloseOnBlur: true,
isDismissable: !isNonModal,
isKeyboardDismissDisabled,
shouldCloseOnInteractOutside(element) {
// Don't close if the click is within the trigger or the popover itself
let trigger = triggerRef?.current;
return !trigger || !trigger.contains(element);
},
},
popoverRef,
);
const {overlayProps: positionProps, arrowProps, placement} = useOverlayPosition({
...otherProps,
scrollRef,
crossOffset,
shouldFlip,
containerPadding,
targetRef: triggerRef,
overlayRef: popoverRef,
isOpen: state.isOpen,
placement: toReactAriaPlacement(placementProp),
offset: showArrow ? offset + 3 : offset,
onClose: () => {},
});
usePreventScroll({
isDisabled: isNonModal,
});
useLayoutEffect(() => {
if (state.isOpen && !isNonModal && popoverRef.current) {
return ariaHideOutside([popoverRef.current]);
}
}, [isNonModal, state.isOpen, popoverRef]);
return {
popoverProps: mergeProps(overlayProps, positionProps),
arrowProps,
underlayProps,
placement,
};
}

View File

@ -1,29 +1,21 @@
import type {PopoverVariantProps, SlotsToClasses, PopoverSlots} from "@nextui-org/theme";
import type {HTMLMotionProps} from "framer-motion";
import type {RefObject, Ref} from "react";
import type {OverlayPlacement} from "@nextui-org/aria-utils";
import {OverlayTriggerState, useOverlayTriggerState} from "@react-stately/overlays";
import {useFocusRing} from "@react-aria/focus";
import {
AriaPopoverProps,
useOverlayTrigger,
usePopover as useReactAriaPopover,
usePreventScroll,
} from "@react-aria/overlays";
import {useOverlayTrigger, usePreventScroll} from "@react-aria/overlays";
import {OverlayTriggerProps} from "@react-types/overlays";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {
toReactAriaPlacement,
getArrowPlacement,
getShouldUseAxisPlacement,
} from "@nextui-org/aria-utils";
import {getArrowPlacement, getShouldUseAxisPlacement} from "@nextui-org/aria-utils";
import {popover} from "@nextui-org/theme";
import {mergeProps, mergeRefs} from "@react-aria/utils";
import {createDOMRef} from "@nextui-org/dom-utils";
import {ReactRef, clsx, dataAttr} from "@nextui-org/shared-utils";
import {useId, useMemo, useCallback, useImperativeHandle, useRef} from "react";
import {useReactAriaPopover, ReactAriaPopoverProps} from "./use-aria-popover";
export interface Props extends HTMLNextUIProps<"div"> {
/**
* Ref to the DOM node.
@ -33,25 +25,10 @@ export interface Props extends HTMLNextUIProps<"div"> {
* The controlled state of the popover.
*/
state?: OverlayTriggerState;
/**
* A ref for the scrollable region within the overlay.
* @default popoverRef
*/
scrollRef?: RefObject<HTMLElement>;
/**
* The ref for the element which the overlay positions itself with respect to.
*/
triggerRef?: RefObject<HTMLElement>;
/**
* The placement of the element with respect to its anchor element.
* @default 'top'
*/
placement?: OverlayPlacement;
/**
* Whether the element should render an arrow.
* @default false
*/
showArrow?: boolean;
/**
* Whether the scroll event should be blocked when the overlay is open.
* @default true
@ -91,7 +68,7 @@ export interface Props extends HTMLNextUIProps<"div"> {
}
export type UsePopoverProps = Props &
Omit<AriaPopoverProps, "placement" | "triggerRef" | "popoverRef"> &
Omit<ReactAriaPopoverProps, "triggerRef" | "popoverRef"> &
OverlayTriggerProps &
PopoverVariantProps;
@ -162,9 +139,8 @@ export function usePopover(originalProps: UsePopoverProps) {
{
triggerRef,
popoverRef,
isNonModal: true,
placement: toReactAriaPlacement(placementProp),
offset: showArrow ? offset + 3 : offset,
placement: placementProp,
offset: offset,
scrollRef,
crossOffset,
shouldFlip,

View File

@ -62,6 +62,10 @@ export interface UseSnippetProps
* @default false
*/
autoFocus?: boolean;
/**
* The code string to copy. if `codeString` is passed, it will be copied instead of the children.
*/
codeString?: string;
/**
* Whether to hide the tooltip.
* @default false
@ -126,6 +130,7 @@ export function useSnippet(originalProps: UseSnippetProps) {
timeout,
copyIcon,
checkIcon,
codeString,
disableCopy = false,
disableTooltip = false,
hideCopyButton = false,
@ -198,11 +203,11 @@ export function useSnippet(originalProps: UseSnippetProps) {
value = children.join("\n");
}
const valueToCopy = value || preRef.current?.textContent || "";
const valueToCopy = codeString || value || preRef.current?.textContent || "";
copy(valueToCopy);
onCopyProp?.(valueToCopy);
}, [copy, disableCopy, onCopyProp, children]);
}, [copy, codeString, disableCopy, onCopyProp, children]);
const defaultCopyButtonProps: ButtonProps = {
"aria-label": "Copy to clipboard",

View File

@ -44,6 +44,7 @@
"@nextui-org/theme": "workspace:*",
"@nextui-org/aria-utils": "workspace:*",
"@nextui-org/framer-transitions": "workspace:*",
"@nextui-org/use-is-mounted": "workspace:*",
"@react-aria/focus": "^3.12.0",
"@react-aria/interactions": "^3.15.0",
"@react-aria/tabs": "^3.5.0",

View File

@ -8,6 +8,7 @@ import {useTab} from "@react-aria/tabs";
import {useHover} from "@react-aria/interactions";
import {motion} from "framer-motion";
import {TRANSITION_EASINGS} from "@nextui-org/framer-transitions";
import {useIsMounted} from "@nextui-org/use-is-mounted";
import {useTabsContext} from "./tabs-context";
@ -57,6 +58,10 @@ const TabItem = forwardRef<TabItemProps, "div">((props, ref) => {
const ariaControls = item?.props.children ? `${tabPanelId}-${key}` : undefined;
const [, isMounted] = useIsMounted({
rerender: true,
});
return (
<Component
ref={domRef}
@ -86,11 +91,11 @@ const TabItem = forwardRef<TabItemProps, "div">((props, ref) => {
}}
onClick={chain(onClick, tabProps.onClick)}
>
{isSelected && !disableAnimation && !disableCursor ? (
{isSelected && !disableAnimation && !disableCursor && isMounted ? (
<motion.span
className={slots.cursor({class: classNames?.cursor})}
layoutDependency={false}
layoutId="tab-item-cursor"
layoutId="cursor"
transition={{
ease: TRANSITION_EASINGS.softSpring,
duration: 0.6,

View File

@ -1,4 +1,5 @@
import {forwardRef, ForwardedRef, ReactElement, Ref} from "react";
import {forwardRef, ForwardedRef, ReactElement, Ref, useId} from "react";
import {LayoutGroup} from "framer-motion";
import {TabsProvider} from "./tabs-context";
import {UseTabsProps, useTabs} from "./use-tabs";
@ -10,13 +11,25 @@ interface Props<T> extends Omit<UseTabsProps<T>, "ref"> {}
function Tabs<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLDivElement>) {
const {Component, state, context, getBaseProps, getTabListProps} = useTabs<T>({ref, ...props});
const layoutId = useId();
const layoutGroupEnabled = !props.disableAnimation && !props.disableCursor;
return (
<TabsProvider value={context}>
<div {...getBaseProps()}>
<Component {...getTabListProps()}>
{[...state.collection].map((item) => (
<TabItem key={item.key} item={item} {...item.props} />
))}
{layoutGroupEnabled ? (
<LayoutGroup id={layoutId}>
{[...state.collection].map((item) => (
<TabItem key={item.key} item={item} {...item.props} />
))}
</LayoutGroup>
) : (
[...state.collection].map((item) => (
<TabItem key={item.key} item={item} {...item.props} />
))
)}
</Component>
</div>
<TabPanel key={state.selectedItem?.key} />

View File

@ -0,0 +1,170 @@
/**
* This code comes from @react-aria/overlays
*/
// Keeps a ref count of all hidden elements. Added to when hiding an element, and
// subtracted from when showing it again. When it reaches zero, aria-hidden is removed.
let refCountMap = new WeakMap<Element, number>();
let observerStack: any = [];
/**
* Hides all elements in the DOM outside the given targets from screen readers using aria-hidden,
* and returns a function to revert these changes. In addition, changes to the DOM are watched
* and new elements outside the targets are automatically hidden.
* @param targets - The elements that should remain visible.
* @param root - Nothing will be hidden above this element.
* @returns - A function to restore all hidden elements.
*/
export function ariaHideOutside(targets: Element[], root = document.body) {
let visibleNodes = new Set<Element>(targets);
let hiddenNodes = new Set<Element>();
let walk = (root: Element) => {
// Keep live announcer and top layer elements (e.g. toasts) visible.
// @ts-ignore
for (let element of root.querySelectorAll(
"[data-live-announcer], [data-react-aria-top-layer]",
)) {
visibleNodes.add(element);
}
let acceptNode = (node: Element) => {
const parentElement = node.parentElement as HTMLElement;
// Skip this node and its children if it is one of the target nodes, or a live announcer.
// Also skip children of already hidden nodes, as aria-hidden is recursive. An exception is
// made for elements with role="row" since VoiceOver on iOS has issues hiding elements with role="row".
// For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).
if (
visibleNodes.has(node) ||
(hiddenNodes.has(parentElement) && parentElement.getAttribute("role") !== "row")
) {
return NodeFilter.FILTER_REJECT;
}
// Skip this node but continue to children if one of the targets is inside the node.
for (let target of visibleNodes) {
if (node.contains(target)) {
return NodeFilter.FILTER_SKIP;
}
}
return NodeFilter.FILTER_ACCEPT;
};
let walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {acceptNode});
// TreeWalker does not include the root.
let acceptRoot = acceptNode(root);
if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
hide(root);
}
if (acceptRoot !== NodeFilter.FILTER_REJECT) {
let node = walker.nextNode() as Element;
while (node != null) {
hide(node);
node = walker.nextNode() as Element;
}
}
};
let hide = (node: Element) => {
let refCount = refCountMap.get(node) ?? 0;
// If already aria-hidden, and the ref count is zero, then this element
// was already hidden and there's nothing for us to do.
if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
return;
}
if (refCount === 0) {
node.setAttribute("aria-hidden", "true");
}
hiddenNodes.add(node);
refCountMap.set(node, refCount + 1);
};
// If there is already a MutationObserver listening from a previous call,
// disconnect it so the new on takes over.
if (observerStack.length) {
observerStack[observerStack.length - 1].disconnect();
}
walk(root);
let observer = new MutationObserver((changes: any) => {
for (let change of changes) {
if (change.type !== "childList" || change.addedNodes.length === 0) {
continue;
}
// If the parent element of the added nodes is not within one of the targets,
// and not already inside a hidden node, hide all of the new children.
if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
for (let node of change.removedNodes) {
if (node instanceof Element) {
visibleNodes.delete(node);
hiddenNodes.delete(node);
}
}
for (let node of change.addedNodes) {
if (
(node instanceof HTMLElement || node instanceof SVGElement) &&
(node.dataset.liveAnnouncer === "true" || node.dataset.reactAriaTopLayer === "true")
) {
visibleNodes.add(node);
} else if (node instanceof Element) {
walk(node);
}
}
}
}
});
observer.observe(root, {childList: true, subtree: true});
let observerWrapper = {
observe() {
observer.observe(root, {childList: true, subtree: true});
},
disconnect() {
observer.disconnect();
},
};
observerStack.push(observerWrapper);
return () => {
observer.disconnect();
for (let node of hiddenNodes) {
let count = refCountMap.get(node);
if (count == null) {
continue;
}
if (count === 1) {
node.removeAttribute("aria-hidden");
refCountMap.delete(node);
} else {
refCountMap.set(node, count - 1);
}
}
// Remove this observer from the stack, and start the previous one.
if (observerWrapper === observerStack[observerStack.length - 1]) {
observerStack.pop();
if (observerStack.length) {
observerStack[observerStack.length - 1].observe();
}
} else {
observerStack.splice(observerStack.indexOf(observerWrapper), 1);
}
};
}

View File

@ -1,2 +1,3 @@
export * from "./types";
export * from "./utils";
export * from "./ariaHideOutside";

3
pnpm-lock.yaml generated
View File

@ -1647,6 +1647,9 @@ importers:
'@nextui-org/theme':
specifier: workspace:*
version: link:../../core/theme
'@nextui-org/use-is-mounted':
specifier: workspace:*
version: link:../../hooks/use-is-mounted
'@react-aria/focus':
specifier: ^3.12.0
version: 3.12.0(react@18.2.0)