mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
* feat: Slider Component (#1686) * feat(slider): initial structure * chore(slider): readme improved * fix: fixed border color of slider track * docs: added range story * feat: added rtl support to slider * feat: improved value label formatting * feat: refactor styling and add colors support to slider * chore: improved thumb color * docs: improved slider stories * chore(slider): default color changed by foreground * Update packages/core/theme/src/components/slider.ts Co-authored-by: Jakob Guddas <github@jguddas.de> * feat: added fillOffset prop * Update packages/components/slider/src/use-slider.ts * fix(slider): animation * Update packages/components/slider/src/use-slider.ts * Update packages/core/theme/src/components/slider.ts * feat: slider steps * refactor: renamed variables * feat: improved slider step styling * fix: hide infinite steps * fix: fixed step transparency issue * fix: fixed thumb focus issue * Update packages/components/slider/src/use-slider.ts Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * feat(slider): vertical orientation added, start & end content, bug fixes * chore(slider): tests added * fix(docs): scrollbar added to the sidebar (#1743) * feat(slider): marks added * chore(slider): example ts-doc changed * feat(slider): vertical marks support * feat(core): slider tooltip support added, popover modified (#1746) * Feat/slider custom styles (#1751) * feat(slider): custom styles story added * fix(slider): custom styles on different sizes * Fix/slider tooltip android position (#1753) * feat(slider): custom styles story added * fix(slider): tooltip update position dependecies added * fix(popover): arrow placements improved * feat(slider): docs started, custom thumb and custom output stories added * feat(slider): render function added to custom render the slider parts * feat(slider): docs in progress, new properties and examples added * fix(slider): some issues fixed, output renamed to value, documentation improved * feat(slider): docs done * chore: changeset --------- Co-authored-by: Jakob Guddas <github@jguddas.de> * Client side routing (#1764) * feat(slider): initial structure * chore(slider): readme improved * fix: fixed border color of slider track * docs: added range story * feat: added rtl support to slider * feat: improved value label formatting * feat: refactor styling and add colors support to slider * chore: improved thumb color * docs: improved slider stories * chore(slider): default color changed by foreground * Update packages/core/theme/src/components/slider.ts Co-authored-by: Jakob Guddas <github@jguddas.de> * feat: added fillOffset prop * Update packages/components/slider/src/use-slider.ts * fix(slider): animation * Update packages/components/slider/src/use-slider.ts * Update packages/core/theme/src/components/slider.ts * feat: slider steps * refactor: renamed variables * feat: improved slider step styling * fix: hide infinite steps * fix: fixed step transparency issue * fix: fixed thumb focus issue * Update packages/components/slider/src/use-slider.ts Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * feat(slider): vertical orientation added, start & end content, bug fixes * chore(slider): tests added * feat(slider): marks added * chore(slider): example ts-doc changed * feat(slider): vertical marks support * feat(core): slider tooltip support added, popover modified (#1746) * Feat/slider custom styles (#1751) * feat(slider): custom styles story added * fix(slider): custom styles on different sizes * Fix/slider tooltip android position (#1753) * feat(slider): custom styles story added * fix(slider): tooltip update position dependecies added * fix(popover): arrow placements improved * feat(slider): docs started, custom thumb and custom output stories added * feat(slider): render function added to custom render the slider parts * feat(slider): docs in progress, new properties and examples added * fix(slider): some issues fixed, output renamed to value, documentation improved * feat(slider): docs done * chore: changeset * chore: react aria packages upgraded * feat(system): router provider added * feat: client side routing support added to items components * chore(docs): routing docs started * chore: changeset * feat(docs): client side routing documented * feat(pagination): client router support added to pagination * fix(link): csr added --------- Co-authored-by: Jakob Guddas <github@jguddas.de> * feat: added RTL support to accordion component (#1725) * feat: added RTL support to accordion component * Create healthy-cobras-crash.md * feat: added RTL support to avatar and avatar group component (#1727) * feat: added RTL support to avatar and avatar group components * Create tender-penguins-love.md * Update tender-penguins-love.md * feat: added RTL support to button group component (#1726) * feat: added RTL support to button component * feat: added RTL support to button component * Create orange-bobcats-kneel.md * Update button.ts * refactor(root): styles refactor (#1688) * refactor(root): styles refactor * chore(tabs): remove needless type extends * fix(tabs): typecheck * Breadcrumbs component (#1794) * feat(components): breadcrumbs initial structure * feat(breadcrumbs): tests and examples added * feat(docs): breadcrumbs documentation done * feat(docs): breadcrumbs docs done * chore(breadcrumbs): readme changed * fix(slider): Place last dragged slider handle over the other handle (#1778) * Set `data-focused` in handle * Style focused handle on top * Add tests * fix(core): tests and build * fix(button): show only spinner if button isLoading & isIconOnly (#1800) * fix(button): show only spinner if button isLoading & isIconOnly * chore: add changeset * fix(button): remove repeating code * Fix blur/opaque Popover (#1812) * fix(popover): double underlay click * chore: use ref instead of a state * Feat/controlled scroll shadow (#1819) * feat(scroll-shadow): logic improved, controlled support added * feat(scroll-shadow): controlled visibility * fix(scroll-shadow): visibility tests * Autocomplete component (Single Selection) (#1797) * feat(autocomplete): initial structure * feat(core): use press custom hook implemented, autocomplete improved, input and select animations fixed * chore(button): add console.log on press * feat(core): select & input label position, autocomplete improvements, listbox and menu empty state * chore: tailwind-variants upgraded * chore: autocomplete stories in progress * fix: input and select label placement * fix(autocomplete): popover trigger * chore(root): react-aria pkgs and framer-motion upgraded * fix(input,select): outside label scale removed * feat(autocomplete): more stories and use cases supported * fix(core): custom items support on collection-based comps, menu generic items support added * chore(core): tailwind-variants upgraded, custom tv function adapted * chore(docs): select on-change controlled e.g. changed, sandpack theme fixed * chore(autocomplete): docs started, tests added * chore: in progress * feat(menu): top and end content added, autocomplete docs improved * feat(docs): autocomplete documentation almost done * fix(input): outside styles * feat(docs): autocomplete async filtering added * chore(docs): autocomplete highlighted lines added * feat(docs): autocomplete a11y and slots added * feat(docs): autocomplete a11y and slots added * feat(docs): autocomplete docs done * fix(root): peer dependencies (#1848) * Fix/small issues (#1851) * fix(input): visible outline * fix(button): button group radius prop * chore(navbar): menu item active status added * fix(docs): pagination missing keys custom items * fix(core): extendVariants support added to collection-based components * fix(theme): card body padding * chore(theme): transition added to menu/listbox items * fix(input): textarea styles fixed, new prop added * chore(input): textarea height animated * feat(button): hover opacity added * chore(textarea): hide scroll when the limit hasn't been reached * chore(docs): updated badge added to textarea * feat(docs): blog v2.2.0 image and entry added * fix(avatar): ring primary color * chore(docs): tabs added again, autocomplete iframes removed --------- Co-authored-by: Jakob Guddas <github@jguddas.de> Co-authored-by: Maurici Abad Gutierrez <hello@mauriciabad.com> Co-authored-by: Ivan Kucher <dev.kucher@gmail.com>
360 lines
11 KiB
TypeScript
360 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import {useRef, useState, FC, ReactNode, Key} from "react";
|
|
import {
|
|
link,
|
|
Navbar as NextUINavbar,
|
|
NavbarContent,
|
|
NavbarMenu,
|
|
NavbarMenuToggle,
|
|
NavbarBrand,
|
|
NavbarItem,
|
|
Link,
|
|
Button,
|
|
Kbd,
|
|
Dropdown,
|
|
DropdownMenu,
|
|
DropdownItem,
|
|
DropdownTrigger,
|
|
Chip,
|
|
} from "@nextui-org/react";
|
|
import {dataFocusVisibleClasses} from "@nextui-org/theme";
|
|
import {ChevronDownIcon, LinkIcon} from "@nextui-org/shared-icons";
|
|
import {isAppleDevice} from "@react-aria/utils";
|
|
import {clsx} from "@nextui-org/shared-utils";
|
|
import NextLink from "next/link";
|
|
import {usePathname} from "next/navigation";
|
|
import {includes} from "lodash";
|
|
import {motion, AnimatePresence} from "framer-motion";
|
|
import {useEffect} from "react";
|
|
import {usePress} from "@react-aria/interactions";
|
|
import {useFocusRing} from "@react-aria/focus";
|
|
|
|
import {currentVersion} from "@/utils/version";
|
|
import {siteConfig} from "@/config/site";
|
|
import {Route} from "@/libs/docs/page";
|
|
import {LargeLogo, SmallLogo, ThemeSwitch} from "@/components";
|
|
import {
|
|
TwitterIcon,
|
|
GithubIcon,
|
|
DiscordIcon,
|
|
HeartFilledIcon,
|
|
SearchLinearIcon,
|
|
} from "@/components/icons";
|
|
import {useIsMounted} from "@/hooks/use-is-mounted";
|
|
import {DocsSidebar} from "@/components/docs/sidebar";
|
|
import {useCmdkStore} from "@/components/cmdk";
|
|
import {trackEvent} from "@/utils/va";
|
|
|
|
export interface NavbarProps {
|
|
routes: Route[];
|
|
mobileRoutes?: Route[];
|
|
tag?: string;
|
|
slug?: string;
|
|
children?: ReactNode;
|
|
}
|
|
|
|
export const Navbar: FC<NavbarProps> = ({children, routes, mobileRoutes = [], slug, tag}) => {
|
|
const [isMenuOpen, setIsMenuOpen] = useState<boolean | undefined>(false);
|
|
const [commandKey, setCommandKey] = useState<"ctrl" | "command">("command");
|
|
|
|
const ref = useRef<HTMLElement>(null);
|
|
const isMounted = useIsMounted();
|
|
|
|
const pathname = usePathname();
|
|
|
|
const cmdkStore = useCmdkStore();
|
|
|
|
useEffect(() => {
|
|
if (isMenuOpen) {
|
|
setIsMenuOpen(false);
|
|
}
|
|
}, [pathname]);
|
|
|
|
useEffect(() => {
|
|
setCommandKey(isAppleDevice() ? "command" : "ctrl");
|
|
}, []);
|
|
|
|
const handleOpenCmdk = () => {
|
|
cmdkStore.onOpen();
|
|
trackEvent("Navbar - Search", {
|
|
name: "navbar - search",
|
|
action: "press",
|
|
category: "cmdk",
|
|
});
|
|
};
|
|
|
|
const {pressProps} = usePress({
|
|
onPress: handleOpenCmdk,
|
|
});
|
|
const {focusProps, isFocusVisible} = useFocusRing();
|
|
|
|
const docsPaths = [
|
|
"/docs/guide/introduction",
|
|
"/docs/guide/installation",
|
|
"/docs/guide/upgrade-to-v2",
|
|
];
|
|
|
|
const searchButton = (
|
|
<Button
|
|
aria-label="Quick search"
|
|
className="text-sm font-normal text-default-500 bg-default-400/20 dark:bg-default-500/20"
|
|
endContent={
|
|
<Kbd className="hidden py-0.5 px-2 lg:inline-block" keys={commandKey}>
|
|
K
|
|
</Kbd>
|
|
}
|
|
startContent={
|
|
<SearchLinearIcon
|
|
className="text-base text-default-400 pointer-events-none flex-shrink-0"
|
|
size={18}
|
|
strokeWidth={2}
|
|
/>
|
|
}
|
|
onPress={handleOpenCmdk}
|
|
>
|
|
Quick Search...
|
|
</Button>
|
|
);
|
|
|
|
if (pathname.includes("/examples")) {
|
|
return null;
|
|
}
|
|
|
|
const navLinkClasses = clsx(link({color: "foreground"}), "data-[active=true]:text-primary");
|
|
|
|
const handleVersionChange = (key: Key) => {
|
|
if (key === "v1") {
|
|
const newWindow = window.open("https://v1.nextui.org", "_blank", "noopener,noreferrer");
|
|
|
|
if (newWindow) newWindow.opener = null;
|
|
}
|
|
};
|
|
|
|
const handlePressNavbarItem = (name: string, url: string) => {
|
|
trackEvent("NavbarItem", {
|
|
name,
|
|
action: "press",
|
|
category: "navbar",
|
|
data: url,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<NextUINavbar
|
|
ref={ref}
|
|
className={clsx({
|
|
"z-[100001]": isMenuOpen,
|
|
})}
|
|
isMenuOpen={isMenuOpen}
|
|
maxWidth="xl"
|
|
position="sticky"
|
|
onMenuOpenChange={setIsMenuOpen}
|
|
>
|
|
<NavbarContent className="basis-1/5 sm:basis-full" justify="start">
|
|
<NavbarBrand as="li" className="gap-3 max-w-fit">
|
|
<NextLink
|
|
aria-label="Home"
|
|
className="flex justify-start items-center gap-2 tap-highlight-transparent transition-opacity active:opacity-50"
|
|
href="/"
|
|
onClick={() => handlePressNavbarItem("Home", "/")}
|
|
>
|
|
<SmallLogo className="w-6 h-6 md:hidden" />
|
|
<LargeLogo className="h-5 md:h-6" />
|
|
</NextLink>
|
|
{ref.current ? (
|
|
<Dropdown placement="bottom-start" portalContainer={ref.current}>
|
|
<AnimatePresence>
|
|
{isMounted && (
|
|
<motion.div animate={{opacity: 1}} exit={{opacity: 0}} initial={{opacity: 0}}>
|
|
<DropdownTrigger>
|
|
<Button
|
|
className="hidden text-xs h-6 w-[74px] py-1 min-w-fit sm:flex gap-0.5 bg-default-400/20 dark:bg-default-500/20"
|
|
endContent={<ChevronDownIcon className="text-tiny" />}
|
|
radius="full"
|
|
size="sm"
|
|
variant="flat"
|
|
>
|
|
v{currentVersion}
|
|
</Button>
|
|
</DropdownTrigger>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
<DropdownMenu
|
|
aria-label="NextUI versions"
|
|
defaultSelectedKeys={["v2"]}
|
|
selectionMode="single"
|
|
onAction={handleVersionChange}
|
|
>
|
|
<DropdownItem key="v2">v{currentVersion}</DropdownItem>
|
|
<DropdownItem key="v1" endContent={<LinkIcon />}>
|
|
v1.0.0
|
|
</DropdownItem>
|
|
</DropdownMenu>
|
|
</Dropdown>
|
|
) : (
|
|
<div className="w-[74px]" />
|
|
)}
|
|
</NavbarBrand>
|
|
<ul className="hidden lg:flex gap-4 justify-start items-center">
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={navLinkClasses}
|
|
color="foreground"
|
|
data-active={includes(docsPaths, pathname)}
|
|
href="/docs/guide/introduction"
|
|
onClick={() => handlePressNavbarItem("Docs", "/docs/guide/introduction")}
|
|
>
|
|
Docs
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={navLinkClasses}
|
|
color="foreground"
|
|
data-active={includes(pathname, "components")}
|
|
href="/docs/components/avatar"
|
|
onClick={() => handlePressNavbarItem("Components", "/docs/components/avatar")}
|
|
>
|
|
Components
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={navLinkClasses}
|
|
color="foreground"
|
|
data-active={includes(pathname, "blog")}
|
|
href="/blog"
|
|
onClick={() => handlePressNavbarItem("Blog", "/blog")}
|
|
>
|
|
Blog
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<NextLink
|
|
className={navLinkClasses}
|
|
color="foreground"
|
|
data-active={includes(pathname, "figma")}
|
|
href="/figma"
|
|
onClick={() => handlePressNavbarItem("Figma", "/figma")}
|
|
>
|
|
Figma
|
|
</NextLink>
|
|
</NavbarItem>
|
|
<NavbarItem>
|
|
<Chip
|
|
as={NextLink}
|
|
className="hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer"
|
|
color="secondary"
|
|
href="/blog/v2.1.0"
|
|
variant="dot"
|
|
onClick={() => handlePressNavbarItem("Introducing v2.2.0", "/blog/v2.2.0")}
|
|
>
|
|
Introducing v2.2.0
|
|
<span aria-label="rocket emoji" role="img">
|
|
🚀
|
|
</span>
|
|
</Chip>
|
|
</NavbarItem>
|
|
</ul>
|
|
</NavbarContent>
|
|
|
|
<NavbarContent className="flex w-full gap-2 sm:hidden" justify="end">
|
|
<NavbarItem className="flex h-full items-center">
|
|
<Link
|
|
isExternal
|
|
aria-label="Github"
|
|
className="p-1"
|
|
href="https://github.com/nextui-org/nextui"
|
|
onClick={() => handlePressNavbarItem("Github", "https://github.com/nextui-org/nextui")}
|
|
>
|
|
<GithubIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
</NavbarItem>
|
|
<NavbarItem className="flex h-full items-center">
|
|
<ThemeSwitch />
|
|
</NavbarItem>
|
|
<NavbarItem className="flex h-full items-center">
|
|
<button
|
|
className={clsx(
|
|
"transition-opacity p-1 hover:opacity-80 rounded-full cursor-pointer outline-none",
|
|
// focus ring
|
|
...dataFocusVisibleClasses,
|
|
)}
|
|
data-focus-visible={isFocusVisible}
|
|
{...focusProps}
|
|
{...pressProps}
|
|
>
|
|
<SearchLinearIcon className="mt-px text-default-600 dark:text-default-500" size={20} />
|
|
</button>
|
|
</NavbarItem>
|
|
<NavbarItem className="w-10 h-full">
|
|
<NavbarMenuToggle
|
|
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
|
className="w-full h-full pt-1"
|
|
/>
|
|
</NavbarItem>
|
|
</NavbarContent>
|
|
|
|
<NavbarContent className="hidden sm:flex basis-1/5 sm:basis-full" justify="end">
|
|
<NavbarItem className="hidden sm:flex">
|
|
<Link
|
|
isExternal
|
|
aria-label="Twitter"
|
|
className="p-1"
|
|
href={siteConfig.links.twitter}
|
|
onPress={() => handlePressNavbarItem("Twitter", siteConfig.links.twitter)}
|
|
>
|
|
<TwitterIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<Link
|
|
isExternal
|
|
aria-label="Discord"
|
|
className="p-1"
|
|
href={siteConfig.links.discord}
|
|
onPress={() => handlePressNavbarItem("Discord", siteConfig.links.discord)}
|
|
>
|
|
<DiscordIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<Link
|
|
isExternal
|
|
aria-label="Github"
|
|
className="p-1"
|
|
href={siteConfig.links.github}
|
|
onPress={() => handlePressNavbarItem("Github", siteConfig.links.github)}
|
|
>
|
|
<GithubIcon className="text-default-600 dark:text-default-500" />
|
|
</Link>
|
|
<ThemeSwitch />
|
|
</NavbarItem>
|
|
<NavbarItem className="hidden lg:flex">{searchButton}</NavbarItem>
|
|
<NavbarItem className="hidden md:flex">
|
|
<Button
|
|
isExternal
|
|
as={Link}
|
|
className="group text-sm font-normal text-default-600 bg-default-400/20 dark:bg-default-500/20"
|
|
href={siteConfig.links.sponsor}
|
|
startContent={
|
|
<HeartFilledIcon className="text-danger group-data-[hover=true]:animate-heartbeat" />
|
|
}
|
|
variant="flat"
|
|
onPress={() => handlePressNavbarItem("Sponsor", siteConfig.links.sponsor)}
|
|
>
|
|
Sponsor
|
|
</Button>
|
|
</NavbarItem>
|
|
<NavbarMenuToggle
|
|
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
|
|
className="hidden sm:flex lg:hidden ml-4"
|
|
/>
|
|
</NavbarContent>
|
|
|
|
<NavbarMenu>
|
|
<DocsSidebar className="mt-4" routes={[...mobileRoutes, ...routes]} slug={slug} tag={tag} />
|
|
{children}
|
|
</NavbarMenu>
|
|
</NextUINavbar>
|
|
);
|
|
};
|