feat(root): accordion animation improved, units introduced, button spacing migrated to units

This commit is contained in:
Junior Garcia 2023-07-06 20:28:09 -03:00
parent 5b5756bb33
commit 29f0d9a937
98 changed files with 425 additions and 514 deletions

View File

@ -7,15 +7,38 @@ export default function App() {
return (
<Accordion
motionProps={{
startingY: 0,
transition: {
variants: {
enter: {
height: { duration: 0.1 },
opacity: { duration: 0.3 },
y: 0,
opacity: 1,
height: "auto",
transition: {
height: {
type: "spring",
stiffness: 500,
damping: 30,
duration: 1,
},
opacity: {
easings: "ease",
duration: 1,
},
},
},
exit: {
height: { duration: 0.2 },
opacity: { duration: 0.3 },
y: -10,
opacity: 0,
height: 0,
transition: {
height: {
easings: "ease",
duration: 0.25,
},
opacity: {
easings: "ease",
duration: 0.3,
},
},
},
},
}}

View File

@ -35,7 +35,7 @@ export default function App() {
}}
>
<DropdownTrigger>
<Button variant="ghost">Open Menu</Button>
<Button variant="ghost" disableRipple>Open Menu</Button>
</DropdownTrigger>
<DropdownMenu
aria-label="Custom item styles"

View File

@ -3,7 +3,7 @@
"version": "2.0.0",
"private": true,
"scripts": {
"dev": "concurrently \"contentlayer dev\" \"next dev\"",
"dev": "rimraf .contentlayer && concurrently \"contentlayer dev\" \"next dev\"",
"build": "contentlayer build && next build",
"build:analyze": "ANALYZE=true next build",
"start": "next start",

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/accordion",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Collapse display a list of high-level options that can expand/collapse to reveal more information.",
"keywords": [
"react",

View File

@ -1,7 +1,8 @@
import {forwardRef} from "@nextui-org/system";
import {useMemo, ReactNode} from "react";
import {ChevronIcon} from "@nextui-org/shared-icons";
import {CollapseTransition} from "@nextui-org/framer-transitions";
import {AnimatePresence, motion} from "framer-motion";
import {TRANSITION_VARIANTS} from "@nextui-org/framer-transitions";
import {UseAccordionItemProps, useAccordionItem} from "./use-accordion-item";
@ -46,9 +47,20 @@ const AccordionItem = forwardRef<AccordionItemProps, "div">((props, ref) => {
}
return (
<CollapseTransition in={isOpen} {...motionProps}>
<div {...getContentProps()}>{children}</div>
</CollapseTransition>
<AnimatePresence initial={false}>
{isOpen && (
<motion.section
key="content"
animate="enter"
exit="exit"
initial="exit"
variants={TRANSITION_VARIANTS.collapse}
{...motionProps}
>
<div {...getContentProps()}>{children}</div>
</motion.section>
)}
</AnimatePresence>
);
}, [isOpen, disableAnimation, children, motionProps]);

View File

@ -3,11 +3,11 @@ import type {
AccordionItemSlots,
SlotsToClasses,
} from "@nextui-org/theme";
import type {CollapseTransitionProps} from "@nextui-org/framer-transitions";
import {ItemProps, BaseItem} from "@nextui-org/aria-utils";
import {FocusableProps, PressEvents} from "@react-types/shared";
import {ReactNode, MouseEventHandler} from "react";
import {HTMLMotionProps} from "framer-motion";
export type AccordionItemIndicatorProps = {
/**
@ -54,7 +54,7 @@ export interface Props<T extends object = {}>
/**
* The props to modify the framer motion animation. Use the `variants` API to create your own animation.
*/
motionProps?: CollapseTransitionProps;
motionProps?: HTMLMotionProps<"section">;
/**
* The native button click event handler.
* @deprecated - use `onPress` instead.

View File

@ -359,15 +359,38 @@ export const CustomMotion = Template.bind({});
CustomMotion.args = {
...defaultProps,
motionProps: {
startingY: 0,
transition: {
exit: {
height: {duration: 0.2},
opacity: {duration: 0.3},
},
variants: {
enter: {
height: {duration: 0.3},
opacity: {duration: 0.6},
y: 0,
opacity: 1,
height: "auto",
transition: {
height: {
type: "spring",
stiffness: 500,
damping: 30,
duration: 1,
},
opacity: {
easings: "ease",
duration: 1,
},
},
},
exit: {
y: -10,
opacity: 0,
height: 0,
transition: {
height: {
easings: "ease",
duration: 0.25,
},
opacity: {
easings: "ease",
duration: 0.3,
},
},
},
},
},

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/avatar",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The Avatar component is used to represent a user, and displays the profile picture, initials or fallback icon.",
"keywords": [
"avatar"

View File

@ -17,7 +17,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -18,7 +18,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "2xl", "3xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/badge",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Badges are used as a small numerical value or status descriptor for UI elements.",
"keywords": [
"badge"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/button",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Buttons allow users to perform actions and choose with a single tap.",
"keywords": [
"button"

View File

@ -1,9 +1,8 @@
import {Spinner} from "@nextui-org/spinner";
import {Ripple} from "@nextui-org/ripple";
import {forwardRef} from "@nextui-org/system";
import {lazy} from "react";
import {UseButtonProps, useButton} from "./use-button";
const Ripple = lazy(() => import("@nextui-org/ripple").then(({Ripple}) => ({default: Ripple})));
export interface ButtonProps extends Omit<UseButtonProps, "ref"> {}
@ -34,7 +33,7 @@ const Button = forwardRef<ButtonProps, "button">((props, ref) => {
{children}
{isLoading && spinnerPlacement === "end" && spinner}
{endContent}
{!disableRipple && <Ripple suspense ripples={ripples} />}
{!disableRipple && <Ripple ripples={ripples} />}
</Component>
);
});

View File

@ -139,6 +139,7 @@ export function useButton(props: UseButtonProps) {
} as AriaButtonProps,
domRef,
);
const {isHovered, hoverProps} = useHover({isDisabled});
const getButtonProps: PropGetter = useCallback(

View File

@ -20,9 +20,10 @@ export default {
options: ["default", "primary", "secondary", "success", "warning", "danger"],
},
},
isRounded: {
radius: {
control: {
type: "boolean",
type: "select",
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -41,7 +41,7 @@ export default {
radius: {
control: {
type: "select",
options: ["sm", "md", "lg", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
isDisabled: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/card",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Card is a container for text, photos, and actions in the context of a single subject.",
"keywords": [
"card"

View File

@ -1,11 +1,9 @@
import {forwardRef} from "@nextui-org/system";
import {lazy} from "react";
import {Ripple} from "@nextui-org/ripple";
import {CardProvider} from "./card-context";
import {useCard, UseCardProps} from "./use-card";
const Ripple = lazy(() => import("@nextui-org/ripple").then(({Ripple}) => ({default: Ripple})));
export interface CardProps extends Omit<UseCardProps, "ref"> {}
const Card = forwardRef<CardProps, "div">((props, ref) => {
@ -26,7 +24,7 @@ const Card = forwardRef<CardProps, "div">((props, ref) => {
return (
<Component {...getCardProps()}>
<CardProvider value={context}>{children}</CardProvider>
{isPressable && !disableAnimation && !disableRipple && <Ripple suspense ripples={ripples} />}
{isPressable && !disableAnimation && !disableRipple && <Ripple ripples={ripples} />}
</Component>
);
});

View File

@ -21,7 +21,7 @@ export default {
radius: {
control: {
type: "select",
options: ["sm", "md", "lg"],
options: ["none", "sm", "md", "lg"],
},
},
fullWidth: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/checkbox",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.",
"keywords": [
"checkbox"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/chip",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Chips help people enter information, make selections, filter content, or trigger actions.",
"keywords": [
"chip"

View File

@ -25,7 +25,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/code",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Code is a component used to display inline code.",
"keywords": [
"code"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/divider",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": ". A separator is a visual divider between two groups of content",
"keywords": [
"divider"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/dropdown",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A dropdown displays a list of actions or options that a user can choose.",
"keywords": [
"dropdown"

View File

@ -124,8 +124,7 @@ export function useDropdownItem<T extends object>(originalProps: UseDropdownItem
ref: domRef,
...mergeProps(
itemProps,
focusProps,
isReadOnly ? {} : pressProps,
isReadOnly ? {} : mergeProps(focusProps, pressProps),
hoverProps,
filterDOMProps(otherProps, {labelable: true}),
props,

View File

@ -103,7 +103,7 @@ export function useDropdown(props: UseDropdownProps) {
...classNamesProp,
...props.classNames,
base: clsx(classNames, classNamesProp?.base, props.className),
arrow: clsx("border border-default-100", classNamesProp?.arrow),
arrow: clsx(classNamesProp?.arrow),
},
});

View File

@ -41,7 +41,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
placement: {
@ -146,7 +146,7 @@ const DividerTemplate: ComponentStory<any> = ({
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="copy">Copy link</DropdownItem>
<DropdownItem key="edit">Edit file</DropdownItem>
<DropdownItem key="delete" showDivider className="text-danger" color="danger">
<DropdownItem key="delete" className="text-danger" color="danger">
Delete file
</DropdownItem>
</DropdownMenu>
@ -172,7 +172,7 @@ const DisabledKeysTemplate: ComponentStory<any> = ({
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="copy">Copy link</DropdownItem>
<DropdownItem key="edit">Edit file</DropdownItem>
<DropdownItem key="delete" showDivider className="text-danger" color="danger">
<DropdownItem key="delete" className="text-danger" color="danger">
Delete file
</DropdownItem>
</DropdownMenu>
@ -273,7 +273,7 @@ const WithShortcutTemplate: ComponentStory<any> = ({color, variant, ...args}) =>
<DropdownItem key="edit" shortcut="⌘⇧E">
Edit file
</DropdownItem>
<DropdownItem key="delete" showDivider className="text-danger" color="danger" shortcut="⌘⇧D">
<DropdownItem key="delete" className="text-danger" color="danger" shortcut="⌘⇧D">
Delete file
</DropdownItem>
</DropdownMenu>
@ -319,7 +319,6 @@ const WithStartContentTemplate: ComponentStory<any> = ({
</DropdownItem>
<DropdownItem
key="delete"
showDivider
className="text-danger"
color="danger"
shortcut="⌘⇧D"
@ -359,7 +358,6 @@ const WithEndContentTemplate: ComponentStory<any> = ({
</DropdownItem>
<DropdownItem
key="delete"
showDivider
className="text-danger"
color="danger"
endContent={<DeleteDocumentBulkIcon className={clsx(iconClasses, "!text-danger")} />}
@ -413,7 +411,6 @@ const WithDescriptionTemplate: ComponentStory<any> = ({
</DropdownItem>
<DropdownItem
key="delete"
showDivider
className="text-danger"
color="danger"
description="Permanently delete the file"
@ -506,19 +503,13 @@ const CustomTriggerTemplate: ComponentStory<any> = ({variant, ...args}) => {
<p className="font-semibold">Signed in as</p>
<p className="font-semibold">zoey@example.com</p>
</DropdownItem>
<DropdownItem key="settings" showDivider>
My Settings
</DropdownItem>
<DropdownItem key="settings">My Settings</DropdownItem>
<DropdownItem key="team_settings">Team Settings</DropdownItem>
<DropdownItem key="analytics" showDivider>
Analytics
</DropdownItem>
<DropdownItem key="analytics">Analytics</DropdownItem>
<DropdownItem key="system">System</DropdownItem>
<DropdownItem key="configurations">Configurations</DropdownItem>
<DropdownItem key="help_and_feedback" showDivider>
Help & Feedback
</DropdownItem>
<DropdownItem key="logout" showDivider color="danger">
<DropdownItem key="help_and_feedback">Help & Feedback</DropdownItem>
<DropdownItem key="logout" color="danger">
Log Out
</DropdownItem>
</DropdownMenu>
@ -543,19 +534,13 @@ const CustomTriggerTemplate: ComponentStory<any> = ({variant, ...args}) => {
<p className="font-bold">Signed in as</p>
<p className="font-bold">@tonyreichert</p>
</DropdownItem>
<DropdownItem key="settings" showDivider>
My Settings
</DropdownItem>
<DropdownItem key="settings">My Settings</DropdownItem>
<DropdownItem key="team_settings">Team Settings</DropdownItem>
<DropdownItem key="analytics" showDivider>
Analytics
</DropdownItem>
<DropdownItem key="analytics">Analytics</DropdownItem>
<DropdownItem key="system">System</DropdownItem>
<DropdownItem key="configurations">Configurations</DropdownItem>
<DropdownItem key="help_and_feedback" showDivider>
Help & Feedback
</DropdownItem>
<DropdownItem key="logout" showDivider color="danger">
<DropdownItem key="help_and_feedback">Help & Feedback</DropdownItem>
<DropdownItem key="logout" color="danger">
Log Out
</DropdownItem>
</DropdownMenu>

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/image",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A simple image component",
"keywords": [
"image"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/input",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The input component is designed for capturing user input within a text field.",
"keywords": [
"input"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/kbd",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The keyboard key components indicates which key or set of keys used to execute a specificv action",
"keywords": [
"kbd"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/link",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Links allow users to click their way from page to page. This component is styled to resemble a hyperlink and semantically renders an &lt;a&gt;",
"keywords": [
"link"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/modal",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Displays a dialog with a custom content that requires attention or provides additional information.",
"keywords": [
"modal"

View File

@ -33,7 +33,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl"],
options: ["none", "sm", "md", "lg"],
},
},
backdrop: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/navbar",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A responsive navigation header positioned on top side of your page that includes support for branding, links, navigation, collapse and more.",
"keywords": [
"navbar"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/pagination",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The Pagination component allows you to display active page and navigate between multiple pages.",
"keywords": [
"pagination"

View File

@ -1,6 +1,7 @@
import React from "react";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {button, pagination, cn} from "@nextui-org/theme";
import {button, pagination} from "@nextui-org/theme";
import {cn} from "@nextui-org/system";
import {ChevronIcon} from "@nextui-org/shared-icons";
import {
@ -45,7 +46,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/popover",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A popover is an overlay element positioned relative to a trigger.",
"keywords": [
"popover"

View File

@ -33,7 +33,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
placement: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/progress",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Progress bars show either determinate or indeterminate progress of an operation over time.",
"keywords": [
"progress"

View File

@ -17,7 +17,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/radio",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Radios allow users to select a single option from a list of mutually exclusive options.",
"keywords": [
"radio"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/ripple",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A simple implementation to display a ripple animation when the source component is clicked",
"keywords": [
"ripple"

View File

@ -1,4 +1,4 @@
import {FC, Suspense, Fragment} from "react";
import {FC} from "react";
import {AnimatePresence, HTMLMotionProps, motion} from "framer-motion";
import {HTMLNextUIProps} from "@nextui-org/system";
@ -7,7 +7,6 @@ import {RippleType} from "./use-ripple";
export interface RippleProps extends HTMLNextUIProps<"span"> {
ripples: RippleType[];
color?: string;
suspense?: boolean;
motionProps?: HTMLMotionProps<"span">;
style?: React.CSSProperties;
}
@ -16,46 +15,36 @@ const clamp = (value: number, min: number, max: number) => {
return Math.min(Math.max(value, min), max);
};
const Ripple: FC<RippleProps> = ({
ripples = [],
motionProps,
suspense = false,
color = "currentColor",
style,
}) => {
const Ripple: FC<RippleProps> = ({ripples = [], motionProps, color = "currentColor", style}) => {
return (
<>
{ripples.map((ripple) => {
const duration = clamp(0.01 * ripple.size, 0.2, ripple.size > 100 ? 0.75 : 0.5);
const Wrapper = suspense ? Suspense : Fragment;
return (
<Wrapper key={ripple.key}>
<AnimatePresence mode="popLayout">
<motion.span
animate={{transform: "scale(2)", opacity: 0}}
className="nextui-ripple"
exit={{opacity: 0}}
initial={{transform: "scale(0)", opacity: 0.35}}
style={{
position: "absolute",
backgroundColor: color,
borderRadius: "100%",
transformOrigin: "center",
pointerEvents: "none",
zIndex: 10,
top: ripple.y,
left: ripple.x,
width: `${ripple.size}px`,
height: `${ripple.size}px`,
...style,
}}
transition={{duration}}
{...motionProps}
/>
</AnimatePresence>
</Wrapper>
<AnimatePresence key={ripple.key} mode="popLayout">
<motion.span
animate={{transform: "scale(2)", opacity: 0}}
className="nextui-ripple"
exit={{opacity: 0}}
initial={{transform: "scale(0)", opacity: 0.35}}
style={{
position: "absolute",
backgroundColor: color,
borderRadius: "100%",
transformOrigin: "center",
pointerEvents: "none",
zIndex: 10,
top: ripple.y,
left: ripple.x,
width: `${ripple.size}px`,
height: `${ripple.size}px`,
...style,
}}
transition={{duration}}
{...motionProps}
/>
</AnimatePresence>
);
})}
</>

View File

@ -1,46 +0,0 @@
import React from "react";
import {ComponentStory, ComponentMeta} from "@storybook/react";
import {ripple} from "@nextui-org/theme";
import {Ripple, RippleProps} from "../src";
export default {
title: "Components/Ripple",
component: Ripple,
argTypes: {
color: {
control: {
type: "select",
options: ["neutral", "primary", "secondary", "success", "warning", "danger"],
},
},
radius: {
control: {
type: "select",
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {
control: {
type: "select",
options: ["sm", "md", "lg"],
},
},
isDisabled: {
control: {
type: "boolean",
},
},
},
} as ComponentMeta<typeof Ripple>;
const defaultProps = {
...ripple.defaultVariants,
};
const Template: ComponentStory<typeof Ripple> = (args: RippleProps) => <Ripple {...args} />;
export const Default = Template.bind({});
Default.args = {
...defaultProps,
};

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/skeleton",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Skeleton is used to display the loading state of some component.",
"keywords": [
"skeleton"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/snippet",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Display a snippet of copyable code for the command line.",
"keywords": [
"snippet"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/spacer",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A flexible spacer component designed to create consistent spacing and maintain alignment in your layout.",
"keywords": [
"spacer"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/spinner",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Loaders express an unspecified wait time or display the length of a process.",
"keywords": [
"loading",

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/switch",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A switch is similar to a checkbox, but represents on/off values as opposed to selection.",
"keywords": [
"switch"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/table",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Tables are used to display tabular data using rows and columns. ",
"keywords": [
"table"

View File

@ -32,13 +32,13 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
shadow: {
control: {
type: "select",
options: ["none", "sm", "md", "lg", "xl", "2xl", "inner"],
options: ["none", "sm", "md", "lg"],
},
},
selectionMode: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/tabs",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Tabs organize content into multiple sections and allow users to navigate between them.",
"keywords": [
"tabs"

View File

@ -35,7 +35,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
size: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/tooltip",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A React Component for rendering dynamically positioned Tooltips",
"keywords": [
"tooltip"

View File

@ -30,7 +30,7 @@ export default {
radius: {
control: {
type: "select",
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
options: ["none", "sm", "md", "lg", "full"],
},
},
placement: {

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/user",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Flexible User Profile Component.",
"keywords": [
"user"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/react",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "🚀 Beautiful and modern React UI library.",
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/system",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "NextUI system primitives",
"keywords": [
"system"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/theme",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The default theme for NextUI components",
"keywords": [
"theme",

View File

@ -65,7 +65,6 @@ const accordionItem = tv({
heading: "",
trigger: [
"flex py-4 w-full h-full gap-3 outline-none items-center tap-highlight-transparent",
"data-[pressed=true]:opacity-disabled",
// focus ring
...dataFocusVisibleClasses,
],

View File

@ -51,9 +51,9 @@ const button = tv({
ghost: "border-medium bg-transparent",
},
size: {
sm: "px-3 min-w-[4rem] h-8 text-small gap-2 rounded-small",
md: "px-4 min-w-[5rem] h-10 text-small gap-2 rounded-medium",
lg: "px-6 min-w-[6rem] h-12 text-medium gap-3 rounded-large",
sm: "px-unit-3 min-w-16 h-unit-8 text-tiny gap-unit-2 rounded-small",
md: "px-unit-4 min-w-20 h-unit-10 text-small gap-unit-2 rounded-medium",
lg: "px-unit-6 min-w-24 h-unit-12 text-medium gap-unit-3 rounded-large",
},
color: {
default: "",
@ -83,8 +83,8 @@ const button = tv({
true: "[&:not(:first-child):not(:last-child)]:rounded-none",
},
isIconOnly: {
true: "px-0 !gap-0",
false: "[&>svg]:max-w-[2em]",
true: "px-unit-0 !gap-unit-0",
false: "[&>svg]:max-w-[theme(spacing.unit-8]",
},
disableAnimation: {
true: "!transition-none",
@ -344,22 +344,22 @@ const button = tv({
{
isInGroup: true,
variant: ["bordered", "ghost"],
class: "[&:not(:first-child)]:ml-[calc(theme(borderWidth.2)*-1)]",
class: "[&:not(:first-child)]:ml-[calc(theme(borderWidth.medium)*-1)]",
},
{
isIconOnly: true,
size: "sm",
class: "min-w-8 w-8 h-8",
class: "min-w-unit-8 w-unit-8 h-unit-8",
},
{
isIconOnly: true,
size: "md",
class: "min-w-10 w-10 h-10",
class: "min-w-unit-10 w-unit-10 h-unit-10",
},
{
isIconOnly: true,
size: "lg",
class: "min-w-12 w-12 h-12",
class: "min-w-unit-12 w-unit-12 h-unit-12",
},
],
});

View File

@ -1,6 +1,7 @@
import {LayoutTheme} from "./types";
export const defaultLayout: LayoutTheme = {
spacingUnit: 4,
disabledOpacity: ".5",
dividerWeight: "1px",
fontSize: {

View File

@ -2,6 +2,7 @@ export * from "./components";
export * from "./utils";
export * from "./colors";
export * from "./plugin";
export * from "./types";
export {tv} from "tailwind-variants";
export type {VariantProps} from "tailwind-variants";

View File

@ -16,7 +16,7 @@ import {semanticColors, commonColors} from "./colors";
import {animations} from "./animations";
import {utilities} from "./utilities";
import {flattenThemeObject} from "./utils/object";
import {isBaseTheme} from "./utils/theme";
import {createSpacingUnits, generateSpacingScale, isBaseTheme} from "./utils/theme";
import {baseStyles} from "./utils/classes";
import {ConfigTheme, ConfigThemes, DefaultThemeType, NextUIPluginConfig} from "./types";
import {lightLayout, darkLayout, layouts, defaultLayout} from "./default-layout";
@ -118,6 +118,20 @@ const resolveConfig = (
forEach(value, (v, k) => {
const layoutVariable = `--${prefix}-${key}-${k}`;
resolved.utilities[cssSelector]![layoutVariable] = v;
});
} else if (key === "spacing-unit") {
const layoutVariable = `--${prefix}-${key}`;
// add the base unit "--spacing-unit: value"
resolved.utilities[cssSelector]![layoutVariable] = value;
const spacingScale = generateSpacingScale(Number(value));
// add the list of spacing units "--spacing-unit-[key]: value"
forEach(spacingScale, (v, k) => {
const layoutVariable = `--${prefix}-${key}-${k}`;
resolved.utilities[cssSelector]![layoutVariable] = v;
});
} else {
@ -169,20 +183,27 @@ const corePlugin = (
width: {
divider: `var(--${prefix}-divider-weight)`,
},
spacing: {
unit: `var(--${prefix}-spacing-unit)`,
...createSpacingUnits(prefix),
},
minWidth: {
1: "0.25rem",
2: "0.5rem",
3: "0.75rem",
"3.5": "0.875rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
8: "2rem",
9: "2.25rem",
10: "2.5rem",
11: "2.75rem",
12: "3rem",
"unit-1": `var(--${prefix}-spacing-unit)`,
"unit-2": `var(--${prefix}-spacing-unit-2`,
"unit-3": `var(--${prefix}-spacing-unit-3)`,
"unit-3.5": `var(--${prefix}-spacing-unit-3.5)`,
"unit-4": `var(--${prefix}-spacing-unit-4)`,
"unit-5": `var(--${prefix}-spacing-unit-5)`,
"unit-6": `var(--${prefix}-spacing-unit-6)`,
"unit-7": `var(--${prefix}-spacing-unit-7)`,
"unit-8": `var(--${prefix}-spacing-unit-8)`,
"unit-9": `var(--${prefix}-spacing-unit-9)`,
"unit-10": `var(--${prefix}-spacing-unit-10)`,
"unit-11": `var(--${prefix}-spacing-unit-11)`,
"unit-12": `var(--${prefix}-spacing-unit-12)`,
"unit-16": `var(--${prefix}-spacing-unit-16)`,
"unit-20": `var(--${prefix}-spacing-unit-20)`,
"unit-24": `var(--${prefix}-spacing-unit-24)`,
},
fontSize: {
tiny: [`var(--${prefix}-font-size-tiny)`, `var(--${prefix}-line-height-tiny)`],

View File

@ -12,7 +12,70 @@ export type FontThemeUnit = BaseThemeUnit & {
tiny?: string;
};
export const spacingScaleKeys = [
"0",
"xs",
"sm",
"md",
"lg",
"xl",
"2xl",
"3xl",
"4xl",
"5xl",
"6xl",
"7xl",
"8xl",
"9xl",
"1",
"2",
"3",
"3.5",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"20",
"24",
"28",
"32",
"36",
"40",
"44",
"48",
"52",
"56",
"60",
"64",
"72",
"80",
"96",
];
export const mappedSpacingScaleKeys = spacingScaleKeys.map((key) => `unit-${key}`);
export type SpacingScaleKeys = typeof spacingScaleKeys[number];
export type SpacingScale = Partial<Record<SpacingScaleKeys, string>>;
export interface LayoutTheme {
/**
* Base unit token that defines a consistent spacing scale across the components.
*
* @default 4 (px)
*/
spacingUnit?: number;
/**
* The default font size applied across the components.
*

View File

@ -1,3 +1,5 @@
import {spacingScaleKeys, SpacingScaleKeys, SpacingScale} from "../types";
/**
* Determines if the theme is a base theme
*
@ -5,3 +7,58 @@
* @returns "light" | "dark
*/
export const isBaseTheme = (theme: string) => theme === "light" || theme === "dark";
const baseScale = [1, 2, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const extendedScale = [20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96];
export const generateSpacingScale = (spacingUnit: number) => {
const scaleLabels: Partial<Record<SpacingScaleKeys, number>> = {
xs: 2,
sm: 3,
md: 4,
lg: 5.5,
xl: 9,
"2xl": 12,
"3xl": 20,
"4xl": 30,
"5xl": 56,
"6xl": 72,
"7xl": 96,
"8xl": 128,
"9xl": 160,
};
const scale = {0: "0px"} as SpacingScale;
Object.entries(scaleLabels).forEach(([label, multiplier]) => {
scale[label as SpacingScaleKeys] = multiplier
? `${spacingUnit * multiplier}px`
: `${spacingUnit}px`;
});
baseScale.forEach((i) => {
const key = `${i}` as SpacingScaleKeys;
scale[key] = `${spacingUnit * i}px`;
});
extendedScale.forEach((i) => {
const key = `${i}` as SpacingScaleKeys;
scale[key] = `${spacingUnit * i}px`;
});
return scale;
};
export function createSpacingUnits(prefix: string) {
let result = spacingScaleKeys.reduce(
(acc, key) => ({
...acc,
[`unit-${key}`]: `var(--${prefix}-spacing-unit-${key})`,
}),
{},
);
return result as Record<SpacingScaleKeys, string>;
}

View File

@ -1,5 +1,6 @@
import {tv as tvBase, TV} from "tailwind-variants";
import {mappedSpacingScaleKeys} from "../types";
const COMMON_UNITS = ["small", "medium", "large"];
export const tv: TV = (options, config) =>
@ -11,7 +12,7 @@ export const tv: TV = (options, config) =>
theme: {
...config?.twMergeConfig?.theme,
opacity: ["disabled"],
spacing: ["divider"],
spacing: ["divider", "unit", ...mappedSpacingScaleKeys],
borderWidth: COMMON_UNITS,
borderRadius: COMMON_UNITS,
},

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-aria-accordion-item",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Internal impl for react aria accordion item",
"keywords": [
"use-aria-accordion-item"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-aria-button",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Internal hook to handle button a11y and events, this is based on react-aria button hook but without the onClick warning",
"keywords": [
"use-aria-button"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-aria-label",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Based on react-aria label hook, it provides the accessibility implementation for labels and their associated elements. Labels provide context for user inputs.",
"keywords": [
"use-aria-label"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-aria-modal-overlay",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A custom implementation of react aria modal overlay, this removes the prevent scroll",
"keywords": [
"use-aria-modal-overlay"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-aria-toggle-button",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Internal hook to handle button a11y and events, this is based on react-aria button hook but without the onClick warning",
"keywords": [
"use-aria-toggle-button"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-callback-ref",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "React hook to persist any value between renders, but keeps it up-to-date if it changes.",
"keywords": [
"use-callback-ref"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-clipboard",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Wrapper around navigator.clipboard with feedback timeout",
"keywords": [
"use-clipboard"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-disclosure",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "The hook in charge of managing modals",
"keywords": [
"use-disclosure"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-image",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "React hook for progressing image loading",
"keywords": [
"use-image"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-infinite-scroll",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A hook for handling infinity scroll based on the IntersectionObserver API",
"keywords": [
"use-infinite-scroll"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-is-mobile",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A hook that returns whether the device is mobile or not",
"keywords": [
"use-is-mobile"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-is-mounted",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "This hook can be used to render client-based components or run client logic",
"keywords": [
"use-is-mounted"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-pagination",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "State management hook for Pagination component, it lets you manage pagination with controlled and uncontrolled state",
"keywords": [
"use-pagination"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-real-shape",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Hook that returns the real dimensions of an element",
"keywords": [
"use-real-shape"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-ref-state",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Hook for saving the state in a ref value",
"keywords": [
"use-ref-state"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-resize",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Hook that adds an event listener to the resize window event",
"keywords": [
"use-resize"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-safe-layout-effect",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "",
"keywords": [
"use-safe-layout-effect"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-scroll-position",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Provides the logic to control the scroll over an element",
"keywords": [
"use-scroll-position"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-ssr",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "",
"keywords": [
"use-ssr"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/use-update-effect",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "React effect hook that invokes only on update",
"keywords": [
"use-update-effect"

View File

@ -137,5 +137,5 @@ import {link, button} from "@nextui-org/theme";
<div class="block text-xs text-default-400">
Last updated on <time datetime="2023-03-07">Jul 04, 2023</time>
Last updated on <time datetime="2023-03-07">Jul 06, 2023</time>
</div>

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/aria-utils",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A package for managing @react-aria nextui utils.",
"keywords": [
"aria-utils"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/framer-transitions",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A set of framer motion transitions for react",
"keywords": [
"framer-transitions"

View File

@ -1,155 +0,0 @@
/**
* Part of this code is taken from @chakra-ui/system
*/
import {warn, isNumeric} from "@nextui-org/shared-utils";
import {AnimatePresence, HTMLMotionProps, motion, Variants as _Variants} from "framer-motion";
import {forwardRef, useEffect, useState} from "react";
import {TRANSITION_EASINGS, Variants, WithTransitionConfig, withDelay} from "./transition-utils";
export interface CollapseTransitionOptions {
/**
* If `true`, the opacity of the content will be animated
* @default true
*/
animateOpacity?: boolean;
/**
* The height you want the content in its collapsed state.
* @default 0
*/
startingHeight?: number;
/**
* The height you want the content in its expanded state.
* @default "auto"
*/
endingHeight?: number | string;
/**
* The y-axis offset you want the content in its collapsed state.
* @default 10
*/
startingY?: number;
/**
* The y-axis offset you want the content in its expanded state.
* @default 0
*/
endingY?: number;
}
const defaultTransitions = {
exit: {
height: {duration: 0.2, ease: TRANSITION_EASINGS.ease},
opacity: {duration: 0.3, ease: TRANSITION_EASINGS.ease},
},
enter: {
height: {duration: 0.3, ease: TRANSITION_EASINGS.ease},
opacity: {duration: 0.4, ease: TRANSITION_EASINGS.ease},
},
};
const variants: Variants<CollapseTransitionOptions> = {
enter: ({animateOpacity, endingHeight, transition, transitionEnd, delay}) => ({
...(animateOpacity && {opacity: 1}),
height: endingHeight,
transitionEnd: transitionEnd?.enter,
transition: transition?.enter ?? withDelay.enter(defaultTransitions.enter, delay),
}),
exit: ({animateOpacity, startingHeight, transition, transitionEnd, delay}) => ({
...(animateOpacity && {opacity: isNumeric(startingHeight) ? 1 : 0}),
height: startingHeight,
transitionEnd: transitionEnd?.exit,
transition: transition?.exit ?? withDelay.exit(defaultTransitions.exit, delay),
}),
};
export type ICollapseTransition = CollapseTransitionProps;
export interface CollapseTransitionProps
extends WithTransitionConfig<HTMLMotionProps<"div">>,
CollapseTransitionOptions {}
export const CollapseTransition = forwardRef<HTMLDivElement, CollapseTransitionProps>(
(props, ref) => {
const {
in: isOpen,
unmountOnExit,
animateOpacity = true,
startingHeight = 0,
endingHeight = "auto",
style,
className,
transition,
transitionEnd,
...rest
} = props;
const [mounted, setMounted] = useState(false);
useEffect(() => {
const timeout = setTimeout(() => {
setMounted(true);
});
return () => clearTimeout(timeout);
}, []);
/**
* Warn 🚨: `startingHeight` and `unmountOnExit` are mutually exclusive
*
* If you specify a starting height, the collapsed needs to be mounted
* for the height to take effect.
*/
if (Boolean(startingHeight > 0 && unmountOnExit)) {
warn(
`startingHeight and unmountOnExit are mutually exclusive. You can't use them together`,
"FramerTransitions - Collapse",
);
}
const hasStartingHeight = parseFloat(startingHeight.toString()) > 0;
const custom = {
startingHeight,
endingHeight,
animateOpacity,
transition: !mounted ? {enter: {duration: 0}} : transition,
transitionEnd: {
enter: transitionEnd?.enter,
exit: unmountOnExit
? transitionEnd?.exit
: {
...transitionEnd?.exit,
display: hasStartingHeight ? "block" : "none",
},
},
};
const show = unmountOnExit ? isOpen : true;
const animate = isOpen || unmountOnExit ? "enter" : "exit";
return (
<AnimatePresence custom={custom} initial={false}>
{show && (
<motion.div
ref={ref}
{...rest}
animate={animate}
className={className}
custom={custom}
exit="exit"
initial={unmountOnExit ? "exit" : false}
style={{
overflow: "hidden",
display: "block",
...style,
}}
variants={variants as _Variants}
/>
)}
</AnimatePresence>
);
},
);
CollapseTransition.displayName = "NextUI.CollapseTransition";

View File

@ -1,2 +1 @@
export * from "./collapse-transition";
export * from "./transition-utils";

View File

@ -1,9 +1,11 @@
/**
* Part of this code is taken from @chakra-ui/system
*/
import type {Target, TargetAndTransition, Transition} from "framer-motion";
type WithMotionState<P> = Partial<Record<"enter" | "exit", P>>;
export type TransitionConfig = WithMotionState<Transition>;
export type TransitionEndConfig = WithMotionState<Target>;
export type TransitionProperties = {
/**
* Custom `transition` definition for `enter` and `exit`
@ -13,29 +15,20 @@ export type TransitionProperties = {
* Custom `transitionEnd` definition for `enter` and `exit`
*/
transitionEnd?: TransitionEndConfig;
/**
* Custom `delay` definition for `enter` and `exit`
*/
delay?: number | DelayConfig;
};
type TargetResolver<P = {}> = (props: P & TransitionProperties) => TargetAndTransition;
type Variant<P = {}> = TargetAndTransition | TargetResolver<P>;
export type Variants<P = {}> = {
enter: Variant<P>;
exit: Variant<P>;
initial?: Variant<P>;
};
type WithMotionState<P> = Partial<Record<"enter" | "exit", P>>;
export type TransitionConfig = WithMotionState<Transition>;
export type TransitionEndConfig = WithMotionState<Target>;
export type DelayConfig = WithMotionState<number>;
export type Variants<P = {}> = Record<
string,
{
enter: Variant<P>;
exit: Variant<P>;
initial?: Variant<P>;
}
>;
export const TRANSITION_EASINGS = {
ease: [0.36, 0.66, 0.4, 1],
@ -47,7 +40,18 @@ export const TRANSITION_EASINGS = {
softSpring: [0.16, 1.11, 0.3, 1.02],
} as const;
export const TRANSITION_VARIANTS = {
export const TRANSITION_DEFAULTS = {
enter: {
duration: 0.2,
ease: TRANSITION_EASINGS.easeOut,
},
exit: {
duration: 0.1,
ease: TRANSITION_EASINGS.easeIn,
},
} as const;
export const TRANSITION_VARIANTS: Variants = {
scaleSpring: {
initial: {
opacity: 0,
@ -146,99 +150,34 @@ export const TRANSITION_VARIANTS = {
},
},
},
pushLeft: {
enter: {x: "100%"},
exit: {x: "-30%"},
},
pushRight: {
enter: {x: "-100%"},
exit: {x: "30%"},
},
pushUp: {
enter: {y: "100%"},
exit: {y: "-30%"},
},
pushDown: {
enter: {y: "-100%"},
exit: {y: "30%"},
},
slideLeft: {
position: {left: 0, top: 0, bottom: 0, width: "100%"},
enter: {x: 0, y: 0},
exit: {x: "-100%", y: 0},
},
slideRight: {
position: {right: 0, top: 0, bottom: 0, width: "100%"},
enter: {x: 0, y: 0},
exit: {x: "100%", y: 0},
},
slideUp: {
position: {top: 0, left: 0, right: 0, maxWidth: "100vw"},
enter: {x: 0, y: 0},
exit: {x: 0, y: "-100%"},
},
slideDown: {
position: {bottom: 0, left: 0, right: 0, maxWidth: "100vw"},
enter: {x: 0, y: 0},
exit: {x: 0, y: "100%"},
collapse: {
enter: {
opacity: 1,
height: "auto",
transition: {
height: {
ease: TRANSITION_EASINGS.ease,
duration: 0.25,
},
opacity: {
ease: TRANSITION_EASINGS.ease,
duration: 0.3,
},
},
},
exit: {
opacity: 0,
height: 0,
transition: {
height: {
ease: TRANSITION_EASINGS.ease,
duration: 0.3,
},
opacity: {
ease: TRANSITION_EASINGS.ease,
duration: 0.1,
},
},
},
},
};
export type SlideDirection = "top" | "left" | "bottom" | "right";
export function getSlideTransition(options?: {direction?: SlideDirection}) {
const side = options?.direction ?? "right";
switch (side) {
case "right":
return TRANSITION_VARIANTS.slideRight;
case "left":
return TRANSITION_VARIANTS.slideLeft;
case "bottom":
return TRANSITION_VARIANTS.slideDown;
case "top":
return TRANSITION_VARIANTS.slideUp;
default:
return TRANSITION_VARIANTS.slideRight;
}
}
export const TRANSITION_DEFAULTS = {
enter: {
duration: 0.2,
ease: TRANSITION_EASINGS.easeOut,
},
exit: {
duration: 0.1,
ease: TRANSITION_EASINGS.easeIn,
},
} as const;
export type WithTransitionConfig<P extends object> = Omit<P, "transition"> &
TransitionProperties & {
/**
* If `true`, the element will unmount when `in={false}` and animation is done
*/
unmountOnExit?: boolean;
/**
* Show the component; triggers when enter or exit states
*/
in?: boolean;
};
export const withDelay = {
enter: (
transition: Transition,
delay?: number | DelayConfig,
): Transition & {delay: number | undefined} => ({
...transition,
delay: typeof delay === "number" ? delay : delay?.["enter"],
}),
exit: (
transition: Transition,
delay?: number | DelayConfig,
): Transition & {delay: number | undefined} => ({
...transition,
delay: typeof delay === "number" ? delay : delay?.["exit"],
}),
};

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/react-utils",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A set of utilities for react on client side",
"keywords": [
"react-utils"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/shared-icons",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "Internal icons set, commonly used in the components stories",
"keywords": [
"icons-utils"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/shared-utils",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A set of NextUI utilities",
"keywords": [
"system"

View File

@ -1,6 +1,6 @@
{
"name": "@nextui-org/test-utils",
"version": "0.0.0-dev-v2-20230706030638",
"version": "0.0.0-dev-v2-20230706232536",
"description": "A set of utilities for react testing",
"keywords": [
"test-utils"